Tips : 他のapkのリソース参照に、実はsharedUserId が必要ないらしい

Androidのapk間でリソースを参照するには、sharedUserIdを利用すると言われています。 一部では既知だったりするのかもしれませんが、実はこれ、不要なようです。

たとえば、よくあるサンプルコードは下記のようになっています。

Context context = mContext.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
Resources resource = context.getResources();
int resourceId = resource.getIdentifier(stringName, defType, packageName);
String text = resource.getString(resourceId);

このコードそのままにAndroidManifestからsharedUserIdを外してみても、UserIdの異なるアプリ間でリソースの参照ができます。

既存の別アプリのリソースを参照してみる

特に署名等の制限がないので、resourceIdとパッケージ名がわかれば公開されている他のアプリのリソースも参照できることになります。

Android Projectのデフォルトにある、app_nameを使ってYouTubeのリソースを参照してみたいと思います。

Androidアプリのパッケージ名は、Google PlayのURLにあるのでそちらを確認します。

f:id:tomoyukim:20140904200022p:plain

このPackage名を利用して、Defaultで提供されるapp_nameを参照してみます。

        try {
            Context context = createPackageContext("com.google.android.youtube", Context.CONTEXT_RESTRICTED);
            Resources resource = context.getResources();
            int resourceId = resource.getIdentifier("app_name", "string", "com.google.android.youtube");
            String text = resource.getString(resourceId);

            TextView textView = (TextView)findViewById(R.id.text);
            textView.setText(text);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

YouTubeがインストールされた端末で実行すると、こんな感じでYouTubeapp_nameが表示できます。 おもしろいですね。

f:id:tomoyukim:20140904203216p:plain

まとめ

ちょっと憶測入りますが、SharedUserIdはどうやら、アプリ固有で保存したデータ領域のデータを参照するようなケースで利用する想定のようです。

結局のところ、この方法が有効であってもresourceIdさえわからなければ無闇に他のアプリからリソースを参照されることはないはずです。今回試してみたapp_name も入っている可能性が高いから使ってみたというだけなので、利用していないアプリからは何も得ることができません。

なので、先のエントリーの<meta-data>を使ってresourceIdを公開しておくと、アプリ間でリソースを参照し合うことが簡単にできます。SharedUserIdを使うと同一アプリとしてみなされてしまうので、署名などを合わせる必要がでてしまいます。

そこまで結合されないけどリソースは共有できるような使い方ができるので、プラグインのような形式で追加機能を配布するようなアプリ作成にとっては、有用かなと思いました。