Android 自定义 ContentProvider
前几章节我们用的都是 Android 系统提供的 ContentProvider
,其实,我们还可以自定义 ContentProvider
一般情况下,很少需要自己来定义 ContentProvider
,因为很多时候我们并不希望自己应用的数据暴露给其它应用
但即使这样,学习如何 ContentProvider 还是有必要的,多一种数据传输的方式
自定义 ContentProvider
自定义 ContentProvider
的流程一般如下
-
在分享数据的 APP 中创建一个类,继承
ContentProvider
-
按需实现对应的方法,不需要的直接空实现
方法 说明 onCreate() 只执行一次,用于初始化 Provider
insert() 插入 delete() 删除 update() 更新 query() 查询 getType() 获得 ContentProvider
数据的MIME
类型 -
在
AndroidManifest.xml
中注册自定义的ContentProvider
<provider <!-- 全限定类名 --> android:name = "cn.twle.android.bean.NameContentProvider" <!-- 用于匹配的 URI --> android:authorities = "cn.twle.android.providers.msprovider" <!-- 是否共享数据 --> android:exported="true"> </provider>
-
使用
UriMatcher
完成Uri
的匹配-
初始化
UriMatcher
对象private static UriMatcher matcher = new UriMatcher (UriMatcher.NO_MATCH);
-
使用静态代码块,通过
addURI()
方法将uri
添加到matcher
中static { matcher.addURI("cn.twle.android.providers.msprovider","test","1"); }
前两个参数构成
URI
, 第三个参数:匹配后返回的标识码,如果不匹配返回-1
-
在下面需要匹配
Uri
的地方使用match()
方法switch( matcher.match(uri)) { case 1: break; case 2: break; default: break; }
当然还可以使用通配符,比如
test/*
或test/#
*
代表所有字符,#
代表数字
-
-
使用
ContentUris
类为Uri
追加id
, 或者解析Uri
中的id
-
withAppendedId(uri,id)
为路径添加id
部分Uri nameUri = ContentUris.withAppendedId(uri,rowId);
-
parseId(uri)
解析uri
中的id
值long nameId = ContentUris.parseId(uri);
-
-
然后在另一个工程中,调用
getContentResolver()
方法获得Resolver
对象,再调用相应的操作方法,比如插入操作ContentValues values = new ContentValues(); values.put("name","测试"); Uri uri = Uri.parse("cn.twle.android.providers.msprovider/test"); resolver.insert(uri,values);
范例
-
创建一个 空的 Android 项目
cn.twle.android.CustomProvider
-
在
MainActivity.java
同一目录下添加一个数据库创建类DBOpenHelper.java
package cn.twle.android.customprovider; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DBOpenHelper extends SQLiteOpenHelper { final String CREATE_SQL = "CREATE TABLE test(_id INTEGER PRIMARY KEY AUTOINCREMENT,name)"; public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_SQL); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
-
在
MainActivity.java
同一目录下添加一个自定义ContentProvider
类,实现onCreate()
,getType()
NameContentProvider.java
package cn.twle.android.customprovider; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class NameContentProvider extends ContentProvider { //初始化一些常量 private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private DBOpenHelper dbOpenHelper; //为了方便直接使用UriMatcher,这里addURI,下面再调用Matcher进行匹配 static{ matcher.addURI("cn.twle.android.providers.msprovider", "test", 1); } @Override public boolean onCreate() { dbOpenHelper = new DBOpenHelper(this.getContext(), "test.db", null, 1); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { switch(matcher.match(uri)) { //把数据库打开放到里面是想证明uri匹配完成 case 1: SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); long rowId = db.insert("test", null, values); if(rowId > 0) { //在前面已有的Uri后面追加ID Uri nameUri = ContentUris.withAppendedId(uri, rowId); //通知数据已经发生改变 getContext().getContentResolver().notifyChange(nameUri, null); return nameUri; } } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
-
修改
AndroidManifest.xml
中为ContentProvider
进行注册<provider android:name="cn.twle.android.customprovider.NameContentProvider" android:authorities="cn.twle.android.providers.msprovider" android:exported="true" />
-
修改
activity_main.xml
这里我们就不创建新项目了,直接用一个 App 完成所有的动作
<?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" > <Button android:text="插入数据" android:id="@+id/btn_insert" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
-
修改
MainActivity.java
实现 ContentResolver 的部分,点击按钮插入一条数据这里我们就不创建新项目了,直接用一个 App 完成所有的动作
package cn.twle.android.customprovider; import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private Button btn_insert; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_insert = (Button) findViewById(R.id.btn_insert); //读取contentprovider 数据 final ContentResolver resolver = this.getContentResolver(); btn_insert.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentValues values = new ContentValues(); values.put("name", "测试"); Uri uri = Uri.parse("content://cn.twle.android.providers.msprovider/test"); resolver.insert(uri, values); Toast.makeText(getApplicationContext(), "数据插入成功", Toast.LENGTH_SHORT).show(); } }); } }
运行 APP ,点击插入数据,然后打开 file exploer 将 ContentProvider 的 db 数据库取出,用图形查看工具查看即可发现插入数据