Tips : <meta-data>を使って他のapkからデータを読み取る

あまりないケースだと思いますが、他のapkからデータを読み込む方法を考えてみたのでメモしておきます。 利用ケースとしては、例えばplug-inのような形式で拡張できるアプリなどをつくるときに使えるかなと思います。

<meta-data>タグの定義

今回は<meta-data>タグを利用しました。
静的なデータの参照について検討していたので、データを提供する側のapkはJavaで何か書くような形にしたくないという意図がありました。

Android Developer | <meta-data>

<meta-data>タグはAndroidManifestに記載できるタグです。 nameとvalueのペアになっていて、参照するときはnameをkeyにしてデータを引くことができます。データとして指定できるのは、ResourceとStringになります。

<meta-data>タグはApplicationなどAndroidが提供するコンポーネント単位の配下に記述する必要があります。 つまり、各コンポーネントに対するメタデータという構造になっています。 例えば、下記のようになります。

<service
    android:name="your.app.name">
    <meta-data
        android:name="your.key"
        android:value="hello world"
    />
</service>

Resourceの場合は、

<service
    android:name="your.app.name">
    <meta-data
        android:name="your.key"
        android:resource="@xml/data"
    />
</service>

のようになります。

<meta-data>で定義したデータの参照

<meta-data>タグの情報はPackageManager経由で取得できます。 サンプルとして、res/以下に配置したStringを同じapk内から取得してみます。

<service
    android:name="your.app.name">
    <meta-data
        android:name="your.key"
        android:resource="@string/sample"
    />
</service>

読み込みます。

    ComponentName name = new ComponentName(this, this.getClass());
    ServiceInfo info = getPackageManager().getServiceInfo(name, PackageManager.GET_META_DATA);
    Bundle bundle = info.metaData;
    String value = getResources().getString(bundle.getInt("your.key"));

今回はService配下にデータを定義したので、getServiceInfoを利用していますが、ActivityやApplicationなどのときにはそれぞれのInfoを取得するメソッドがありますので、それらを利用して同じように取得できます。

他のapkから参照する

次に、他のapkからこのデータを参照してみます。定義したServiceを特定するために、IntentFilterを追記します。

<service
    android:name="your.app.name">
    <intent-filter>
        <action android:name="sample.action">
    </intent-filter>
    <meta-data
        android:name="your.key"
        android:resource="@string/sample"
    />
</service>

読み込みます。

    PackageManager pm = mContext.getPackageManager();
    List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent("sample.action"), PackageManager.GET_META_DATA);

    for (ResolveInfo resolveInfo : resolveInfos) {
        Bundle bundle = resolveInfo.serviceInfo.metaData;
        String value = getResources().getString(bundle.getInt("your.key"));
    }

PackageManagerのqueryIntentServicesを利用することで特定のIntentFilterを持ったServiceの一覧を取得できます。 ここから同一apkのときと同じようにServiceInfoが参照できるので、あとは同じようにデータを引くことができます。