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
        );
    }
}
相关推荐
哲科软件4 小时前
跨平台开发的抉择:Flutter vs 原生安卓(Kotlin)的优劣对比与选型建议
android·flutter·kotlin
jyan_敬言10 小时前
【C++】string类(二)相关接口介绍及其使用
android·开发语言·c++·青少年编程·visual studio
程序员老刘10 小时前
Android 16开发者全解读
android·flutter·客户端
不想迷路的小男孩11 小时前
Android Studio 中Palette跟Component Tree面板消失怎么恢复正常
android·ide·android studio
餐桌上的王子11 小时前
Android 构建可管理生命周期的应用(一)
android
菠萝加点糖11 小时前
Android Camera2 + OpenGL离屏渲染示例
android·opengl·camera
用户20187928316711 小时前
🌟 童话:四大Context徽章诞生记
android
yzpyzp11 小时前
Android studio在点击运行按钮时执行过程中输出的compileDebugKotlin 这个任务是由gradle执行的吗
android·gradle·android studio
aningxiaoxixi12 小时前
安卓之service
android