真面目なブログはこっち 👉 blog.s64.jp

`NoClassDefFoundError: Failed resolution of: Landroid/arch/lifecycle/Observer`というエラーが出たら

Androidアプリ起動時に、以下のようなエラーが出てる場合がある:

I/***: Rejecting re-init on previously-failed class java.lang.Class<android.databinding.ViewDataBinding$LiveDataListener>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/arch/lifecycle/Observer;
        at void android.databinding.ViewDataBinding.<clinit>() (ViewDataBinding.java:120)
        at android.databinding.ViewDataBinding jp.voicy.app.player.DataBinderMapperImpl.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (DataBinderMapperImpl.java:246)
        at android.databinding.ViewDataBinding android.databinding.MergedDataBinderMapper.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (MergedDataBinderMapper.java:74)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.bind(android.databinding.DataBindingComponent, android.view.View, int) (DataBindingUtil.java:199)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean, android.databinding.DataBindingComponent) (DataBindingUtil.java:130)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean) (DataBindingUtil.java:95)
        at android.view.View com.arrvis.library.app.BaseFragment.onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (BaseFragment.kt:67)
        at android.view.View android.support.v4.app.Fragment.performCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (Fragment.java:2261)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(android.support.v4.app.Fragment, int, int, int, boolean) (FragmentManager.java:1419)
        at void android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(android.support.v4.app.Fragment) (FragmentManager.java:1750)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(int, boolean) (FragmentManager.java:1819)
        at void android.support.v4.app.BackStackRecord.executeOps() (BackStackRecord.java:797)
        at void android.support.v4.app.FragmentManagerImpl.executeOps(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2590)
        at void android.support.v4.app.FragmentManagerImpl.executeOpsTogether(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2377)
        at void android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(java.util.ArrayList, java.util.ArrayList) (FragmentManager.java:2332)
        at boolean android.support.v4.app.FragmentManagerImpl.execPendingActions() (FragmentManager.java:2239)
        at void android.support.v4.app.FragmentManagerImpl.dispatchStateChange(int) (FragmentManager.java:3231)
        at void android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated() (FragmentManager.java:3181)
        at void android.support.v4.app.FragmentController.dispatchActivityCreated() (FragmentController.java:192)
        at void android.support.v4.app.FragmentActivity.onStart() (FragmentActivity.java:572)
        at void android.support.v7.app.AppCompatActivity.onStart() (AppCompatActivity.java:177)
        at void android.app.Instrumentation.callActivityOnStart(android.app.Activity) (Instrumentation.java:1391)
        at void android.app.Activity.performStart(java.lang.String) (Activity.java:7165)
        at void android.app.ActivityThread.handleStartActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions) (ActivityThread.java:2975)
        at void android.app.servertransaction.TransactionExecutor.performLifecycleSequence(android.app.ActivityThread$ActivityClientRecord, android.util.IntArray) (TransactionExecutor.java:180)
        at void android.app.servertransaction.TransactionExecutor.cycleToPath(android.app.ActivityThread$ActivityClientRecord, int, boolean) (TransactionExecutor.java:165)
        at void android.app.servertransaction.TransactionExecutor.executeLifecycleState(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:142)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:70)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1816)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106)
        at void android.os.Looper.loop() (Looper.java:193)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6718)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:493)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:858)
    Caused by: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.Observer" on path: DexPathList[[zip file "/data/app/***/base.apk"],nativeLibraryDirectories=[/data/app/***/lib/arm64, /system/lib64]]
        at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:134)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
        at void android.databinding.ViewDataBinding.<clinit>() (ViewDataBinding.java:120)
        at android.databinding.ViewDataBinding jp.voicy.app.player.DataBinderMapperImpl.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (DataBinderMapperImpl.java:246)
        at android.databinding.ViewDataBinding android.databinding.MergedDataBinderMapper.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (MergedDataBinderMapper.java:74)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.bind(android.databinding.DataBindingComponent, android.view.View, int) (DataBindingUtil.java:199)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean, android.databinding.DataBindingComponent) (DataBindingUtil.java:130)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean) (DataBindingUtil.java:95)
        at android.view.View com.arrvis.library.app.BaseFragment.onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (BaseFragment.kt:67)
        at android.view.View android.support.v4.app.Fragment.performCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (Fragment.java:2261)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(android.support.v4.app.Fragment, int, int, int, boolean) (FragmentManager.java:1419)
        at void android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(android.support.v4.app.Fragment) (FragmentManager.java:1750)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(int, boolean) (FragmentManager.java:1819)
        at void android.support.v4.app.BackStackRecord.executeOps() (BackStackRecord.java:797)
        at void android.support.v4.app.FragmentManagerImpl.executeOps(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2590)
        at void android.support.v4.app.FragmentManagerImpl.executeOpsTogether(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2377)
        at void android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(java.util.ArrayList, java.util.ArrayList) (FragmentManager.java:2332)
        at boolean android.support.v4.app.FragmentManagerImpl.execPendingActions() (FragmentManager.java:2239)
        at void android.support.v4.app.FragmentManagerImpl.dispatchStateChange(int) (FragmentManager.java:3231)
        at void android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated() (FragmentManager.java:3181)
        at void android.support.v4.app.FragmentController.dispatchActivityCreated() (FragmentController.java:192)
        at void android.support.v4.app.FragmentActivity.onStart() (FragmentActivity.java:572)
        at void android.support.v7.app.AppCompatActivity.onStart() (AppCompatActivity.java:177)
        at void android.app.Instrumentation.callActivityOnStart(android.app.Activity) (Instrumentation.java:1391)
        at void android.app.Activity.performStart(java.lang.String) (Activity.java:7165)
        at void android.app.ActivityThread.handleStartActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions) (ActivityThread.java:2975)
        at void android.app.servertransaction.TransactionExecutor.performLifecycleSequence(android.app.ActivityThread$ActivityClientRecord, android.util.IntArray) (TransactionExecutor.java:180)
        at void android.app.servertransaction.TransactionExecutor.cycleToPath(android.app.ActivityThread$ActivityClientRecord, int, boolean) (TransactionExecutor.java:165)
        at void android.app.servertransaction.TransactionExecutor.executeLifecycleState(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:142)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:70)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1816)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106)
        at void android.os.Looper.loop() (Looper.java:193)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6718)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:493)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:858)
I/***: Rejecting re-init on previously-failed class java.lang.Class<android.databinding.ViewDataBinding$LiveDataListener>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/arch/lifecycle/Observer;
        at void android.databinding.ViewDataBinding.<clinit>() (ViewDataBinding.java:120)
        at android.databinding.ViewDataBinding jp.voicy.app.player.DataBinderMapperImpl.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (DataBinderMapperImpl.java:246)
        at android.databinding.ViewDataBinding android.databinding.MergedDataBinderMapper.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (MergedDataBinderMapper.java:74)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.bind(android.databinding.DataBindingComponent, android.view.View, int) (DataBindingUtil.java:199)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean, android.databinding.DataBindingComponent) (DataBindingUtil.java:130)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean) (DataBindingUtil.java:95)
        at android.view.View com.arrvis.library.app.BaseFragment.onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (BaseFragment.kt:67)
        at android.view.View android.support.v4.app.Fragment.performCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (Fragment.java:2261)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(android.support.v4.app.Fragment, int, int, int, boolean) (FragmentManager.java:1419)
        at void android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(android.support.v4.app.Fragment) (FragmentManager.java:1750)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(int, boolean) (FragmentManager.java:1819)
        at void android.support.v4.app.BackStackRecord.executeOps() (BackStackRecord.java:797)
        at void android.support.v4.app.FragmentManagerImpl.executeOps(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2590)
        at void android.support.v4.app.FragmentManagerImpl.executeOpsTogether(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2377)
        at void android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(java.util.ArrayList, java.util.ArrayList) (FragmentManager.java:2332)
        at boolean android.support.v4.app.FragmentManagerImpl.execPendingActions() (FragmentManager.java:2239)
        at void android.support.v4.app.FragmentManagerImpl.dispatchStateChange(int) (FragmentManager.java:3231)
        at void android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated() (FragmentManager.java:3181)
        at void android.support.v4.app.FragmentController.dispatchActivityCreated() (FragmentController.java:192)
        at void android.support.v4.app.FragmentActivity.onStart() (FragmentActivity.java:572)
        at void android.support.v7.app.AppCompatActivity.onStart() (AppCompatActivity.java:177)
        at void android.app.Instrumentation.callActivityOnStart(android.app.Activity) (Instrumentation.java:1391)
        at void android.app.Activity.performStart(java.lang.String) (Activity.java:7165)
        at void android.app.ActivityThread.handleStartActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions) (ActivityThread.java:2975)
        at void android.app.servertransaction.TransactionExecutor.performLifecycleSequence(android.app.ActivityThread$ActivityClientRecord, android.util.IntArray) (TransactionExecutor.java:180)
        at void android.app.servertransaction.TransactionExecutor.cycleToPath(android.app.ActivityThread$ActivityClientRecord, int, boolean) (TransactionExecutor.java:165)
        at void android.app.servertransaction.TransactionExecutor.executeLifecycleState(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:142)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:70)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1816)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106)
        at void android.os.Looper.loop() (Looper.java:193)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6718)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:493)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:858)
    Caused by: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.Observer" on path: DexPathList[[zip file "/data/app/***/base.apk"],nativeLibraryDirectories=[/data/app/***/lib/arm64, /system/lib64]]
        at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:134)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
        at void android.databinding.ViewDataBinding.<clinit>() (ViewDataBinding.java:120)
        at android.databinding.ViewDataBinding jp.voicy.app.player.DataBinderMapperImpl.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (DataBinderMapperImpl.java:246)
        at android.databinding.ViewDataBinding android.databinding.MergedDataBinderMapper.getDataBinder(android.databinding.DataBindingComponent, android.view.View, int) (MergedDataBinderMapper.java:74)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.bind(android.databinding.DataBindingComponent, android.view.View, int) (DataBindingUtil.java:199)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean, android.databinding.DataBindingComponent) (DataBindingUtil.java:130)
        at android.databinding.ViewDataBinding android.databinding.DataBindingUtil.inflate(android.view.LayoutInflater, int, android.view.ViewGroup, boolean) (DataBindingUtil.java:95)
        at android.view.View com.arrvis.library.app.BaseFragment.onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (BaseFragment.kt:67)
        at android.view.View android.support.v4.app.Fragment.performCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) (Fragment.java:2261)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(android.support.v4.app.Fragment, int, int, int, boolean) (FragmentManager.java:1419)
        at void android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(android.support.v4.app.Fragment) (FragmentManager.java:1750)
        at void android.support.v4.app.FragmentManagerImpl.moveToState(int, boolean) (FragmentManager.java:1819)
        at void android.support.v4.app.BackStackRecord.executeOps() (BackStackRecord.java:797)
        at void android.support.v4.app.FragmentManagerImpl.executeOps(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2590)
        at void android.support.v4.app.FragmentManagerImpl.executeOpsTogether(java.util.ArrayList, java.util.ArrayList, int, int) (FragmentManager.java:2377)
        at void android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(java.util.ArrayList, java.util.ArrayList) (FragmentManager.java:2332)
        at boolean android.support.v4.app.FragmentManagerImpl.execPendingActions() (FragmentManager.java:2239)
        at void android.support.v4.app.FragmentManagerImpl.dispatchStateChange(int) (FragmentManager.java:3231)
        at void android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated() (FragmentManager.java:3181)
        at void android.support.v4.app.FragmentController.dispatchActivityCreated() (FragmentController.java:192)
        at void android.support.v4.app.FragmentActivity.onStart() (FragmentActivity.java:572)
        at void android.support.v7.app.AppCompatActivity.onStart() (AppCompatActivity.java:177)
        at void android.app.Instrumentation.callActivityOnStart(android.app.Activity) (Instrumentation.java:1391)
        at void android.app.Activity.performStart(java.lang.String) (Activity.java:7165)
        at void android.app.ActivityThread.handleStartActivity(android.app.ActivityThread$ActivityClientRecord, android.app.servertransaction.PendingTransactionActions) (ActivityThread.java:2975)
        at void android.app.servertransaction.TransactionExecutor.performLifecycleSequence(android.app.ActivityThread$ActivityClientRecord, android.util.IntArray) (TransactionExecutor.java:180)
        at void android.app.servertransaction.TransactionExecutor.cycleToPath(android.app.ActivityThread$ActivityClientRecord, int, boolean) (TransactionExecutor.java:165)
        at void android.app.servertransaction.TransactionExecutor.executeLifecycleState(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:142)
        at void android.app.servertransaction.TransactionExecutor.execute(android.app.servertransaction.ClientTransaction) (TransactionExecutor.java:70)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1816)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106)
        at void android.os.Looper.loop() (Looper.java:193)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6718)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:493)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:858)

なぜ起きるのか

これは単にLiveDataの関連クラスが含まれていないということ。つまり、当該のクラスを追加すればよい:

dependenceis {
    ...
    implementation "android.arch.lifecycle:livedata:1.1.1"
    ...
}

adbにPATHを通す on Mac

Android Studioをインストールすれば、標準でAndroid SDKやadb (Android Debug Bridge) などのツールが付属する。
が、これらはPATHが通っていないため、ターミナルから利用することができない。

検証環境

  • macOS High Sierra 10.13.6 (on Parallels)
    • HAXMのインストールはSkip
  • Android Studio 3.4
    • android-studio-ide-183.5452501-mac.dmg

前提: Android Studioを推奨設定でインストールしておく

一度Android Studioを起動し、初回ウィザード内のInstall TypeでStandardを選択しておく。

このとき、標準で /Users/$USER/Library/Android/sdk にAndroid SDKがダウンロード・インストールされる。

$ANDROID_HOME を設定する

これはマストではないが、一般的に設定されているためやってしまう。
$ANDROID_HOME は上記のAndroid SDKがインストールされたルートを指す。

echo 'export ANDROID_HOME=$HOME/Library/Android/sdk' >> ~/.bash_profile

PATHを通す

adbは $ANDROID_HOME/platform-tools/adb にある。fastbootコマンドもここにある。

echo 'export PATH=$ANDROID_HOME/platform-tools:$PATH' >> ~/.bash_profile

新しいシェルを起動するなりして再読込し、adbが見えるようになったら完了。

which adb
# /Users/username/Library/Android/sdk/platform-tools/adb

androiddebugkey (debug.keystore) がない場合の対処

前提として、Android実機 / エミュレータ上で動作するアプリ(APKファイル)は署名済みである必要がある。
デバッグ用keystoreは通常、Android Studioを用いてAndroidアプリ開発をしていれば自動生成されている。

が、Android Studioをインストールしていないなどの理由でdebug用keystoreがない場合、以下のコマンドで生成することができる:

keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname 'C=US, O=Android, CN=Android Debug'
# 10,000日間有効な2,048ビットのRSAの鍵ペアと自己署名型証明書(SHA256withRSA)を生成しています
#  ディレクトリ名: C=US, O=Android, CN=Android Debug
# [debug.keystoreを格納中]
# 
# Warning:
# JKSキーストアは独自の形式を使用しています。"keytool -importkeystore -srckeystore debug.keystore -destkeystore debug.keystore -deststoretype pkcs12"を使用する業界標準の形式であるPKCS12に移行することをお薦めします。
ls
# debug.keystore

Apktoolのインストール手順 for Mac

apkを展開したり再パッケージするツールとして有名なものに、ApktoolというCLIツールがある。これのMacにおけるインストール手順について。

検証環境

  • macOS High Sierra 10.13.6 (on Parallels)
  • Apktool 2.4.0
  • OpenJDK 1.8.0_202 (Amazon)

今回クリーンな環境で試してみてわかったが、Android SDKはインストールされていなくてもよい。

wrapperにPATHを通す

Apktool自体はjarとしてパッケージされています。直接実行するためのwrapperスクリプトが用意されているため、これを導入します。

pwd
# /Users/username
mkdir bin && cd bin && pwd
# /Users/username/bin
curl https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/osx/apktool -Lo apktool
chmod +x apktool
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bash_profile

ここで新たなシェルを起動するなどし、~/.bash_profile を読み直す。

Apktool本体を配置する

さきほどのwrapper scriptと同じディレクトリに置く。

pwd
# /Users/username
cd bin && pwd
# /Users/username/bin
curl https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.4.0.jar -Lo apktool.jar
ls
# apktool      apktool.jar
which apktool
# /Users/username/bin/apktool
apktool
# Apktool v2.4.0 - a tool for reengineering Android apk files
# with smali v2.2.6 and baksmali v2.2.6

以上。

SDKMANでJDKを入れる on macOS

SDKMANを使ってJRE / JDKをインストールする手順。

検証環境

  • macOS High Sierra 10.13.6 (on Parallels)
  • SDKMAN 5.7.3

SDKMANをインストールする

curl -s "https://get.sdkman.io" | bash
# ...
# All done!
# 
# Please open a new terminal, or run the following in the existing one:
# 
#     source "/Users/username/.sdkman/bin/sdkman-init.sh"
# 
# Then issue the following command:
# 
#     sdk help
# 
# Enjoy!!!

新しいターミナルを開くなりし、SDKMANを初期化する。

sdk version
# 
# SDKMAN 5.7.3+337

利用するJavaバージョンを選びインストール

たとえばAndroid開発では標準でJDK 8系が用いられたり、JavaでGUI開発をするならJava FXの含まれたものにする必要がある。

sdk list java
# ================================================================================
# Available Java Versions
# ================================================================================
#      13.ea.19-open       10.0.2-zulu         1.0.0-rc-15-grl                    
#      12.0.1-sapmchn      10.0.2-open         1.0.0-rc-14-grl                    
#      12.0.1-zulu         9.0.7-zulu                                             
#      12.0.1-open         9.0.4-open                                             
#      12.0.1.j9-adpt      8.0.212-zulu                                           
#      12.0.1.hs-adpt      8.0.212-amzn                                           
#      12.0.1-librca       8.0.212.j9-adpt                                        
#      11.0.3-sapmchn      8.0.212.hs-adpt                                        
#      11.0.3-zulu         8.0.212-librca                                         
#      11.0.3-amzn         8.0.202-zulu                                           
#      11.0.3.j9-adpt      8.0.202-amzn                                           
#      11.0.3.hs-adpt      8.0.202-zulufx                                         
#      11.0.3-librca       7.0.222-zulu                                           
#      11.0.2-open         7.0.181-zulu                                           
#      11.0.2-zulufx       1.0.0-rc-16-grl                                        

# ================================================================================
# + - local version
# * - installed
# > - currently in use
# ================================================================================

今回は 8.0.202-amzn を使うことにした。

sdk install java 8.0.202-amzn
# 
# Downloading: java 8.0.202-amzn
# ...
# Done installing!
# 
# Setting java 8.0.202-amzn as default.

デフォルトで用いるJavaとして設定

必要に応じて。PATHが通るバイナリが切り替わる。

sdk default java 8.0.202-amzn
# Default java version set to 8.0.202-amzn
sdk current java
# Using java version 8.0.202-amzn
which java
# /Users/username/.sdkman/candidates/java/current/bin/java
java -version
# openjdk version "1.8.0_202"
# OpenJDK Runtime Environment Corretto-8.202.08.2 (build 1.8.0_202-b08)
# OpenJDK 64-Bit Server VM Corretto-8.202.08.2 (build 25.202-b08, mixed mode)

MavenにKotlin Multiplatform向けライブラリを公開する方法

先日 Kotlin Multiplatform 製モバイルアプリ向けの ライブラリを作って公開した ので、その時にちょっとつまづいた部分について。

検証環境

  • Gradle 4.10.3
  • Kotlin Gradle Plugin 1.3.31
    • org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31
  • Android Gradle plugin 3.3.2
    • com.android.tools.build:gradle:3.3.2

前提

Kotlin Multiplatform ProjectはGradleを用いて(Kotlin Gradle Pluginを用いて)ビルドされ、それらの依存関係はMavenリポジトリから取得されます。
すなわちKotlin MPP向けにライブラリを公開する際はMavenリポジトリへ公開する必要があり、またGradleでの依存性解決が前提とされています。

Gradle Module Metadataを有効にする

settings.gradleに以下の1行を記述します:

enableFeaturePreview('GRADLE_METADATA')

Kotlin MPP向けのライブラリは、Gradle Module Metadataを用いて依存関係を解決します。具体的には こういったファイル を出力します。

これがあると何が嬉しいかというと、依存関係としてたったこの1行だけでiOSやAndroid向けのバイナリをよしなに読み込ませることができます:

kotlin {
    ...
    sourceSets {
        commonMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                implementation "com.example:your-published-library:1.0.0"
                // ^ これだけ
            }
        }
        iosMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // なにか参照しなくても、commonで記述した内容でiOS向けバイナリが取ってこれる
            }
        }
        androidMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // こっちも同様
            }
        }
    }
}

各プラットフォーム向けに複数バイナリを出力する

平易な表現にしようとして厳密さを失っていますが、これはiOSやAndroidなどのプラットフォーム特有の問題を解決するためのものです。

iOSの場合

iOS向けアプリ開発では、開発時にはSimulatorを用いる場合が多々あります。
流通しているiOSデバイス実機はARM64アーキテクチャなのに対し、このSimulatorはx64アーキテクチャで動きます。
そのため、ビルドするアーキテクチャとして2通り分(必要ならそれ以上) の記述が必要です:

kotlin {
    def ios64 = iosArm64('iosArm64')
    def iosSim = iosX64('iosX64')

    sourceSets {
        ...
        iosMain {
            ...
        }
        ios64.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
        iosSim.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
    }
}

Androidの場合

現行のGradle Module Metadataの都合上、各variant向けの出力を設定しておく必要があります:

kotlin {
    android {
        publishLibraryVariants('debug', 'release')
    }
   ...
}

maven-publishプラグインを設定する

Kotlin Gradle Pluginは親切なので、maven-publishプラグインが適切に有効化されていれば MavenPublication を自動的に設定してくれます。
よって必要な設定は以下です:

plugins {
    id 'maven-publish' // apply plugin: 'maven-publish'
}
...
group = 'com.example.your.artifact.group'
version = '0.0.1-SNAPSHOT-YOUR-VERSION'

publishする

ローカルリポジトリであれば以下のコマンドでOKです:

./gradlew publishToMavenLocal

実行すると、たとえばiOS, Android向けにここまでの内容を参考に設定していた場合は 以下の成果物が得られます:

  • com.example:your-published-library:1.0.0
  • com.example:your-published-library-metadata:1.0.0
  • com.example:your-published-library-android:1.0.0
  • com.example:your-published-library-android-debug:1.0.0
  • com.example:your-published-library-iosarm64:1.0.0
  • com.example:your-published-library-iosx64:1.0.0

先頭のサフィックスが無いパッケージは .pom と .module ファイルしか持ちません。ここを起点に各プラットフォーム向けの依存性を解決する役割のみを持ちます。
-metadataはcommonで記述された公開I/Fの定義を持ちます。
残るモジュールは、各プラットフォームやABI向けのバイナリです。

使われ方

公開したライブラリの参照方法は Gradle Module Metadataを有効にする のセクションの通りで、サフィックスの無いモジュールを依存関係に加えるのみです。
ただし、Gradle Module Metadataを有効にしていないアプリの場合、各モジュールの参照が必要になる模様です。

こうして開発したライブラリは Kotlin MPP, Kotlin/Native, Pure Androidプロジェクト(Kotlinが有効なもの)で共通して利用することができました。

拒否設定してるのに非通知着信めっちゃくるときの正体はUber Eatsかも

自分が起きている時間帯に 2分に1回以上の頻度で非通知着信が来ており、かつ非通知拒否設定(番号通知お願いサービス, 148)を設定しているにも関わらず端末まで着信する場合、それはUber Eatsサポートセンターの可能性があります。

もし自身の注文した商品がいつになっても届かないなら、Uber Eatsアプリを開いて配達状況を確認してみてください。
たとえば配達パートナーにトラブルが発生し 配達がそのまま遂行できない場合などに、そのまま再配達を行ってもよいかの確認として電話を掛けているようです。

このようなトラブルによる事象は緊急性を伴うため、テキストメッセージ等は利用していないようです。