ContentProvider 设计
四大组件之一,主要用于在不同应用之间共享数据,ContentProvider 提供了一个一致的接口,应用能够以一种受控和安全的方式访问和修改数据,同时,通过 CP,数据可以被跨进程共享,而不必将数据直接暴露给其他应用。
ContentProvider 设计初衷
- 数据共享:Android 中,应用之间不允许直接访问数据,CP 提供了一种标准方式让应用安全共享数据
- 数据封装:通过 CP,数据存取逻辑可以封装在一个单独组件中,其他组件只需要通过 CP 提供的接口操作
- 统一接口:CP 提供了一个统一的接口,支持多种数据存取方式,并且支持对数据进行事务操作
ContentProvider 基本结构
- URI:统一资源标识符,用于定位 CP 中的数据
- MIME 类型:用于标识返回的数据类型
- 数据存储:实际存储数据的地方,如数据库、文件
- 数据操作方法:CRUD
ContentProvider 实现
继承 ContentProvider 并重写抽象方法
这一块不赘述了
高级特性
ContentObserver 监听数据变化
刚好最近做的需求中有用到,根据投影仪投射距离是否合适来展示正确的UI,我们就可以监听算法返回的距离状态去做更新UI操作
创建一个 ContentObserver 并注册它来监听数据变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class SettingsContentObserver extends ContentObserver { private final Context context; private final String settingKey;
public SettingsContentObserver(Handler handler, Context context, String settingKey) { super(handler); this.context = context; this.settingKey = settingKey; }
@Override public void onChange(boolean selfChange) { super.onChange(selfChange); handleSettingChange(); }
@Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange, uri); handleSettingChange(); }
private void handleSettingChange() { int value = Settings.Global.getInt( context.getContentResolver(), DISTANCE_STATUS, 1 );
LogUtils.d("SettingsObserver", DISTANCE_STATUS + " changed to: " + value); if(value==0){ isDistanceCorrect = true; }else { isDistanceCorrect = false; } updatUI();
} }
|
在合适位置注册和注销观察者
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Override protected void onResume() { super.onResume(); settingsObserver = new SettingsContentObserver(new Handler(),this,DISTANCE_STATUS); getContentResolver().registerContentObserver(Settings.Global.getUriFor(DISTANCE_STATUS), false, settingsObserver); } @Override protected void onPause() { super.onPause(); getContentResolver().unregisterContentObserver(settingsObserver); }
|
数据同步
ContentProvider 可以与 SyncAdapter 结合,实现自动数据同步;例如,当远程服务器上的数据更新时,可以通过 SyncAdapter 同步到本地数据库,并通过 ContentProvider 提供的数据接口进行访问;
创建 SyncAdapter
1 2 3 4 5 6 7 8 9 10 11
| public class ExampleSyncAdapter extends AbstractThreadedSyncAdapter { public ExampleSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); }
@Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { } }
|
配置 SyncAdapter 和 ContentProvider 结合
1 2 3 4 5 6 7
| <sync-adapter android:contentAuthority="com.example.provider" android:accountType="com.example.account" android:userVisible="true" android:supportsUploading="true" android:allowParallelSyncs="false" android:isAlwaysSyncable="true" />
|
权限控制
为保护敏感数据,CP 可以通过声明权限来控制数据访问
AndroidManifest.xml 中进行声明
1 2 3 4 5 6 7 8
| <permission android:name="com.example.provider.READ" android:protectionLevel="signature" /> <permission android:name="com.example.provider.WRITE" android:protectionLevel="signature" /> <provider android:name=".ExampleProvider" android:authorities="com.example.provider" android:exported="true" android:readPermission="com.example.provider.READ" android:writePermission="com.example.provider.WRITE" />
|
代码中检查
1 2 3 4 5 6 7 8
| @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { if (getContext().checkCallingOrSelfPermission("com.example.provider.READ") == PackageManager.PERMISSION_DENIED) { throw new SecurityException("Permission denied"); } }
|