You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android跨应用数据共享方案咨询:替代已废弃的SharedPreferences与Context.MODE_WORLD_READABLE

嘿,这个问题我太熟了——自从MODE_WORLD_READABLE被谷歌废弃后,确实得换更安全合规的跨应用数据共享方案,毕竟现在Android对隐私和权限管控越来越严格了。下面给你列几个实用的替代方案,都是当前Android开发中常用且推荐的:

1. ContentProvider(官方推荐的标准方案)

这是Android官方主推的跨应用数据共享方式,安全可控,能精细粒度地控制访问权限,我在多个项目里都用过,非常稳妥。

实现步骤:

  • 在提供数据的应用中,自定义一个ContentProvider,重写query()等核心方法(按需实现insert/update/delete),把你要共享的数据(比如从SharedPreferences或数据库读取)包装成Cursor返回。
  • 在Manifest中注册这个Provider,同时声明自定义权限(或使用系统权限),限制只有授权应用才能访问。
  • 访问数据的应用需要在Manifest中申请对应权限,然后通过ContentResolver来查询数据。

代码示例:

提供方自定义ContentProvider

public class MyDataProvider extends ContentProvider {
    // 定义ContentProvider的Uri,访问方会用这个Uri来查询数据
    public static final Uri CONTENT_URI = Uri.parse("content://com.example.dataprovider/mydata");

    @Override
    public boolean onCreate() {
        // 初始化数据源,比如SharedPreferences
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        // 从SharedPreferences读取数据,用MatrixCursor包装成Cursor返回
        SharedPreferences sp = getContext().getSharedPreferences("app_shared_data", Context.MODE_PRIVATE);
        MatrixCursor cursor = new MatrixCursor(new String[]{"key", "value"});
        // 这里示例返回一个username键值对,你可以扩展成更多数据
        cursor.addRow(new Object[]{"username", sp.getString("username", "")});
        return cursor;
    }

    // 其他方法如果不需要可以返回默认值
    @Override
    public String getType(Uri uri) {
        return "vnd.android.cursor.dir/vnd.com.example.dataprovider.mydata";
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

提供方Manifest注册Provider与自定义权限

<!-- 自定义权限,设置保护级别,比如signature只允许同签名应用访问 -->
<permission
    android:name="com.example.READ_APP_DATA"
    android:protectionLevel="normal" />

<provider
    android:name=".MyDataProvider"
    android:authorities="com.example.dataprovider"
    android:exported="true"
    android:permission="com.example.READ_APP_DATA" />

访问方申请权限并查询数据

<!-- 在Manifest中申请权限 -->
<uses-permission android:name="com.example.READ_APP_DATA" />
// 通过ContentResolver查询数据
Cursor cursor = getContentResolver().query(
        MyDataProvider.CONTENT_URI,
        null, null, null, null
);
if (cursor != null && cursor.moveToFirst()) {
    String username = cursor.getString(cursor.getColumnIndex("value"));
    // 处理获取到的数据
    cursor.close();
}

优缺点:

  • ✅ 优点:完全符合Android规范,权限控制精细,支持复杂数据结构,安全性高。
  • ❌ 缺点:需要编写的代码量稍多,适合中大型数据共享场景。

2. SharedPreferences + 签名权限(同签名应用专属)

如果两个应用都是你自己开发的,并且使用同一个签名证书,这个方案会比ContentProvider简单很多,适合快速实现键值对数据共享。

实现步骤:

  • 提供方的SharedPreferences依然用MODE_PRIVATE(安全),然后在Manifest中声明一个protectionLevel="signature"的自定义权限。
  • 提供方可以手动设置SharedPreferences文件的可读权限(确保其他应用能访问到文件)。
  • 访问方申请该自定义权限,通过创建提供方的Context来直接读取SharedPreferences。

代码示例:

提供方Manifest声明签名权限

<permission
    android:name="com.example.ACCESS_SHARED_PREFS"
    android:protectionLevel="signature" />

提供方设置SharedPreferences文件权限

SharedPreferences sp = getSharedPreferences("app_shared_data", Context.MODE_PRIVATE);
File spFile = new File(getApplicationInfo().dataDir + "/shared_prefs/app_shared_data.xml");
if (spFile.exists()) {
    // 设置文件为其他应用可读,结合签名权限只有同签名应用能访问
    spFile.setReadable(true, false);
}

访问方读取数据

<uses-permission android:name="com.example.ACCESS_SHARED_PREFS" />
try {
    // 创建提供方的Context,忽略安全检查(因为有签名权限)
    Context providerContext = createPackageContext("com.example.providerapp", Context.CONTEXT_IGNORE_SECURITY);
    SharedPreferences sp = providerContext.getSharedPreferences("app_shared_data", Context.MODE_PRIVATE);
    String username = sp.getString("username", "");
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

优缺点:

  • ✅ 优点:代码量少,实现快速,适合同签名的兄弟应用。
  • ❌ 缺点:仅支持同签名应用,权限控制依赖签名,灵活性不如ContentProvider。

3. FileProvider(文件类数据共享)

如果你的共享数据是以文件形式存储的(比如文本、JSON、二进制文件),FileProvider是Android推荐的安全文件共享方式,避免直接暴露文件路径带来的安全风险。

实现步骤:

  • 提供方注册FileProvider,配置共享文件的路径。
  • 提供方生成文件的授权Uri,通过Intent或权限授权给访问方。
  • 访问方通过Uri读取文件内容。

代码示例:

提供方Manifest注册FileProvider

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

res/xml/file_paths.xml(配置共享路径)

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 共享files目录下的shared文件夹 -->
    <files-path name="shared_files" path="shared/" />
</paths>

提供方生成授权Uri并授权

// 创建共享文件并写入数据
File sharedFile = new File(getFilesDir(), "shared/data.json");
// 写入数据到文件...

// 生成授权Uri
Uri fileUri = FileProvider.getUriForFile(this, "com.example.fileprovider", sharedFile);
// 给访问方授权读取权限
grantUriPermission("com.example.clientapp", fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

访问方读取文件

try {
    InputStream inputStream = getContentResolver().openInputStream(fileUri);
    // 读取文件内容,比如用BufferedReader解析JSON
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    StringBuilder content = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        content.append(line);
    }
    inputStream.close();
    // 处理文件内容
} catch (IOException e) {
    e.printStackTrace();
}

优缺点:

  • ✅ 优点:安全,避免暴露文件路径,适合文件类型的数据共享。
  • ❌ 缺点:需要自己处理文件解析,不适合直接共享键值对数据。

4. AIDL/Binder(实时交互与复杂数据)

如果需要跨应用实时通信,或者共享复杂的对象数据,AIDL(Android Interface Definition Language)是不错的选择,本质是基于Binder实现跨进程通信。

实现步骤:

  • 定义AIDL接口,声明要暴露的方法。
  • 提供方实现AIDL接口的服务,在服务中返回要共享的数据。
  • 访问方绑定服务,通过AIDL接口调用方法获取数据。

代码示例:

定义AIDL文件(IMyDataService.aidl)

package com.example;

interface IMyDataService {
    // 声明获取用户名的方法,你可以扩展更多方法
    String getUsername();
}

提供方实现服务

public class MyDataService extends Service {
    // 实现AIDL接口的Stub类
    private final IMyDataService.Stub mBinder = new IMyDataService.Stub() {
        @Override
        public String getUsername() throws RemoteException {
            SharedPreferences sp = getSharedPreferences("app_shared_data", Context.MODE_PRIVATE);
            return sp.getString("username", "");
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

提供方Manifest注册服务

<service
    android:name=".MyDataService"
    android:exported="true"
    android:permission="com.example.ACCESS_DATA_SERVICE" />

访问方绑定服务并调用方法

private IMyDataService mDataService;
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 将IBinder转换为AIDL接口实例
        mDataService = IMyDataService.Stub.asInterface(service);
        try {
            String username = mDataService.getUsername();
            // 处理获取到的数据
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mDataService = null;
    }
};

// 绑定服务
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.providerapp", "com.example.MyDataService"));
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

优缺点:

  • ✅ 优点:支持实时交互,可共享复杂对象,适合频繁通信的场景。
  • ❌ 缺点:代码复杂度较高,需要处理跨进程异常,适合有一定Android开发经验的开发者。

方案选择建议:

  • 常规键值对/简单数据共享:优先选ContentProvider,最规范安全。
  • 同签名应用:选SharedPreferences+签名权限,快速省事。
  • 文件类数据:选FileProvider。
  • 实时交互/复杂数据:选AIDL/Binder。

内容的提问来源于stack exchange,提问作者Anto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:52:33