Background
Asynchronous Callbacks in Android
Trying to perform an asynchronous operation in a reliable fashion on Android is unnecessarily convoluted i.e. Is AsyncTask really conceptually flawed or am I just missing something?
Now, this is all prior to the introduction of Fragments. With the introduction of Fragments, onRetainNonConfigurationInstance() has been deprecated. So the latest Google condoned hack is to use a persistent non-UI fragment that attaches/detaches from your Activity when configuration changes occur (i.e. Rotating the screen, changing language settings etc.)
Example:
https://code.google.com/p/android/issues/detail?id=23096#c4
IllegalStateException - Can not perform this action after onSaveInstanceState
Theoretically the hack above allows you to get around the dreaded:
IllegalStateException - "Can not perform this action after onSaveInstanceState"
because a persistent non-UI fragment will receive callbacks for onViewStateRestored() (alternatively onResume) and onSaveInstanceState() (alternatively onPause). As such you can tell when the instance state is saved/restored. It's a fair bit of code for something so simple, but utilising this knowledge, you could queue up your asynchronous callbacks until the activity's FragmentManager has its mStateSaved variable set to false before executing them.
mStateSaved being the variable who is ultimately responsible for firing this exception.
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
So theoretically, now you know when it's safe to perform fragment transactions, and you can hence avoid the dreaded IllegalStateException.
Wrong!
Nested Fragments
The solution above only works for the Activity's FragmentManager. Fragments themselves also have a child fragment manager, which is utilised for nesting fragments. Unfortunately, child fragment managers are not kept in sync with the Activity's fragment manager at all. So whilst the activity's fragment manager is up-to-date and always has the correct mStateSaved; child fragments think otherwise and will happily throw the dreaded IllegalStateException at inappropriate times.
Now, if you've looked at Fragment.java and FragmentManager.java in the support library you won't in any way be surprised that everything and anything to do with fragments is error prone. The design and code quality is... ah, questionable. Do you like booleans?
mHasMenu = false
mHidden = false
mInLayout = false
mIndex = 1
mFromLayout = false
mFragmentId = 0
mLoadersStarted = true
mMenuVisible = true
mNextAnim = 0
mDetached = false
mRemoving = false
mRestored = false
mResumed = true
mRetainInstance = true
mRetaining = false
mDeferStart = false
mContainerId = 0
mState = 5
mStateAfterAnimating = 0
mCheckedForLoaderManager = true
mCalled = true
mTargetIndex = -1
mTargetRequestCode = 0
mUserVisibleHint = true
mBackStackNesting = 0
mAdded = true
Anyway, getting a bit off topic.
Dead-end solution
So, you might think the solution to the problem is simply, which seems like a bit of an antonym at this point, to add another one of those nice hacky non-UI fragments to your child fragment managers. Presumably its callbacks are in sync with its internal state and things will all be dandy.
Wrong again!
Android doesn't support retained fragment instances that are attached as children to other fragments (aka nested fragments). Now, in hindsight this should make sense. If the parent fragment is destroyed when the activity is destroyed because its not retained, there is nowhere to reattach the child fragment. So that's just not going to work.
My Question
Does someone out there have a solution for determining when it is safe to perform fragment transactions on child fragments in conjunction with asynchronous code callbacks?
Update 2
React Native
If you can stomach it, use React Native. I know, I know... "dirty web technologies", but in all seriousness, the Android SDK is a disaster, so swallow your pride and just give it a go. You might surprise yourself; I know I did!
Can't or Won't use React Native
No worries, I'd suggest fundamentally changing your approach to networking. Firing a request and running a request handler to update the UI just doesn't work well with Android's component life-cycles.
Instead try one of:
Move to simple message passing system based around LocalBroadcastReceiver and have long-living objects (regular Java classes or Android Services) do your requests and fire events when your app's local state changes. Then in your Activity/Fragment, just listen for certain Intent and update accordingly.
Use a Reactive event library (e.g. RxJava). I've not tried this myself on Android, but had pretty good success using a similar concept library, ReactiveCocoa for a Mac/desktop app. Admittedly these libraries have a fairly steep learning curve, but the approach is quite refreshing once you get used to it.
Update 1: Quick and Dirty (Official) Solution
I believe this is latest official solution from Google. However, the solution really doesn't scale very well. If you're not comfortable messing with queues, handlers and retained instance states yourself then this may be your only option... but don't say I didn't warn you!
Android activities and fragments have support for a LoaderManager which can be used with AsyncTaskLoader. Behind the scenes loader managers are retained in precisely the same way as retained fragments. As such this solution does share a bit in common with my own solution below. AsyncTaskLoader is a partially pre-canned solution that does technically work. However, the API is extremely cumbersome; as I'm sure you'll notice within a few minutes of using it.
My Solution
Firstly, my solution is by no means simple to implement. However, once you get your implementation working it's a breeze to use and you can customise it to your heart's content.
I use a retained fragment that is added to the Activity's fragment manager (or in my case support fragment manager). This is the same technique mentioned in my question. This fragment acts as a provider of sorts which keeps track of which activity it is attached to, and has Message and Runnable (actually a custom sub-class) queues. The queues will execute when the instance state is no longer saved and the corresponding handler (or runnable) is "ready to execute".
Each handler/runnable stores a UUID that refers to a consumer. Consumers are typically fragments (which can be nested safely) somewhere within the activity. When a consumer fragment is attached to an activity it looks for a provider fragment and registers itself using its UUID.
It is important that you use some sort of abstraction, like UUID, instead of referencing consumers (i.e. fragments) directly. This is because fragments are destroyed and recreated often, and you want your callbacks to have a "reference" to new fragments; not old ones that belong to a destroyed activity. As such, unfortunately, you rarely can safely use variables captured by anonymous classes. Again, this is because these variables might refer to an old destroyed fragment or activity. Instead you must ask the provider for the consumer that matches the UUID the handler has stored. You can then cast this consumer to whatever fragment/object it actually is and use it safely, as you know its the latest fragment with a valid Context (the activity).
A handler (or runnable) will be "ready to execute" when the consumer (referred to by UUID) is ready. It is necessary to check if the consumer is ready in addition to the provider because as mentioned in my question, the consumer fragment might believe its instance state is saved even though the provider says otherwise. If the consumer (or provider) are not ready then you put the Message (or runnable) in a queue in the provider.
When a consumer fragment reaches onResume() it informs the provider that it is ready to consume queued messages/runnables. At which point the provider can try execute anything in its queues that belong to the consumer that just became ready.
This results in handlers always executing using a valid Context (the Activity referenced by the provider) and the latest valid Fragment (aka "consumer").
Conclusion
The solution is quite convoluted, however it does work flawlessly once you work out how to implement it. If someone comes up with a simpler solution then I'd be happy to hear it.
Related
I am using google Firebase database in android. I need two fetch two set of data from database - userInfo and assignmentInfo. Then I need to combine them and show the data in a recycler view. What is the best way to approach this? As the fetching of data is async, this is getting messy.
One way i can think of is, check in both async functions success if other has completed. If so, get its data, combine and initialize the adapter. Is this the best way do this?
I have solved this kind of problem when i had to download something from a database before login in the user into the app, with this i fixed this problem.
To use ObservableInteger you can do this
first declare it
private ObservableInteger mObsInt;
then in your onCreate you will have a listener waiting for the values of the mObsInt to change, after those values change you can do anything you want
//Listener
mObsInt = new ObservableInteger();
mObsInt.set(0);
mObsInt.setOnIntegerChangeListener(new OnIntegerChangeListener()
{
#Override
public void onIntegerChanged(int newValue)
{
if (mObsInt.get()==1)
//Do something if the first asyncTask finishes
if (mObsInt.get()==2){
//Do something if the second asyncTask finishes, in this case i just go to another activity when both asyncTasks finish
Intent mainIntent = new Intent().setClass(LoginActivity.this, Principal.class);
startActivity(mainIntent);
finish();
}
}
});
So, how it works
ObservableInteger will be looking for changes in the variable mObsInt, so lets say if mObsInt is equal to 1 it will do something, if is equal to 2 will do another thing, so, to solve this problem with 2 asynctasks is easy, when one of the asynctasks finishes mObsInt will be equal to 1 , if the other asyncTask finishes so mObsInt will be mObsInt++ , and then your mObsInt will be equal to 2, the listener will be waiting for the values, and then do what you want to do when the values match your if statment at the onCreate method
now, just in your asynctasks just put in your onPostExecute() method this line
mObsInt.set(mObsInt.get()+1);
so if the first async finish, mObsint == 1 , if the second finish mObsInt == 2, and then you handle what you want to do in your onCreate method
hope this helps for you, it helped me
You can get more info at this doc : https://developer.android.com/reference/android/databinding/ObservableInt.html
happy coding !
This can be achieved using a simple variable which will be incremented or will be keeping a flag if the both data is available to be merged and returned successfully from Firebase. However, this is neither the best approach and nor will work all the time as it can fail if the both async thread tries to update the flag at the same time. Then with the implementation given above, will work only if you can make the whole operation thread-safe.
If you consider building a thread-safe implementation on your own, that is not so difficult either. You might just consider using a Synchronized function which will update the flag you are keeping to detect if the both data from firebase is fetched.
However, I would suggest to get your implementation done using a LocalBroadcastReceiver. Which is easier to implement and this is an Android solution. Though there might be a several other approaches which are great as well, I think the implementation with BroadcastReceiver will serve your purpose fully.
You can check this answer for checking the implementation of a BroadcastReceiver. When the first part of the data is fetched from firebase, send the broadcast to be received by the BroadcastReceiver in your Activity and set a flag value for example, 1. Then when the second part is received, you will have to set the value to 2 again by just sending the broadcast on getting response from firebase. And then, when the value is found 2, that means the both operations has completed and now you can merge the two lists.
To avoid the overall thread-safe and fail safety coding overhead, you might consider fetching the data from firebase, synchronously. On getting the data for the first part, initiate the fetch operation for the second part for better control over your code.
I have just put some ideas, pick any that suits you. Hope that helps!
The simplest way I use in projects w/o Rx/Coroutines/other stuff. Just create AtomicInteger. Init it with value equals number of async operations. Then in each callback of your async functions call this:
if(counter.decrementAndGet == 0) { your_final_action}.
If you need some help with other ways like I mentioned before, feel free to ask me.
I have old Android/java code, that contains two derives from IntentService,
and these services not run in separate processes.
The question is about the way to return result from these IntentService.
One service return result by using Handler + Runnable, to run code in main loop:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
MyApplication.get().setFoo(someThing);
}
});
the other one is uses LocalBroadcastManager.getInstance(this).sendBroadcast(in); to send message to Activity, and Activity subscribe via BroadcastReceiver on message in onResume, and unsubscribe in onPause.
Am I right, and in both case it is possible to use LiveData to simplify things?
IntentService should create LiveData and who want result should observe it,
and when new data arrives IntentService should call postValue,
or may be there are some reefs to prevent usage of LiveData here?
I think that LiveData will not help you in sending any data from Service to other components.
The problem with communication from any Service to other components is that you don't usually obtain a direct reference to the Service, therefore you can't directly "subscribe" to notifications.
Theoretically, if the Service runs in the same process, you can bind it, obtain a reference to Service object and then directly perform subscription. However, this is often an overkill and I don't see this pattern being used widely.
In your examples, there are two communication mechanisms:
Service reaches statically to Application object and sets some data. This is a communication through global state, and is generally considered an anti-pattern.
Communication through LocalBroadcastManager
From the above two mechanisms, I would use only #2 and avoid #1 at all costs.
Back to LiveData.
In order to be able to get LiveData object from the Service you will need to have a reference to that Service. This is usually impossible unless you bind Service in the same process, or use some ugly hack that involves global state.
Therefore, usefulness of LiveData in this context is very limited.
By the way, while LocalBroadcastManager is alright, I find this mechanism too complicated and restricting. Therefore, if the Service runs in the same process, I prefer to use EventBus in order to communicate from Service to other components (or vice-versa).
An example of such a communication you can see in SQLite benchmarking application that I wrote several days ago. In this app, TestService posts status changes and test results to EventBus as sticky events, and TestActivity subscribes to those events.
Both methods work with using LiveData since the purpose of LiveData is to have it on another thread and still notify users when something has changed. Seems like it would definitely replace LocalBroadcastManager.getInstance(this).sendBroadcast(in); and your IntentService would postValue. Just have your activity or anything that needs to be aware of the changes become an observer.
I am new to Android and I have a kind of design question. So I understand that it is recommended to use Fragments. I also understand that an Activity "owns" a Fragment.
So my question is the following:
Assume we want to make a long running background call e.g. HTTP to a server.
The results should be displayed in the UI.
So what is the best/standard practice?
1) Should the object doing the long running calls be "owned" by the Activity and the results send back to the Activity and then from the Activity to the Fragment so that the UI is updated?
2) Or should the object doing the long running called be "owned" by the Fragment and the results are send back to the Fragment for the UI to be updated? So the Activity is completely unaware of this?
Neither, IMHO. The object doing the long running should be managed by a Service, where you are using WakeLock (and, if needed, WifiLock) while the long-running HTTP operation is running. An IntentService might be a good candidate, as you need a background thread anyway.
The service, in turn, can use some sort of in-process event bus (LocalBroadcastManager, greenrobot's EventBus, Square's Otto, etc.), to let the UI layer know about any significant events that occurred from that HTTP operation. Whether the activity or the fragment is the one to subscribe to the events on the bus depends on what needs to happen for the event. If a fragment's widgets need to be updated, the fragment would subscribe. If the mix of fragments change, or some other activity-level UI change is needed, the activity would subscribe.
All of this assumes that by "long running" you mean something taking over a second or so.
For the long running task it's recommended to implement a sticky Service that contains a thread for the ServerSocket listener. Next I'd recommend to process requests by dedicated Thread's which are managed by a thread pool (check for instance this example).
In order to display results in your activity there are several approaches possible:
send a local broadcast from your service or thread which gets processed by registered BroadcastReceiver's which are part of your UI component (either Fragment's or Activity's)
bind your Service to the Activity (which might contain further fragments) and propagate information to containing fragments. There are three ways to go.
Note: In this post it's being said it's better to bind to the Application
pass data via Intent or a SQLiteDatabase
What I like and prefer is using local BroadcastReceiver's but this is just my personal preference.
It depends on your requirements, what might be the best solution.
I'm using a variation of the design recommended by #CommonsWare. I have an ApiClient class that listens to the event bus for requests to invoke API methods asynchronously. Any parameters that are needed for the API call go into the bus request message.
The ApiClient uses Retrofit to make the async API call, and posts a 'result message' containing the result to the event bus on success, and an 'error message' if there's an error. Each API call has it's own triplet of bus messages - xxxRequest, xxxResponse, xxxError.
So, when an Activity or a Fragment (or other, non-ui class) wants to invoke the api, it registers to the bus for the xxxResponse and xxxError messages, and then posts an xxxRequest message to the bus.
The only potential down-sides are:
The ApiClient is a singleton, and is owned by the Application class, just so that it doesn't get garbage collected.
You wind up with a large number of Message classes - I deal with this by putting them into their own package.
For android I am trying persist state if onDestroy() is called. I'm wondering what are common design patterns used to do such a thing so that the actual app functionality is decoupled from the persistence functionality? For example, I have a for loop that iterates through all of the players in the game. This update happens at the end of each round in the game. Would it be possible that the app gets destroyed half way through the update? If that happened, what kind of logic would i need to include to remember which player was updated.
You have two main options for saving state. If you are concerned with instance variables, then override Activity.onSaveInstanceState The answer to this question provides a good sample of how to do this.
For persistent data, you can either store it in SharedPreferences, the SQLite database, or write it to a file. This article should help you get started: http://developer.android.com/reference/android/app/Activity.html#SavingPersistentState
Your app can be killed half way during the update if users switch to another app such as receiving a phone call.
Considering such cases, you may need to persist your state in onPause() instead of onDestroy() since onPause() is the last method which is guaranted to be called when your activity is killed by the system.
And in onPause(), you can start a background service to do the update which can give your process higher priority so that it's less likely to be killed by the system when you are doing the update.
See more details in the Activity lifecycle and the Process document.
Android is not very nice about killing apps. You may or may not get onPause or onDestory. You may or may not be able to receive a hook onShutdown of the VM. Basically your app can die at any time (low memory pressure), user kills it, or etc and you won't get any warning. Basically it is best practice to assume that you can die at ANY time and store or update critical state as soon as you get it.
Basically I would either make a SQLitedatabase or use shared preferences for this.
I can't seem to find any documentation on the details of an Activity's run loop for Android.
Apple documents the "anatomy of a run loop", and that's pretty much what I'm looking for. The Android documentation just says "Activity Is Running" in its life cycle state diagram. Obviously that's backed up by some sort of run loop.
Anyone have some insight (aka Documentation) into the internals of an Activity's run loop?
edit - I should clarify that I presume the run loop is actually owned and run by the main UI thread. The current Activity's functionality is likely injected into this runloop at a certain point. I'm interested in the overall UI thread run loop, as well as what role the Activity takes in it.
The short answer is, "don't worry about it, it's done for you."
Activities and other constructs sit on top of android.os.Looper, communicating with it through instances of android.os.Handler. A Looper manages your "run loop," dispatching messages from a queue and blocking the thread when it's empty. Handlers communicate with a thread's Looper and provide a mechanism for working with the message queue.
Most of the time you won't need to work with either one directly. Lifecycle events for your major application components like Activities and Services will be dispatched to your code. If you're curious as to what's under the hood, sources for both are available:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Looper.java
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Handler.java
Updated:
There's really nothing specific being referred to by "Activity is running." The Activity is simply displaying its UI, handling input, executing any necessary functions, and starting another Activity.
If you're interested in what implications multi-threading would have on the run-loop, there isn't really a concrete relationship. Your threads can just do their work, and the Activity's state will function independently and automatically update its UI (provided you call postInvalidate() correctly).
Original:
Take a look at the first diagram on this page: http://developer.android.com/reference/android/app/Activity.html
It specifies the "lifetime" of each Activity and what states it can be in, if that's what you're looking for.