IPC机制之使用ContentProvider
我们知道在Android中,ipc机制的基础概念中主要包括了三个方面的内容:
- 1、Serializable
- 2、Parceable
- 3、Binder
对于上述中的三个内容再次就不再详述,今天我们要讲的是另一个我们经常用的东西但是很容易忽视它,我们经常操作的一些东西,比如说打电话、发短信、查看日程表信息……,或许你可能已经猜到了,没错,它就是我们常说的内容提供者(ContentProvider)
在接触Android之初,老师常常教我们四大组件(Activity、Service、Broadcast、ContentProvider),或许我们对activity并不陌生,比较看到的东西都可以称之为activity。好了,言归正传。
ContentProvider,如果你去看源码就会发现它的底层还是实现的是Binder,虽然它的底层是实现的Binder,但是它的使用过程比AIDL简单,这是因为系统已经为我们封装好了。
通过源码我们发现它有如下几个必定会实现的方法:
public abstract boolean onCreate();
public abstract Uri insert( Uri uri, ContentValues values);
public abstract int delete(Uri uri, String selection,String[] selectionArgs);
public abstract int update( Uri uri, @Nullable ContentValues values,String selection, String[] selectionArgs);
public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder, CancellationSignal cancellationSignal) {
return query(uri, projection, selection, selectionArgs, sortOrder);
}
public abstract Cursor query(Uri uri,String[] projection, String selection,String[] selectionArgs, String sortOrder)
public abstract String getType( Uri uri);
其实对于这6个抽象方法(虽然写了7个)都很好理解,除了onCreate和getType方法,其他的四个方法是不是和数据库中的CRUD一样呢,即数据库的增删改查功能。onCreate()代码ContentProvider的创建,getType()用于返回一个Uri请求所对应的MIME类型。
下面看一个简单的例子,它演示了ContentProvider的工作过程,我们创建一个类,它继承自ContentProvider并实现了上述中的6个必要抽象方法。
package com.example.userdefindcontentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
public class BookProvider extends ContentProvider {
private static final String TAG="BookProvider";
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
Log.d(TAG, "onCreate,current thread:"+Thread.currentThread().getName());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Log.d(TAG, "query,current thread:"+Thread.currentThread().getName());
return null;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
Log.d(TAG, "getType()");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
Log.d(TAG, "insert,current thread:"+Thread.currentThread().getName());
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
Log.d(TAG, "delete,current thread:"+Thread.currentThread().getName());
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
Log.d(TAG, "onCreate,current thread:"+Thread.currentThread().getName());
return 0;
}
}
当然为了我们能够使用它,也要和service一样进行注册才可以使用,这里建议加上包名,可以方便外界的访问。
在manifest文件中添加如下代码:
<provider android:name="com.example.userdefindcontentproviderdemo.BookProvider" android:authorities="com.example.userdefindcontentproviderdemo.provider"
android:permission="com.example.PROVIDER"
android:process=":progress" >
</provider>
注册了ContentProvider后,我们就可以在外部应用中访问它了
package com.example.userdefindcontentproviderdemo;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri uri=Uri.parse("content://com.example.userdefindcontentproviderdemo.provider");
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri, null, null, null, null);
}
}
上面代码通过getContentResolver对象的query方法去查询了BookProvider中的数据,其中用content://com.example.userdefindcontentproviderdemo.provider作为BookProvider的唯一标识。
到目前为止,整个ContentProvider流程已经跑通了。下面我们来完善BookProvider,让它能对外部提供数据。为了更好的展示数据信息,我们需要一个数据库用于管理图书和用户信息。
package com.example.userdefindcontentproviderdemo;
import android.R.integer;
import android.R.string;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper {
public static final String DB_NAME = "bookprovider.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
public static final int DB_VERSION = 1;
private String CREATE_BOOK_TABEL = "create table if not exists" + BOOK_TABLE_NAME + "(_id integer primary key," + "name text)";
private String CREATE_USER_TABEL = "create table if not exists"+ USER_TABLE_NAME + "(_id integer primary key," + "name text,"+ "sex int)";
public DBOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABEL);
db.execSQL(CREATE_USER_TABEL);
// TODO Auto-generated method stub
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
上面的代码就是一个最简单的数据库的简单实现,实现数据库的创建和更新,现在我们可以通过使用UriMatcher匹配想要访问的表。现在将BookProvider类修改如下:
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
private static final String AUTHORITIES = "com.example.userdefindcontentproviderdemo.provider";
private static final Uri BOOK_CONTENT_URI = Uri.parse("content://"
+ AUTHORITIES + "/book");
private static final Uri USER_CONTENT_URI = Uri.parse("content://"
+ AUTHORITIES + "/user");
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
private static final UriMatcher sURIM_MATCHER = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIM_MATCHER.addURI(AUTHORITIES, "book", BOOK_URI_CODE);
sURIM_MATCHER.addURI(AUTHORITIES, "user", USER_URI_CODE);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
Log.d(TAG, "onCreate,current thread:"
+ Thread.currentThread().getName());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Log.d(TAG, "query,current thread:" + Thread.currentThread().getName());
return null;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
Log.d(TAG, "getType()");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
Log.d(TAG, "insert,current thread:" + Thread.currentThread().getName());
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
Log.d(TAG, "delete,current thread:" + Thread.currentThread().getName());
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
Log.d(TAG, "onCreate,current thread:"
+ Thread.currentThread().getName());
return 0;
}
}
从上面这段代码中分别指定了book和user表的Uri,所关联的Uri_code分别是0和1,是通过下面的过程关联上来的:
static {
sURIM_MATCHER.addURI(AUTHORITIES, "book", BOOK_URI_CODE);
sURIM_MATCHER.addURI(AUTHORITIES, "user", USER_URI_CODE);
}
最后BookProvider的完整代码如下:
package com.example.userdefindcontentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
private static final String AUTHORITIES = "com.example.userdefindcontentproviderdemo.provider";
private static final Uri BOOK_CONTENT_URI = Uri.parse("content://"
+ AUTHORITIES + "/book");
private static final Uri USER_CONTENT_URI = Uri.parse("content://"
+ AUTHORITIES + "/user");
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
private static final UriMatcher sURIM_MATCHER = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIM_MATCHER.addURI(AUTHORITIES, "book", BOOK_URI_CODE);
sURIM_MATCHER.addURI(AUTHORITIES, "user", USER_URI_CODE);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
Log.d(TAG, "onCreate,current thread:"
+ Thread.currentThread().getName());
return false;
}
private Context mContext;
private SQLiteDatabase mDb;
private void initProviderData() {
mDb = new DBOpenHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from " + DBOpenHelper.BOOK_TABLE_NAME);
mDb.execSQL("delete from " + DBOpenHelper.USER_TABLE_NAME);
mDb.execSQL("insert into book values(3,'android');");
mDb.execSQL("insert into book values(4,'ios');");
mDb.execSQL("insert into book values(5,'html5');");
mDb.execSQL("insert into user values(1,'jack',1);");
mDb.execSQL("insert into book values(2,'tom'),2;");
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("UnSupported URI:" + uri);
}
return mDb.query(table, projection, selection, selectionArgs, null,
sortOrder, null);
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
Log.d(TAG, "getType()");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("UnSupported URI:" + uri);
}
mDb.insert(table, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("UnSupported URI:" + uri);
}
int count = mDb.delete(table, selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("UnSupported URI:" + uri);
}
int row = mDb.update(table, values, selection, selectionArgs);
if (row > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return row;
}
private String getTableName(Uri uri) {
String tableNameString = null;
switch (sURIM_MATCHER.match(uri)) {
case BOOK_URI_CODE:
tableNameString = DBOpenHelper.BOOK_TABLE_NAME;
case USER_URI_CODE:
tableNameString = DBOpenHelper.USER_TABLE_NAME;
break;
default:
break;
}
return tableNameString;
}
}
在此需要注意的是:query、insert、update、delete这四个方法是存在多线程并发访问的,因此在方法内部需要做好线程同步。
ok,现在我们在activity中调用它一下看看效果。
package com.example.userdefindcontentproviderdemo;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
private static final String TAG_STRING = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri bookUri = Uri
.parse("content://com.example.userdefindcontentproviderdemo.provider/book");
ContentValues values = new ContentValues();
values.put("_id", 6);
values.put("name", "开发者探索");
getContentResolver().insert(bookUri, values);
Cursor bookCursor = getContentResolver().query(bookUri,
new String[] { "_id", "name" }, null, null, null);
while (bookCursor.moveToNext()) {
Book book = new Book();
book.bookId = bookCursor.getInt(0);
book.bookName = bookCursor.getString(1);
Log.i(TAG_STRING, "query book:" + book.toString());
}
bookCursor.close();
Uri userUri = Uri
.parse("content://com.example.userdefindcontentproviderdemo.provider/user");
Cursor userCursor = getContentResolver().query(userUri,
new String[] { "_id", "name", "sex" }, null, null, null);
while (userCursor.moveToNext()) {
User user = new User();
user.userId = userCursor.getInt(0);
user.userName = userCursor.getString(1);
user.isMale = userCursor.getInt(2) == 1;
Log.i(TAG_STRING, user.toString());
}
userCursor.close();
}
}
到此为止我们的代码编写就完成了,当然这上面有俩个实体类很简单就没有贴代码了。
写到这里不知道你是否对ContentProvider有了进一步的了解了呢?希望能对你自己定义ContentProvider有一些的帮助。