在工作中用到了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
);
}
}