I am developing an application in which I am using retrofit library for calling the web services . After calling web-service a callback return Response then I am passing the Response to the next activity. I want to know the best approach for this .
I am new in memory related problems please correct me if I am totally wrong.
Shall I make a new class then pass a weak reference to that class and call the function from that class on the main activity.
Or
I shall register a new event on event bus and when the callback returns the object ,fire the event and call the function.
Please consider what is good to avoid Memory Leaks.
Saving callbacks/listeners in weak references is not a good idea. See
Pros and Cons of Listeners as WeakReferences
Are anonymous listeners incompatible with weak references?
You can "broadcast" results of asynchronous operations (Network calls in your case) on completion using event bus, and have other objects (Views, Fragments, Activities) register to event bus. Points to note:
Listeners must always un-register properly, else its a memory leak.
You'll need to create a new class for each event type. Soon this number will grow.
Inheritance and Event bus do not play well. There is no proper "overriding" of listening methods possible.
Perhaps some other object also requested the same data. Since EventBus broadcasts to all, listeners might get multiple events of same type, at unsuspected timing.
Particular to Retrofit usage, if you do requests asynchronously as:
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
repos.enqueue(myCallback)
Then you must remember to cancel all calls that a component has made, when the life-cycle of component is complete. I.e onDestroy() of Activity, onDetach() of Fragment, onDetachFromWindow() of View:
repos.cancel();
Related
I been struggling a lot thinking about where to place Android Services in the new Android recommended Architecture. I came up with many possible solutions, but I cannot make up my mind about which one is the best approach.
I did a lot of research, and I couldn't find any useful guideline nor tutorial. The only hint I found about where to place the Service in my app architecture is this one, from #JoseAlcerreca Medium post
Ideally, ViewModels shouldn’t know anything about Android. This improves testability, leak safety and modularity. A general rule of thumb is to make sure there are no android.* imports in your ViewModels (with exceptions like android.arch.*). The same applies to presenters.
According to that, I should place my Android Services on the top of my Architecture Components hierarchy, at the same level as my Activities and Fragments. That's because Android Services are part of the Android framework, so ViewModels shouldn't know about them.
Now, I will explain briefly my scenario, but only to make the panorama clearer, not because I want an answer for this specific scenario.
I have an Android Application that has a MainActivity with many fragments in it, all of them tied together in a BottomNavBar.
I have a BluetoothService bound to myActivity and one of its fragments (because I want the Service to have the same lifecycle as the Activty but I also want to interact with it directly from my fragment).
The fragment interacts with the BluetoothService to get two types of information:
Information about the state of the Bluetooth connection. Doesn't need to be persisted.
Data that comes from the Bluetooth Device (it is a Scale, so weight and body composition in this case). Needs to be persisted.
Here are the 3 different architectures I can think of:
LiveData inside AndroidService
UPDATE: This is the approach I personally went with at the time because it worked well and allowed me to get it done relatively fast. However, I suggest following the updated answer by Jeel Vankhede for what seems to be a more "idiomatic" implementation.
The LiveData with the state of the connection and with the weight
measurements coming from the Bluetooth Device are inside the BluetoothService.
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The Fragment observes the LiveData about the state of the connection
and adapts the UI accordingly (for example, enable a button if the
state is connected).
The Fragment observes the LiveData of the new weight measurements. If a new weight measurement comes from the BluetoothDevice, the Fragment then tells its own ViewModel to save the new data. It is done via a Repository class.
Shared ViewModel between fragment and AndroidService
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The BluetoothService updates the Bluetooth related LiveData in the shared ViewModel.
The Fragment observes the LiveData in its own ViewModel.
Service ViewModel
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The BluetoothService updates the Bluetooth related LiveData in its own ViewModel.
The Fragment observes the LiveData in its own ViewModel and the BluetoothService ViewModel.
I am pretty sure I should place them on top of the architecture and treat them just like an Activity/Fragment, because BoundServices are part of the Android Framework, they are managed by the Android OS and they are bound to other Activities and Fragments. In that case, I don't know what's the best way to interact with LiveData, ViewModels and Activities/Fragments.
Some might think that they should be considered as a DataSource (since in my case it's getting data from a scale using Bluetooth), but I don't think this is a good idea, because of all what I've said in the previous paragraph and specially because of what it says here:
Avoid designating your app's entry points—such as activities,
services, and broadcast receivers—as sources of data. Instead, they should only coordinate with other components to retrieve the
subset of data that is relevant to that entry point. Each app
component is rather short-lived, depending on the user's interaction
with their device and the overall current health of the system.
So, finally, my question is:
Where should we place our Android (Bound) Services and what is their relation with the other architectural components? Is any of these alternatives a good approach?
Updated:
After getting suggestion from #Ibrahim Disouki (Thank you for that) I dig deeper and found out something interesting! Here's background.
O.P. seeks for solution "Where Service component of Android Framework stands considering Android Architecture Components". So, here's out the box(SDK) solution.
It stands at the same level as Activity/Fragment. How? If you're extending Service class though rather than that, start extending LifecycleService. Reason behind that is simple that previously we had to rely on Activity/Fragment lifecycle in order to receive updates/do some contextual operations on Service. But now it's not the case.
LifecycleService now has it's own lifecycle registry/maintainer called ServiceLifecycleDispatcher which takes care of lifecycle of service which also makes it LifecycleOwner.
It leaves us in condition that from now on, You can have a ViewModel to LifecycleService doing operations for itself & if you're following proper app architecture and having repository pattern leaves you to single source of truth!
In context of O.P. LifecycleService now can have ability to maintain it's ViewModel to do business logic related to repository layer, and later on another lifecycle aware component like, Activity/Fragment can also consume/reuse same ViewModel to have their specific operations to it.
Please note that by doing so, you're in state of having two different LifecycleOwners (Activity & LifecycleServie) which means you can't share view models between LifecycleService & other lifecycle aware components. If you don't like approach then be good with old callback kind of approach having callbacks back to Activity/Fragment from service when data is ready to serve etc.
Obselete:
(I suggest not to read through)
In my opinion, Service should be on same level as Activity/Fragment, because it's Framework component & not MVVM. but because of that Service doesn't implements LifecycleOwner and it's Android Framework Component, it shouldn't be treated as data source because it can be entry point to application.
So, dilemma here is that sometimes (In your case), Service acts as data source which provides data from some long running task to UI.
So what it should be in Android Architecture Component? I think you can treat it as LifecycleObserver. because, no matter what you do in background, you'll need to consider about lifecycle of the LifecycleOwner.
Why? because, we usually do bind it to LifecycleOwner (Activity/Fragments) & to do long running tasks off the UI. So, it can be treated like LifecycleObserver. In such a way we made our Service as "Lifecycle aware component" !
How you can implement it?
Take your service class and implement LifecycleObserver interface to it.
When you bind your service to Activity/Fragment, during your service connection of your service class, add your service to your activity as LifecycleObserver by calling method getLifecycle().addObserver(service class obj)
Now, Take an interface in service class to provide callback from service to your UI and every time your data changes, check that if your service has at least on Lifecycle event create or resume to provide callback with.
In such a way, we won't require LiveData to update to from service and even no ViewModel (Why do we need it for service? We don't need configuration changes to survive on service lifecycle. And main task to VM is that to consist data between lifecycles).
Side Note: If you think you're having long running background operations, then consider using WorkManager. After using this library, you'll feel like Services should be marked as deprecated by now! (Just a random thought)
One way to avoid direct contact with an Android service while still being able to use it is through an interface object. This is part of the "I" for Interface Segregation in the acronym, SOLID. Here is a small example:
public interface MyFriendlyInterface {
public boolean cleanMethodToAchieveBusinessFunctionality();
public boolean anotherCleanMethod();
}
public class MyInterfaceObject implements MyFriendlyInterface {
public boolean cleanMethodToAchieveBusinessFunctionality() {
BluetoothObject obj = android.Bluetooth.nastySubroutine();
android.Bluetooth.nastySubroutineTwo(obj);
}
public boolean anotherCleanMethod() {
android.Bluetooth.anotherMethodYourPresentersAndViewModelsShouldntSee();
}
}
public class MyViewModel {
private MyFriendlyInterface _myInterfaceObject;
public MyViewModel() {
_myInterfaceObject = new MyInterfaceObject();
_myInterfaceObject.cleanMethodToAchieveBusinessFunctionality();
}
}
Given the above paradigm, you are free to place your services in a package that's outside your packages that contain POJO code. There is no "right" location to put your services -- but there are definitely WRONG places to put them (e.g. where your POJO code goes).
What if we bind/unbind to/from service from activity or multiple activities as usual in onStart/onStop, then we have singleton instance that holds Bluetooth related manager (I use nordic lib for ble manager) . That instance is in service so that we can disconnect for example when service is destroyed because ui unbound from it and reconnect to ble when service is created. We also inject that ble manager singleton into viewmodel to make interaction and data listening easier via livedata or rx or similar reactive data provided by ble manager, for example for connection state. This way we can interact from viewmodel with ble, subscribe to characteristics etc and service is there to provide scope that can survive over multiple activities and basically knows when to connect or disconnect. I have tried this approach in my app and so far it works ok.
Sample project
https://github.com/uberchilly/BoundServiceMVVM
In my opinion using LiveData in servise is comfortable
class OneBreathModeTimerService : Service() {
var longestHoldTime = MutableLiveData<Int>().apply { value = 0 }
...
}
Then in fragment
override fun onCreate(savedInstanceState: Bundle?) {
mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
mOneBreathModeService = (binder as OneBreathModeTimerService.MyBinder).service
mOneBreathModeService!!.longestHoldTime.observe(this#OneBreathModeFragment, androidx.lifecycle.Observer {
binding.tvBestTime.text = "Best $it"
})
}
override fun onServiceDisconnected(name: ComponentName) {}
}
I am not pro in LiveData, but what can be wrong with such approach?
This question confuse me a long time. I don't think bind service should have a viewModle , as we known , the service are not view layer !
By the way, Service need to start/bind,unbind at activity
Finally, I think a easy and not bad way is the LiveData inside AndroidService. But not use Livedata to send data, use custom callback method.
Live data only send the newest data every time, if you need got the complete data also the page is onPause, there is a mistake.( Now, we can use kotlin flow)
Also We not just receive data from ble(bluetooth low energe) device, we also need send data to ble device.
There is a simple project code:
https://github.com/ALuoBo/TestTemp/blob/main/bluetooth/src/main/java/com/lifwear/bluetooth/BLECommService.java
How about treating your service like this?
I have 2 activities: MainActivity and MySecondActivity.
I start MySecondActivity, but while I'm working in it, I receive a callback from an external library in MainActivity. This callback should update some information in MySecondActivity.
Question: Can I keep a reference of MySecondActivity in MainActivity in order to update it later ?
Basically it would be like this:
val mySecondIntent = Intent(applicationContext, MySecondActivity::class.java)
startActivity(mySecondIntent)
(...)
fun MyCallBackFunction(newInfo: Integer)
mySecondIntent.updateMyInfo(newInfo) <-- here I cannot access "MySecondIntent"
I tried to store the reference of mySecondIntent in the companion object but I could not make it work.
Do you have any suggestion ?
Thank you !
Here is my suggestion. Don't let any activity keeps the other activity reference, it's not the best practice.
Suggestion
If the callback you receive has impact on multiple activities, then make it global. I usually create a singleton manager class for handling each global event's logic that may have impact on many places in the app. Anywhere in your app need to listen for the event, register the event to the manager.
For example, my AppLocationManager is a singleton class, responsible for making gps refresh and other location stuff. When location change, fire events to all registered listeners. In your case, both MainActivity and SecondActivity knows the changes and update itself, remember to unregister the listener in activity onDestroy, or you will have a memory leak.
Workaround for quick implementation
If you still want to keep the activity reference for quick modification, which is not recommended, use WeakReference
Store a WeakReference of MySecondActivity as a static global variable:
public static WeakReference<Activity> mTmpAtivity
Unlike default StrongReference, this variable will release the instance if the activity is GC so it's memory safe.
I'm try to use event bus lib, I could not understand it's functionality and see multiple examples. Is it use only services class or use activity and fragment then is use activity what situation we use event bus in activity or fragment.
Instead of interface we can simply use EventBus .we can pass messages from one class to one or more classes. EventBus in 3 steps
Define
Register and unregister
Post Event
Define events:
public static class MessageEvent { /* your getter and setter */ }
Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
/* event fire here when you post event from other class or fragment */
};
Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);//Register
}
unRegister
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);//unregister
}
Post events:
EventBus.getDefault().post(new MessageEvent());//post event
You can consider EventBus as a lightweight communication channel for passing data within activities, or services, or fragments or between any of them.
Think of EventBus as an underlying layer in your app which is independent of any active activities or services or fragments and their lifecycle.
The main concept on which an EventBus works is that you subscribe to events in either an activity or fragment or service or any components like that and whenever the EventBus has a specific event of the type you subscribed, it notifies your subscribed method in that component and you can perform any task there based on the event that you have received.
Triggering an event is easy and you can do it from any area of your app by just passing a specific event (which is basically a POJO class, let's say MyEvent) to the EventBus, and the bus will handle the rest and correctly deliver it to the appropriate receiver/s.
I would recommend you to try out EventBus 3 from GreenRobot, and go through their documentation here to incorporate their library n your code. I have been using the same without any problems.
EventBus 3 by GreenRobot
Hope this helps.
Mostly you shouldn't. In general its a hack for when you misarchitected your program and can't easily pass data from one point to another due to how your app is encapsulated and what objects are known at what levels. It can cause real spaghetti code where its difficult to figure out what code will actually be called when an event occurs. You shouldn't be writing your code around having an event bus, it should be a last resort if you can't refactor things to work the correct way.
One of the advantages of events is that you can pass objects around to 'somewhere', and you don't need to know where it is picked up. This makes it easy to push your activity or fragment state to a controller class, and then use events sent from this controller back to the activity or fragment.
Because of events, the controller doesn't need to know if the receiving end is an activity or a fragment. This makes it very easy to switch one for the other. On the other hand this also makes it easy to put the controller somewhere else. Like first you have it as an instance in the application class, and then you move it to a service.
I wrote an article with a very concrete example on how you can use events to deal with the Android life cycle as described above: https://medium.com/#JuliusHuijnk/beating-the-android-life-cycle-d00a2f3ed88
I have an Activity called MainActivity that starts a Service called MainService. It is also binds the Service, so MainActivity can access methods and public variables within MainService. Is it possible to do it the other way round, i.e. the Service is able to access the Activity's methods?
I wish to implement it this way because I have a variable in MainActivity that is set upon onResume(), and on first startup the service has not yet started by the time onResume() runs, so at that point in time the service is still null.
This answer assumes that the Service in question runs in a different process:
Yes, it is possible. The general idea is that not only your Activity binds the remote Service through some AIDL defined interface, but it also implements additional AIDL interface which the Service is aware of, and sets itself as a callback target of the remote Service.
You'll have to have 2 AIDL files: the first one describes the interface of the Service, and the second one describes the interface of the Activity.
The implementation of such a scheme is very similar to "remote Service callbacks" described in this answer, though "callback" method would no longer be void, but return the value you're interested in.
Design considerations:
The above scheme will allow you to get values from Activity, but I don't think you should take this path. From the description of your use case, it looks that you only want to pass some value to the Service when Activity gets resumed. Since your Service is bound anyway, you can simply add a method setSomeValue(int value) to its AIDL definition and call this method from onServiceConnected() callback.
Yes it's possible.
You have to prepare method in your service to return back your activity just after service is bound:
public void bindActivity(MyActivity activity){...}
Then after service is bound to activity just call this method with MyActivity.this as parameter.
However...
You probably should not do it. Much more clear solution is using LocalBroadcastManager to pass events and data or use some more efficient solutions like Otto to do this same, but still - without direct access to one component's fields / methods from another.
I'm trying to pass an event from a fragment to a dialog using otto. Unfortunately by the time the dialog is created the event has already fired and the #Subscribe method in my dialog and the data that was being sent gets passed over.
The data I'm trying to pass is an Object and an index (Best case) or a String (worst case) from a listview/ArrayList.
I know they have an #Produce method; but I don't understand how a method that takes no arguments helps in anyway or how I can get it to work.
Here's an example they provide
#Produce public AnswerAvailableEvent produceAnswer() {
// Assuming 'lastAnswer' exists.
return new AnswerAvailableEvent(this.lastAnswer);
}
For my newly created dialog this.lastAnswer; won't exist, it can't - that's what I'm passing in.So how do I get around this?
Side note:
I think the event bus, in it's current form is a bit of overkill since it's only communicating with my dialog. Later on I hope to use the event bus in more dialogfrag/fragment communication.
I've also asked same question for myself, when I was investigating Otto. And decided to read their site(http://square.github.io/otto/) more thoughtfully.
Here is their description under #Produce annotation:
...
Producers, like subscribers, must also be registered...
When registering, the producer method will be
called once for each subscriber previously registered for the same
type. The producer method will also be called once for each new method
that subscribes to an event of the same type.
You may only have one producer per event type registered at a time on
a bus.
So I think in time you register your producer, "lastAnswer" should be initialized.
And no parameters needed because you don't suppose to call your producer-method by yourself.
I hope it will help.