Android 跨应用共享存储
Android Shared Storage across apps
我正在努力寻找在用户设备上的多个应用程序之间共享数据的最佳解决方案。所有这些 android 应用程序都将使用相同的签名。由于用户体验很重要(因此不考虑共享意图)
,将无法提示共享或授予数据权限
我尝试了以下解决方案,结果如下;
- 带有
sharedUserId
的 SharedPreferences 不是一个选项,因为这些现有应用程序已经具有默认 sharedUserId
值,这将阻止用户升级
ContentProvider
使用 SQLite
是有前途的,但是由于任何应用程序都可以共享数据,并且不知道将首先安装或安装哪个应用程序,因此无法确定哪个应用程序应该拥有<provider>
。除非有一种方法可以创建任何应用程序都可以初始化的内容提供者,如果它是第一个应用程序,那么我不确定如何继续这个解决方案
- 我目前正在尝试使用外部存储并简单地存储一个包含需要共享的数据的文件。这显然是最脆弱的解决方案,但我可能只存储临时敏感数据(几分钟后过期的令牌)。但是仍在寻找最佳解决方案。
任何专业知识或见解都会有所帮助。
谢谢@morrison-chang 和@blackapps。使用您评论中的要点,我找到了一个可行的解决方案。我在每个应用程序上对同一提供商拥有独特的权限。但是默认情况下它们都被禁用。每当任何应用程序需要访问其提供程序时,它都会检查是否有任何现有的可用程序。
SomeClassUsingMyContextProvider.java
...
private Uri getActiveSharedStorageContentUri(){
List<PackageInfo> packs = packageManager.getInstalledPackages(PackageManager.GET_PROVIDERS);
for (PackageInfo pack : packs) {
ProviderInfo[] providers = pack.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if(provider != null && provider.authority != null && provider.authority.endsWith(SharedStorageProvider.AUTHORITY_SUFFIX)){
return SharedStorageProvider.createContentUri(provider.authority);
}
}
}
}
// If no valid provider.authority was found for SharedStorageProvider then we can assume
// that the current app's SharedStorageProvider provider has not been enabled yet.
// Lets activate it and then use its CONTENT_URI
ComponentName componentName = new ComponentName(reactContext, SharedStorageProvider.class);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
return SharedStorageProvider.CONTENT_URI;
}
...
SharedStorageProvider.java
public class SharedStorageProvider extends ContentProvider {
public static final String AUTHORITY_SUFFIX = String.format(".SharedStorageProvider.%s", BuildConfig.APP_GROUP);
public static final String AUTHORITY = String.format("%s%s", BuildConfig.APPLICATION_ID, AUTHORITY_SUFFIX);
...
public static final Uri CONTENT_URI = createContentUri(AUTHORITY);
...
public static Uri createContentUri(String authority) {
return Uri.parse("content://" + authority + "/" + BASE_PATH);
}
...
AndroidManifest.xml
...
<uses-permission android:name="com.myapps.READ_DATABASE" />
<uses-permission android:name="com.myapps.WRITE_DATABASE" />
<permission android:name="com.myapps.READ_DATABASE" android:protectionLevel="signature" />
<permission android:name="com.myapps.WRITE_DATABASE" android:protectionLevel="signature" />
...
<application
...
<provider
android:name=".SharedStorageProvider"
android:authorities="@string/shared_storage_provider_authority"
android:exported="true"
android:enabled="false"
android:readPermission="com.myapps.READ_DATABASE"
android:writePermission="com.myapps.WRITE_DATABASE"
/>
</application>
...
build.gradle
...
resValue "string", "shared_storage_provider_authority", applicationId + ".SharedStorageProvider." + project.env.get("APP_GROUP") // Used to generate dynamic authority name
...
已知限制;
由于这仅将启用它的应用程序中的数据存储在内容提供者中,因此如果卸载该应用程序,则“共享”数据将从所有使用它的应用程序中丢失。这对我来说很好。
我正在努力寻找在用户设备上的多个应用程序之间共享数据的最佳解决方案。所有这些 android 应用程序都将使用相同的签名。由于用户体验很重要(因此不考虑共享意图)
,将无法提示共享或授予数据权限我尝试了以下解决方案,结果如下;
- 带有
sharedUserId
的 SharedPreferences 不是一个选项,因为这些现有应用程序已经具有默认sharedUserId
值,这将阻止用户升级 ContentProvider
使用SQLite
是有前途的,但是由于任何应用程序都可以共享数据,并且不知道将首先安装或安装哪个应用程序,因此无法确定哪个应用程序应该拥有<provider>
。除非有一种方法可以创建任何应用程序都可以初始化的内容提供者,如果它是第一个应用程序,那么我不确定如何继续这个解决方案- 我目前正在尝试使用外部存储并简单地存储一个包含需要共享的数据的文件。这显然是最脆弱的解决方案,但我可能只存储临时敏感数据(几分钟后过期的令牌)。但是仍在寻找最佳解决方案。
任何专业知识或见解都会有所帮助。
谢谢@morrison-chang 和@blackapps。使用您评论中的要点,我找到了一个可行的解决方案。我在每个应用程序上对同一提供商拥有独特的权限。但是默认情况下它们都被禁用。每当任何应用程序需要访问其提供程序时,它都会检查是否有任何现有的可用程序。
SomeClassUsingMyContextProvider.java
...
private Uri getActiveSharedStorageContentUri(){
List<PackageInfo> packs = packageManager.getInstalledPackages(PackageManager.GET_PROVIDERS);
for (PackageInfo pack : packs) {
ProviderInfo[] providers = pack.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if(provider != null && provider.authority != null && provider.authority.endsWith(SharedStorageProvider.AUTHORITY_SUFFIX)){
return SharedStorageProvider.createContentUri(provider.authority);
}
}
}
}
// If no valid provider.authority was found for SharedStorageProvider then we can assume
// that the current app's SharedStorageProvider provider has not been enabled yet.
// Lets activate it and then use its CONTENT_URI
ComponentName componentName = new ComponentName(reactContext, SharedStorageProvider.class);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
return SharedStorageProvider.CONTENT_URI;
}
...
SharedStorageProvider.java
public class SharedStorageProvider extends ContentProvider {
public static final String AUTHORITY_SUFFIX = String.format(".SharedStorageProvider.%s", BuildConfig.APP_GROUP);
public static final String AUTHORITY = String.format("%s%s", BuildConfig.APPLICATION_ID, AUTHORITY_SUFFIX);
...
public static final Uri CONTENT_URI = createContentUri(AUTHORITY);
...
public static Uri createContentUri(String authority) {
return Uri.parse("content://" + authority + "/" + BASE_PATH);
}
...
AndroidManifest.xml
...
<uses-permission android:name="com.myapps.READ_DATABASE" />
<uses-permission android:name="com.myapps.WRITE_DATABASE" />
<permission android:name="com.myapps.READ_DATABASE" android:protectionLevel="signature" />
<permission android:name="com.myapps.WRITE_DATABASE" android:protectionLevel="signature" />
...
<application
...
<provider
android:name=".SharedStorageProvider"
android:authorities="@string/shared_storage_provider_authority"
android:exported="true"
android:enabled="false"
android:readPermission="com.myapps.READ_DATABASE"
android:writePermission="com.myapps.WRITE_DATABASE"
/>
</application>
...
build.gradle
...
resValue "string", "shared_storage_provider_authority", applicationId + ".SharedStorageProvider." + project.env.get("APP_GROUP") // Used to generate dynamic authority name
...
已知限制; 由于这仅将启用它的应用程序中的数据存储在内容提供者中,因此如果卸载该应用程序,则“共享”数据将从所有使用它的应用程序中丢失。这对我来说很好。