Android Provider实战

作者: 随行的羊 | 来源:发表于2018-08-01 23:44 被阅读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

    相关文章

      网友评论

        本文标题:Android Provider实战

        本文链接:https://www.haomeiwen.com/subject/iljdvftx.html