如何在两个不同的Android应用程序之间共享SharedPreferences文件?

mat*_*784 18 android android-intent android-context sharedpreferences

我一直在努力解决这个问题.基本上,我希望有两个应用程序(将始终一起安装)共享首选项,其中一个只是一个在后台运行并需要使用首选项的服务(应该拥有首选项但只需要真正需要阅读它们) )另一个应用程序是一个前端UI应用程序,需要能够写入其他应用程序拥有的首选项文件.该服务将在后台执行(可能由首选项确定),UI将允许用户编辑首选项并查看服务中的一些信息.但是,它们将是不同的包/应用程序.

我尝试了这个教程,这让我非常了解如何在一个应用程序中拥有可以被另一个应用程序读取的首选项.本质上,我通过myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);然后调用创建一个新的上下文myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);然而,我无法从外部应用程序成功写入首选项 - (SharedPreferences.Editor).commit()返回false并且我在logcat中收到有关无法编辑的警告pref_name.xml.bak.

如何成功设置我的应用程序,以便它们都可以读取和写入相同的首选项文件(存储在其中一个的数据文件夹中)?

Ome*_*glu 44

最好为文件设置私有模式.应用程序需要使用相同的证书集签名才能共享此文件.

将两个应用中的sharedUserId设置为相同.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hello"
android:versionCode="1"
android:versionName="1.0" 
android:sharedUserId="com.example">
....
Run Code Online (Sandbox Code Playgroud)

从其他包获取上下文:

mContext = context.createPackageContext(
                        "com.example.otherapp",
                        Context.MODE_PRIVATE);
 mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE);
Run Code Online (Sandbox Code Playgroud)

从SharedPreference像往常一样获取项目.你现在可以访问它.

  • 不知道为什么这不是公认的答案.这是正确的方法,并且完美无缺. (3认同)
  • @Omer 和 Miro 如何成功设置我的应用程序,以便他们都可以读取和写入相同的首选项文件(存储在其中一个的数据文件夹中)?你能做到这一点吗? (2认同)

mat*_*784 6

首先,我应该注意到这没有得到官方支持,尽管将来可能会有一种支持的方式(即它不会是这种方法)添加到Android中(两种声明的来源:请参阅此链接的第二段) .

同样,这是不受支持的,并且很可能不稳定.我主要是做这个实验,看看它是否可能; 如果您计划将此方法实际合并到应用程序中,请格外小心.

但是,如果满足一些要求,似乎可以在应用程序之间共享首选项.首先,如果您希望App B能够访问App A的首选项,则App B的包名必须是App A的包名的子项(例如App A:com.example.pkgApp B :) com.example.pkg.stuff.此外,他们不能同时访问该文件(我假设适用于在活动之间访问它们的相同规则,如果您想确保原子访问,则必须使用其他安全措施,例如.wait( )和.notify(),但我不会在这里讨论).

注意:所有这些都适用于2.2和2.3.3上的模拟器 - 我没有在设备或Android版本上进行广泛测试.




应用程序中将要拥有首选项的事情(App A from above):

1.)声明SharedPreferences文件
这很简单.只需为您的sharedpreferences文件和类中的编辑器声明几个变量,并在onCreate方法中实例化它们.您现在可以在首选项中放置一个字符串,以确保其他应用程序可以正确读取它.

public class stuff extends Activity {
    SharedPreferences mPrefs = null;
    SharedPreferences.Editor mEd= null; 
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mPrefs = (getApplicationContext()).getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
        mEd = mPrefs.edit();
        mEd.putString("test", "original send from prefs owner");
        mEd.commit();
Run Code Online (Sandbox Code Playgroud)

2.)设置备份文件 getSharedPreferences方法似乎检查.bak文件以从中加载首选项.这就是为什么它在文档中说它不能跨多个进程工作; 为了最小化I/O,它会在您获取它们时加载prefs ONCE,并在您关闭应用程序/活动时仅备份它们.但是,如果您从外部应用程序调用此方法,您将收到一条警告,指出该文件夹没有正确的文件权限(这是第一个应用程序的数据文件夹).为了解决这个问题,我们将自己创建.bak文件并使其公开可读/可写.我选择这样做的方法是在我的整个类中定义三个变量.

final String[] copyToBackup = { "dd", "if=/data/data/com.example.pkg/shared_prefs/prefs.xml", "of=/data/data/com.example.pkg/shared_prefs/prefs.xml.bak", "bs=1024" };
final String[] mainFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml"};
final String[] bakFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml.bak"};
Run Code Online (Sandbox Code Playgroud)

并在我的主类中创建一个函数,它将这些作为参数并执行它们

public void execCommand(String[] arg0){
     try {
         final Process pr = Runtime.getRuntime().exec(arg0);
         final int retval = pr.waitFor();
         if ( retval != 0 ) {
             System.err.println("Error:" + retval);
         }
     }
     catch (Exception e) {}
}
Run Code Online (Sandbox Code Playgroud)

它不是非常漂亮或好,但它的工作原理.现在,在onCreate方法中(在editor.commit()之后),您将使用三个字符串中的每一个调用此函数.

execCommand(copyToBackup);
execCommand(mainFixPerm);
execCommand(bakFixPerm);
Run Code Online (Sandbox Code Playgroud)

这将复制文件并使外部程序可以访问主.xml和.xml.bak文件.您还应该在onDestroy()中调用这三个方法,以确保在应用程序退出时正确备份数据库,并在应用程序中的其他位置调用getSharedPreferences之前另外调用它们(否则它将加载.bak文件,如果另一个进程正在编辑主.xml文件,则可能已过期.不过,这就是你在这个应用程序中需要做的所有事情.您可以在此活动的其他位置调用getSharedPreferences,它将从.xml文件中获取所有数据,然后允许您调用getdatatype("key")方法并检索它.


访问文件中的事情(上面的App B)

1.)写入文件
这甚至更简单.我在此活动上创建了一个按钮,并在其中设置了onClick方法中的代码,该方法可以将某些内容保存到共享首选项文件中.请记住,App B的软件包必须是App A软件包的子代.我们将基于App A的上下文创建一个上下文,然后在该上下文中调用getSharedPreferences.

prefsbutton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        Context myContext = null;
        try {
            // App A's context
            myContext = createPackageContext("com.example.pkg", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
        } catch (NameNotFoundException e) {e.printStackTrace();}

        testPrefs = myContext.getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
        testEd = testPrefs.edit();
        String valueFromPrefs = testPrefs.getString("test", "read failure");
        TextView test1 = (TextView)findViewById(R.id.tvprefs);
        test1.setText(valueFromPrefs);
        testEd.putString("test2", "testback");
        boolean edit_success = testEd.commit();
Run Code Online (Sandbox Code Playgroud)

这将抓取我在其他应用程序中设置的字符串,并在此应用程序的textview中显示它(或错误消息).此外,它在首选项文件中设置一个新字符串并提交更改.运行后,如果您的其他应用程序调用getSharedPreferences,它将检索包含此应用程序更改的文件.

  • "尽管可能在未来" - Android SDK中永远不会支持使用命令行二进制文件.就个人而言,我不会用10米的杆子接触这种技术.如果您想在应用程序之间共享数据,请使用真正的API,例如实现由`SharedPreferences`支持的`ContentProvider`. (3认同)