21

我已经为此苦苦挣扎了一段时间。基本上,我希望有两个应用程序(将始终安装在一起)共享首选项,其中一个只是在后台运行并需要使用首选项的服务(应该拥有首选项,但只需要阅读它们) 并且另一个应用程序是前端 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。

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

4

2 回答 2

47

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

将两个应用程序中的 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">
....

从其他包获取上下文:

mContext = context.createPackageContext(
                        "com.example.otherapp",
                        Context.MODE_PRIVATE);
 mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE);

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

于 2014-07-15T00:14:30.497 回答
7

首先,我应该注意,这不是官方支持的,尽管将来可能会在 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 的模拟器——我没有在设备或安卓版本上进行广泛的测试。




在将拥有偏好的应用程序中要做的事情(上面的应用程序 A):

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();

2.) 设置备份文件 getSharedPreferences 方法似乎是检查 .bak 文件以从中加载首选项。这就是为什么它在文档中说它不能跨多个进程工作;为了最大限度地减少 I/O,它会在您抓取首选项时加载它们一次,并且仅在您关闭应用程序/活动时备份它们。但是,如果您从外部应用程序调用它,您将收到有关该文件夹(这是第一个应用程序的数据文件夹)没有正确文件权限的警告。为了解决这个问题,我们将自己创建 .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"};

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

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) {}
}

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

execCommand(copyToBackup);
execCommand(mainFixPerm);
execCommand(bakFixPerm);

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


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

1.) 写入文件
这更简单。我在这个活动上做了一个按钮,并在它的 onClick 方法中设置了代码,这会将一些东西保存到共享首选项文件中。请记住,应用 B 的包必须是应用 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();

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

于 2012-06-14T00:26:49.697 回答