web-dev-qa-db-ja.com

SharedPreferencesファイルを2つの異なるAndroidアプリ間で共有するにはどうすればよいですか?

私はしばらくこれに苦労してきました。基本的に、2つのアプリケーション(常に一緒にインストールされます)に設定を共有させたいと思います。そのうちの1つは、バックグラウンドで実行され、設定を使用する必要がある単なるサービスです(設定を所有する必要がありますが、本当にはそれらを読み取る必要があります)そして他のアプリは他のアプリが所有する設定ファイルに書き込むことができる必要があるフロントエンド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を返し、pref_name.xml.bakを編集できないことについてlogcatに警告が表示されます。

アプリケーションを正常にセットアップして、両方が同じ設定ファイル(一方のデータフォルダーに保存されている)を読み書きできるようにするにはどうすればよいですか?

18
matt5784

まず、これは公式にはサポートされていないことに注意してください。ただし、将来的にAndroidに追加されるサポートされている方法(つまり、このメソッドではない)がある可能性があります(両方のソース主張: このリンク の2番目の段落を参照してください。

繰り返しますが、これはサポートされておらず、不安定である可能性が非常に高いです。私は主に、それが可能かどうかを確認するための実験としてこれを行いました。このメソッドを実際にアプリケーションに組み込むことを計画している場合は、細心の注意を払ってください。

ただし、いくつかの要件が満たされている場合は、アプリケーション間で設定を共有できるようです。まず、アプリBがアプリAの設定にアクセスできるようにする場合、アプリBのパッケージ名はアプリAのパッケージ名の子である必要があります(例:アプリA:com.example.pkgアプリB:com.example.pkg.stuff)。さらに、ファイルに同時にアクセスすることはできません(アトミックアクセスを確保したい場合は、.wait(などの追加の保護手段を使用する必要があります。アクティビティ間でアクセスする場合と同じルールが適用されると思います。 )と.notify()ですが、ここでは説明しません)。

注:これはすべて、2.2および2.3.3のエミュレーターで機能します-デバイス間またはAndroidバージョン。




設定を所有する予定のアプリで行うこと(上記のアプリ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ファイルを作成し、公開して読み取り/書き込み可能にします。これを行うために選択した方法は、クラス全体で3つの変数を定義することでした。

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()の直後)で、3つの文字列のそれぞれを使用してこの関数を呼び出します。

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

これにより、ファイルがコピーされ、メインの.xmlファイルと.xml.bakファイルの両方に外部プログラムからアクセスできるようになります。また、onDestroy()でこれらの3つのメソッドを呼び出して、アプリの終了時にデータベースが適切にバックアップされていることを確認し、さらに、アプリケーションの他の場所でgetSharedPreferencesを呼び出す直前に呼び出す必要があります(そうしないと、.bakファイルが読み込まれます。別のプロセスがメインの.xmlファイルを編集している場合は、古くなっている可能性があります)。ただし、このアプリケーションで行う必要があるのはこれだけです。このアクティビティの他の場所でgetSharedPreferencesを呼び出すと、.xmlファイルからすべてのデータが取得され、getdatatype( "key")メソッドを呼び出して取得できるようになります。


アクセスファイルで行うこと(上からのアプリB)

1。)ファイルへの書き込み
これはさらに簡単です。このアクティビティにボタンを作成し、onClickメソッドにコードを設定して、共有設定ファイルに何かを保存しました。アプリBのパッケージはアプリAのパッケージの子である必要があることに注意してください。アプリ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を呼び出すと、このアプリからの変更を含むファイルが取得されます。

6
matt5784

ファイルにプライベートモードを設定することをお勧めします。このファイルを共有するには、アプリに同じ証明書のセットで署名する必要があります。

両方のアプリで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から通常どおりアイテムを取得します。今すぐアクセスできます。

43
Omer Cansizoglu