Android で動的にクラスをロードする方法(1)

実行後に jar ファイルを ClassLoader で読み込み、jar ファイルに格納されているクラスのメソッドを動的に実行する Android アプリのサンプルを作った。 本機能を拡張していくことで機能拡張が可能な Android アプリを構想できるようになる。


サンプル概要

  1. サンプルは、2つのプロジェクト(for Eclipse)で構成した。
    • 動的に読み込む対象となるライブラリ(Java プロジェクト)
      • 動的クラスロードの単純なテストなので、非常に簡単なクラスを1つ用意した。
    • Android アプリ本体(Android プロジェクト)
      • 動的クラスロードの単純なテストなので、上記ライブラリのクラスのメソッドを呼び出し結果を表示するだけのアプリケーションである。
  2. 動的に読み込む対象となるライブラリは class 形式 なので、Android Tool の dx コマンドを遣い Android で処理可能な dex 形式に変換する。
  3. 上記変換したファイルを Android アプリの asset に保管し、Android アプリから参照可能に設定する。

Android アプリの処理概要

  1. asset 上のライブラリを Android のローカルファイルとしてコピーする。
  2. DexClassLoader で上記のファイルを読み込む。
  3. 2. で生成したクラスローダーの loadClass メソッドでクラスを読み込む。
  4. 3, で生成したクラスの getMethod メソッドでメソッドを読み込む。
  5. 4. で生成したメソッドの invoke メソッドでメソッドを呼び出し、その結果を画面に表示する。

ポイント

ポイントだけ解析する。詳細は添付したソースコードを参照して欲しい。

class 形式から dex 形式への変換

Android Tool にある dx コマンドに --dex オプションを付けて実行することで class 形式のファイルを Android で処理可能な dex 形式に変換してくれる。

本ツールは、ディレクトリや jar の単位で処理してくれるのでとても簡単に使える。

ディレクトリを指定して変換した結果は --output オプションを使い、jar ファイルを指定することで一発で jar ファイルにまとめて出力してくれる。

利用する際の注意点はただ一つ。入力と出力のディレクトリ指定やファイル指定は、絶対パスで指定すること。

最初、ファイルが無いとエラーが出て戸惑った。

コマンドラインで以下のようなコマンドを入力することで相対パスっぽい指定ができる。ちなみに環境は Windows である。DynamicClassLoadingA のルートフォルダでこれを実行する。

dx --dex --output=%CD%\..\DynamicClassLoading\asset\a.jar %CD%\bin

%CD% がカレントディレクトリに展開されて実行される。

上記のコマンドは、以下の想定のもと a.jar を Android アプリの asset に配置している。

  • DynamicClassLoadingA\bin にコンパイルされた sample\a\A.class がある(Eclipse プロジェクトのデフォルトの動作)
  • DynamicClassLoading プロジェクトが DynamicClassLoadingA プロジェクトが同じディレクトリに配置されている。

asset の a.jar をローカルファイルにコピー

asset 上の jar ファイルをクラスローダーに認識させる方法が良く分からなかったので、 本サンプルでは Android のローカルファイルとしてコピーしてから読み込ませる方法を取った。

asset 上のファイルをローカルファイルにコピーする copyAssetToLocal メソッドを作った。

詳細はソースコードを参照のこと。


jar ファイルの読み込み

DexClassLoader クラスを使って jar ファイルを読み込む。

以下の記述がその部分である。

final ClassLoader classLoader = new DexClassLoader(

        getFilesDir().getAbsolutePath() + "/a.jar",
        getFilesDir().getAbsolutePath(), null,
        ClassLoader.getSystemClassLoader());

jar ファイルの指定は絶対パスで指定する必要があるため getFilesDir().getAbsolutePath() を使っている。

ちなみに第2引数には getFilesDir().getAbsolutePath() を指定していますが、これで良いのか不明です...。

もしかしたら temp フォルダみたいなのを自分で作って指定した方が良いのかなぁ?