> Configure project :
Gradle version Gradle 7.0
FAILURE: Build failed with an exception.
* Where:
Script '/script/maven-plugin.gradle' line: 2
* What went wrong:
A problem occurred evaluating script.
> Failed to apply plugin 'com.github.dcendents.android-maven'.
> Could not create plugin of type 'AndroidMavenPlugin'.
> Could not generate a decorated class for type AndroidMavenPlugin.
> org/gradle/api/publication/maven/internal/MavenPomMetaInfoProvider
...
Getting tasks: ./gradlew tasks --all
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -Dhttps.protocols=TLSv1.2
Tasks:
WARNING:
Gradle 'install' task not found. Please add the 'maven' or 'android-maven' plugin.
See the documentation and examples: https://jitpack.io/docs/
Adding android plugin
Adding maven plugin
Found android library build file in cast-ktx
Found android library build file in cast-framework-ktx
Found android library build file in cast-tv-ktx
Running: ./gradlew clean -Pgroup=com.github.nashcft -Pversion=0.1.0 install
...
publicstaticvoid injectIfNeededIn(Activity activity) {
if (Build.VERSION.SDK_INT >= 29) {
// On API 29+, we can register for the correct Lifecycle callbacks directly
LifecycleCallbacks.registerIn(activity);
}
// Prior to API 29 and to maintain compatibility with older versions of// ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and// need to support activities that don't extend from FragmentActivity from support lib),// use a framework fragment to get the correct timing of Lifecycle events
android.app.FragmentManager manager = activity.getFragmentManager();
if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
// Hopefully, we are the first to make a transaction.
manager.executePendingTransactions();
}
}
// ...@RequiresApi(29)staticclass LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
// ...
}
void clearNonConfigState(@NonNull Fragment f) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "Clearing non-config state for " + f);
}
// Clear and remove the Fragment's child non config state
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
if (childNonConfig != null) {
childNonConfig.onCleared();
mChildNonConfigs.remove(f.mWho);
}
// Clear and remove the Fragment's ViewModelStore
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore != null) {
viewModelStore.clear();
mViewModelStores.remove(f.mWho);
}
}
Through discussions with adamp@, we wanted to make a firm decision on exactly when this will run as the final LifecycleObserver going out. onDestroy() does not offer the same guarantee since developers can call it at any point in their onDestroy() method.
ViewModel cleared at com.github.nashcft.app.ActivityViewModel.onCleared(ActivityViewModel.kt:23)
at androidx.lifecycle.ViewModel.clear(ViewModel.java:138)
at androidx.lifecycle.ViewModelStore.clear(ViewModelStore.java:62)
at androidx.activity.ComponentActivity$4.onStateChanged(ComponentActivity.java:261)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:68)
at androidx.lifecycle.ReportFragment$LifecycleCallbacks.onActivityPreDestroyed(ReportFragment.java:224)
at android.app.Activity.dispatchActivityPreDestroyed(Activity.java:1498)
at android.app.Activity.performDestroy(Activity.java:8241)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1344)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5096)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5140)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:44)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
ViewModel cleared at com.github.nashcft.app.ActivityViewModel.onCleared(ActivityViewModel.kt:23)
at androidx.lifecycle.ViewModel.clear(ViewModel.java:138)
at androidx.lifecycle.ViewModelStore.clear(ViewModelStore.java:62)
at androidx.activity.ComponentActivity$4.onStateChanged(ComponentActivity.java:261)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:68)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:144)
at androidx.lifecycle.ReportFragment.onDestroy(ReportFragment.java:134)
at android.app.Fragment.performDestroy(Fragment.java:2782)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1451)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1576)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1637)
at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3046)
at android.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:3026)
at android.app.FragmentController.dispatchDestroy(FragmentController.java:248)
at android.app.Activity.performDestroy(Activity.java:7394)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1306)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4443)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4476)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:39)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Fragment
ViewModel cleared at com.github.nashcft.app.FragmentViewModel.onCleared(FragmentViewModel.kt:23)
at androidx.lifecycle.ViewModel.clear(ViewModel.java:138)
at androidx.lifecycle.ViewModelStore.clear(ViewModelStore.java:62)
at androidx.fragment.app.FragmentManagerViewModel.clearNonConfigState(FragmentManagerViewModel.java:199)
at androidx.fragment.app.FragmentStateManager.destroy(FragmentStateManager.java:769)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:350)
at androidx.fragment.app.SpecialEffectsController$FragmentStateManagerOperation.complete(SpecialEffectsController.java:742)
at androidx.fragment.app.SpecialEffectsController$Operation.cancel(SpecialEffectsController.java:594)
at androidx.fragment.app.SpecialEffectsController.forceCompleteAllOperations(SpecialEffectsController.java:329)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3116)
at androidx.fragment.app.FragmentManager.dispatchDestroy(FragmentManager.java:3091)
at androidx.fragment.app.FragmentController.dispatchDestroy(FragmentController.java:334)
at androidx.fragment.app.FragmentActivity.onDestroy(FragmentActivity.java:322)
at com.github.nashcft.app.MyFragmentActivity.onDestroy(MyFragmentActivity.kt:43)
at android.app.Activity.performDestroy(Activity.java:8245)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1344)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5096)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5140)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:44)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
*1:余談だが androidx.core にも ComponentActivity が存在し、 androidx.activity.ComponentActivity の直接の親 class となっている
そもそもなんで使う必要があるのかというと、Android アプリとしてコードを実行する時と local test を実行する時の環境の差が関係している。Android アプリとして実行される際は、大雑把に説明すると、アプリのプロセスが起動した後まず ActivityThread で main looper の準備と実行が行われ*1、それから諸々の準備ができた後に我々の書いたアプリケーションコードが動く。しかし local test は JUnit test として実行されるので、Android アプリに関わるセットアップの類は実行されない。これがどう関係するかというと、local test 実行時には main looper のセットアップが行われず Looper#getMainLooper は null のままとなるので、それに依存する Handler や、 main looper をもとに "Android アプリにおける main thread" を判定する処理が機能しなくなってしまう。
class MyTest {
fun test() {
val mainLooper = Looper.getMainLooper() // nullval livedata = MutableLiveData<Int>()
livedata.observeForever { println("onChanged") }
// java.lang.NullPointerException// at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread// at androidx.arch.core.executor.ArchTaskExecutor.isMainThread// at androidx.lifecycle.LiveData.assertMainThread// at androidx.lifecycle.LiveData.observeForever
}
}
class MyTestWithInstantTaskExecutorRule {
@get:Ruleval instantTaskExecutorRule = InstantTaskExecutorRule()
fun `test with InstantTaskExecutorRule`() {
val livedata = MutableLiveData<Int>()
var count = 0
livedata.observeForever {
println("onChanged: $it")
count++
}
livedata.value = 1// ArchTaskExecutor#postToMainThead の振る舞いが同期化されるので、// postValue もテスト実行時に observer に通知が届くようになる
livedata.postValue(2)
assertThat(count).isEquelTo(2) // passed
}
}
使い分けとしては, LiveData や Room しか関わらないような local test については InstantTaskExecutorRule だけ使って、他にも Android framework への依存があるようなコンポーネントに対する local test では Robolectric も使う、みたいな感じで良いと思う。