Android ContentProvider的使用

在工作中用到了ContentProvider,以前用得不多,既然这次用到了那就记录下。

使用情况:A项目首次进入添加基础数据,B项目点击按钮后获取A项目的数据,B项目修改后保存数据,并通知A项目,A项目收到通知后更新UI

1、在A项目中创建自定义ContentProvider的配置文件Config

java 复制代码
public class HomeCommonAppConfig {

    public static final String AUTHORITY = "包名.provider";
    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/表名");



    public static final String _ID = "_id";
    public static final String COLUMN_PKG_NAME = "pkgName";
    public static final String COLUMN_ID = "id";
    public static final String COLUMN_APP_NAME = "appName";
    public static final String COLUMN_APK = "apk";
    public static final String COLUMN_HOME_MODEL = "homeModel";
}

其中AUTHORITY 是授权标识对应的AndroidManifest.xml中添加<provider>标签的android:authorities属性;

BASE_CONTENT_URI是用于连接ContentProvider的数据库,通过它来增删改查

其他数据是数据库表的列项

2、在A项目中创建自定义ContentProvider

java 复制代码
public class HomeCommonAppDataProvider extends ContentProvider {

    private static final String TABLE_NAME = "HOME_COMMON_APP";
    private SQLiteDatabase db;

    @Override
    public boolean onCreate() {//创建数据库
        DatabaseHelper dbHelper = new DatabaseHelper(getContext());
        db = dbHelper.getWritableDatabase();
        return db != null;
    }

    //查询通过表名查询数据
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
    }


    /**
    * 为 ContentProvider 处理的数据指定标准的 MIME 类型,告诉该URI是查询单个数据还是数据集
    * vnd.android.cursor.dir:表示返回的是数据集合(多个记录)
    * vnd.android.cursor.item:表示返回的是单个数据项(如果实现单条记录查询)
    * HomeCommonAppConfig.AUTHORITY:ContentProvider 的授权标识
    * TABLE_NAME:数据库表名
    *
    **/
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return "vnd.android.cursor.dir/vnd." + HomeCommonAppConfig.AUTHORITY + "." + TABLE_NAME;
    }

    //插入数据时收到通知
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long rowId = db.insert(TABLE_NAME, null, values);
        notifyChange(uri);
        return ContentUris.withAppendedId(uri, rowId);
    }

    //删除数据时收到通知
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = db.delete(TABLE_NAME, selection, selectionArgs);
        if (count > 0) notifyChange(uri);
        return count;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = db.update(TABLE_NAME, values, selection, selectionArgs);
        if (count > 0) notifyChange(uri);
        return count;
    }

    /**
     * 变更后通知
     *
     * @param uri
     */
    private void notifyChange(Uri uri) {
        if (getContext() != null) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
    }

    /**
     * 创建数据库
     */
    private static class DatabaseHelper extends SQLiteOpenHelper {
        static final String DATABASE_NAME = "app_data.db";
        static final int DATABASE_VERSION = 1;

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
                    + HomeCommonAppConfig._ID + " INTEGER PRIMARY KEY,"
                    + HomeCommonAppConfig.COLUMN_PKG_NAME + " TEXT,"
                    + HomeCommonAppConfig.COLUMN_ID + " INTEGER,"
                    + HomeCommonAppConfig.COLUMN_APP_NAME + " TEXT,"
                    + HomeCommonAppConfig.COLUMN_APK + " INTEGER,"
                    + HomeCommonAppConfig.COLUMN_HOME_MODEL + " INTEGER)");

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }
}

3、在A项目中创建数据表的Bean,对应每个列

java 复制代码
public class HomeCommonAppBean {
    // App包名
    private String mPkgName;
    // 快捷ID
    private int mID;
    // App名称
    private String mAppName;
    // 是否是APK
    private boolean mApk;

    private boolean mHomeModel;

    public HomeCommonAppBean(String pkgName, int ID, String appName, boolean apk, boolean homeModel) {
        mPkgName = pkgName;
        mID = ID;
        mAppName = appName;
        mApk = apk;
        mHomeModel = homeModel;
    }

    public String getPkgName() {
        return mPkgName;
    }

    public void setPkgName(String pkgName) {
        mPkgName = pkgName;
    }

    public int getID() {
        return mID;
    }

    public void setID(int ID) {
        mID = ID;
    }

    public String getAppName() {
        return mAppName;
    }

    public void setAppName(String appName) {
        mAppName = appName;
    }

    public boolean isApk() {
        return mApk;
    }

    public void setApk(boolean apk) {
        mApk = apk;
    }

    public boolean isHomeModel() {
        return mHomeModel;
    }

    public void setHomeModel(boolean homeModel) {
        mHomeModel = homeModel;
    }
}

4、在A项目AndroidManifest.xml中添加自定义ContentProvider标签

java 复制代码
<application>
 <provider
            android:name=".contentprovider.HomeCommonAppDataProvider"
            android:authorities="cn.huidu.box.launcher.provider"
            android:exported="true"
            android:readPermission="cn.huidu.box.launcher.PERMISSION_READ_DATA"
            android:writePermission="cn.huidu.box.launcher.PERMISSION_WRITE_DATA">
        </provider>
    </application>

   <permission
        android:name="cn.huidu.box.launcher.PERMISSION_READ_DATA"
        android:protectionLevel="signature" />
    <permission
        android:name="cn.huidu.box.launcher.PERMISSION_WRITE_DATA"
        android:protectionLevel="signature" />

需要注意: android:exported="true" 必须为true,如果为false就只能当前项目使用,外部项目无法使用

5、在A项目中在合适的地方往表中添加数据,并添加监听通知

java 复制代码
        //查看常用app数据库表中数据是否有数据,如果为空则获取常用app信息插入表中
        ProviderUtils.insertInitialData(this);

        //监听通知
        mDataObserver = new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfChange) {
                有人往表中添加,删除,修改数据,都会进入
            }
        };
        getContentResolver().registerContentObserver(HomeCommonAppConfig.BASE_CONTENT_URI, true, mDataObserver);
java 复制代码
public class ProviderUtils {

    /**
     * 初始化时插入数据
     * @param context
     */
    public static void insertInitialData(Context context) {
        if (hasDataForTable(context)) return;
        ArrayList<ContentProviderOperation> operations = new ArrayList<>();
        List<EditAppModel> models = AppModelsManager.getInstance().getEditAppModels();
        List<HomeCommonAppBean> homeCommonAppBeans = new ArrayList<>();
        for (EditAppModel model : models) {
            homeCommonAppBeans.add(new HomeCommonAppBean(model.getPkgName(), model.getId(), model.getAppName(), model.isApk(), model.isHomeModel()));
        }

        for (HomeCommonAppBean app : homeCommonAppBeans) {
            operations.add(ContentProviderOperation.newInsert(
                            HomeCommonAppConfig.BASE_CONTENT_URI)
                    .withValues(HomeCommonAppConverter.toContentValues(app))
                    .build());
        }

        try {
            // 执行批量操作
            context.getContentResolver().applyBatch(
                    HomeCommonAppConfig.AUTHORITY,
                    operations
            );

        } catch (Exception e) {

        }

    }

    /**
     * 查询表中是否有数据
     *
     * @return
     */
    private static boolean hasDataForTable(Context context) {
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(
                    HomeCommonAppConfig.BASE_CONTENT_URI,
                    null, null, null, null
            );

            return cursor != null && cursor.getCount() > 0;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 重新初始化首页常用app数据,
     */
    public static void againInitHomeCommonAppData(Context context){
        context.getContentResolver().delete(
                HomeCommonAppConfig.BASE_CONTENT_URI,
                null,
                null
        );
        insertInitialData(context);
    }
}

6、将A项目的Config和Bean复制到B项目

7、B项目创建asyncTask查询A项目的数据(在子线程中查询,在主线程更新代码)

java 复制代码
public class CommonAppTask extends AsyncTask<Void, Void, List<HomeCommonAppBean>> {
    private Context mContext;
    private OnCommonAppListener mOnCommonAppListener;

    public CommonAppTask(Context context, OnCommonAppListener onCommonAppListener) {
        mContext = context;
        mOnCommonAppListener = onCommonAppListener;
    }

    @Override
    protected List<HomeCommonAppBean> doInBackground(Void... voids) {
        Cursor cursor = null;
        List<HomeCommonAppBean> appList = new ArrayList<>();

        try {

            cursor = mContext.getContentResolver().query(
                    HomeCommonAppConfig.BASE_CONTENT_URI,
                    null, null, null, null
            );
            if (cursor != null) {

                while (cursor.moveToNext()) {
                    appList.add(HomeCommonAppConverter.parseFromCursor(cursor));
                }

                cursor.close();
            }

        } catch (SecurityException e) {
            Log.e("CommonAppTask", "权限不足: " + e.getMessage());

        } catch (Exception e) {
            Log.e("CommonAppTask", "获取数据失败: " + e.getMessage());
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return appList;
    }

    @Override
    protected void onPostExecute(List<HomeCommonAppBean> editAppBeans) {
        super.onPostExecute(editAppBeans);
        Log.e("CommonAppTask", "onPostExecute: " + editAppBeans);
        if (mOnCommonAppListener != null) {
            mOnCommonAppListener.onResult(editAppBeans);
        }
    }

    /**
     * 首页返回的常用app监听
     */
    public interface OnCommonAppListener {
        void onResult(List<HomeCommonAppBean> resolveInfos);
    }
}
java 复制代码
  mCommonAppTask = new CommonAppTask(getActivity(), new CommonAppTask.OnCommonAppListener() {
            @Override
            public void onResult(List<HomeCommonAppBean> resolveInfos) {
                查询到A项目的数据
            }
        });
        mCommonAppTask.execute();

8、B项目修改A项目里的数据(先清空表中内容,再添加)

java 复制代码
  ArrayList<ContentProviderOperation> operations = new ArrayList<>();
        operations.add(ContentProviderOperation.newDelete(
                        HomeCommonAppConfig.BASE_CONTENT_URI)
                .build());
        List<HomeCommonAppBean> list = AppUtils.getCommonAppBeans();
        for (HomeCommonAppBean bean : list) {
            ContentValues values = HomeCommonAppConverter.toContentValues(bean);
            operations.add(ContentProviderOperation.newInsert(
                            HomeCommonAppConfig.BASE_CONTENT_URI)
                    .withValues(values)
                    .build());
        }
        try {
            // 执行批量操作
            getActivity().getContentResolver().applyBatch(
                    HomeCommonAppConfig.AUTHORITY,
                    operations
            );

        } catch (Exception e) {

        }

最后再来一个数据转换类

java 复制代码
public class HomeCommonAppConverter {
    /**
     * HomeCommonAppBean转成ContentValues
     * @param bean
     * @return
     */
    public static ContentValues toContentValues(HomeCommonAppBean bean) {
        ContentValues values = new ContentValues();
        values.put(HomeCommonAppConfig.COLUMN_PKG_NAME, bean.getPkgName());
        values.put(HomeCommonAppConfig.COLUMN_ID, bean.getID());
        values.put(HomeCommonAppConfig.COLUMN_APP_NAME, bean.getAppName());
        values.put(HomeCommonAppConfig.COLUMN_APK, bean.isApk() ? 1 : 0);
        values.put(HomeCommonAppConfig.COLUMN_HOME_MODEL, bean.isHomeModel() ? 1 : 0);
        return values;
    }

    /**
     * 通过Cursor查询的数据转成HomeCommonAppBean
     * @param cursor
     * @return
     */
    @SuppressLint("Range")
    public static HomeCommonAppBean parseFromCursor(Cursor cursor) {
        return new HomeCommonAppBean(
                cursor.getString(cursor.getColumnIndex(HomeCommonAppConfig.COLUMN_PKG_NAME)),
                cursor.getInt(cursor.getColumnIndex(HomeCommonAppConfig.COLUMN_ID)),
                cursor.getString(cursor.getColumnIndex(HomeCommonAppConfig.COLUMN_APP_NAME)),
                cursor.getInt(cursor.getColumnIndex(HomeCommonAppConfig.COLUMN_APK)) == 1,
                cursor.getInt(cursor.getColumnIndex(HomeCommonAppConfig.COLUMN_HOME_MODEL)) == 1
        );
    }
}
相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker11 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952712 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android