Chendd's Blog

世界上没有什么事情是一行代码不能解决的。如果有,那就两行。

Android学习日记13--数据存储之ContentProvide

3、ContentProvider

       数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。ContentProvider实现多应用程序间的数据共享类 一般利用ContentProvider为需要共享的数据定义一个URI(统一资源定位符) 然后其他程序通过Context获得ContentResolver并将数据的URI传入即可

       Android已为一些常用的数据创建ContentProvider,这些ContentProvider位于 android.provider包下,常用的就是手机上联系人信息,但是要取得相应的权限自己的应用程序才能访问 具体设置是在AndroidManifest.xml

1
<uses-permission android:name="android.permission.READ_CONTACTS" />

       对于ContentProvide重要的是数据模型和URI 数据模型:ContentProvide为所需要的数据创建表,每行代表一条记录,每条记录有唯一的'_ID'标识 URI:每个ContentProvide对外提供一个URI来标识自己的数据集

URI特点:

  • 1、无法改变的标准前缀,包括;"content://“、"tel://"等。当前缀是"content://"时,说明通过一个Content Provider控制这些数据
  • 2、URI的标识,它通过authorities属性声明,用于定义了是哪个ContentProvider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的类名(数据路径)。例如;“content://com.example.contentprovide.myprovider” 3、如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部

举个例子,如:

所有联系人的URI: content://contacts/people

某个联系人的URI: content://contacts/people/5

具体使用步骤:

1、在当前应用程序中定义一个ContentProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class MyProvider extends ContentProvider {

    @Override
    public int delete(Uri arg0, String arg1, String[] arg2) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getType(Uri arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri arg0, ContentValues arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    // 创建数据库,建表和插入数据
    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        SQLiteDatabase db =this.getContext().openOrCreateDatabase("mydb.db", Context.MODE_PRIVATE, null);
        db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
        ContentValues values =new ContentValues();
        values.put("name", "Hello ContentProvider!");
        db.insert("tab", "_id", values);
        db.close();
        return true;
    }

    // 查询
    @Override
    public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
            String arg4) {
        // TODO Auto-generated method stub
         SQLiteDatabase db =this.getContext().openOrCreateDatabase("mydb.db", Context.MODE_PRIVATE, null);
         Cursor c = db.query("tab", null, null, null, null, null,null);
         return c;
    }

    @Override
    public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
        // TODO Auto-generated method stub
        return 0;
    }

这里为了演示方便只实现创建和查询两个简单的方法

2、在当前应用程序的AndroidManifest.xml中注册此ContentProvider

1
<provider android:name=".MyProvider" android:authorities="com.example.contentprovider.MyProvider"/>

3、其他应用程序通过ContentResolver和Uri来获取此ContentProvider的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取当前context
        Context context = MainActivity.this;

        // 得到ContentResolver对象
        ContentResolver resolver = context.getContentResolver();

        // uri格式 "content://"、数据的路径、标示ID(可选)
        Uri uri = Uri.parse("content://com.example.contentprovider.MyProvider");
        Cursor c = resolver.query(uri, null, null, null, null);

        // 打印获取数据
        c.moveToFirst();
        for(int i=0; i<c.getCount(); i++){
        int index = c.getColumnIndexOrThrow("name");
                     String src = c.getString(index);
                     Log.d("", src);
                     c.moveToNext();
                 }
        }

日志打印:

image

  上面MyProvider代码和应用程序MainActivity代码不放在同一个包下是想说明ContentProvider不同程序间的数据共享,

但是注册获取权限那段代码要放在调用的程序包里。

  其实通过代码可以看出为了共享数据库.可以让数据库披上ContentProvider外衣,主要还是通过SQLiteDatabase去操作数据库。

当然对于已封装的共享数据。我们只要设置获取权限,通过ContentResolver就可以直接调用。

4、获取系统的联系人、媒体库信息

  对于系统程序的联系人、多媒体等信息可通过指定的Uri来获取。

  数据附录是获取本地联系人信息代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public String getContactInfo(){
        String result="";
        ContentResolver resolver=getContentResolver();
        //查询联系人
        Cursor cursor=resolver.query(Contacts.CONTENT_URI, null, null, null, null);
        int idIndex=cursor.getColumnIndex(Contacts._ID);
        // 取得联系人名字 (显示出来的名字),实际内容在 ContactsContract.Contacts中
        int nameIndex=cursor.getColumnIndex(Contacts.DISPLAY_NAME);
        for (cursor.moveToFirst();(!cursor.isAfterLast());cursor.moveToNext()) {
            //获取联系人ID
            String contactId =cursor.getString(idIndex);
            result=result+contactId+"\t\t\t";
            result=result+cursor.getString(nameIndex)+"\t\t\t";
             // 根据联系人ID查询对应的电话号码  
            Cursor phoneNumbers = resolver.query(CommonDataKinds.Phone.CONTENT_URI, null,
                    CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
            // 取得电话号码(可能存在多个号码)    
            while (phoneNumbers.moveToNext())
            {
                String strPhoneNumber = phoneNumbers.getString(phoneNumbers.getColumnIndex(CommonDataKinds.Phone.NUMBER));
                result=result+strPhoneNumber+"\t\t\t";
            }
            phoneNumbers.close();

            // 根据联系人ID查询对应的email  
            Cursor emails = resolver.query(CommonDataKinds.Email.CONTENT_URI, null,
                    CommonDataKinds.Email.CONTACT_ID + " = " + contactId, null, null);
            // 取得email(可能存在多个email)    
            while (emails.moveToNext())
            {
                String strEmail = emails.getString(emails.getColumnIndex(CommonDataKinds.Email.DATA));
                result=result+strEmail+"\t\t\t";
            }
            emails.close();
            result=result+"\n";
        }
        cursor.close();
        return result;
    }

5、监听ContentProvider的数据改变

  随着ContentProvider的共享数据可能发生改变,要提供给有使用该共享数据的相应,具体步骤如下:

  • 在ContentProvider类的insert\update\delete方法加入this.getContext().getContentResolver().notifyChange(URI,null); 其中URI表示监听的URI null表示发送消息给任何人;

  • 在访问者的类中调用如下方法: context.getContentResolver().registerContentObserver(uri,true,new ContentObserver(new Handler()));

  • 创建内部类继承ContentObserver并重写下面两个方法:
    • 带有Handler的构造函数;
    • public void onChange(boolean selfChange); 此函数在内容提供者发出改变信号时就会被调用 参数selfChange为如果是自己改变的原因,则为true;如果不是自己改变的,则为false;

Comments