20200118のAndroidに関する記事は3件です。

UnityでAndroidビルドエラーUnityEditor.BuildPlayerWindow+BuildMethodExceptionが出た時のライブラリ競合解決が辛過ぎて泣いた

概要

Unityでスマホゲーム作っててiOS向けのリリースがひと段落したのでAndroid向けビルドしたらめっちゃエラー出て泣いた。
2020-01-18_20h49_42.jpg
つら…

環境

  • OS:Windows 10
  • Unity:2019.1.0f2

Macでも同様のエラー出たので、OS依存のエラーではないことは検証済み。

エラー内容を確認する

ビルド失敗時に表示されるUnity Editorのダイアログには、何もメッセージが表示されず無事死亡。
2020-01-18_20h47_54.jpg

Consoleを確認するとUnityEditor.BuildPlayerWindow+BuildMethodException: 145 errorsという素敵なログが出ていた。

UnityEditor.BuildPlayerWindow+BuildMethodException: 145 errors

  at UnityEditor.BuildPlayerWindow+DefaultBuildMethods.BuildPlayer (UnityEditor.BuildPlayerOptions options) [0x00242] in C:\buildslave\unity\build\Editor\Mono\BuildPlayerWindowBuildMethods.cs:194 

  at UnityEditor.BuildPlayerWindow.CallBuildMethods (System.Boolean askForBuildLocation, UnityEditor.BuildOptions defaultBuildOptions) [0x0007f] in C:\buildslave\unity\build\Editor\Mono\BuildPlayerWindowBuildMethods.cs:97 
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

エラー145個て。初代のポケモン図鑑ならもう少しでコンプやぞ。

UnityEditor.BuildPlayerWindow+BuildMethodExceptionでググると結構情報出てくるけど、PlayServicesResolverだけで解決するパターンばかり。どれも今回のエラーとはちょっと違いそうな雰囲気だ。

もう少し手前のログを見ると、Gradle buildでコケていることがわかった。

CommandInvokationFailure: Gradle build failed. 
C:\Program Files\Unity\Hub\Editor\2019.1.0f2\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\OpenJDK\Windows\bin\java.exe -classpath "C:\Program Files\Unity\Hub\Editor\2019.1.0f2\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-4.6.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"

stderr[
D8: Program type already present: com.facebook.unity.FBDialogUtils

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformDexArchiveWithExternalLibsDexMergerForRelease'.
> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 
...

以下、つらつらと導入ライブラリの一覧が羅列されていた。
stderrにはFacebookのFBDialogUtilsがすでに存在しているとのメッセージが。

D8: Program type already present: com.facebook.unity.FBDialogUtils

Facebook SDKで何か引っかかっているのかな?
正直これだけでは具体的な原因が特定できない。

とりあえず現状得られたエラーメッセージでググってみると、SDKの重複に起因して発生するエラーっぽい情報がでてきた。
D8: Program type already present: com.facebook.unity.Constants - Unity Answers
Program type already present error when gradle build - Unity Forum

そういえばAndroidアプリのビルドでいろんなSDK入れると、バージョン違いの重複ライブラリが存在してしまい「どっち見ればええねん」的なエラーが出るのを何度も経験していた…

ライブラリの依存関係を調査する

おおよその目星がついたので深掘ってみる。Androidプロジェクト吐いてgradlew dependenciesコマンド叩くと、ライブラリの依存関係を確認できるらしい。
【Android】ライブラリの依存関係を調査する方法(gradle) - テクノモンキーのアプリ開発日記

知らなかった。

Build Settingsで [Export Project] にチェックを付けてAndroidプロジェクトを出力。
2020-01-18_21h39_22.jpg
Android Studioでプロジェクト開いてgradlew sync実行。エラー出てたら解消させとく。

コマンドプロンプト(Macの場合はターミナル)でAndroidプロジェクトのフォルダに移動してgradlew dependenciesコマンドを叩く。ライブラリ一覧や依存関係がズラリと表示される。

2020-01-18_21h51_00.jpg
2020-01-18_21h52_01.jpg

細かい部分はスルーするが、同名のバージョン違いが存在したりplay-services系で被っているものがいくつかあった。

ライブラリの競合を解決する

おそらく以下3つのライブラリが重複対象として該当。

  • play-services-ads-identifier
  • play-services-basement
  • facebook-android-wrapper

対応方法としては

  • バージョンを合わせる
  • 依存関係を無効化する
  • 重複ライブラリを除外する

といった案が考えられるようだ。

重複ライブラリを除外する

Androidプロジェクト上ではbuild.gradledependenciesから対象のimplementationを削除することで、ビルドエラーが解消されapkの生成ができた。

build.gradle
dependencies {
...
    implementation(name: 'play-services-ads-identifier-16.0.0', ext:'aar')
    implementation(name: 'play-services-basement-16.0.1', ext:'aar')
    implementation(name: 'facebook-android-wrapper-7.16.1', ext:'aar')
...
}

とはいえ毎回UnityからAndroidプロジェクト吐いてAndroid Studioでビルドするのはダル過ぎる。そんなのはiOSだけで十分です。

軽く調べてみると、Unityプロジェクト上ではmainTemplate.gradleを自前で用意して依存関係を書かなければならないらしい。

ちょっと時間も無くて「なるほどわからん」って感じだったので、重複してるaarファイルのバージョン低い方を削除するという力技でUnity上でもビルドエラーを解決(^ω^)

その後、対象ライブラリが関係している広告まわりを実装。ひと通りの動作確認でも問題なさそうだったのでクローズとした。

Androidのビルドエラー解消しんどい

Androidのライブラリ競合問題、5年くらい前から悩まされてた気がするけど、未だにサクッと解消できなくてつらい。はやくAIに仕事奪われたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android Studio 3.5でjava.lang.ClassNotFoundException

applicationのbuld.gradleに追加したら解消された

android {
    defaultConfig {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android で OpenCL の Image2D を使って画像変換する

Android で OpenCL を試す
の続きです

OpenCL の Image2D

OpenCL では,基本的な1次元の配列の他に,
画像の2次元のデータを取り扱う Image2D というデータ型が用意されている。

使い方
clCreateImage2D を使って画像データから Image2D を作成する。
引数は、画像データが格納された1次元配列のアドレスを示すポインター。

カーネルプログラムからは、
read_imagef と write_imagef を使って読み書きする。

Image2D のサンプル

Image2D のサンプルコードとして、
github に公開されているものを試す。
パソコン向けのC++ 版なので、Android向けに変更する。

このサンプルはガウシアンフィルタを使って画像のぼかしを行う。

重みは 3x3 の基本的なもの
ぼかしの効果は画像を拡大しないとわからない。

|1|2|1|
|2|4|2|
|1|2|1|

Android の画像から Image2D を作成する

当初は、Android の Bitmap から Image2D を作成しようとした。
先人の例があるだろと思ったが、全く見つからない。
結局、うまくいかなかった。

OpenCV の Maから Image2D を作成する例が見つかったので、
Mat を仲介役にした。

Java プログラム

画像を Bitmap 形式にする
Bitmap bitmap = BitmapFactory.decodeFile(pathName);

OpenCV の Mat 形式にする
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    Mat mat = new Mat(height, width, CvType.CV_8UC4);
    Utils.bitmapToMat(bitmap, mat);

native プログラムを呼ぶ
    ImageFilter(
            mat.getNativeObjAddr() );

    public native void ImageFilter(long matAddr);

native プログラム

JNIEXPORT void JNICALL
Java_jp_ohwada_android_opencl5_MainActivity_ImageFilter(
        jlong addr
        ) 
{

// 引数から Mat を復元する
    Mat& mat  = *(Mat*)addr;

    char *buffer = reinterpret_cast<char *>(mat.data);

 Image2D を作成する
    cl_mem image = clCreateImage2D(context,
                            CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                            &clImageFormat,
                            width,
                            height,
                            0,
                            buffer,
                            &errNum);

アプリを作成する

OpenCL と OpenCV の環境が必要です。

下記を読んでください。

_ AndroidStudio で OpenCV をインポートする

(1) プロジェクトを作成する

(2) OpenCV の Android 用 SDK を用意する

(3) OpenCL のライブラリを用意する

(4) app/src/main/Assets フォルダーにOPenCLのカーネルプログラムファイルを配置する

(5) レイアウトファイルに ImageView を配置する

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    <ImageView
        android:id="@+id/outputImageView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/picture" />

(6) MainActivity を作成する

MainActivity.java
public class MainActivity extends Activity

    private native void nativeStepOpenCL (
// 詳細 略

(7) C++ のプログラムファイルを作成する

step.cpp
extern "C" void Java_com_intel_sample_androidbasicocl_MainActivity_nativeStepOpenCL
(
// 詳細 略

(8) CMakeLists.txt を作成する

CMakeLists.txt
add_library( # Sets the name of the library.
             ImageFilter2D

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ImageFilter2D.cpp )

(9) app/build.gradle を変更する

build.gradle
    externalNativeBuild {
        cmake {
            path 'src/main/cpp/CMakeLists.txt'
        }
    }

アプリを実行する

analyze apk により下記の4つが同封されていることが確認できる。

  • libImageFilter2D.so
  • libOpenCL.so
  • libopencv_java4.so
  • libc++_shared.so

opencl5_analyze_apk.png

スクリーンショット
ぱっと見では、変換前後で違いがわからない。
左が変換前、右が変換後

opencl5_original.png pencl5_filtered.png

画像を拡大すると、ぼかしが効いているのがわかる
左が変換前、右が変換後

 opencl5_comparison.png

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/OpenCL5

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む