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.
Related
I have a service where I declare this liveData
public static final MutableLiveData<String> newMessageDispatcher = new MutableLiveData<>();
Inside the main activity I cannot do newMessageDispatcher.observe(myActivity, newMessageObserver) because my activity do not extends AppCompatActivity so it's not a LifecycleOwner.
Now If I do newMessageDispatcher.observeForever(newMessageObserver), what will happen when the user will close the app (by swiping up in the recent apps window), so closing the main activity without calling newMessageDispatcher.removeObserve(newMessageObserver)? Does the observer will be successfully removed or do I absolutely need to call newMessageDispatcher.removeObserve(newMessageObserver)?
because my activity do not extends AppCompatActivity so it's not a LifecycleOwner.
You need to extend ComponentActivity to get LifecycleOwner. Or, you could implement LifecycleOwner logic yourself, if you wanted.
what will happen when the user will close the app (by swiping up in the recent apps window), so closing the main activity without calling newMessageDispatcher.removeObserve(newMessageObserver)?
The precise behavior of that, or even if that behavior exists, will vary across the tens of thousands of Android device models.
Does the observer will be successfully removed
You should not assume that it will.
or do I absolutely need to call newMessageDispatcher.removeObserve(newMessageObserver)?
That would be a good idea. Do it in the counterpart lifecycle method to where you are calling addObserver() (e.g., if you call addObserver() in onCreate(), call removeObserver() in onDestroy()).
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.
I have noticed that I can create a callback by using two methods:
Receive an interface at the constructor of the class implementing the callback.
Receive the activity itself at the constructor of the class implementing the callback.
First Approach
For example I could do this:
public MyClass(MyInterface listener) {
this.listener = listener;
}
And I could call myCallBackFunction() defined in MyActivity (which implements MyInterface) by writing listener.myCallBackFunction()
Second Approach
Or I could do this:
public MyClass(MyActivity activity) {
this.activity = activity;
}
And I could call myCallBackFunction() defined in MyActivity by writing activity.myCallBackFunction()
My concern: Is one approach better than the other? And if so, why?
Usually speaking, you'd better use first approach. The reason is here:
Suppose you have 4 classes, first is Vehicle, second is Bicycle, third is Bus and third is Subway. Bicycle, Bus and Subway are subclasses of Vehicle. There may be a method call drive(), which should have a parameter. Which one do you think best for parameter type? Bicycle, Bus, Subway, or Vehicle?
Apparently, passing Vehicle is best because you may want to add other kinds of vehicles in the future or you don't want to write nearly same code for different kinds of vehicles in your project. It is same to use Interface rather than specific class.
As a result, passing an interface to a method is always correct and better than passing a specific type of object to it. You can always implement the interface in other classes and they will also be parameter of that method. You don't need to think about actual type of the parameter, which will confuse you and make you think more about specific code for specific type. Instead, only one type, one piece of code macroscopically.
So the conclusion is: using MyActivity is good, but using MyInterface is better.
I think it can depend on what you're trying to achieve: the first approach may in general be better suited since MyClass is not required to know anything about the implementation of that interface method, so it's great for passing different objects (e.g. a RecyclerView Adapter being created with an OnItemClickedListener injected in the constructor can be re-used in different activities/fragments implementing the interface, whilst the adapter doesn't need to change). It helps to prevent coupling.
The second approach leaves one wondering: is MyClass tied to the activity lifecycle? It may still hold a reference to the activity after that activity has actually been destroyed by the system, which would leak memory as the Activity object is not garbage-collected. It's a matter of design, and can be seen as code smell, can you not achieve what you wanted within the activity itself, and rely on the lifecycle callbacks onCreate/.../onDestroy?
Is one approach better than the other? And if so, why?
Using Interface is the best way..
Assume that you are having
1) Activity MyActivity
2) class which extends Activity or View or Asynctask is Myclass.
Both MyActivity and Myclass are Implements MyInterface
If you are passing Activity you need to add one more constructor
public MyClass(MyActivity activity) {
this.activity = activity;
}
public MyClass(Myclass myclass) {
this.myclass= myclass;
}
If you are using interface
public MyClass(MyInterface listener) {
this.listener = listener;
}
that's it.
In one approach you are creating an instance of Interface and in another an instance of implementing activity. what is best is:
Interface interface;
public myClass(Activity acitivity)
{
interface = (Interface)activity;
}
i.e. typecast activity to interface. Now you can callback the overridden functions in Activity.
This way you can now loose information of Activity's functions and just access the overriden functions of interface from the activity.
You can avoid typecasting and create an object of Activity, if you need access to interface callbacks AND the activity's function/variables.
It depends on your needs.
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.
I'm using #ContextSingleton to mark singletons that depend on the injection on contexts. From looking at RoboGuice sources and from my own tests it seems however as if it makes a difference between the Application context and different activity contexts. This makes perfectly sense, just that it is - at least for me - problematic when I'm using it together with the event management facility like this:
#ContextSingleton
public class Service {
#Inject
private Context context;
public void doSomething(#Observes MyEvent ev) {
...
}
}
Though the service is defined as singleton, no instance of it seems to be created until it is first injected somewhere, apparently through lazy-loading. So fireing a MyEvent does not make the listener call upon. We thought then we could "manually" eager-load the classes beforehand in our application like
RoboGuice.get(context).getInstance(Service.class);
and therefor get the listeners registered, but this only worked properly when executed within the Activity that later also injected the EventManager to fire the event, but not the application.
So, in an ideal world I'd expect that I could tell RoboGuice to which context it should bind a singleton, much like this
#ContextSingleton(MyApplication.class)
public class Service {
...
}
but apparently this is not possible.
What am I missing?
I think You're missing that events don't propogate across contexts and are context specific - see the "Things to be aware of" section in the (old, but still valid) events documentation. So even if your singleton is loaded in the application context, it can't be made aware of events occurring within the context of each activity. I assume they had a good reason for doing it that way, but Maybe if you subclass Application you can inject an EventManager there to be globally accessible.
I haven't tried it so I'm not sure it will work, and at any rate at that point you'd have to be injecting events into a different EventManager, so you might be better off defining an interface for this singleton and work with it that way, since events won't propogate the way you'd like them to.
#JRaymond is right. An instance of a RoboGuice Event listener can't listen to events in more than one context.
But this can be easily solved. Don't make your singleton listen to events directly. Each activity can listen to its own events and then put the singletong in a given state :
public class MyActivity extends RoboActivity {
#Inject
private InsideAppManager mInsideAppManager;
public void onHandleResumeEvent( #Observes OnResumeEvent onStartEvent ) {
mInsideAppManager.setInsideApp(true);
}
public void onHandlePauseEvent( #Observes OnPauseEvent onStopEvent ) {
mInsideAppManager.setInsideApp(false);
}
}
And your singleton :
#Singleton
public class InsideAppManager {
private boolean isInsideApp;
public boolean isInsideApp() {
return isInsideApp;
}
public void setInsideApp(boolean isInsideApp) {
this.isInsideApp = isInsideApp;
}
}