虚苦心観察ブログ

ブログ管理者である虚苦心が私利私欲に基づいて書いているブログです。主にガジェットのレビューだったり、画像処理のことだったりを記事にしています。

Error:Execution failed for task ':app:transformClassesWithDexForDebug'.

公開中のアプリのメンテナンスでSDKをアップデートしたところ、端末へのインストール時にタイトルのようなエラーが発生した。

エラーの詳細は以下の通り

Error:Execution failed for task ':app:transformClassesWithDexForDebug'.
> com.android.build.transform.api.TransformException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk1.8.0_51\bin\java.exe'' finished with non-zero exit value 2

上記はStackoverflowで引っかかったエラーの内容をそのまま掲載しているが、私のアプリで発生していたエラーも全く同じ。stackoverflow.com

どうもAndroidStudio、つまりはAndroidのビルドシステムのアップデートにより仕様が変更された様子(詳細はしらない)。
リンク先に解決方法が書かれているが、ちょっと工程が多すぎ。
実際にはbuild.configにmultiDexEnableの項目を加えればよいみたい

stackoverflow.com

とりあえずアプリは動くようになったので解決した。
ここまで書いてなんとなく理解がすすんだのだけど、build.configのdefaultConfigに書かなければいけない項目が変更されたのだろう。おそらく。


Androidは昔に比べれば安定しているとはいえアップデートが早く、仕様が固定されるとも限らないのは辛いですね。
それに比べWindowsLinuxのような開発環境だと仕様が変わることはまずないし、開発手段もたくさんあるので気に入らなければ乗り換えれば良いから精神的に楽なんですよね。Windowsのデスクトップアプリを作ってて最近そういうことを実感してきました。

今回はここまで。

Android Studio + Microsoft Android Emulator

ちょっと手間取ったのでメモ

Microsoft製のAndroidEmulatorをインストール&起動してみたのだがadbに認識されずデバッグに使えないという現象が発生。
公式のサポートに解決方法が載っていた。

Troubleshooting the Visual Studio Emulator for Android

が、そもそもレジストリキーが存在しなかった。
ならキーを新しく作ればいいのか?と思い、

  1. HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node の下に「Android SDK Tools」という名前でキーを作成するとフォルダができる。
  2. そのフォルダを選択し、「Path」という名前で文字列値を追加
  3. 追加する文字列はAndroidSDKのルートフォルダのフルパス。add-onsとかbuild-toolsなどSDKが全部詰まっているフォルダ

以上をしたらエミュレータを起動。起動済みなら再起動。これで動いた。

AndroidStudio単体なら以上で問題なかったのだが、AndroidStudioからAndroidMonitor(Eclipseで動くやつ)を起動するとADBが再起動してしまうためかエミュレータを認識しなかった。

AndroidStudioを起動せずにAndroidMonitorを起動した場合は問題なくエミュレータが認識されることは確認できたが、AndroidStudio↔AndroidMonitorの切り替えは未解決。結構問題なんだよなー・・・

Windows に ipython notebook インストール

基本的にはこちらを参考にした
Python for Windows インストールメモ

ただし、ipythonそのもののインストールはGithubからWindows用のバイナリをダウンロード・インストールする必要があるgithub.com

pipでのインストール時にバージョンを指定する必要がある様子
pip install ipython==3.2.1

FragmentのレイアウトでViewPagerを使い、PagerAdapterにFragment(State)PagerAdapterを使う場合の注意点

題名のままですが、実際にはFragmentの中でFragmentを管理する場合の注意点です。

どういった状況で発生するかというと(私の場合)、
1. Parent1Fragmentの中でViewPagerを使ってChildAFragmentとChildBFragmentを表示します。
2. このParent1Fragmentから別のParent2Fragmentへ遷移します。
3. 戻るボタンでParent1Fragmentへ戻ります。

このような流れをとるとFragment(State)PagerAdapterへ与えたFragmentManagerがgetFragmentManager()メソッドから得たものだと、1.の状態が正常に復元されません。
Parent1Fragmentの状態だけが復元され、ChildAFragmentとChildBFragmentは復元されないという気持ち悪い状態に陥ります。

これはFragmentの中でFragmentを管理している場合に起こります。
またFragmentの状態の復元はFragmentの遷移が発生し、戻るボタンで元のFragmentへ戻った場合に発生します(他の場合もあるかも)。
FragmentA→FragmentB→(戻るボタン)→FragmentA というような移動の時に発生し、Homeボタンでアプリを終了したときには発生しないようです。

解決方法はgetChildFragmentManager()メソッドで別のFragmentManagerを用意すること。
これによりFragmentの管理が階層化され正常に復元がされるようになります。


getFragmentManagerを使おうがgetChildFragmentManagerを使おうがプログラム上は正常に動いてしまい何が原因だかわからず半日つぶしてしまいました。。。。
誰かの助けになれば幸いです。


参考
Fragmentをネストする際の注意点 - Just for Fun
Y.A.M の 雑記帳: Fragment in Fragment 時の注意点

AndroidStudioでdebug.keystoreを共有する

GitなどのVCSを使って複数のPCで作業しているとDebugで使うdebug.keystoreを共有して使いたいときがあります。
なぜかというと、デフォルトで使用されるdebug.keystoreはPC毎に異なるもののため、PC1でデバッグ後、PC2でデバッグしようとするとインストール時にAndroid端末側でエラーが発生し、PCを変えるごとにアプリをアンインストール・インストールを繰り返さなくてはなるからです。
これは同じアプリの開発者間でAndroid端末を共有している場合にもいえることです。

アプリ内にデータを保存していないものであればアンインストールしても特に問題ありませんが、ほとんどのアプリはデータを保存していると思うのでアンインストールしたくない、という問題は多いと思います。

このような問題はdebug.keystoreを共有することで解決します。
解決策はこちら

What is the equivalent of Eclipse "Custom debug Keystore" in Android studio? - Stack Overflow

アプリとして起動可能なモジュールのgradle.buildを修正します。
修正箇所は以下。

android {
    signingConfigs {
        debug {
            storeFile file("your.keystore")
        }
    }
}

your.keystoreには相対パスも指定可能です。パスの表記は(WindowsPCでも)Unix方式。
たとえば以下のようなプロジェクトのディレクトリ構造だったとしたら(だいぶ省いてます)、

Project
├── build.gradle
├── debug.keystore <-デバッグ用キーストア
├── settings.gradle
└── AppModule
    ├── build
    ├── build.gradle <-開発中アプリのgradle設定ファイル(これを修正)
    └── src

"your.keystore"には"../debug.keystore"と指定することが可能です。
プロジェクトのリポジトリ内にdebug.keystoreを含め、デバッグ用キーストアの位置を相対パスで指定することでどのPCでも設定を変更せずに開発が行えます。

追記

debub.keystoreがどこにあるのかの記載が抜けていることに数年越しで気づきました。
参照させていただいたのはこちら。qiita.com

Macなら
~/.android/debug.keystore

Windowsなら
C:\Users\\.android\
にあります。

これらを使ってもよいですし、先ほどの参照先にも書かれているように自作してもよいでしょう。

Crashlyticsでたまに発生する謎のNoSuchMethodError

エラー文はこちら。特にプログラムはCrashlyticsに関するコードを変更していません。というかできません。

01-26 16:48:42.323    7239-7239/com.zeeyousee.app.camera E/Fabric﹕ Failed to execute task.
    java.util.concurrent.TimeoutException
            at java.util.concurrent.FutureTask.get(FutureTask.java:176)
            at com.crashlytics.android.CrashlyticsUncaughtExceptionHandler.executeSyncLoggingException(CrashlyticsUncaughtExceptionHandler.java:622)
            at com.crashlytics.android.CrashlyticsUncaughtExceptionHandler.uncaughtException(CrashlyticsUncaughtExceptionHandler.java:319)
            at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
            at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
            at dalvik.system.NativeStart.main(Native Method)
01-26 16:48:42.363    7239-7239/com.zeeyousee.app.camera E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.zeeyousee.app.camera, PID: 7239
    java.lang.NoSuchMethodError: io.fabric.sdk.android.services.concurrency.PriorityCallable.addDependency
            at com.crashlytics.android.Crashlytics.finishInitSynchronously(Crashlytics.java:739)
            at com.crashlytics.android.Crashlytics.onPreExecute(Crashlytics.java:334)
            at com.crashlytics.android.Crashlytics.onPreExecute(Crashlytics.java:223)
            at io.fabric.sdk.android.InitializationTask.onPreExecute(InitializationTask.java:27)
            at io.fabric.sdk.android.services.concurrency.AsyncTask.executeOnExecutor(AsyncTask.java:594)
            at io.fabric.sdk.android.services.concurrency.PriorityAsyncTask.executeOnExecutor(PriorityAsyncTask.java:26)
            at io.fabric.sdk.android.Kit.initialize(Kit.java:49)
            at io.fabric.sdk.android.Fabric.initializeKits(Fabric.java:417)
            at io.fabric.sdk.android.Fabric.init(Fabric.java:364)
            at io.fabric.sdk.android.Fabric.setFabric(Fabric.java:321)
            at io.fabric.sdk.android.Fabric.with(Fabric.java:292)
            at com.zeeyousee.app.account.fragment.TwitterLoginFragment.initialize(TwitterLoginFragment.java:191)
            at com.zeeyousee.app.camera.activity.MainActivity.initializeCrashReport(MainActivity.java:185)
            at com.zeeyousee.app.camera.activity.MainActivity.onCreate(MainActivity.java:58)
            at android.app.Activity.performCreate(Activity.java:5248)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2162)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
            at android.app.ActivityThread.access$800(ActivityThread.java:139)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5086)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)

原因はCrashlyticsのプログラムアップデート?なんですかね。よくわかってないです。
プログラム側では修正できなくて開発中のアプリを端末からアンインストールしたら動きました。

2015/02/27 アプリ側の問題ではなくCrashlytics側の問題のようです。サービスに不具合があるようででそれでアプリも落ちてしまっていたようです。Fabricの初期化時にRuntimeExceptionをCatchすることでアプリの突然死を回避できます。ただこうするとアプリのクラッシュレポートが一時的でも使えなくなってしまうので、私はCatchした中で別のクラッシュレポートサービスを設定するようにしています。

OpenCVをEclipse、AndroidStudioで使うときの設定

はじめに

OpenCVは画像処理をするためのC/C++ライブラリ。
現在最新版は3.0となっているが、Android版は出ていません。
理由はしらないのですが、おそらく純C++で書かれている(2系はCで書かれていてそれをC++で使えるようにしていた)ため、Androidでそのまま使えないことが理由と思われる。

したがってここでは2系の最新版2.4.9を使うものとする。

OpenCVは適宜ダウンロードしましょう。
またOpenCV Managerを使わない方法を説明します。

Eclipseでの設定

AndroidOpenCVの準備

ダウンロードしたAndroidOpenCVを解凍し、OpenCV-2.4.9-android-sdk\sdk\java をインポートします。

これを別プロジェクトのライブラリとして設定します。

また、OpenCVの本体は.soファイルで.jarファイルと異なりライブラリとして設定しただけでは動きません。
ここまでの設定でアプリを起動し、OpenCVメソッドを実行するとUnsatisfiedLinkedErrorという例外が発生してしまうので注意。

.soファイルの配置

ndkを使わない方法

ここでは単体アプリとして動作することを目標とするので、.soファイルをアプリのプロジェクトのlibsディレクトリにコピーします。
OpenCV-2.4.9-android-sdk\sdk\native\libs 以下にあるディレクトリごとアプリのプロジェクト直下にあるlibsプロジェクトにコピー。

ndkを使う方法

.soファイルの配置方法はもうひとつあり、ndk-buildを使用する。
この方法では、プロジェクト直下にjniディレクトリを作成し、その下にAndroid.mkを作成する。
中身は以下。
```
include $(CLEAR_VARS)

# OpenCV
OPENCV_CAMERA_MODULES:=off
OPENCV_INSTALL_MODULES:=on
include C:\\\\OpenCV-2.4.9-android-sdk\\sdk\\native\\jni\\OpenCV.mk
```
作成後、jniディレクトリを作成したプロジェクトの直下でndk-buildを実行するとlibs以下にアーキテクチャごとの.soファイルが配置される。

プログラム側での設定

ライブラリとして設定するとプロジェクト内でOpenCVのクラスが利用できるようになっています。
ActivityやApplicationクラスのonCreateメソッド内でinitDebugメソッドを使うことでlibs以下に配置した.soファイルをロードします。

これでEclipseの設定は完了。

Android Studioでの設定

AndroidOpenCVの準備(主にGradleの設定)

ImportモジュールでOpenCV-2.4.9-android-sdk\sdk\java をインポートする。Eclipseのプロジェクトだがこれをモジュールとしてインポートすることができる。
モジュールとしてインポートした場合、直下のディレクトリにbuild.gradleファイルが出来上がり、トップディレクトリのsetting.gradleにモジュールが追加されているはずである。モジュールのディレクトリアイコンがただのディレクトリと違うものになっていればOK。
そうでなければsetting.gradleにモジュールを追加する。
アプリのモジュール名がapp、モジュール名がopencv-javaなら以下のようになる。
```
include ':app', ':java'
```
またアプリのbuild.gradleにはopencv-javaモジュールと関連付けるために以下の設定を追記します。
```
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.2'
compile project(':opencv-java') ## <<この行を追記する。ほかはそのまま
}
```

gradleファイルを変更したらsync project with gradle filesを実行することを忘れないように。

.soファイルの配置

Android StudioではNDKによるビルドはまだ敷居が高いです。
なのでここでは.soファイルの配置方法だけ説明します。
.soファイルの配置場所はappモジュール、opencv-javaモジュールのどちらでも良いのですが、<モジュールトップディレクトリ>/src/main/jniLibs以下に配置します。
配置方法はエクリプスと同じで、jniLibs/<アーキテクチャ名>/<.soファイル群>となっていれば良いです。

プログラム側での設定

残りはプログラムを書き、ActivityやApplicationクラスのonCreateでOpenCVLoader.initDebug()を実行すれば完了です。