viewModelScope.launch{} runs only first time

579 views Asked by At

I came across an issue while building my live app.

Example 1 shows the situation when I open my main activity for the first time, the viewModelScope.launch in my MainActivityViewModel works as expected, however when I open the second activity and close the current activity (main activity) and then open the main activity again the viewModelScope.launch does not work because the viewModelScope is not active.

Example 2 shows the situation when I open my main activity for the first time, the viewModelScope.launch in my MainActivityViewModel works as expected, however when I open the second activity and NOT close the current activity (main activity) and then open the main activity again the viewModelScope.launch continues with the previous work and also starts a new work.

I am not sure if that is somehow related to this issue, however, I am using Dagger-Hilt for dependency injection.

My goal here is to find out how to launch the viewModelScope.launch every time when I open my main activity and what causing the issue (Example 1).

Secondly, find out how to stop the viewModelScope.launch (Example 2) when I am opening the second activity without closing the current activity (main activity).

Example 1 - Activity (Java)

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
private MainActivityViewModel viewModel;
...
 @Override
    protected void onCreate(Bundle savedInstanceState) {
...
viewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);

viewModel.testOneViewModelScopeMA();

binding.btnSecondActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSecondActivityIntentWithFinishCurrentActivity(MainActivity.this);
//                startSecondActivityIntentWithOutFinishCurrentActivity(MainActivity.this);
            }
        });
...

private void startSecondActivityIntentWithFinishCurrentActivity(Context _context) {
    Intent intent = new Intent(_context, SecondActivity.class);
    _context.startActivity(intent);
    finish();
}

Example 1 - ViewModel (Kotlin)

@HiltViewModel
class MainActivityViewModel @Inject constructor(
    private val mainActivityRepository: MainActivityRepository,
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
    @MainDispatcher private val mainDispatcher: CoroutineDispatcher,
    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
) : ViewModel() {
...

fun testOneViewModelScopeMA() {
    Timber.d("-> testOneViewModelScope, " +
            "\nviewModelScope is active: ${viewModelScope.isActive}")
    viewModelScope.launch {
        try {
            var count = 0
            while (true) {
                delay(1000L)
                count += 1
                Timber.d(
                    "-> testOneViewModelScope, " +
                            "\nviewModelScope is active: ${viewModelScope.isActive} " +
                            "\noutput: $count "
                )
            }

        } catch (e: CancellationException) {
            Timber.e(
                "-> testOneViewModelScope, " +
                        "\nviewModelScope is active: ${viewModelScope.isActive} " +
                        "\nCancellationException: \n${e.localizedMessage}"
            )
            throw e
        } catch (ex: Exception) {
            Timber.e(
                "-> testOneViewModelScope, " +
                        "\nviewModelScope is active: ${viewModelScope.isActive} " +
                        "\nException: \n${ex.localizedMessage}"
            )
        }
    }
}

Example 2 - Activity (Java)

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
private MainActivityViewModel viewModel;
...
 @Override
    protected void onCreate(Bundle savedInstanceState) {
...
viewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);

viewModel.testOneViewModelScopeMA();

binding.btnSecondActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // startSecondActivityIntentWithFinishCurrentActivity(MainActivity.this);
          startSecondActivityIntentWithOutFinishCurrentActivity(MainActivity.this);
            }
        });
...

   private void startSecondActivityIntentWithOutFinishCurrentActivity(Context _context) {
        Intent intent = new Intent(_context, SecondActivity.class);
        _context.startActivity(intent);
    }

Example 2 - ViewModel (Kotlin) - the same as Example 1 ViewModel

1

There are 1 answers

10
fshdn19 On

Let separate your issues

  1. You want to launch a coroutine in ActivityA, every time you open it (newly created and/or go back from other activities). If these, put a call to your view model in ActivityA#onResume. Because Activity#onCreate is called only one time when the your application creates the activity

  2. You want to cancel your coroutine in Activity A, when you leave it to navigate to other activities.

  • Add a supervisor job to the coroutine's launch
  • In ActivityA's onPause call your view model to cancel the supervisor job