LoginSignup
5
5

More than 5 years have passed since last update.

Androidのビルド時に出るIllegalArgumentExceptionと戦う

Last updated at Posted at 2016-03-05

Titaniumで作ったAndroidアプリのプロジェクトをビルドしていると

java.lang.IllegalArgumentException: already added [パッケージ名]

こんなエラーに遭遇することがあります。読んで字のごとく、すでに追加されたクラスをさらに追加しようとしてdexerがエラーになっているのですが、モジュールを複数利用している際にはしばしば発生します。

Titaniumには現在のところ競合したクラスを解決する手段がありません。なので、自分で解決しないといけません。まだ泣いている場合ではありません。

大きく分けると2つのアプローチがあります。例えば、使っているモジュールのlib以下にgoogle-play-services.jarのような巨大で雑にまとまったパッケージがある場合、proguardで不要なクラスをごっそり削除してしまいましょう。これで動作しなければ、取りこぼしたものがあるということになるのでコツコツ除いていきます。

Proguardで絞る

Android SDKのtools/proguard/lib以下にproguard.jarがあります。Proguardの機能を使って、jarファイルから必要最小限のパッケージを利用するよう余計なものを削り落とします。

最初に、モジュールのソースコードからlib/以下のjarファイルをバックアップして(まあgitで管理してもいいんだけど)android sdkのtools/proguard/lib/にコピーします。自分もそちらに移動して、設定ファイルを用意します。

$ cd $ANDROID_SDK/tools/proguard/lib/
$ cp YOUR_MODULE/android/lib/your_file.jar ./
$ cp your_file.jar your_file.jar.`date +%Y%m%d`

設定ファイルを同じくtools/proguard/lib/以下に用意します。ここではsettings.txtとしました。[と]に囲まれた箇所はそれぞれ編集してください

-injars [これから処理するjarファイル]
-outjars [処理したjarファイルを保存する際のファイル名]

-libraryjars [Android SDKのパス]/extras/android/support/v4/android-support-v4.jar
-libraryjars [Android SDKのパス]/platforms/android-21/android.jar

-dontoptimize
-dontobfuscate
-dontwarn com.google.**.R
-dontwarn com.google.**.R$*
-dontnote

-keep public class [キープするpublicなクラス、省略する場合はcom.hoge.**、複数指定する場合はカンマ区切り] {
    public protected *;
}

-keep class [キープするクラス、省略する場合はcom.hoge.**、複数指定する場合はカンマ区切り] {
    java.lang.String NULL;
}

キープするクラスはソースコードを参照して探すことができます。例えば

import com.google.android.gms.analytics.ExceptionParser;
import com.google.android.gms.analytics.ExceptionReporter;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.Tracker;

こんな感じでpublicなクラスを利用している場合はキープします。com.google.android.gms.analytics.**でキープするか、個々のクラスを指定するかは、場合によりますね…

これで作成したjarファイルをモジュールのソースコードに戻して、そのモジュールがビルドできたらOKです。必ず動作確認しましょう。

モジュールの試験は必ず専用のプロジェクトを作って最小限の機能を試すようにしましょう。モジュール同士が衝突して原因を追いにくくなることがあります。

コツコツと不要なクラスを消す

モジュールのビルド自体はそのままのjarファイルでないといけないので、プロジェクトのmodules/android/以下のjarファイルを操作します。

(1) 各モジュールのlib以下のjarファイルを探します。

$ find modules/android | grep "lib/.*jar"

(2) ダブッたclassはあるかな?

jarファイルの中身は

$ jar tf your_file.jar

で一覧することができます。ビルド時のエラーメッセージが

$ java.lang.IllegalArgumentException: already added: L[パッケージ名];

であれば、[パッケージ名]に該当するものがあるか探してみましょう。

$ jar tf your_file.jar | grep "[パッケージ名]"

見つかったら、おめでとうございます。そいつが犯人です。

(3) jarファイルを作り直す

jarファイルは割と簡単に展開することができます。プロジェクトディレクトリ以下の該当するjarファイルを作業場所にコピーして、jarで展開します。

$ cd $PROJECT_DIR
$ mkdir tmp && cd tmp
$ cp ../modules/android/YOUR_MODULE/VERSION/lib/your_file.jar ./
$ jar -xfv your_file.jar

これでディレクトリが作成されてjarファイルが展開されます。

$ find . -name "*[パッケージのクラス名]*"
./com/example/android/duplicatedClass.class

これが重複していたクラスなので、削除しちゃいます。jarコマンドにcオプションが付くとパッケージにするので、jarファイル名とそれに含めるクラスのディレクトリを引数として指定します。

$ cp your_file.jar your_file.jar.`date +%Y%m%d`
$ rm your_file.har
$ jar -cfv your_file.jar ./com

こうして出来たjarファイルをプロジェクト以下、modules/android以下の該当するモジュールのlib/直下に置いてプロジェクトをビルドします。もちろん、トリッキーなやり方ではあるので、ちゃんと動作確認しましょう。別のパッケージ名でjava.lang.IllegalArgumentExceptionが出る場合は、そちらのクラスを同じ手順で除きます。

1つや2つならこれでいいのですが、大量に見つかるような場合は利用するjarファイルをjar tf your_file.jarで一覧を作成してdiffを取って探すほうがいいと思います。最初はプロジェクトのディレクトリで

$ CLASSNAME="KeepName"; \
find modules/android -name "*jar" | grep "lib/.*jar" | while read file; \
do GREP=`jar tf $file | grep ${CLASSNAME}`;
  if test -n "${GREP}";
  then
    echo $file
  fi;
done

という感じで探していたのですが、雑なパッケージがあるとti.mapなんかを使った日にはもうすごいことになります。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5