Android里内容提供者ContentProvider的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lyz_zyx/article/details/59483614

定义

在Android中,ContentProvider也是四大组件之一,它被翻译成内容提供者。如果在开发中需要实现跨包数据共享,那就需要使用ContentProvider,它存储数据的方式和使用它的应用程序无关,因为它为存储和获取数据提供统一的接口。Android附带了许多有用的ContentProvider,包括:

         browser                     存储诸如浏览器书签、浏览器历史记录等数据

         call_log                      存储诸如未接电话、通话详细信息等数据

         contacts                    存储联系人详细信息

         media                         存储媒体文件,如音频、视频和图像

         settings                     存储设备的设置和首选项

查询ContentProvider,可使用URI形式指定查询字符串:<standard_prefix>://<authority>/<data_path>/<id>

其中:

         standard_prefix:      ContentProvider里始终是content://

         authority:                   指定名称,Android内置的Contacts名称如上列表:browser、call_log、contacts,等。而自定义的,一般如:com.xxx.xxx

         data_path:                指定请求数据的类型。例如,如果是从Contacts获取所有联系人,那么datapath就是people,而URI为content://contacts/people。

         id:                              指定请求的特定记录,例如,从Contacts中查找10号联系人,那么URL为content:// contacts /people/10。

一些查询字符串的示例:

         content://media/internal/images 返回设备上所有内部图像的列表

         content://media/external/images 返回存储在外部的所有图像的列表

         content://call_log/calls 返回在CallLog中记录的所有通话的列表

         content://contacts/people                                          返回联系人列表

         content://browser/bookmarks 返回存储在浏览器中的书签列表

使用ContentProvider查询系统联系人

Uri callContacts =ContactsContract.Contacts.CONTENT_URI; // Uri callContacts = Uri.parse("content://contacts/people");
 
Cursor contactsCursor;
if (Build.VERSION.SDK_INT < 11) {
   contactsCursor = managedQuery(callContacts, null, null, null, null);
} else {
   CursorLoader cursorLoader = new CursorLoader(this, callContacts, null,null, null, null);
   contactsCursor = cursorLoader.loadInBackground();
}
 
if (contactsCursor.moveToFirst()) {
   do {
       String contactID =contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
       String contactDisplayName = contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
    }while (contactsCursor.moveToNext());
}
contactsCursor.close();

上例只是获取联系人ID和名称,但还没有获得电话号码,如果想获得联系人电话号码,由于这信息存储于另外一张表中,因此需要再次查询:

Uri callContacts =ContactsContract.Contacts.CONTENT_URI; // Uri callContacts = Uri.parse("content://contacts/people");
 
Cursor contactsCursor;
if (Build.VERSION.SDK_INT < 11) {
   contactsCursor = managedQuery(callContacts, null, null, null, null);
} else {
   CursorLoader cursorLoader = new CursorLoader(this, callContacts, null,null, null, null);
   contactsCursor = cursorLoader.loadInBackground();
}
 
if (contactsCursor.moveToFirst()) {
   do {
       String contactID =contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
       String contactDisplayName =contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
 
       int hasPhone =contactsCursor.getInt(contactsCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
       if (hasPhone == 1) {
           Cursor phoneCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,
                   ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " +contactID, null, null);
           while (phoneCursor.moveToNext()) {
                String contactPhoneNumber =phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
           }
           phoneCursor.close();
       }
    }while (contactsCursor.moveToNext());
}
contactsCursor.close();

别忘了在AndroidManifest.xml中加入 <uses-permissionandroid:name="android.permission.READ_CONTACTS" /> 权限。

上面使用了Android中的一个预定义常量ContactsContract.Contacts.CONTENT_URI 来代替Uri.parse(“content://contacts/people”)

如果只想获取第一个联系人,可以这样:

Uri allContacts =ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, 1);
// 或者
Uri allContacts =Uri.parse(“content://contacts/people/1”);

投影projection

managedQuery方法的第二个参数和CursorLoader类的第三个参数是接收一个叫projection的字符串数组,我们叫它作投影,它是控制查询返回的列,即像SQL里SELECT后接着写的字段,如果此参数为null,则表示返回所有的列,也就像SQL里的SELECT * FROM XXX。我们来修改上例,加入一个projection的字符串数组:

String[] projection = new String[] {
           ContactsContract.Contacts._ID,
           ContactsContract.Contacts.DISPLAY_NAME,
           ContactsContract.Contacts.HAS_PHONE_NUMBER
   };       
 
   Uri callContacts = ContactsContract.Contacts.CONTENT_URI;  // Uri callContacts =Uri.parse("content://contacts/people");      
 
   Cursor contactsCursor;
    if(Build.VERSION.SDK_INT < 11) {
       contactsCursor = managedQuery(callContacts, projection, null, null,null);
    }else {
       CursorLoader cursorLoader = new CursorLoader(this, callContacts,projection, null, null, null);
       contactsCursor =cursorLoader.loadInBackground();
}
……

筛选selection

managedQuery方法的第三、第四个参数和CursorLoader类的第四和第五个参数,这两个参数分别接收一个字符串和一个字符串数组,它们是用来指定查询结果进行筛选的,即像SQL里WHERE。我们继续修改上例,加入相关selection参数:

……
Cursor contactsCursor;
   if (Build.VERSION.SDK_INT < 11) {
       // contactsCursor = managedQuery(callContacts, projection,ContactsContract.Contacts.DISPLAY_NAME + " LIKE '%云心'",null, null);
   // 或
       contactsCursor = managedQuery(callContacts, projection,ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?", new String[] {"%云心" }, null);
    }else {
       // CursorLoader cursorLoader = new CursorLoader(this, callContacts,projection, ContactsContract.Contacts.DISPLAY_NAME + " LIKE '%云心'",null, null);
   // 或
       CursorLoader cursorLoader = newCursorLoader(this, callContacts, projection,ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?", new String[] {"%云心" }, null);
       contactsCursor = cursorLoader.loadInBackground();
    }
……

排序sortOrder

managedQuery方法和CursorLoader类的最后一个参数可以用来指定一个像SQL里ORDER BY 子句来对查询结果排序,如:

……
Cursor contactsCursor;
   if (Build.VERSION.SDK_INT < 11) {
       contactsCursor = managedQuery(callContacts, projection,ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?", new String[] {"%云心" }, ContactsContract.Contacts.DISPLAY_NAME + "ASC");
    }else {
       CursorLoader cursorLoader = new CursorLoader(this, callContacts,projection, ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?", newString[] { "%云心" }, ContactsContract.Contacts.DISPLAY_NAME + "ASC");
       contactsCursor = cursorLoader.loadInBackground();
    }
……

创建自己的ContentProvider

除了一些内置的ContentProvider以外,还可以创建自己的ContentProvider。在Android中创建自己的ContentProvider非常简单,主要是扩展抽象类ContentProvider,并重写其中定义的各种方法即可。示例:

新建数据库帮助类:DbOpenHelper,使其继承自SQLiteOpenHelper:

public class DbOpenHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "persion_database";
    public static final String TABLE_NAME = "persion_table";

    private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT NOT NULL)";
    private static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;

    public DbOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(DROP_TABLE);
        onCreate(db);
    }
}

再新建PersonProvider类并让其继承ContentProvider和重写必要方法:

public class PersonProvider extends ContentProvider {

    private DbOpenHelper mDbOpenHelper;

    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    public static final String AUTHORITY = "com.zyx.PersonProvider";
    private final static int ITEM = 1;
    private final static int ID = 2;

    static {
        mUriMatcher.addURI(AUTHORITY, "person", ITEM);
        mUriMatcher.addURI(AUTHORITY, "person/#", ID);
    }

    @Override
    public boolean onCreate() {
        mDbOpenHelper = new DbOpenHelper(this.getContext());
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
//            case DIR:
//                return "vnd.android.cursor.dir/person";
            case ID:
            case ITEM:
                return "vnd.android.cursor.item/person";
            default:
                throw new IllegalArgumentException("this is unkown uri:" + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = mDbOpenHelper.getWritableDatabase();
        switch (mUriMatcher.match(uri)) {
            case ITEM:
                long id = db.insert(DbOpenHelper.TABLE_NAME, "name", values);
                Uri insertUri = ContentUris.withAppendedId(uri, id);
                return insertUri;
            default:
                throw new IllegalArgumentException("this is unkown uri:" + uri);
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mDbOpenHelper.getWritableDatabase();
        switch (mUriMatcher.match(uri)) {
            case ITEM: {
                int number = db.delete(DbOpenHelper.TABLE_NAME, selection, selectionArgs);
                return number;
            }
            case ID: {
                long id = ContentUris.parseId(uri); // 取得跟在URI后面的数字
                // 或者使用 String idVal = uri.getPathSegments().get(1);
                int number = db.delete(DbOpenHelper.TABLE_NAME, "_id = ?", new String[]{String.valueOf(id)});
                return number;
            }
            default: {
                throw new IllegalArgumentException("this is unkown uri:" + uri);
            }
        }
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = mDbOpenHelper.getReadableDatabase();
        switch (mUriMatcher.match(uri)) {
            case ITEM: {
                Cursor cursor = db.query(DbOpenHelper.TABLE_NAME, projection, selection, selectionArgs,
                        null, null, sortOrder);
                return cursor;
            }
            case ID: {
                long id = ContentUris.parseId(uri);
                Cursor cursor = db.query(DbOpenHelper.TABLE_NAME, projection, "_id = ?", new String[]{String.valueOf(id)},
                        null, null, sortOrder);
                return cursor;
            }
            default:
                throw new IllegalArgumentException("this is unkown uri:" + uri);
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mDbOpenHelper.getWritableDatabase();
        switch (mUriMatcher.match(uri)) {
            case ITEM: {
                int number = db.update(DbOpenHelper.TABLE_NAME, values, selection, selectionArgs);
                return number;
            }
            case ID: {
                long id = ContentUris.parseId(uri);
                int number = db.update(DbOpenHelper.TABLE_NAME, values, "_id = ?", new String[]{String.valueOf(id)});
                return number;
            }
            default: {
                throw new IllegalArgumentException("this is unkown uri:" + uri);
            }
        }
    }
}


这类中重写的各个方法如下所示:

getType()          根据给定的Uri返回一个MIME类型的数据,如果是单条数据,那么我们的MIME类型应该以vnd.android.cursor.item开头,如果是多条数据,我们的MIME类型的数据应该以vnd.android.cursor.dir开头,同时,对于没有访问该ContentProvider权限的应用依然可以调用它的getType方法

onCreate()       当启动ContentProvider时调用

query()              接收客户端请求,结果以Cursor对象形式返回

insert()              向ContentProvider中插入一条新记录

delete()             向ContentProvider中删除一条现有的记录

update()            向ContentProvider中更新一条现有的记录

ContentProvider中可以自由选择如何存储数据,可以使有文件系统、XML、数据库或通过Web服务等,上例中使用了SQLite数据库方法,类DbOpenHelper.class便就是为其服务。UriMatcher对象mUriMatcher定义了两种访问ContentProvider的类型,分别是:单项ID和单项ITEM,如果想实现所有项的处理,可以再行定义DIR,这里就不再列举了。

接着修改AndroidManifest.xml,在application中添加provider项:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    ……
    <provider
            android:name=".PersonProvider"
            android:authorities="com.zyx.PersonProvider"
            android:enabled="true"
            android:exported="true"/>
    
 </application>


刚才新建的PersonProvider,可以在本程序使用也可以新建另一个程序来跨程序使用,增删改查的使用如下:

// 插入数据
ContentValues values = new ContentValues();
values.put("name", "子云心");
Uri uri = getContentResolver().insert(Uri.parse("content://" + PersonProvider.AUTHORITY + "/person"), values);
// 删除数据
int delResult =getContentResolver().delete(Uri.parse("content://"+PersonProvider.AUTHORITY+"/person/1"),null, null);
//int delResult =getContentResolver().delete(Uri.parse("content://"+PersonProvider.AUTHORITY+"/person"),"_id = ?", new String[]{"1"});
// 查询数据
Cursor cursor = getContentResolver().query(Uri.parse("content://"+PersonProvider.AUTHORITY+"/person/1"), null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
    String id = cursor.getString(cursor.getColumnIndex("_id"));
    String name = cursor.getString(cursor.getColumnIndex("name"));
}
cursor.close();
// 更新数据
ContentValues contentValues2 = new ContentValues();
contentValues2.put("name", "子云心心");
int updateResult = getContentResolver().update(Uri.parse("content://"+PersonProvider.AUTHORITY+"/person/1"),contentValues2, null, null);

猜你喜欢

转载自blog.csdn.net/lyz_zyx/article/details/59483614