I've been using following Kotlin extension to add my lifecycle observers:
fun Lifecycle.addObserverUntilDestroy(observer: LifecycleObserver) {
addObserver(observer)
addObserver(object : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
removeObserver(this)
removeObserver(observer)
}
})
}
The idea was to automatically remove the observer when the Lifecycle is being destroyed.
So I can call it in Activity's onPostCreate (in this case a Java code but it is the same when calling from Kotlin):
ArchitectureComponentExtensions.addObserverUntilDestroy(getLifecycle(), myViewModel);
And have this code in my ViewModel (a custom one, not the on from Architecture Components):
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onViewDestroyed() {
// This is not being called anymore
}
But this stopped working after migrating from:
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.OnLifecycleEvent
to:
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
Any ideas why this is not working anymore?
Related
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
#RunWith(AndroidJUnit4::class)
class MainActivityTest {
#Test
fun setupData() {
var activityScenario = ActivityScenario.launch(MainActivity::class.java)
activityScenario.onActivity {
}
}
}
I don't know why it forces finish() is called.
I override finish method, but it called twice and force closing.
I'm learning android development from Android_developer. while using CoroutinesWorker, I encountered a problem whilst working with Kotlin
Logcat
com.google.samples.apps.devbyteviewer E/WM-WorkerFactory: Could
not instantiate
com.google.samples.apps.devbyteviewer.work.RefreshDataWorker
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at
androidx.work.WorkerFactory.createWorkerWithDefaultFallback(Worker
Factory.java:96)
at
androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:
244)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:136)
The Application class from where I instantiate workmanager.
Application class
package com.google.samples.apps.devbyteviewer
import android.app.Application
import androidx.work.*
import
com.google.samples.apps.devbyteviewer.work.RefreshDataWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.concurrent.TimeUnit
As I have read in related posts, my worker class is a top level class and in an independent file.
Worker class
package com.google.samples.apps.devbyteviewer.work
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.google.samples.apps.devbyteviewer.database.getDatabase
import com.google.samples.apps.devbyteviewer.repository.VideosRepository
import retrofit2.HttpException
import timber.log.Timber
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
val database = getDatabase(applicationContext)
val repository = VideosRepository(database)
try {
repository.refreshVideos()
Timber.d("Work request for sync is run")
} catch (e: HttpException) {
return Result.retry()
}
return Result.success()
}
companion object {
/**
* Define a work name to uniquely identify this worker.
*/
const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
}
I've had the same problem while working with WorkManager.
Pulling meaning from your post and the error message, this is very likely an import related issue. Check your Application dependencies.
Rather than:
implementation "android.arch.work:work-runtime-ktx:$work_version"
Use:
implementation "androidx.work:work-runtime-ktx:$work_version"
I am trying to upgrade my koin usage from 2.1.6 -> 3.0.2 and am having troubles with the scoped injections.
I have MVPs where the Activity/Fragment is the view and i want to inject the view in the presenter.
so i have
module {
scope(named<MainActivity>()) {
scoped<View> { getSource() }
scoped<Presenter> {
MainPresenter(
view = get()
)
}
}
in 2.1.6 i used to used to do this and all was fine:
class MainActivity :
AppCompatActivity(),
MainContract.View {
private val presenter: MainContract.Presenter by currentScope.inject()
...
}
and then in MainActivity i NOW have:
class MainActivity :
AppCompatActivity(),
MainContract.View,
AndroidScopeComponent {
override val scope : Scope by activityScope()
private val presenter: MainContract.Presenter by scope.inject()
...
}
and Presenter:
class MainPresenter(
private val view: MainContract.View
){
...
}
but it cannot get the source object and i get the error:
Instance creation error : could not create instance for [Single:'uk.co.sentinelweb.cuer.app.ui.main.MainContract$View',scope:q:'uk.co.sentinelweb.cuer.app.ui.main.MainActivity']: java.lang.IllegalStateException: Can't use Scope source for uk.co.sentinelweb.cuer.app.ui.main.MainContract$View - source is:null
(i.e. when it tries to create the presenter it can't find the scoped MainActivity)
this is the existing code (using 2.1.6)
https://github.com/sentinelweb/cuer/blob/develop/app/src/main/java/uk/co/sentinelweb/cuer/app/ui/main/MainActivity.kt
Have i got a lot more re-writing to do here? I am struggling to find a good example for scoped injection in the koin docs and a lot of it seems old. A lot of projects seem not to use scoping.
So if anyone can tell me what wrong here or point me to a decent example of something similar id appreciate it a lot!
So it seems for the lifecycle aware extension methods it just doesnt set the scope variable - perhaps they are paranoid about mempory leaks but since the scope is cleared and on the destroy lifecycle method - this shouldnt be a problem really.
My solution was to just make new extension methods which actually just pass in the source - I am not sure why this would be a problem. There is an issue here for it https://github.com/InsertKoinIO/koin/issues/851
package xxx
import android.app.Service
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import org.koin.android.scope.AndroidScopeComponent
import org.koin.android.scope.createScope
import org.koin.android.scope.getScopeOrNull
import org.koin.androidx.scope.LifecycleScopeDelegate
import org.koin.core.Koin
import org.koin.core.component.getScopeId
import org.koin.core.component.getScopeName
import org.koin.core.context.GlobalContext
import org.koin.core.context.KoinContext
import org.koin.core.scope.Scope
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/** copied from org.koin.androidx.scope.FragmentExt but scope wont link as fragment is not attached */
fun ComponentActivity.activityScopeWithSource() = LifecycleScopeWithSourceDelegate(this)
/** copied from org.koin.androidx.scope.FragmentExt but scope wont link as fragment is not attached */
fun Fragment.fragmentScopeWithSource() = LifecycleScopeDelegate(this) { koin: Koin ->
koin.createScope(getScopeId(), getScopeName(), this)
}
/** links the fragment scope to the activity scope */
fun Fragment.linkScopeToActivity() {
(this as AndroidScopeComponent).scope.linkTo((requireActivity() as AndroidScopeComponent).scope)
}
/** copied from org.koin.android.scope.ServiceExtKt */
fun Service.serviceScopeWithSource() = lazy { getScopeOrNull() ?: createScope(this) }
/** wraps org.koin.androidx.scope.LifecycleScopeDelegate - to add source */
class LifecycleScopeWithSourceDelegate(
val lifecycleOwner: LifecycleOwner,
koinContext: KoinContext = GlobalContext,
createScope: (Koin) -> Scope = { koin: Koin ->
koin.createScope(
lifecycleOwner.getScopeId(),
lifecycleOwner.getScopeName(),
lifecycleOwner
)
},
) : ReadOnlyProperty<LifecycleOwner, Scope> {
private val _lifecycleDelegate = LifecycleScopeDelegate(lifecycleOwner, koinContext, createScope)
override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>): Scope {
return _lifecycleDelegate.getValue(thisRef, property)
}
}
I have checked all answers on the stack. But nothing helps. Might be problem in Kotlin + DI
So I got an exception that Eventbus for some reason cant initialize itself in presenter class. By debugger I see that method of initializing executed proper way from onCreate. I use the same code as I use in other classes (Java classes) and other works correctly. Could you please give an advice where might be an issue
Error
Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class com.myapp.mvp.ui.myprofile.MyProfileActivity and its super classes have no public methods with the #Subscribe annotation
at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:67)
at org.greenrobot.eventbus.EventBus.register(EventBus.java:140)
at com.myapp.mvp.ui.myprofile.MyProfileActivity.onCreate(MyProfileActivity.kt:67)
at android.app.Activity.performCreate(Activity.java:7981)
at android.app.Activity.performCreate(Activity.java:7970)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
My presenter class
import com.arellomobile.mvp.InjectViewState
import com.arellomobile.mvp.MvpPresenter
import com.myapp.UserProfileItemsList
import com.myapp.eventbus.VisitsEvent
import com.myapp.eventbus.UserEvent
import com.myapp.models.UserDescriptionModel
import com.myapp.mvp.model.interactor.myprofile.MyProfileInteractor
import com.myapp.utils.ActionsCountInfoCallback
import com.myapp.utils.UserCallback
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.util.ArrayList
import javax.inject.Inject
#InjectViewState
class MyProfilePresenter #Inject constructor(private val interactor: MyProfileInteractor) : MvpPresenter<MyProfileView>() {
private val userDescriptionList = ArrayList<UserDescriptionModel>()
override fun onFirstViewAttach() {
super.onFirstViewAttach()
setAllCurrentUserInfo()
setActionsCount()
}
fun setAllCurrentUserInfo() {
interactor.getAllCurrentUserInfo(UserCallback{ fsUser ->
viewState.setUserData(fsUser.name, fsUser.age, fsUser.country, fsUser.image)
userDescriptionList.addAll(UserProfileItemsList.initData(fsUser))
viewState.setList(userDescriptionList)
EventBus.getDefault().post(UserEvent(fsUser))
})
}
private fun setActionsCount() {
interactor.getActionsCountInfo(
ActionsCountInfoCallback{ visits, likes -> viewState.setActionsCount(visits, likes) })
}
#Subscribe
private fun updateActionsCount(event: VisitsEvent){
viewState.setActionsCount(event.getmVisits(), event.getmLikes())
}
fun registerSubscribers() {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this)
}
}
fun unsubscribe(){
EventBus.getDefault().unregister(this)
}
}
MyProfileActivity and its super classes have no public methods with the #Subscribe annotation
Emphasis mine. Remove the private from your function here:
#Subscribe
private fun updateActionsCount(event: VisitsEvent)
I'm using firebase_messaging in my flutter application.
To handle background messages with firebase messaging in pub they suggested to create new Application.java file and replace java file name in AndroidManifest file.
In my application i'm using kotlin and i already implemented some native code in MainActivity.kt
So how to write this code in kotlin.
package io.flutter.plugins.firebasemessagingexample;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
it is mandatory to replace MainActivity to Application in AndroidManifest file?
Here is the working background notification kotlin code:
package com.example.yourapp
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry?) {
io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
Here is the Kotlin code for the new firebase cloud messaging version:
package id.your.app
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService
// import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry?) {
// FlutterFirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin"))
}
}