LearningCharlito
LearningCharlito

Reputation: 368

Android ViewModel object recreate with kotlin but not with java

Im new to kotlin, and mvvm, but i was able to make it work in java, but when i made a new example mvvm-retrofit-corutines in kotlin, the view model gets called all the time on the OnCreate function is called, (which shouldn't happen according to docs and works fine in java).

MainActivity:

lateinit var viewModel : MyViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    //Here we can see the logs in every orientation changed in the emulator.
    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    viewModel.getMutableLiveDataModel().observe(this, Observer {
        Log.d("zzzz","lamda executes onChanged method -> "+ it.otherValues). //element from model
    })
}

MyViewModel:

 class MyViewModel : ViewModel() {

private lateinit var objectTypeModel: MutableLiveData<MyTestModel>

fun getMutableLiveDataModel():MutableLiveData<MyTestModel>{

    //Gets the model from a retrofit service call
    objectTypeModel = MyRepository.getModelFromService()

    return objectTypeModel
}
}

Am i doing something wrong? already tried convert 'viewModel' into local variable as suggested in other post.

Java Code, MainActivity

MyViewModel model;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    model =  new ViewModelProvider(this).get(MyViewModel.class);
    model.getUsers().observe(this, new Observer<Integer>() {
        @Override
        public void onChanged(Integer users) {
            Log.d("zzzz","updated value..")
        }
    });
}

Model

 public class MyViewModel extends ViewModel {

private MutableLiveData<Integer> users;
public LiveData<Integer> getUsers() {
    if (users == null) {
        users = new MutableLiveData<Integer>();
        users.setValue(10);
    }
    return users;
}
}

Upvotes: 1

Views: 2422

Answers (4)

bitDeveloper
bitDeveloper

Reputation: 132

I've tried the same concept and as expected, the functionality in Java and Kotlin is identical. In the LogCat, I expected that the log should be printed on every rotation and it does. Now, let me tell you why it happens.

So, as per the documentation ViewModel instance stays alive after the configuration change. Basically, ViewModel uses the same instance if your activity is re-creating numerous times but it's not getting destroyed (calling finish()). But it's not the magic of the ViewModel it's the magic of LiveData.

LiveData is an observable data view holder so it sends the latest preserved value to the active observers on every configuration change which you're observing in the onCreate().

Let me present you my code.

Java

// Activity

public class JavaActivity extends AppCompatActivity {
    private static final String TAG = "JavaActivity";

    private JavaViewModel javaViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_java);

        // Ignore this listener
        findViewById(R.id.go_to_kotlin_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        // Main
        javaViewModel = new ViewModelProvider(this).get(JavaViewModel.class);
        javaViewModel.getJavaLiveData().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                Log.d(TAG, "onChanged: " + integer);
            }
        });
    }
}

// ViewModel

public class JavaViewModel extends ViewModel {
    private MutableLiveData<Integer> javaLiveData;

    public LiveData<Integer> getJavaLiveData() {
        if(javaLiveData == null) {
            javaLiveData = new MutableLiveData<>();
            javaLiveData.setValue(10);
        }
        return javaLiveData;
    }
}

Kotlin

// Activity

class KotlinActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "KotlinActivity"
    }

    private lateinit var kotlinViewModel: KotlinViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin)

        // Ignore this listener
        findViewById<Button>(R.id.go_to_java_activity_btn).setOnClickListener {
            startActivity(Intent(this, JavaActivity::class.java))
        }

        // Main
        kotlinViewModel = ViewModelProvider(this).get(KotlinViewModel::class.java)
        kotlinViewModel.getKotlinLiveData().observe(this, Observer {
            Log.d(TAG, "onCreate: $it")
        })
    }
}

// ViewModel

class KotlinViewModel : ViewModel() {
    private lateinit var kotlinLiveData: MutableLiveData<Int>

    fun getKotlinLiveData(): LiveData<Int> {
        if (!::kotlinLiveData.isInitialized) {
            kotlinLiveData = MutableLiveData()
            kotlinLiveData.value = 10
        }
        return kotlinLiveData
    }
}

If you have any follow-up questions, leave them in comments. Thanks!

References

  • LiveData - Official Documentation
  • ViewModel - Official Documentation
  • This is a great article on how ViewModel works internally.
  • Do read this article as well

Upvotes: 0

Mohammad Khair
Mohammad Khair

Reputation: 476

Try

MainActivity

lateinit var viewModel : MyViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    viewModel.objectTypeModel.observe(this, Observer {
        Log.d("zzzz","lamda executes onChanged method -> "+ it.otherValues).         
        //element from model
    })
}

ViewModel

class MyViewModel : ViewModel() {

    val objectTypeModel= MutableLiveData<MyTestModel>()

    init {
        objectTypeModel.value = MyRepository.getModelFromService()

    }
}

Upvotes: 0

Ranjan
Ranjan

Reputation: 1356

I think the issue lies in your kotlin viewmodel class, if you are not getting the value(unless you have few more issues in other classes)

  1. Fix your kotlin viewmodel class in which data is not set in MutableLiveData, you forgot to add a piece of code.

     //Here it is like this
     objectTypeModel.value= MyRepository.getModelFromService()
    
  2. AFAIK onCreate() only gets called when activity is created. So its natural if your viewmodel is getting created again. You can also check it by init{} method in your viewmodel class.

  3. Still if you are not satisfied move your api call from activity's onCreate() method to viewmodels init{} method and just observe the changes from Activity. Your getMutableLiveDataModel() will called once when viewmodel object gets created.

  4. If your java viewmodel example is running as you expected. Then,try to convert the java class to kotlin and run it again(just paste the java code to a kotlin file, it will ask you to convert it), it should work.

Upvotes: 1

Sasi Kumar
Sasi Kumar

Reputation: 13358

If you don't want to recreate view model declare your view model like this

 private val model: MyViewModel by activityViewModels()

for more details refer ViewModel

Upvotes: 1

Related Questions