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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11025234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:46:48  来源:igfitidea点击:

How can I share a SharedPreferences file across two different android apps?

androidandroid-intentsharedpreferencesandroid-context

提问by matt5784

I've been struggling with this for a while. Basically, I want to have two applications (which will always be installed together) share preferences, with one of them being just a service which runs in the background and needs to use the preferences (should own the preferences but only reallyneeds to read them) and the other app being a front-end UI app which needs to be able to write to the preferences file owned by the other app. The service will be doing things in the background (which may be determined by the preferences) and the UI will allow the user to edit the preferences and view some information from the service. However, they will be different packages/apps.

我已经为此苦苦挣扎了一段时间。基本上,我希望有两个应用程序(将始终安装在一起)共享首选项,其中一个只是在后台运行并需要使用首选项的服务(应该拥有首选项但只需要真正阅读它们) 而另一个应用程序是前端 UI 应用程序,它需要能够写入另一个应用程序拥有的首选项文件。该服务将在后台执行操作(这可能由首选项决定),并且 UI 将允许用户编辑首选项并查看来自该服务的一些信息。但是,它们将是不同的包/应用程序。

I tried following this tutorialwhich gave me a pretty good idea of how to have preferences in one app which can be read by another. Essentially, I create a new context through myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);and then call myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);However, I can't write to the preferences successfully from the outside app - (SharedPreferences.Editor).commit() returns false and I get a warning in logcat about being unable to edit pref_name.xml.bak.

我尝试遵循本教程,它让我很好地了解了如何在一个应用程序中设置可供另一个应用程序阅读的首选项。本质上,我创建了一个新的上下文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。

How can I successfully set up my applications so both of them can read and write to the same preferences file (which is stored in the data folder of one of them)?

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

采纳答案by matt5784

First, I should note that this is not officially supported, although there may be a supported way to do this (i.e. it would NOT be this method) added to Android in the future (source for both claims: see second paragraph of this link).

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

Again, this is unsupported and is very possibly unstable. I primarily did this as an experiment to see if it was possible; take extreme caution if you are planning to actually incorporate this method into an application.

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

However, it appears to be possible to share preferences between applications if a few requirements are met. First, if you want App B to be able to access App A's preferences the package name of App B must be a child of App A's package name (e.g. App A: com.example.pkgApp B: com.example.pkg.stuff). Additionally, they can't be wanting to access the file at the same time (I assume the same rules apply as for accessing them between activities, if you want to ensure atomic access you'll have to use additional safeguards such as .wait() and .notify(), but I won't go into that here).

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

Note: all of this works on the emulator on 2.2 and 2.3.3- I haven't extensively tested across devices or android versions.

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




Things to do in the app which is going to own the preferences (App A from above):




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

1.) Declare the SharedPreferences file
This is fairly simple. Simply declare a couple variables for your sharedpreferences file and the editor in your class and instantiate them in your onCreate method. You can put a string in the preferences now that you will use to make sure the other app can read it properly.

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.) Set up the backup fileThe getSharedPreferences method appears to check for a .bak file to load the preferences from. This is why it says in the documentation that it will not work across multiple processes; to minimize I/O, it loads the prefs ONCE when you grab them and only backs them up when you close your application/activity. However, if you call this from an outside application you will get a warning about not having the right file permissions for the folder (which is the first app's data folder). To fix this we are going to create the .bak file ourselves and make it publicly readable/writable. The way I chose to do this was to define three variables in my overall class.

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"};

and make a function in my main class which would take these as arguments and execute them

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

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

It's not terribly pretty or good but it works. Now, in your onCreate method (right after editor.commit())you will call this function with each of the three strings.

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

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

This will copy the file and make both the main .xml and the .xml.bak files accessible to outside programs. You should also call these three methods in your onDestroy() to make sure the database is backed up properly when your app exits, and additionally call them right before you call getSharedPreferences elsewhere in your application (as otherwise it will load the .bak file which is likely out of date if another process has been editing the main .xml file). That's all you need to do in this application though, though. You can call getSharedPreferences elsewhere in this activity and it will grab all the data from the .xml file, allowing you to then call the getdatatype("key") methods and retrieve it.


Things to do in the accessing file(s) (App B from above)

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


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

1.) Write to the file
This is even simpler. I made a button on this activity and set up code in it's onClick method which would save something to the shared preferences file. Remember that App B's package must be a child of App A's package. We will be creating a context based on App A's context and then calling getSharedPreferences on that context.

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

This grabs the string I set in the other application and displays it (or an error message) in a textview in this application. Additionally it sets a new string in the preferences file and commits the changes. After this runs, if your other application calls getSharedPreferences it will retrieve the file including the changes from this app.

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

回答by Omer Cansizoglu

It is better to set private mode for the file. App needs to be signed with same set of certificates to share this file.

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

Set sharedUserId in both apps to be same.

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

Get Context from other package:

从其他包获取上下文:

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

Get items as usual from SharedPreference. You can access it now.

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