iOS && AndroidAndroid开发android实用技术

Android Provider实战

2018-08-01  本文已影响3人  随行的羊

相信很多人都在网上查到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应用的数据的。

-------------------------------我是分割线-------------------------------

2018-08-01

上一篇下一篇

猜你喜欢

热点阅读