Android 应用程序之间数据共享—ContentProvider

在Android中,我们的应用有的时候需要对外提供数据接口,可以有如下几种方法:1)AIDL 2)Broadcast 3)ContentProvider。

使用AIDL需要我们编写AIDL接口以及实现,而且对方也要有相应的接口描述,有点麻烦;使用Broadcast,我们不需要任何接口描述,只要协议文档就可以了,但是有点不好就是,这种方式不直接而且是异步的;使用ContentProvider我们不需要接口描述,只需要知道协议,同时这种方式是同步的,使用方便。

 Android提供了ContentProvider,一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content providers是以类似数据库中表的方式将数据暴露。Content providers存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。要想使应用程序的数据公开化,可通过2种方法:创建一个属于你自己的Content provider或者将你的数据添加到一个已经存在的Content provider中,前提是有相同数据类型并且有写入Content provider的权限。

如何通过一套标准及统一的接口获取其他应用程序暴露的数据?Android提供了ContentResolver,外界的程序可以通过ContentResolver接口访问ContentProvider提供的数据。

当前篇主要说明,如何获取其它应用程序共享的数据,比如获取Android 手机电话薄中的信息。

什么是URI?

在学习如何获取ContentResolver前,有个名词是必须了解的:URI。URI是网络资源的定义,在Android中赋予其更广阔的含义,先看个例子,如下:
URI
将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;
B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的   类名。这个标识在<provider> 元素的 authorities属性中说明:
<provider name=”.TransportationProvider”  authorities=”com.example.transportationprovider”  . . .  >
C:路径,Content Provider使用这些路径来确定当前需要生什么类型的数据,URI中可能不包括路径,也可能包括多个;
D:如果URI中包含,表示需要获取的记录的ID;如果没有ID,就表示返回全部;
由于URI通常比较长,而且有时候容易出错,切难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串,例如:People.CONTENT_URI

        在上面已经说明了Android是如何实现应用程序之间数据共享的,并详细解析了如何获取其他应用程序共享的数据。ContentProviders存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。那么如何将应用程序的数据暴露出去?

通过以前文章的学习,知道ContentResolver是通过ContentProvider来获取其他与应用程序共享的数据,那么ContentResolver与ContentProvider的接口应该差不多的。

其中ContentProvider负责

  • 组织应用程序的数据;
  • 向其他应用程序提供数据;

ContentResolver则负责

  • 获取ContentProvider提供的数据;
  • 修改/添加/删除更新数据等;

ContentProvider 是如何向外界提供数据的?

Android提供了ContentProvider,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。至于如何从URI中识别出外界需要的是哪个“数据库”,这就是Android底层需要做的事情了,不在此详细说。

 

下面的代码是ContentProvider示例程序。

 1. 先创建本地的数据库。

 Common类为公共数据。

[java] view plaincopy
  1. package com.demo.contentprovider;  
  2.   
  3. import android.net.Uri;  
  4.  public class Common {   
  5.     
  6.         public static final String DBNAME = "BageDb";    
  7.         public static final String TNAME = "friendsInfo";   
  8.         public static final int VERSION = 3;   
  9.             
  10.         public static String TID = "tid";   
  11.         public static final String USERNAME = "username";   
  12.         public static final String EMAIL = "email";  
  13.           
  14.         public static final String AUTOHORITY = "com.demo.contentprovider";   
  15.         public static final int ITEM = 1;   
  16.         public static final int ITEM_ID = 2;   
  17.             
  18.         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.bage.login";   
  19.         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.bage.login";   
  20.             
  21.         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/"+TNAME);   
  22. }   

创建本地数据库:

[java] view plaincopy
  1. package com.demo.contentprovider;  
  2.   
  3. import android.content.ContentValues;  
  4. import android.content.Context;  
  5. import android.database.sqlite.SQLiteDatabase;  
  6. import android.database.sqlite.SQLiteOpenHelper;  
  7. import android.database.sqlite.SQLiteDatabase.CursorFactory;  
  8.   
  9. public class DBLite extends SQLiteOpenHelper{  
  10.   
  11.       
  12.     public DBLite(Context context) {  
  13.         super(context, Common.TNAME, null, Common.VERSION);  
  14.     }  
  15.   
  16.     @Override  
  17.     public void onCreate(SQLiteDatabase db) {  
  18.           db.execSQL("create table "+Common.TNAME+"(" +   
  19.                   Common.TID+" integer primary key autoincrement not null,"+   
  20.                   Common.EMAIL+" text not null," +  
  21.                   Common.USERNAME+" text not null);");   
  22.   
  23.     }  
  24.   
  25.     @Override  
  26.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  27.           
  28.     }  
  29.       
  30.      public void add(String email,String username){   
  31.          SQLiteDatabase db = getWritableDatabase();   
  32.          ContentValues values = new ContentValues();   
  33.          values.put(Common.EMAIL, email);   
  34.          values.put(Common.USERNAME, username);   
  35.          db.insert(Common.TNAME,"",values);   
  36.  }   
  37.   
  38. }  



 

   2.SQLite示例程序添加ContentProvider

  实现ContentProvider这个抽象类的抽象方法,具体如下所示:

public boolean onCreate(),当ContentProvider生成的时候调用此方法。

public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) ,此方法返回一个Cursor 对象作为查询结果集。

public Uri insert(Uri uri, ContentValues initialValues),此方法负责往数据集当中插入一列,并返回这一列的Uri。

public int delete(Uri uri, String where, String[] whereArgs),此方法负责删除指定Uri的数据。

public int update(Uri uri, ContentValues values, String where,String[] whereArgs),此方法负责更新指定Uri的数据。

public String getType(Uri uri),返回所给Uri的MIME类型。

下面为该类的具体代码:

[java] view plaincopy
  1. package com.demo.contentprovider;  
  2.   
  3. import android.content.ContentProvider;  
  4. import android.content.ContentUris;  
  5. import android.content.ContentValues;  
  6. import android.content.UriMatcher;  
  7. import android.database.Cursor;  
  8. import android.database.sqlite.SQLiteDatabase;  
  9. import android.net.Uri;  
  10. import android.text.TextUtils;  
  11. import android.util.Log;  
  12. /**  
  13.  * ContentProvider是什么时候创建的,是谁创建的?访问某个应用程序共享的数据,是否需要启动这个应用程序?  
  14.  * 这个问题在 Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。   
  15.  * 这就要求在AndroidManifest.XML中使用元素明确定义。  
  16.  *  可通过2种方法:创建一个属于你自己的 ContentProvider或者将你的数据添加到一个已经存在的ContentProvider中,当然前提是有相同数据类型并且有写入 Content provider的权限。  
  17.  *   
  18.  * */    
  19.   
  20. public class MyContentProvider extends ContentProvider{  
  21.     private DBLite dBlite;   
  22.     private SQLiteDatabase db;   
  23.   
  24.     private static final UriMatcher sMatcher ;    
  25.     //注册需要匹配的Uri     
  26.       static{   
  27.           //实例化  
  28.           sMatcher = new UriMatcher(UriMatcher.NO_MATCH);   
  29.           //实例化后调用addURI方法注册URI,该方法有三个参数,分别需要传入URI字符串的authority部分、path部分以及自定义的整数code三者;  
  30.           sMatcher.addURI(Common.AUTOHORITY,Common.TNAME, Common.ITEM);   
  31.           sMatcher.addURI(Common.AUTOHORITY, Common.TNAME+"/#", Common.ITEM_ID);   
  32.   }   
  33.   
  34.     @Override  
  35.     public boolean onCreate() {  
  36.         // TODO Auto-generated method stub  
  37.         dBlite = new DBLite(this.getContext());  
  38.         return true;  
  39.     }  
  40.   
  41.     @Override  
  42.     public String getType(Uri uri) {  
  43.         // TODO Auto-generated method stub  
  44.          switch (sMatcher.match(uri)) {   
  45.          case Common.ITEM:   
  46.                  return Common.CONTENT_TYPE;   
  47.          case Common.ITEM_ID:   
  48.              return Common.CONTENT_ITEM_TYPE;   
  49.          default:   
  50.                  throw new IllegalArgumentException("Unknown URI"+uri);   
  51.          }   
  52.   
  53.     }  
  54.   
  55.     @Override  
  56.     public int delete(Uri uri, String selection, String[] selectionArgs) {  
  57.         db = dBlite.getWritableDatabase();   
  58.         int count = 0;   
  59.         switch (sMatcher.match(uri)) {   
  60.         case Common.ITEM:   
  61.                 count = db.delete(Common.TNAME,selection, selectionArgs);   
  62.                 break;   
  63.         case Common.ITEM_ID:   
  64.                 String id = uri.getPathSegments().get(1);   
  65.                 count = db.delete(Common.TID, Common.TID+"="+id+(!TextUtils.isEmpty(Common.TID="?")?"AND("+selection+')':""), selectionArgs);   
  66.             break;   
  67.         default:   
  68.                 throw new IllegalArgumentException("Unknown URI"+uri);   
  69.         }   
  70.         getContext().getContentResolver().notifyChange(uri, null);   
  71.         return count;   
  72. }   
  73.   
  74.   
  75.     @Override  
  76.     public Uri insert(Uri uri, ContentValues values) {  
  77.           db = dBlite.getWritableDatabase();   
  78.           long rowId;   
  79.           if(sMatcher.match(uri)!=Common.ITEM){   
  80.                   throw new IllegalArgumentException("Unknown URI"+uri);   
  81.           }   
  82.           rowId = db.insert(Common.TNAME,Common.TID,values);   
  83.              if(rowId>0){  
  84.                      Uri noteUri=ContentUris.withAppendedId(Common.CONTENT_URI, rowId);   
  85.                      getContext().getContentResolver().notifyChange(noteUri, null);   
  86.                      return noteUri;   
  87.              }   
  88.              throw new IllegalArgumentException("Unknown URI"+uri);   
  89.   }   
  90.   
  91.     @Override  
  92.     public Cursor query(Uri uri, String[] projection, String selection,  
  93.             String[] selectionArgs, String sortOrder) {  
  94.         db = dBlite.getWritableDatabase();                   
  95.         Cursor c;   
  96.         Log.d("-------", String.valueOf(sMatcher.match(uri)));   
  97.         switch (sMatcher.match(uri)) {   
  98.         case Common.ITEM:   
  99.                 c = db.query(Common.TNAME, projection, selection, selectionArgs, nullnullnull);   
  100.                 break;   
  101.         case Common.ITEM_ID:   
  102.                 String id = uri.getPathSegments().get(1);   
  103.                 c = db.query(Common.TNAME, projection, Common.TID+"="+id+(!TextUtils.isEmpty(selection)?"AND("+selection+')':""),selectionArgs, nullnull, sortOrder);   
  104.             break;   
  105.         default:   
  106.                 Log.d("!!!!!!""Unknown URI"+uri);   
  107.                 throw new IllegalArgumentException("Unknown URI"+uri);   
  108.         }   
  109.         c.setNotificationUri(getContext().getContentResolver(), uri);   
  110.         return c;   
  111. }   
  112.   
  113.     @Override  
  114.     public int update(Uri uri, ContentValues values, String selection,  
  115.             String[] selectionArgs) {  
  116.         // TODO Auto-generated method stub  
  117.         return 0;  
  118.     }  
  119.   
  120. }  

并且要牢记添加ContentProvider配置:
[html] view plaincopy
  1. <strong></strong><pre class="java" name="code">     <provider   
  2.          android:name=".MyContentProvider"   
  3.          android:authorities="com.demo.contentprovider"   
  4.          />   
  5. </pre>  
  6. <pre></pre>  
  7. <p><strong>   </strong></p>  
  8. <p>android:name要写ContentProvider继承类的全名。android:authorities要写和CONTENT_URI常量的B部分(见下图)。注意不要把上图C和D部分加到authorities中去。authorities是用来识别ContentProvider的,C和D部分实际上是ContentProvider内部使用的。<br>  
  9. </p>  
  10. <p class="mbn"><img id="aimg_7105" title="content_uri.png" alt="content_uri.png" src="http://www.eoeandroid.com/data/attachment/forum/month_1008/100814080202bd3f6c1ccc59fe.png" width="528"></p>  
  11. <p> </p>  
  12. <p><strong>3.测试SQLite示例程序的ContentProvider</strong></p>  
  13. <p>       ContentProvider即然是提供给其他应用访问本应用数据的,所以我们需要另创建一个Android应用,来测试SQLite示例程序的ContentProvider。</p>  
  14. <pre class="java" name="code">//查询代码  
  15.                 Cursor cursor = contentResolver.query(   
  16.                         Common.CONTENT_URI,null, null, null, null);   
  17.                 Log.i(TAG, "count:"+cursor.getCount()+"");  
  18.                 StringBuffer buffer = new StringBuffer();  
  19.                 while(cursor.moveToNext()){  
  20.                     buffer.append(cursor.getString(cursor.getColumnIndex(Common.EMAIL))+"/");  
  21.                     buffer.append(cursor.getString(cursor.getColumnIndex(Common.USERNAME)));  
  22.                     Log.i(TAG,buffer.toString());  
  23.                     buffer.delete(0, buffer.length());  
  24.                 }  
  25.                 startManagingCursor(cursor);  //查找后关闭游标 </pre>该程序的完整代码下载地址:<a href="http://download.csdn.net/source/3578495">http://download.csdn.net/source/3578495</a>  
  26. <p>-------------------------------------------------------------------------------------------------------------------------------------------------------------------</p>  
  27. <p><span style="color:#ff0000">补充:</span></p>  
  28. <p><span style="color:#000000">上面的那个ContentProvider,如果不设置权限,无论ContentResolver是否在一个程序里,都可以直接访问。</span></p>  
  29. <p>但是为了数据安全,我们一般都会给ContentProvider设置权限。<span style="color:#000000">如果是别的程序要访问我们的ContentProvider的话,需要修改一下配置,增加权限。</span></p>  

我们在provider中增加一个访问权限。代码如下:

[java] view plaincopy
  1. <provider   
  2.     android:name=".MyContentProvider"   
  3.     android:authorities="com.demo.contentprovider"   
  4.     android:permission="bage.see"  
  5.      android:multiprocess="true"/>   


 

  在另一个程序里,我们定义的读取ContentProvider的程序里的manifest文件增加权限,如下:

[html] view plaincopy
  1. <uses-permission android:name="bage.see"></uses-permission>  

这样,两个程序可以共享数据了。


ContentProvider是对各种数据源进行统一包装并外露的工具——不只是我们这里所用到的典型的数据库式的数据源,其他如文件等数据源也可以通过ContentProvider来包装并实现对其他有需求的消费者的数据供应。虽然对于初学者来说ContentProvider是较为难以理解的,但当你用熟后,你便定会感叹于Android系统的细节精妙性!

原文地址:点击打开链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值