1、Content Provider简介
Content Provider是Android中提供的一种专门用于不同应用之间进行数据共享的方式,从这一点来看,它天生就适合IPC(Inter-Process Communication,进程间通信)。Content Provider的底层实现是Binder,但它比AIDL要简单很多,因此系统已经给我们做了封装。
Android系统为我们预置了许多Content Provider,比如通讯录信息、日程表信息等,要想访问这些应用的信息,只需要调用ContentResolver的insert、update、delete、query四个方法即可。但是,我们往往不止需要这些应用的数据,因此,在很多情况下,我们需要创建自定义的Content Provider。
Content Provider主要是以表格的形式来组织数据的,并且可以包含多张表。除了表格的形式,Content Provider还支持文件格式,比如图片、视频等。
2、Content Provider实例
在这个例子中,我们要在客户端操作服务端的SQLite数据库,包括查询、删除、添加和修改的操作。
2.1、服务端
在服务端中,我们先创建一个SQLite数据库,名字叫做server.db,数据库中有一张数据表,名字叫做book表,book表中有两个字段:bookId表示书籍的编号,bookName表示书籍的名称。
我们创建一个MyDBOpenHelper类继承自SQLiteOpenHelper,来初始化数据库中的信息,代码如下:
package my.itgungnir.server; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * 操作数据库的SQLiteOpenHelper工具类 * Created by ITGungnir on 2017/4/6. */ public class MyDBOpenHelper extends SQLiteOpenHelper { private static final String DB_NAME = "server.db"; public static final String TABLE_NAME = "book"; private static final int DB_VERSION = 1; public MyDBOpenHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(bookId INTEGER PRIMARY KEY, bookName TEXT);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
有了数据库之后,我们就可以创建一个Content Provider,用来提供操作数据库的接口。
我们创建一个Content Provider类BookProvider,继承自ContentProvider类,并实现onCreate()、insert()、update()、delete()、query()和getType()这六个方法。其中,onCreate()方法是ContentProvider创建的时候调用的方法,可以在这个方法中做一些初始化的操作;getType()方法用来返回一个 Uri 请求所对应的MIME类型(媒体类型,比如图片、视频等),如果不关注这个选项,可以直接返回null或“*/*”;剩下的四个方法用于CRUD操作,即实现对数据表的增删改查功能。注意:这六个方法均运行在ContentProvider的进程中,除了onCreate()方法运行在主线程之外,其他五个方法都运行在Binder线程池中。
下面是我们创建的BookProvider类中的代码:
package my.itgungnir.server; 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.support.annotation.NonNull; import android.support.annotation.Nullable; /** * 服务端的ContentProvider * Created by ITGungnir on 2017/4/6. */ public class BookProvider extends ContentProvider { private static final String AUTHORITY = "my.itgungnir.server.book_provider"; private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book"); private static final int URI_CODE = 0x001; private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); static { URI_MATCHER.addURI(AUTHORITY, "book", URI_CODE); } private Context context; private SQLiteDatabase database; private String getTableName(Uri uri) { if (URI_MATCHER.match(uri) == URI_CODE) { return MyDBOpenHelper.TABLE_NAME; } return null; } private void initData() { database = new MyDBOpenHelper(context).getWritableDatabase(); database.execSQL("DELETE FROM " + MyDBOpenHelper.TABLE_NAME + ";"); database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(1, ‘Android‘);"); database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(2, ‘iOS‘);"); database.execSQL("INSERT INTO " + MyDBOpenHelper.TABLE_NAME + " VALUES(3, ‘HTML5‘);"); } @Override public boolean onCreate() { context = getContext(); initData(); return true; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException("Unsupported URI: " + uri); } return database.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null); } @Nullable @Override public String getType(@NonNull Uri uri) { String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException("Unsupported URI: " + uri); } return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException("Unsupported URI: " + uri); } database.insert(tableName, null, values); context.getContentResolver().notifyChange(uri, null); return uri; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException("Unsupported URI: " + uri); } int count = database.delete(tableName, selection, selectionArgs); if (count > 0) { context.getContentResolver().notifyChange(uri, null); } return count; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException("Unsupported URI: " + uri); } int count = database.update(tableName, values, selection, selectionArgs); if (count > 0) { context.getContentResolver().notifyChange(uri, null); } return count; } }
由于Content Provider属于Android四大组件,因此,需要在项目的Menifest文件中进行注册。在注册声明Content Provider的时候,需要提供一个name属性和一个authorities属性,name属性是要注册的Content Provider的包路径,而authorities属性是Content Provider的唯一标识。另外,如果服务端和客户端在不同的进程中,我们还需要指定Content Provider的exported属性为true,否则会报错:Permission Denied。下面是我们项目服务端Conttent Provider的注册代码:
<provider android:name=".BookProvider" android:authorities="my.itgungnir.server.book_provider" android:exported="true"> </provider>
以上就是服务端的代码,可以直接运行。