Singleton class in Kotlin with init - android

I just to wish clarify some methodology using singletons in Kotlin.
I have this class:
class TestClass {
companion object {
val instance = TestClass()
fun runSync2() {
Log.d("TAG", "Running sync2")
}
init {
Log.d("TAG", "Init companion")
}
}
init {
Log.d("TAG", "Init class")
}
fun runSync1() {
Log.d("TAG", "Running sync1")
}
}
And this test functions:
1. TestClass.instance.runSync1()
2. TestClass.runSync2()
3. TestClass().runSync1()
When calling function 1 twice, init inside companion object will be called once. So only one instance of TestClass is created and run runSync1() twice, correct?
When calling function 2 twice, init inside companion object will be called once. So only one instance of TestClass is created and run runSync2() twice, correct? So what is the difference between 1 and 2?
When calling function 2 twice, 2 instances of TestClass crated, 2 init inside class will run and 2 runSync1 will run independently?
Can you please provide more clarification and correct the wrong parts?

When understanding the companion objects one thing to remember is that
A companion object is initialized when the corresponding class is loaded (resolved) that matches the semantics of a Java static initializer.
So the init block inside the companion object will be executed only once, when the TestClass is being loaded, same goes with the property named instance, it will be assigned an object of TestClass only once at class load time.
To better understand this you can look at your code converted to java, which will look something like
public final class TestClass {
// Property of companion object and the init block are now part of TestClass
private static final TestClass instance = new TestClass();
static {
Log.d("TAG", "Init companion");
}
public static final TestClass.Companion Companion = new TestClass.Companion((DefaultConstructorMarker)null);
public final void runSync1() {
Log.d("TAG", "Running sync1");
}
public TestClass() {
Log.d("TAG", "Init class");
}
public static final class Companion {
public final TestClass getInstance() {
return TestClass.instance;
}
public final void runSync2() {
Log.d("TAG", "Running sync2");
}
private Companion() { }
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

Related

Define common functions in kotlin

For define Util(common functions) we can use 3 approach :
First :
We can define a File and add function to it like this :
fun func1(): String {
return "x"
}
fun func2(): String {
return "y"
}
Then we can use this in every where like this :
fileName.func1()
Second :
We can define a class and add these functions to class like this :
class Util{
fun func1(): String {
return "x"
}
fun func2(): String {
return "y"
}
}
we can inject it and use this like :
private val mUtil: Util by inject()
mutil.func1()
Third:
We can define an object like this :
object Util{
fun func1(): String {
return "x"
}
fun func2(): String {
return "y"
}
Then we can use this like :
Util.func1()
suppose that we have reusable fun that it use in different class , activity or fragment now is better to use first , second or third ?
According to my understanding :
In the first example, we simply define a function to be used within the scope of component (could be a fragment or an activity ..)
In the second example, which I think more used in Java than Kotlin, you simply create a class in which you define some static variable or function to be accessed in your components later
Example 3, I have no idea as I never used that
first one is static function
second one is normal function
third is singleton function
you can decomplie to see the java code and see their differs.
Kotlin file
fun fun1() {}
class TestClass {
fun testFun() {}
}
object ObjectClass {
fun objectFun() {}
}
Java file
public final class TTTKt {
public static final void fun1() {
}
}
public final class TestClass {
public final void testFun() {
}
}
public final class ObjectClass {
public static final ObjectClass INSTANCE;
public final void objectFun() {
}
private ObjectClass() {
}
static {
ObjectClass var0 = new ObjectClass();
INSTANCE = var0;
}
}

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;
}
}
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
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)
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()
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.
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.
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.
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
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()
}
}

Access Parent class variables in companion Object in Kotlin

I am trying to call static function of one class in other like java , But in kotlin I can not make a static function , and I have to make a companion object in which I have to define my function , But while doing this I am not able to access parent class variables , is there any way I can achieve this in kotlin .
class One {
val abcList = ArrayList<String>()
companion object {
fun returnString() {
println(abcList[0]) // not able to access abcList here
}
}
}
class Two {
fun tryPrint() {
One.returnString()
}
}
// In Java we can do it like this
class One {
private static ArrayList<String> abcList = new ArrayList<>();
public void tryPrint() {
// assume list is not empty
for(String ab : abcList) {
System.out.println(ab);
}
}
public static void printOnDemand() {
System.out.println(abcList.get(0));
}
}
class Two {
public void tryPrint(){
One.printOnDemand();
}
}
I want to access fun returnString() like static function of class one like we do in java , if any one have achieved this please help .
In your case abcList is a member variable of the class. Each instance of a class has their own version of its member variables which means that a static method cannot access them. If you want to access it from your companion object it has to be static too.
class One {
companion object {
val abcList = ArrayList<String>()
fun returnString() {
println(abcList[0])
}
}
}
class Two {
fun tryPrint() {
One.returnString()
}
}
This code will work, but keep in mind that in this case there will be only one instance of abcList. Accessing a member variable from a static function is not possible (not even in Java).
Here's the Kotlin version of your Java example:
class One {
companion object {
val abcList = ArrayList<String>()
fun printOnDemand() {
println(abcList[0])
}
}
fun tryPrint() {
for (ab in abcList) {
println(ab)
}
}
}
class Two {
fun tryPrint() {
One.printOnDemand()
}
}
Rule: You can't access static properties, members of a class in none static members and you can't access none static properties, members of a class in static members which is the companion object class.
This rule is in both Java and Kotlin. If you want to access a none static member of a class
inside static members you have to declare it inside companion object class.
Use the following code for you case.
object One {
val abcList: MutableList<String> = mutableListOf()
fun returnString() {
println(abcList[0])
}
fun printOnDemand() {
println(abcList[0]);
}
}
class Two {
fun tryPrint() {
One.printOnDemand()
}
}

Interface as property in Kotlin

In some Fragments and other classes (in Java) I have a public interface ISomeActions with functions that I then call from some other parts in that class, to denote actions. So, I have a ISomeListener listener that I set in the constructor, or right after I create an object of SomeClass. How can I achieve this in Kotlin?
Example:
public class SomeClass{
public ISomeListener listener;
public interface ISomeListener{
public void doSomething();
}
void actuallyDoSomething(){
listener.doSomething();
}
}
I think I can use a lateinit var listener : SomeListener, but I don't know if that would be adequate. The member is an Interface, and not an implementation of that Interface in case it's called after the Activity finishes.
1) Constructor
class MyClass(private val myInterface: MyInterface) { ... }
This way myInterface is immutable. You can be sure that this will never be null.
2) After object creation - null
class MyClass(var myInterface: MyInterface? = null) { ... }
Now myInterface is mutable and can be null at any time. You can safety access it via myInterface?.someMethod() (is myInterface is null call won't be invoked). To access and assign listener use myClassInstance.myInterface = myInterfaceInstance.
Because of default param you can create your class with or without constructor param:
val myClass1 = MyClass()
val myClass2 = MyClass(interfaceInstanceOrNull)
3) After object creation - lateinit
class MyClass(myInterface: MyInterface? = null) {
lateinit var _myInterface: MyInterface
init {
if (myInterface != null) { _myInterface = myInterface }
}
}
In my opinion the worst solution. lateinit is designed for injecting field after creation, for DI frameworks like dagger. If you try call method on myInterface if it is not init yet UninitializedPropertyAccessException will be thrown. You can check if property is initlialized like this:
if (::_myProperty.isInitialized) { _myProperty.doSth() }
In my opinion you should use the first or the second approach. As I understand you cannot always init object with instance of MyInterface, so the second one seems legit for your puroposes.

Should I make AsyncTask member of LiveData or Repository class - As replacement of Loader

LiveData/ ViewModel is a good replacement for complicated Loader.
Based on https://medium.com/google-developers/lifecycle-aware-data-loading-with-android-architecture-components-f95484159de4 ,
AsyncTask is member of LiveData.
public class JsonLiveData extends LiveData<List<String>> {
public JsonLiveData(Context context) {
loadData();
}
private void loadData() {
new AsyncTask<Void,Void,List<String>>() {
}.execute();
}
}
However, based on the presentation from Lyla Fujiwara:
Should I make AsyncTask member of Repository class?
You should avoid running your AsyncTask in LiveData. LiveData should really only be concerned with the observation of data. Not the act of changing the data.
The best way of dealing with this situation is to use the ViewModel / Repository pattern.
Activity / Fragment observes LiveData from ViewModel, ViewModel observes LiveData from Repository. Changes are made in the repository, which are pushed to its LiveData. Those changes are delivered to the Activity / Fragment (through the ViewModel).
I would avoid using AsyncTask in this situation. The bonus of AsyncTask is that you can get results on the UI thread after doing work. In this case though, that isn't necessary. LiveData will do that for you.
Here is an (untested) example:
Activity
public class MyActivity extends AppCompatActivity {
private MyViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up your view model
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// Observe the view model
viewModel.getMyLiveData().observe(this, s -> {
// You work with the data provided through the view model here.
// You should only really be delivering UI updates at this point. Updating
// a RecyclerView for example.
Log.v("LIVEDATA", "The livedata changed: "+s);
});
// This will start the off-the-UI-thread work that we want to perform.
MyRepository.getInstance().doSomeStuff();
}
}
ViewModel
public class MyViewModel extends AndroidViewModel {
#NonNull
private MyRepository repo = MyRepository.getInstance();
#NonNull
private LiveData<String> myLiveData;
public MyViewModel(#NonNull Application application) {
super(application);
// The local live data needs to reference the repository live data
myLiveData = repo.getMyLiveData();
}
#NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
}
Repository
public class MyRepository {
private static MyRepository instance;
// Note the use of MutableLiveData, this allows changes to be made
#NonNull
private MutableLiveData<String> myLiveData = new MutableLiveData<>();
public static MyRepository getInstance() {
if(instance == null) {
synchronized (MyRepository.class) {
if(instance == null) {
instance = new MyRepository();
}
}
}
return instance;
}
// The getter upcasts to LiveData, this ensures that only the repository can cause a change
#NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
// This method runs some work for 3 seconds. It then posts a status update to the live data.
// This would effectively be the "doInBackground" method from AsyncTask.
public void doSomeStuff() {
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
myLiveData.postValue("Updated time: "+System.currentTimeMillis());
}).start();
}
}

Categories

Resources