Android Provider实战
相信很多人都在网上查到Provider到底是个怎么回事。我会简单地介绍一下理论,但更多的是实战。
一、创建TestContract文件。
public class TestContract {
protected static final String CONTENT_AUTHORITY = "com.alipay.uniqueid";
protected static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
protected static final String PATH_TEST = "test";
protected static final Uri CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY + "/" + PATH_TEST);
public static final class ProviderEntry implements BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_TEST).build();
protected static Uri buildUri(long id) {
return ContentUris.withAppendedId(CONTENT_URI, id);
}
protected static final String TABLE_NAME = "test";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_VALUE = "value";
}
}
文件作用:搞一个类似Map结构的表,ProviderEntry制定了表名和字段。其中buildUri方法是返回一个用于表示新记录的URI。接下去要介绍的例子中,在新增完数据后会用到此方法。
二、创建TestDBHelper文件。
public class TestDBHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;//数据库版本号
private static final String DATABASE_NAME = "ttest.db";//共享数据的数据库名称
public TestDBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_CONTACT_TABLE = "CREATE TABLE " + TestContract.ProviderEntry.TABLE_NAME + "( "
+ TestContract.ProviderEntry._ID + " TEXT PRIMARY KEY, "
+ TestContract.ProviderEntry.COLUMN_NAME + " TEXT NOT NULL, "
+ TestContract.ProviderEntry.COLUMN_VALUE + " TEXT NOT NULL);";
db.execSQL(SQL_CREATE_CONTACT_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TestContract.ProviderEntry.TABLE_NAME);
onCreate(db);
}
}
文件作用:规定了数据库的版本号和名称,实现了创建表和升级表的方法。
三、创建TestProvider文件。
public class TestProvider extends ContentProvider {
private TestDBHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new TestDBHelper(getContext());
return true;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = null;
switch (buildUriMatcher().match(uri)) {
case TEST:
cursor = db.query(TestContract.ProviderEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
break;
}
return cursor;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Uri returnUri;
long _id;
switch (buildUriMatcher().match(uri)) {
case TEST:
{
Cursor cursor = query(TestContract.CONTENT_URI,new String[]{TestContract.ProviderEntry.COLUMN_NAME,TestContract.ProviderEntry.COLUMN_VALUE},TestContract.ProviderEntry.COLUMN_NAME+" =? ",
new String[]{(String)values.get(TestContract.ProviderEntry.COLUMN_NAME)},null);
if (cursor != null && cursor.moveToFirst()) {
ContentValues updateValues = new ContentValues();
updateValues.put(TestContract.ProviderEntry.COLUMN_VALUE, values.get(TestContract.ProviderEntry.COLUMN_VALUE) + "");
update(uri, updateValues, TestContract.ProviderEntry.COLUMN_NAME+" =? ", new String[]{(String)values.get(TestContract.ProviderEntry.COLUMN_NAME)});
cursor.close();
return null;
} else {
cursor.close();
_id = db.insert(TestContract.ProviderEntry.TABLE_NAME, null, values);
if ( _id > 0 ) {
returnUri = TestContract.ProviderEntry.buildUri(_id);
} else {
throw new android.database.SQLException("Failed to insert row into " + uri);
}
}
}
break;
default:
throw new android.database.SQLException("Unknown uri: " + uri);
}
return returnUri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int row = db.update(TestContract.ProviderEntry.TABLE_NAME, values, selection, selectionArgs);
return row;
}
private final static int TEST = 100;
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = TestContract.CONTENT_AUTHORITY;
matcher.addURI(authority, TestContract.PATH_TEST, TEST);
return matcher;
}
}
文件作用:buildUriMatcher方法用来区分不同的URI,增删改查的操作是根据URI来区分不同的表。这里实现了query、insert、update方法。而insert逻辑为:插入一个字段,如果这个字段已经存在,那么更新这个字段。
四、接下来在AndroidManifest.xml文件中加入:
<!-- 共享数据读取权限 -->
<permission android:name="com.test.READ" android:protectionLevel="normal"/>
<permission android:name="com.test.WRITE" android:protectionLevel="normal"/>
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${MY_APP_NAME}"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tool:replace="android:label">
<provider
android:authorities="com.alipay.uniqueid"
android:name=".common.provider.TestProvider"
android:readPermission="com.test.READ"
android:writePermission="com.test.WRITE"
android:exported="true">
</provider>
文件说明:增加权限相关信息,配置统一的authorities,并将exported设置为true。exported的作用是是否支持其它应用调用当前组件。
五、调用与设值。
ContentValues contentValues = new ContentValues();
contentValues.put(TestContract.ProviderEntry.COLUMN_NAME, "key");
contentValues.put(TestContract.ProviderEntry.COLUMN_VALUE, "value");
contentValues.put(TestContract.ProviderEntry._ID, System.currentTimeMillis());
BaseApplication.getAppContext().getContentResolver().insert(TestContract.ProviderEntry.CONTENT_URI, contentValues);
这样便大功告成了。
A应用已经存好数据了,那么B应用如何调用呢?
一、AndroidManifest.xml文件做如下修改:
<!-- 写外置存储的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
二、获取应用A中的数据:
private static final Uri CONTENT_URI = Uri.parse("content://com.alipay.uniqueid/test");
Cursor cursor = getContentResolver().query(CONTENT_URI, null, null, null, null);
cursor.moveToFirst();
TextView tv = (TextView)findViewById(R.id.content);
tv.setText(cursor.getString(1));
cursor.close();
这样便大功告成了。
1、不得不说通过URI的方式访问数据库是一种很巧妙的方式,有兴趣的人可以自己查查这个数据库存储在哪个位置。
2、Provider解耦了底层数据的存储方式,外界对数据的访问方式都是统一的。
3、当然,Provider的牛逼之处还是在于数据共享的通知机制。
4、要注意,如果A应用没有服务存活,即进程被杀死的时候,B应用是无法获取到A应用的数据的。
-------------------------------我是分割线-------------------------------