Dependency Injection in BroadcastReceiver - android

Is it a good practice to inject a Singleton to a BroadcastReceiver?
More specifically lets assume I have singleton like the following:
#Singleton
public class UnitProvider {
private boolean mIsUsingCelsius = false;
protected SharedPreferences mSharedPrefs;
#Inject
public UnitProvider(SharedPreferences sharedPrefs) {
mSharedPrefs = sharedPrefs;
mIsUsingCelsius = isUsingCelsiusPref(Locale.getDefault());
}
public void refreshCelsius() {
if (!mSharedPrefs.contains(SharedPreferencesConstants.SP_KEY_USE_CELSIUS)) {
mIsUsingCelsius = isUsingCelsiusBasedOnLocale(Locale.getDefault());
}
}
}
And there is a broadcast receiver:
public class DummyReceiver extends BroadcastReceiver {
#Inject protected UnitProvider mUnitProvider;
#Override
public void onReceive(Context context, Intent intent) {
DependencyInjectionService.inject(this);
mUnitProvider.refreshCelsius();
}
}
Actually it works but I am not sure about the performance and possible memory leak that situation may cause. Is there any idea about performance and possible lags that injection may cause?

This should be OK. A BroadcastReceiver instance is only alive for as long as it takes it to return from onReceive. It will be eligible for garbage collection after that as long as you didn't do something silly like hold a reference to it.
Also, you can't really "leak" a singleton object because basically they are expected to last forever!

Related

Am I allowed to observe a ViewModel, if I clean up the back references?

The suggested way to implement ViewModel is to expose the changing data by using LiveData objects to activities, fragments and views. There are cases, when LiveData is not an ideal answer or no answer at all.
The natural alternative would be, to apply the observer pattern to the ViewModel, make it an observable. When registering observers to the ViewModel, the ViewModel will hold callback references to notify the observers.
The documentation says, a ViewModel must not hold references to activities, fragments or views. The only answer to the question "why" I found is, that this may cause memory leaks. Then how about cleaning up the references to avoid memory leaks?
For views this is a difficulty. There is no defined moment, when the view goes away. But activities and fragments have a defined lifecycle. So there are places to unregister as observers.
What do you think? Is it valid to register activities as observers to ViewModels if you take care to always unregister them? Did you hit upon any valid information about this question?
I set a small reward for the best answer. It's not because I think it a recommended solution (as it does not work with views). I just want to know and extend my options.
public class ExampleViewModel extends ViewModel {
public interface OnEndListener {
public void onEnd();
}
private List<OnEndListener> onEndListeners = new ArrayList<>();
public void setOnEndListener(OnEndListener onEndListener) {
onEndListeners.add(onEndListener);
}
public void removeOnEndListener(OnEndListener onEndListener) {
onEndListeners.remove(onEndListener);
}
public void somethingHappens() {
for (OnEndListener onEndListener: new ArrayList<OnEndListener>(onEndListeners) ) {
onEndListener.onEnd();
}
}
}
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
ExampleViewModel.OnEndListener onEndListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onEndListener = new ExampleViewModel.OnEndListener() {
#Override
public void onEnd() {
finish();
}
};
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.setOnEndListener(onEndListener);
}
#Override
protected void onDestroy() {
super.onDestroy();
exampleViewModel.removeOnEndListener(onEndListener);
}
}
To ask "am I allowed..." is not really a useful question, IMO. The docs are clear that what you are suggesting is discouraged and why. That said, I expect that your code would probably work as expected and is therefore "allowed" (i.e. not prevented by a technical constraint).
One possible gotcha scenario: InstanceA of ExampleActivity is started and kicks off some long-running task on the ExampleViewModel. Then, before the task completes, the device is rotated and InstanceA is destroyed because of the configuration change. Then, in between the time when InstanceA is destroyed and a new InstanceB is created, the long-running task completes and your view model calls onEndListener.onEnd(). Except: Oh no! The onEndListener is null because it was cleared when InstanceA was destroyed and hasn't yet been set by InstanceB: NullPointerException
ViewModel was designed (in part) precisely to handle edge cases like the gotcha scenario above. So instead of working against the intended use of the ViewModel, why not just use the tools it offers along with LiveData to accomplish the same thing? (And with less code, I might add.)
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.getOnEndLive().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(#Nullable Boolean onEnd) {
if (onEnd != null && onEnd) {
finish();
}
}
});
}
}
public class ExampleViewModel extends ViewModel {
private MutableLiveData<Boolean> onEndLive = new MutableLiveData<>();
public MutableLiveData<Boolean> getOnEndLive() {
return onEndLive;
}
public void somethingHappens() {
onEndLive.setValue(true);
}
}
Think of the LiveData in this case not as actual "data" per se, but as a signal that you can pass from your ViewModel to your Activity. I use this pattern all the time.

Android MVP Strategy

I am migrating my apps to MVP. Have taken hints on a static presenter pattern from this konmik
This is my brief MVP strategy. Removed most of the boilerplate and MVP listeners for brevity. This strategy has helped me orientation change proof my background processes. The activity correctly recovers from a normal pause compared to pause which is finishing the activity. Also the Presenter only has application context so it does not hold onto activity context.
I am not a java expert and this is my first foray into MVP and using a static presenter has made me uncomfortable. Am I missing something? My app is working fine and has become much more responsive.
View
public class MainActivity extends Activity{
private static Presenter presenter;
protected void onResume() {
if (presenter == null)
presenter = new Presenter(this.getApplicationContext());
presenter.onSetView(this);
presenter.onResume();
}
protected void onPause() {
presenter.onSetView(null);
if(isFinishing())presenter.onPause();
}
}
Presenter
public class Presenter {
private MainActivity view;
Context context;
public Model model;
public Presenter(Context context) {
this.context = context;
model = new Model(context);
}
public void onSetView(MainActivity view) {
this.view = view;
}
public void onResume(){
model.resume();
}
public void onPause(){
model.pause();
}
}
Model
public class Model {
public Model(Context context){
this.context = context;
}
public void resume(){
//start data acquisition HandlerThreads
}
public void pause(){
//stop HandlerThreads
}
}
I would suggest two things.
Make Model, View, and Presenter into interfaces.
Your MVP-View (an Activity, Fragment, or View) should be so simple it does not need to be tested.
Your MVP-Presenter never directly interacts with the Activity/Fragment/View so it can be tested with JUnit. If you have dependencies on the Android Framework is bad for testing because you need to Mock out Android objects, use emulator, or use a Testing Framework like Roboelectric that can be really slow.
As an example of the interfaces:
interface MVPView {
void setText(String str);
}
interface MVPPresenter {
void onButtonClicked();
void onBind(MVPView view);
void onUnbind();
}
The MVPPresenter class now does not depend on the Android Framework:
class MyPresenter implements MVPPresenter{
MVPView view;
#Override void bind(MVPView view){ this.view = view; }
#Override void unbind() {this.view = null; }
#Override void onButtonClicked(){
view.setText("Button is Clicked!");
}
}
Instead of making the Presenter a static class, I would make it a Retained Fragment. Static objects need to be tracked carefully and removed for GC manually whenever they are not needed (otherwise it's considered a memory leak). By using a retain fragment, it is much easier to control the lifetime of the presenter. When the fragment that owns the retain fragment finishes, the retain fragment is also destroyed and the memory can be GC'd. See here for an example.
Activity, Fragments should have only overidden methods of View interface and other Android Activity, Fragment's methods.
View has methods like navigateToHome, setError, showProgress etc
Presenter interacts with both View and Interactor(has methods like onResume, onItemClicked etc)
Interactor has all the logics and calculations, does time intensive tasks such as db, network etc.
Interactor is android free, can be tested with jUnit.
Activity/fragment implements view, instantiate presenter.
Suggest edits to my understanding. :)
An example is always better than words, right?
https://github.com/antoniolg
You're on the right track, and you are correct to ask about static - whenever you notice that you have written that keyword, it's time to pause and reflect.
The Presenter's life should be tied directly to the Activity's/Fragment's. So if the Activity is cleaned up by GC, so should the presenter. This means that you should not hold a reference to the ApplicationContext in the presenter. It's ok to use the ApplicationContext in the Presenter, but it's important to sever this reference when the Activity is destroyed.
The Presenter should also take the View as a constructor parameter:
public class MainActivity extends Activity implements GameView{
public void onCreate(){
presenter = new GamePresenter(this);
}
}
and the presenter looks like:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
}
}
then you can notify the Presenter of the Activity LifeCycle Events like so:
public void onCreate(){
presenter.start();
}
public void onDestroy(){
presenter.stop();
}
or in onResume/onPause - try to keep it symmetrical.
In the end you only have 3 files:
(I'm taking some code from another explanation I gave here but the idea is the same.)
GamePresenter:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
NetworkController.addObserver(this);//listen for events coming from the other player for example.
}
public void start(){
applicationContext = GameApplication.getInstance();
}
public void stop(){
applicationContext = null;
}
public void onSwipeRight(){
// blah blah do some logic etc etc
view.moveRight(100);
NetworkController.userMovedRight();
}
public void onNetworkEvent(UserLeftGameEvent event){
// blah blah do some logic etc etc
view.stopGame()
}
}
I'm not sure exactly why you want the ApplicationContext instead of the Activity context, but if there's no special reason for that, then you can alter the void start() method to void start(Context context) and just use the Activity's context instead. To me this would make more sense and also rule out the need to create a singleton in your Application class.
GameView
is an interface
public interface GameView {
void stopGame();
void moveRight(int pixels);
}
GameFragment is a class that extends Fragment and implements GameView AND has a GamePresenter as a member.
public class GameFragment extends Fragment implements GameView {
private GamePresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState){
presenter = new GamePresenter(this);
}
}
The key to this approach is to clearly understand the role of each file.
The Fragment is in control of anything view related (Buttons, TextView etc). It informs the presenter of user interactions.
The Presenter is the engine, it takes the information from the View (in this case it is the Fragment, but notice that this pattern lends itself well to Dependency injection? That's no coincidence. The Presenter doesn't know that the View is a Fragment - it doesn't care) and combines it with the information it is receiving from 'below' (comms, database etc) and then commands the View accordingly.
The View is simply an interface through which the Presenter communicates with the View. Notice that the methods read as commands, not as questions (eg getViewState()) and not to inform (eg onPlayerPositionUpdated()) - commands (eg movePlayerHere(int position)).

is it bad to use public static fields/variables?

I started learning about Android Development, and I read about static variables are bad and may leak memory because they are not garbage collectable.
I've used some in certain situations, but I am so concerned it may leak memory.
Can someone please look at my code below and see if they leak memory or not?
MainActivity.java
public class MainActivity extends Activity {
public static boolean IS_ACTIVITY_OPEN;
public static ImageView image;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
IS_ACTIVITY_OPEN = true;
....
....
VoiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
.....
};
registerReceiver(VoiceReceiver, new IntentFilter(BroadCastReceivers.VoiceIntent));
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(VoiceReceiver);
IS_ACTIVITY_OPEN = false;
}
}
Picture.java
MainActivity.image.setImageBitmap(resizedBitmap);
.....
.....
BroadCast.java
if (!MainAcitivty.IS_ACTIVITY_OPEN) {
//start an activity
Intent intent2 = new Intent(context, MainAcitivty.class);
intent2.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent2);
handler= new Handler();
MyPostDelay = new Runnable() {
#Override
public void run() {
context.sendBroadcast(new Intent(VoiceIntent));
}
};
handler.postDelayed(MyPostDelay, 300);
}
else
{
context.sendBroadcast(new Intent(VoiceleIntent));
}
Thank you very much in advance.
Take a look at Avoiding memory leaks article on the Android Developers Blog. Keeping a static field holding a Context, or any other class that has a (strong) reference to a Context (such as any View) will mean that the garbage collector will not be able to reclaim the storage allocated by the Context. If the Context is an Application, thats OK because they live for as long as your app does and wouldn't be garbage collected anyway. But in case of Views, Context is likely an Activity which should be garbage collected as soon as possible.
That doesn't mean that all static fields will catastrophically leak memory. If they're primitive types, or simple classes, or even more complex classes with weak references to other classes, they might not prevent the garbage collector from reclaiming a lot of memory. But generally having static and especially public static fields is a code smell and should probably be avoided so the code is easier to maintain later.

How to hold an Object as long as a service live?

I have an Object that i use in all my activities which holds a lot of complex data in it, it would be quite an hassle to use Android framework of saving the object state and passing it around from activity to activity, so i thought it would be possible to make a Singleton that manages this object and makes it live as long as the application lives.
Tried to use regular Java Singleton scheme, with normal class and normal static instance, but the instance becomes null after a while (which is very confusing, why would an Object that is still referenced be turned to null and garbage collected?). so i decided to flow with Android designers and created a Service to manage this Object, the Service looks something like that :
public class DataService extends Service {
private Data data;
private static DataService instance;
#Override
public void onCreate() {
instance = this;
data= new Data(...);
instance.data.addProgressListener(listener);
(new Thread() {
#Override
public void run() {
data.doInitProgress();
listener = null;
};
}).start();
}
public static void listenToInitDataProcess(final ProgressBar progressBar,final Runnable onDone) {
listener = new ProgressListener() {
private int progress;
private int max;
#Override
public void onUpdateProgress(final long i) {
progressBar.setProgress(progress+=i);
}
#Override
public void onProgressEndComputed(final long n) {
progressBar.setMax(max=(int) n);
}
#Override
public void onDone() {
progressBar.setProgress(max);
onDone.run();
}
};
if (instance!=null) instance.data.addProgressListener(listener);
}
public static Data getData() {
return instance.data;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
now the problem with that is that after a while that the app is on i get NPE caused by instance is null... notice that i was listenning to the data object creation and i was trying to get it only after it was once inited, so no way that instance was suppose to be null...
how to do this right then?
If you want an Object that lives as long as your application lives (i.e. as long as its process is not killed by OS) you can extend android.app.Application, put your 'global' data there and use that subclass as your app context (needs to be declared in manifest)
However many argue that singletons provide essentially the same result as custom context e.g.
Singletons vs. Application Context in Android?
but the instance becomes null after a while
First, understand Effective Java's singleton recommendations, and how something wouldn't become null:
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
But a Service is different, since there is no public constructor there. So, first check if the Service is running (as referenced in this post: Check if Service is running from a Broadcast Receiver):
private boolean isMyServiceRunning(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (DataService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Then define some methods in your Service to return your needed state variables.

Proper notification of AsyncTaskLoader about data changes from background thread

I want to implement AsyncTaskLoader for my custom data source:
public class DataSource {
public interface DataSourceObserver {
void onDataChanged();
}
...
}
DataSource will keep list of registered observers and will notify them about changes. CustomLoader will implement DataSourceObserver. The question is how to properly notify CustomLoader since Loader.onContentChanged() must be called from UI thread but in my case DataSource operations (and calls to DataSourceObserver.onDataChanged()) will be done from background threads.
Updated with idea from Selvin tip:
public class CustomLoader extends AsyncTaskLoader<...> implements DataSource.DataSourceObserver {
private final Handler observerHandler;
public CustomLoader(Context context) {
super(context);
observerHandler = new Handler()
}
#Override
public void onDataChanged() {
observerHandler.post(new Runnable() {
#Override
public void run() {
onContentChanged();
}
});
}
}
I've had a lot of success using Local Broadcasts in a case that's very similar to yours. The method involves an AsyncTaskLoader implementation that will register a BroadcastReceiver listening for a particular String that describes what's changed. This BroadcastReceiver keeps a reference to the Loader and calls onContentChanged. When the data needs a refresh, make the Local Broadcast with the aforementioned String and the BroadcastReceiver will hear it and trigger the load. Here's some example code, it may not work perfectly if you drop it in, I've generalized some class names, but hopefully you'll get the idea:
Broadcast Receiver to be used in your Loader Implmentation:
public class LoaderBroadcastReceiver extends BroadcastReceiver
{
private Loader loader;
public LoaderBroadcastReceiver(Loader loader)
{
this.loader = loader;
}
#Override
public void onReceive(Context context, Intent intent)
{
loader.onContentChanged();
}
}
Loader Implementation registers the Receiver in onStartLoading()
private LoaderBroadcastReceiver loaderBroadcastReceiver = null;
#Override
protected void onStartLoading()
{
//... some code here
if(loaderBroadcastReceiver == null)
{
loaderBroadcastReceiver = new LoaderBroadcastReceiver(this);
LocalBroadcastManager.getInstance(getContext()).registerReceiver(loaderBroadcastReceiver, new IntentFilter("NEWDATASTRING"));
}
//... some more code here
}
Finally, here's how onDataChanged in DataSource will make the Broadcast. It'll need a Context to help send the Broadcast. Since this can be called from an arbitrary Thread, I'd use your ApplicationContext, since an Context from an Activity could cause problems if the Activity is destroyed.
public class DataSource
{
public interface DataSourceObserver
{
void onDataChanged(Context applicationContext)
{
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NEWDATASTRING"));
}
}
...
}
You'll probably want to play with it a bit to see how it works for you. You can use different Strings to differentiate different data that needs loading. You'll also want to unregister the Receiver at some point, perhaps in onReset(). Let me know if any of this in unclear in the comments, I'll try my best to clarify.

Categories

Resources