複数のパッケージのコンテキストで ContextImpl#getSharedPreferences(String name, int mode) を呼んだときの動作

602 views
Skip to first unread message

nemo

unread,
May 5, 2012, 12:16:54 AM5/5/12
to android-...@googlegroups.com
こんにちは。
Context#getSharedPreferences が返すインスタンスは通常,各パッケージローカルのデータディレクトリーと対応づけられていると思います。
  SharedPreferences prefA = pkgContextA.getSharedPreferences("foo", ...);
  SharedPreferences prefB = pkgContextB.getSharedPreferences("bar", ...);
ならば,prefA は /data/data/domain.packageNameA/shared_prefs/foo.xml に,prefB は/data/data/domain.packageNameB/shared_prefs/bar.xml に紐付いています。

ですが
  SharedPreferences prefA = pkgContextA.getSharedPreferences("foo", ...);
  SharedPreferences prefB = pkgContextB.getSharedPreferences("foo", ...);
としたとき,prefA と prefB は同じインスタンスで, /data/data/domain.packageNameA/shared_prefs/foo.xml に紐付いています。

これは, #getSharedPreferences の実装が, ContextImpl のstaticコンテキストでSharedPreference のインスタンスをキャッシュしており(いわゆるクラスメンバー),
そのキャッシュを検索する際のキーが("contextA/foo" などではなくて単に) "foo" となっているためで,なんといいますか,意図的にそうしたわけではないような感じがします。
回避策が自明なので放置されているのかな?

わたし,何か勘違いしてますでしょうか。ツッコミとか,先行議論とかご存じのかたいらっしゃいましたら反応ください。

Keiji Ariyama

unread,
May 5, 2012, 12:56:13 AM5/5/12
to android-...@googlegroups.com, nemo
有山と申します。

 Context#getSharedPreferences は、アプリケーションのパッケージ名
(AndroidManifest.xmlに記載している)のディレクトリ下のpreferenceを開いて
いるという認識です。

 従ってpkgContextAとpkgContextBが、同一のアプリケーションであれば、パッ
ケージ名は共通なので同じファイルを開く、、、という理解です。



(12/05/05 13:16), nemo wrote:
> こんにちは。
> Context#getSharedPreferences が返すインスタンスは通常,各パッケージロー
> カルのデータディレクトリーと対応づけられていると思います。
> --
> このメールは Google グループのグループ「Android-SDK-Japan」の登録者に送
> られています。
> このディスカッションをウェブ上で閲覧するには、https://groups.google.com
> /d/msg/android-sdk-japan/-/YQmHt4bT9OMJ にアクセスしてください。
> このグループに投稿するには、android-...@googlegroups.com にメール
> を送信してください。
> このグループから退会するには、android-sdk-
> japan+un...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/android-sdk-japan?hl=ja
> からこのグループにアクセスしてください。

--
Keiji,
ml_an...@c-lis.co.jp

ishihata

unread,
Apr 3, 2013, 9:18:41 PM4/3/13
to android-...@googlegroups.com
こんにちは。石畑と申します。

nemoさんの投稿からだいぶ経ってしまっていますが、私も同じ現象にぶち当たりました。
Android 2.3.3 エミュレータではこの問題は発生しないのですが、
Android 4.1.2 エミュレータと Xperia VL (Android 4.0.4)実機では発生します。
Android 2.3.3 のソースは見ていないのですが(ソースの取得がちょっと面倒そうなので…)、
昔の実装ではキャッシュしてなかったのかなと推察しています。

Android の Issue tracker にはまだそれらしい投稿がありませんでしたので
僭越ながら軽く投稿しておきました。

とりあえずの回避策としては、SharedPreferences のファイル名を
(パッケージ名を含めるなどして)ユニークにする、くらいですかね?


2012年5月5日土曜日 13時16分54秒 UTC+9 nemo:

Tatsuo Nagamatsu

unread,
Apr 3, 2013, 9:32:29 PM4/3/13
to android-...@googlegroups.com
Android 2.3より新しいターゲットでは、複数プロセスからプリファレンスを共有する際には注意が必要です。

public static final int MODE_MULTI_PROCESS

Added in API level 11

SharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.

This was the legacy (but undocumented) behavior in and before Gingerbread (Android 2.3) and this flag is implied when targetting such releases. For applications targetting SDK versions greater than Android 2.3, this flag must be explicitly set if desired.





2013/4/4 ishihata <ishihat...@gmail.com>

--
このメールは Google グループのグループ「Android-SDK-Japan」の登録者に送られています。
このグループから退会し、メールの受信を停止するには、android-sdk-ja...@googlegroups.com にメールを送信します。
このグループに投稿するには、android-...@googlegroups.com にメールを送信してください。
http://groups.google.com/group/android-sdk-japan?hl=ja からこのグループにアクセスしてください。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
 
 

石畑恭平

unread,
Apr 3, 2013, 10:04:09 PM4/3/13
to android-...@googlegroups.com
石畑です。

MODE_MULTI_PROCESS のフラグを付けても、この問題は解消しないんです。
ソースを読む限り、MODE_MULTI_PROCESS の指定はプロセス間の競合(と言うのかな?)を防ぐためのものです。
今回の問題は、同一プロセス内で複数のアプリの Context を扱う場合に発生するものですので、
MODE_MULTI_PROCESS は関係ないと考えています。


2013年4月4日 10:32 Tatsuo Nagamatsu <naga...@gmail.com>:

nagamatu

unread,
Apr 3, 2013, 11:00:14 PM4/3/13
to Android-SDK-Japan
すみません、勘違いでした。

ContextImplクラスで staticに保持する Mapにおいて既に同じ名前のプリファレンスが利用されていれば、
それを利用してしまうので問題が起きていますね。ContextBの "foo"を利用する場合にも下記のコードで
sSharedPrefs.get("foo")が ContextAの物を返してしまいます。

@Override
public SharedPreferences getSharedPreferences(String name, int
mode) {
SharedPreferencesImpl sp;
synchronized (sSharedPrefs) {
sp = sSharedPrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
sSharedPrefs.put(name, sp);
return sp;
}
}

--


On 4月4日, 午前11:04, 石畑恭平 <ishihata.k.t...@gmail.com> wrote:
> 石畑です。
>
> MODE_MULTI_PROCESS のフラグを付けても、この問題は解消しないんです。
> ソースを読む限り、MODE_MULTI_PROCESS の指定はプロセス間の競合(と言うのかな?)を防ぐためのものです。
> 今回の問題は、同一プロセス内で複数のアプリの Context を扱う場合に発生するものですので、
> MODE_MULTI_PROCESS は関係ないと考えています。
>
> 2013年4月4日 10:32 Tatsuo Nagamatsu <nagam...@gmail.com>:
>
>
>
>
>
>
>
> > Android 2.3より新しいターゲットでは、複数プロセスからプリファレンスを共有する際には注意が必要です。
> > public static final int MODE_MULTI_PROCESS
> > Added in API level 11<http://developer.android.com/guide/topics/manifest/uses-sdk-element.h...>
>
> > SharedPreference loading flag: when set, the file on disk will be checked
> > for modification even if the shared preferences instance is already loaded
> > in this process. This behavior is sometimes desired in cases where the
> > application has multiple processes, all writing to the same
> > SharedPreferences file. Generally there are better forms of communication
> > between processes, though.
>
> > This was the legacy (but undocumented) behavior in and before Gingerbread
> > (Android 2.3) and this flag is implied when targetting such releases. For
> > applications targetting SDK versions *greater than* Android 2.3, this
> > flag must be explicitly set if desired.
>
> >http://developer.android.com/reference/android/content/Context.html#M...
>
> > 2013/4/4 ishihata <ishihata.k.t...@gmail.com>
>
> >> こんにちは。石畑と申します。
>
> >> nemoさんの投稿からだいぶ経ってしまっていますが、私も同じ現象にぶち当たりました。
> >> Android 2.3.3 エミュレータではこの問題は発生しないのですが、
> >> Android 4.1.2 エミュレータと Xperia VL (Android 4.0.4)実機では発生します。
> >> Android 2.3.3 のソースは見ていないのですが(ソースの取得がちょっと面倒そうなので…)、
> >> 昔の実装ではキャッシュしてなかったのかなと推察しています。
>
> >> Android の Issue tracker にはまだそれらしい投稿がありませんでしたので
> >> 僭越ながら軽く投稿しておきました。
> >>https://code.google.com/p/android/issues/detail?id=53900
>
> >> とりあえずの回避策としては、SharedPreferences のファイル名を
> >> (パッケージ名を含めるなどして)ユニークにする、くらいですかね?
>
> >> 2012年5月5日土曜日 13時16分54秒 UTC+9 nemo:
>
> >>> こんにちは。
> >>> Context#getSharedPreferences が返すインスタンスは通常,**
> >>> 各パッケージローカルのデータディレクトリーと対応づけられてい**ると思います。
> >>> SharedPreferences prefA = pkgContextA.**getSharedPreferences("foo",
> >>> ...);
> >>> SharedPreferences prefB = pkgContextB.**getSharedPreferences("bar",
> >>> ...);
> >>> ならば,prefA は /data/data/domain.**packageNameA/shared_prefs/foo.**xml
> >>> に,prefB は/data/data/domain.**packageNameB/shared_prefs/bar.**xml
> >>> に紐付いています。
>
> >>> ですが
> >>> SharedPreferences prefA = pkgContextA.**getSharedPreferences("foo",
> >>> ...);
> >>> SharedPreferences prefB = pkgContextB.**getSharedPreferences("foo",
> >>> ...);
> >>> としたとき,prefA と prefB は同じインスタンスで, /data/data/domain.**
> >>> packageNameA/shared_prefs/foo.**xml に紐付いています。
>
> >>> これは, #getSharedPreferences の実装が, ContextImpl
> >>> のstaticコンテキストでSharedPreference のインスタンスをキャッシュしており(いわゆるクラスメンバー)**,
> >>> そのキャッシュを検索する際のキーが("contextA/**foo" などではなくて単に) "foo" となっているためで,なんといいますか,*
> >>> *意図的にそうしたわけではないような感じがします。
> >>> 回避策が自明なので放置されているのかな?
>
> >>> わたし,何か勘違いしてますでしょうか。ツッコミとか,**先行議論とかご存じのかたいらっしゃいましたら反応ください。
Reply all
Reply to author
Forward
0 new messages