Tray - マルチプロセスをサポートしたSharedPreferences
問題点
AndroidにはSharedPreferencesというデータを簡単に保存できる方法があります。
このSharedPreferencesは同一プロセス上で読み書きする分には問題はありませんが、Aプロセスで保存し、Bプロセスで読みこむといったような複数プロセスをまたいで使うような時はデータを読み書きする際に問題が生じます。MODE_MULTI_PROCESSというモードがありますが、正しく動きません。
問題点の詳細に関しては次の記事が詳しいです。
SharedPreferences と MODE_MULTI_PROCESS がイマイチよろしくない件
またMODE_MULTI_PROCESSはAPI23から非推奨になりました。
http://developer.android.com/intl/ja/reference/android/content/Context.html#MODE_MULTI_PROCESS
どういう時に困るのか
一例を挙げれば、バックグラウンドで常駐するサービスを使うとき。
サービス自体はアプリのメインプロセスでも動かすことは可能だが、別のプロセスで動かした方が使用するメモリ量が少なくなり、システムに殺されにくくなる。
サービスで取得したデータをメインプロセスで利用する時は通常のSharedPreferencesだと値を正しく読み込めず、バグや不具合の元になります。
解決策
Trayを利用する
Trayとは
ShardePreferencesと同じように簡単にデータ保存できるライブラリです。
ContentProviderをベースに作られており、マルチプロセスでのデータの読み書きをサポートしています。
書き込みと読み込み
final AppPreferences appPreferences = new AppPreferences(getContext());
// 書き込み
appPreferences.put("key", "Tray");
// 読み込み
final String value = appPreferences.getString("key", "default");
Log.v(TAG, "value: " + value); // value: Tray
// 読み込み(デフォルト)
final String defaultValue = appPreferences.getString("key2", "default");
Log.v(TAG, "value: " + defaultValue); // value: default
書き込み時にEditorでのcommit()やapply()は必要ありません。
使い方
build.gradleに次のように記述します。
repositories {
jcenter()
}
dependencies {
compile 'net.grandcentrix.tray:tray:0.9.2'
}
android {
...
defaultConfig {
applicationId "your.app.id" // アプリのパッケージ名
resValue "string", "tray__authority", "${applicationId}.tray"
}
}
記述後、プロジェクトをCleanすると次のファイルが生成されます。
/build/generated/res/generated/BUILDTYPE/values/generated.xml
<!-- Values from default config. -->
<item name="tray__authority" type="string">your.app.id.tray</item>
生成されていれば準備完了です。
検証
簡単に動作を検証してみました。
ボタンを押すたびにcountを1ずつ増やし、別プロセスでその値を読み込んでログに表示します。
public class PrefUtils {
@SuppressWarnings("unused")
private static final String TAG = PrefUtils.class.getSimpleName();
private static final String PREF_NAME = "test";
private static final String KEY_COUNT = "count";
public static void put(Context context, int count) {
SharedPreferences pref = context.getSharedPreferences(PREF_NAME, Context.MODE_MULTI_PROCESS);
pref.edit().putInt(KEY_COUNT, count).commit();
}
public static int get(Context context) {
SharedPreferences pref = context.getSharedPreferences(PREF_NAME, Context.MODE_MULTI_PROCESS);
return pref.getInt(KEY_COUNT, 0);
}
}
public class TrayUtils {
@SuppressWarnings("unused")
private static final String TAG = TrayUtils.class.getSimpleName();
private static final String KEY_COUNT = "count";
public static void put(Context context, int count) {
TrayAppPreferences pref = new TrayAppPreferences(context);
pref.put(KEY_COUNT, count);
}
public static int get(Context context) {
TrayAppPreferences pref = new TrayAppPreferences(context);
return pref.getInt(KEY_COUNT, 0);
}
}
public class TrayAppPreferences extends TrayModulePreferences {
private static final int VERSION = 1;
public TrayAppPreferences(Context context) {
super(context, context.getPackageName(), 1);
}
protected void onCreate(int newVersion) {
}
protected void onUpgrade(int oldVersion, int newVersion) {
throw new IllegalStateException("Can\'t upgrade database from version " + oldVersion + " to " + newVersion + ", not implemented.");
}
}
public class MainActivity extends AppCompatActivity {
private Button mButton;
private int mCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCount = 0;
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCount++;
PrefUtils.put(MainActivity.this, mCount);
TrayUtils.put(MainActivity.this, mCount);
TestService.start(MainActivity.this);
}
});
}
}
public class TestService extends IntentService {
@SuppressWarnings("unused")
private static final String TAG = TestService.class.getSimpleName();
public TestService() {
super(TAG);
}
public TestService(String name) {
super(name);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
protected void onHandleIntent(Intent intent) {
int prefCount = PrefUtils.get(this);
int trayCount = TrayUtils.get(this);
Log.d(TAG, "pref:" + prefCount + "\ttray:" + trayCount);
}
public static void start(Context context) {
Intent intent = new Intent(context, TestService.class);
context.startService(intent);
}
}
検証結果
結果を見ると、SharedPreferencesを使う方法では、データの読み込みが正確に行われずマルチプロセス上では正しく動いていないことがわかります。
Trayの方は1ずつ増えているのでマルチプロセス上でも問題なく動いていることがわかります。
Author And Source
この問題について(Tray - マルチプロセスをサポートしたSharedPreferences), 我々は、より多くの情報をここで見つけました https://qiita.com/gari_jp/items/e62166f885176042814a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .