apk(パッケージ)リソースを共有する

以前にも書いたが、Androidのデプロイの単位はapk(Android Package)であり、Javaのクラスのように同一のパスに置いておけば互いに簡単にアクセスできる訳では無いが、しかし、アクセスする手段はある。

他のapkにアクセスするための方法

  • 1. AndroidMenifest.xmlのルート要素()で、共有ユーザID(sharedUserId属性)を合わせる
  • 2. Context#createPackageContextで、apkにアクセスするためのコンテキストを作成する
  • 3. 2.で作成したContext#getResourceでリソースを取得する

この方法を利用することで.NET Frameworkの"サテライト・アセンブリ"のように、複数のパッケージで共有することを目的にしたリソースだけを含んだapkをデプロイする戦略が可能になる。※

-- hoge.package.A.apk
          ↓共有
-- org.kazz.common.apk (リソースのみのパッケージ)
          ↑共有
-- hoge.package.B.apk
  • ResoourceUtil.Java (org.kazz.common.apkというパッケージがデプロイされている事を前提にしている)
//インストールされているはずの共有するapkの名前(拡張子除く)
public static final String SHARED_PACKAGE_NAME = "org.kazz.common";

public static Context getSharedContext(Context base) throws NameNotFoundRuntimeException {
    if ( base.getPackageName().equals(SHARED_PACKAGE_NAME)) {
        //自身が共有パッケージの場合
        return base;
    } else {
        Context ctx = base.createPackageContext(SHARED_PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY) ;
        return ctx;
    }
}

public static Resources getSharedResource(Context base) {
    return  getSharedContext(base).getResources();
}

Context(Activity)が変わらなければ一度取得すれば良いはずなので、使いかたによっては内部でコンテキストやリソースをキャッシュするのが良いだろう。

元々Dalvikで使用するdexフォーマットは通常の.classに比べて30%以上サイズを減らすことができると言われており、実際にAndroidパッケージ(apk)のサイズは驚くほどに小さい。この方法を併用することで、更に個々のアプリケーションのサイズを小さくすることが期待できそうだ。


※1 別にコードを含んでいても構わないが、現状のAndroid SDKは他のパッケージをロードするためのクラスローダを組み込む手段が無いため、共有するパッケージ(この場合は"org.kazz.apk")に属するクラスをロードするには取得したコンテキストのクラスローダから明示的にロードする必要がある。

Context ctx = ResourceUtil.getSharedContext(this.activity);
Class clazz = Class.forName("org.kazz.common.Foobar", true, ctx.getClassLoader());