Is it a good practice to save activity instance in a WeakReference - android

Here, in this answer Activity instance is saved in WeakReference<Activity> variable. So that it will avoid memory leaks. Is it a good practice to do so?
public class BackgroundService extends IntentService {
private static WeakReference<Activity> mActivityRef;
public static void updateActivity(Activity activity) {
mActivityRef = new WeakReference<>(activity);
}
}
I'm using mActivityRef.get() and casting it to required activity object. Using that object, accessing the methods in activity.
The purpose is to access Activity methods from service, this code does the work but as per the comments I'm confused whether to use it or not
I've referred the document yet not clear.

Is it a good practice to do so?
No.
The purpose is to access Activity methods from service
That activity may not exist. For example, the user could press BACK and destroy the activity while the service is running. Calling methods on a destroyed activity will likely lead to crashes.
Use an event bus (LocalBroadcastManager, greenrobot's EventBus, etc.) for loosely-coupled communications between components, such as between services and activities. Have the activity register for events when it is visible, and have the service post events as needed.

No its not a good practice to store a reference of Activity anywhere in your project but if you want do, create an interface implement your activity with interface and pass that interface as a communication way between your activity and IntentService to your service.
Now your service has a reference of your activity's (selected) methods. Access your data through that interface and clear reference after its usage.

Related

What is faster Communicating, sending broadcast VS calling activity method directly in Android?

In my app, I have to call an activity method from the fragment.
I know I can do this in two ways:
1. Via sending a broadcast to activity:
Intent intent = new Intent("FILTER");
intent.putExtra("EXTRA", 1);
sendBroadcast(intent);
2. Or Calling the activity method directly:
((MyActivity) getActivity()).method();
I would like to know which way is faster and safe to communicate. Any help would be appreciated.
Thank you.
Loosely Coupled Fragment?
I am not sure about the speed. But on the design perspective You should use an interface to communicate with an Activity rather calling Activity method directly from your Fragment. ie ((MyActivity) getActivity()).method();
Because using an interface makes your Fragment independent from your
Activity. Let's say in future you want to use your fragment in Some
other Activity then you will not have to change anything in your
Fragment.
Interface
public interface Somelistener {
public void someMethod();
}
Your Loosely coupled Fragment
YourFragment extends Fragment {
Somelistener listener;
public void onActivityCreated(Context context){
listener = (SomeLisner)context;
}
public void buttonClick()
{
listener.someMethod();
}
}
So if you are using in your MainActivity. No problem
MainActivity implements SomeListener{
#Override
public void someMethod()
{
// Activity method
}
}
In future you want to use Fragment in SomeOtherActivity. No problem
SomeOtherActivity implements SomeListener{
#Override
public void someMethod()
{
// somethother method
}
}
BroadcastReceiver Approach?
TBH I have seen this approach for Service-Activity Communication. Not for Activity - Fragment communication.
For communicating between Fragments and the Activity that contains it, there's actually a much better 3rd option.
The better option is to use an Interface as a callback method. This is described very well in this documentation: https://developer.android.com/training/basics/fragments/communicating
Using an interface is much more preferred over your two methods because it's both safer and more efficient.
For your first method of using Broadcast Receivers, this is actually a very inefficient solution due to Broadcast Receivers not being meant for a task like what you're after.
Let me quote you something from the Android documentation:
Warning: Limit how many broadcast receivers you set in your app. Having too many broadcast receivers can affect your app's performance and the battery life of users' devices. For more information about APIs you can use instead of the BroadcastReceiver class for scheduling background work, see Background Optimizations.
https://developer.android.com/guide/topics/manifest/receiver-element
So yes, Broadcast Receivers will have a bigger effect on your app's performance and the device's battery life over the other method you suggested and the method I suggested.
Additionally, don't forget that a Broadcast Receiver is meant to listen to broadcasts. The type of Broadcast Receiver you're using in your example is actually a Global Broadcast where you didn't explicitly limit it's "range", so any Broadcast Receiver could potentially "listen" in to your broadcast. In terms of security, using a Global Broadcast like this isn't safe either.
You also don't want other apps to potentially fire off a Broadcast that coincidentally coincides with your app's Broadcast Receiver, causing it to receive data not meant for it and crashing due to this accidental and coincidental naming.
Honestly, there's more potential issues of using a Broadcast Receiver in a way it's not meant for.
As for your second method of directly calling the Activity's method... this is actually very inefficient for managing code. You're basically tying the Fragment tightly together with that specific Activity.
However, Fragments, by design, makes it common to be swapped into other Activities or Fragments... you'll basically have to do multiple if statements and casts each time you want to run code from it's parent.
Also, keep in mind that if you later change code in MyActivity, it can cause problems for this fragment due to you forgetting how tightly bound it is to the Activity.
But if you use the more preferred Callback Interface approach, it's simply a middleman meant to deliver a "Hey, DO something for me" message. Quick and direct. It's also plays friendly with any Activity or Fragment you want to attach this Fragment to later since those Activities or Fragments simply have to implement the Interface and the callback bridge between both parent and child is formed.
It is better to use interface to communicate from fragment to activity rather than a Local broadcast.
Activity will implement the interface and fragment will call the methods.

Keep reference of an activity in the companion object

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.

Can a service access a variable within an Activity?

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.

Which context is injected by roboguice?

I'd like to know which context Roboguice injects, is it the application context or the current activity?
I'm trying to use both Roboguice and Robospice. I'm injecting Robospice's SpiceManager in a fragment but the fragment doesn't know about the SpiceManager, it sees it through an interface, let's say MyInterface.
public class MyFragment extends RoboFragment {
//this is where the SpiceManager gets injected
#Inject MyInterface manager;
...
}
//this is the implementation that I'm going to inject
//it is simultaneously an event listener for the fragment's life cycle events so that the
//SpiceManager can be appropriately started and stopped.
public class MyManager implements MyInterface {
private SpiceManager spiceManager = new SpiceManager(MySpiceService.class);
//Which context will get injected here? How can I make Roboguice inject a specific context that I want, for example, a specific activity that I want.
private #Inject Context context;
//Here, I need to start the SpiceManager
public void myFragmentOnStart(#Observes OnStartEvent onStart) {
//SpiceManager requires a context, more specifically an activity which will be destroyed and then garbage collected, so It shouldn't be an application context because the resources SpiceManager uses will never be released.
spiceManager.start(context);
}
public void myFragmentOnStop(#Observes OnStopEvent onStop){
if (spiceManager.isStarted()) {
spiceManager.shouldStop();
}
}
}
My questions are:
Can RoboGuice observe fragment events beside Activity events, the documentation isn't clear?
Am I right in thinking that SpiceManager needs a context that will be destroyed when the fragment/activity is destroyed? I've had a look at the code of SpiceManager.start(Context context) and it creates a WeakReference to the passed Context.
How can I make RoboGuice inject a specific Context/Activity?
Is it possible to do so without MyFragment knowing that the MyInterface object it uses needs a Context?
By the way I found out that OnStopEvent has a getActivity() method, so there's no problem getting the Activity in onStop, but OnStartEvent is just an empty class.
So many questions ;)
A) Can RoboGuice observe fragment events beside Activity events, the documentation isn't clear ?
Event can be anything in RG. By default RG offers some nice events to be notified of lifecycle of an activity. Release 3.1 of RG is actually adding some new events for Fragments. This should be released in a couple of weeks.
But what you do on the event side is perfectly legitimate. Just to be clear. You are listening to the activity lifecycle from within a fragment. Why not ?
The only thing you need is to register to this instance of event manager of the activity. Add #Inject EventManager eventManager to your fragment. This is enough for RG to register your listener automatically.
B) RS will need a context only for callbacks, not to execute a request. The request is gonna be executed in a service. The context you pass to RS is just used to say "If this context dies, then all listeners will die, don't notify them. But still, go ahead, execute the request and cache the result."
Here it's a bit complex the way you do it. The easiest is really to manage a spice manager at the activity level. Send events from your fragments to your activity to ask it to launch requests when needed. That the easiest.
But it's also possible to have spicemanager managed at the fragment level. In that case, manage your spicemanager lifecycle in the fragment itself with its onStart/onStop methods.
C) Is it possible to do so without MyFragment knowing that the MyInterface object it uses needs a Context?
I didn't get it.

How to get notified about activity's lifecycle

I'm writing a helper class for my activity, which uses an external service. Like in a standard design pattern regarding bound services, I want to bind on activity creation and unbind on activity destruction. However, I want to isolate this logic to my helper class, so that activity would only use an instance of that helper and don't call bind and unbind explicitly.
I can pass the activity to the helper class, but I cannot find any way to schedule a callback on activity's lifecycle's events - there's just no such methods in Activity class. While this most probably means that I cannot achieve what I want to, and also that it's probably not a good idea, I still want to ask the community about this. Is it possible? Is it a good idea? Is it possible to achieve similar results with some other classes (not the Activity)?
I'm new to Android development and I'm seeking for the best practices. Any ideas are appreciated.
Thanks!
EDIT: Basically, I want to be notified on activity creation and destruction. I want to be able to schedule a callback on onCreate and onDestroy methods, but from outside of the Activity. Those methods are protected and therefore inaccessible from other classes.
You can use the Application.ActivityLifecycleCallbacks class. Keep in mind that the class was introduced in API level 14. For lower versions you could make hook methods in your library and require that the target Activity will call the appropriate hook method from its corresponding lifecycle method. Of course, this would be a very fragile implementation.
Lifecycle methods are the means to implement behaviour which will be executed when DalvikVM decides to do something with Activity (pause/resume/create/destroy), not to invoke that behaviour artificially.
If you want to externalise logic in helper/controller of some sort and be able to use service connection do initialization within ServiceConnection handler.
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,IBinder service) {
...init helper here...
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
...shutdown helper here...
}
};
then handle connections as usual.

Categories

Resources