What happens if some activity lifecycle events occur while a method is executing?
My activity has some methods where JSON text files (few tens of Kb) are written to disk, for example. Is it wrong designed?
Or, in general, consider the case where there are two instructions to be executed: the first performs an important operation, the second updates some data based on the result from the first.
Something like:
value=performOperationAndYeldResult();
updateAppData(value);
What happens if the lifecycle event occurrs after the first call and before the second, or while it's executing?
I put "long" operations in services but I cannot create a service for every critical data update all over the app. I think I am missing something so I fear for nothing. Maybe the resumed or restarted activity continues operations so they get completed.
Anyway I ask here: Are methods completed, or continued?
new PerformOperationAsyncTask(someValue);
private class PerformOperationAsyncTask extends AsyncTask<Object, Void, Object> {
protected Long doInBackground(Object... values) {
//this is taking place in secondary thread
Object returnValue = performOperationAndYeldResult(values[0]);
return returnValues;
}
protected void onPostExecute(Object result) {
//this is taking place in the UI thread
updateAppData(value);
}
}
Related
I have some fragments loaded in a ViewPager, where each "page" is loaded from a row in a cursor. Each fragment shows an image (JPEG) on the device. When the user dismisses the fragment (i.e swipe/page change, hits back/up, or just closes the app entirely) I want to invoke a method which opens the JPEG file for writing and does an update of its metadata. The actual work is eventually handled by the Apache Commons Imaging library.
I've implemented this by invoking my saveToFile() method from each fragment's life cycle onStop() handler. Does this mean the entire file operation ends up running on the UI thread? Should I definitely set up an AsyncTask for this?
Say the file write for some reason suddenly (for some jpeg) should take a long time, eg 2 minutes. What would then happen? Would the UI just wait (freeze) at this page/fragment before resuming? Or would the process (write to file) carry on "in the background" somehow? Or would the process just be killed, stopped short mid-process?
The way I have this wired up currently (onStop invoking saveToFile(), which calls up the imaging library and then updates the file) seems to work as it should. Even if I end the app, I still see my Toast text popping up, saying "Writing to file..." Seemingly, the process is never disturbed, and I can't say I'm experiencing any UI lag.
onStop() handler. Does this mean the entire file operation ends up
running on the UI thread? Should I definitely set up an AsyncTask for
this?
YES
An AsyncTask has several parts: a doInBackground method that does, in fact, run on a separate thread and the onPostExecute method that runs on the UI thread.
You can also use some sort of observer pattern such as EventBus to run async and post results to the UI.
Say the file write for some reason suddenly (for some jpeg) should
take a long time, eg 2 minutes. What would then happen? Would the UI
just wait (freeze)
The application will crash because Android will forcefully close it due to ANR (Application Not Responding).
Refer to the official documentation for details on this: https://developer.android.com/training/articles/perf-anr.html
Android applications normally run entirely on a single thread by
default the "UI thread" or "main thread"). This means anything your
application is doing in the UI thread that takes a long time to
complete can trigger the ANR dialog because your application is not
giving itself a chance to handle the input event or intent broadcasts.
Therefore, any method that runs in the UI thread should do as little
work as possible on that thread. In particular, activities should do
as little as possible to set up in key life-cycle methods such as
onCreate() and onResume(). Potentially long running operations such as
network or database operations, or computationally expensive
calculations such as resizing bitmaps should be done in a worker
thread (or in the case of databases operations, via an asynchronous
request).
The most effective way to create a worker thread for longer operations
is with the AsyncTask class.
Here is what I recommend though. Use the above mentioned, EventBus and create a BaseActivity which will automatically save the data for you onClose() by firing an event that runs Async. You then extend that base activity in all the places where you need autosave capabilities.
Here's what I mean with an example that uses EventBus.
public abstract class BaseActivity extends Activity{
#Override
protected void onResume(){
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
super.onResume();
}
#Override
protected void onDestroy() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onDestroy();
}
#Override
protected void onStop() {
super.onStop();
//We fire event and pass the current parent class that inherited this base.
EventBus.getDefault().post(new EventBusProcessMySaveData(this.getClass()));
}
}
//Your model class to use with EventBus
public final class EventBusProcessMySaveData{
private final Class className;
public EventBusProcessMySaveData(final Class className){
this.className = className;
}
public Class getClassName(){
return this.className;
}
}
public class MyMainActivity extends BaseActivity{
//Do you standard setup here onCreate() and such...
//Handle Event for Saving Operation, async.
//This will fire everytime theres an onClose() IN ANY activity that
//extends BaseActivity, but will only process if the class names match.
#Subscribe(threadMode = ThreadMode.ASYNC)
public void methodNameDoesNotReallyMatterHere(final EventBusProcessMySaveData model){
//We make sure this is the intended receiving end by comparing current class name
//with received class name.
if(model.getClassName().equals(this.getClass())){
//Do whatever you need to do that's CPUintensive here.
}
}
}
I have what seems like a stupid requirement:
I need to block my IntentService thread while I run a single method which must be accessed from the UI thread. How can I do this?
Obviously I can run the UI method via a Handler with Looper.getMainLooper() but of course the rest of my service processing would then continue.
A bit more detail:
My Service Sync's content while using binder callbacks to progressively update the UI with the new items. All methods which affect the list of items are UI-Thread bound to avoid my StaggeredGridLayout throwing ConcurrentModificationExceptions.
However, when my service starts I want to call the list to get current id's before I sync each source of content, this is what requires the UI thread access.
The reason I can't just provide this list while starting the Service is that the app must remain responsive (meaning an item can be deleted as we sync), and the list needs to be checked before each additional source of content is synced
Solutions:
The best solution I came up with, is to create two Handlers, one on the Main Looper for the UI method, the other for everything else and send messages between them. It doesn't feel like an acceptable, clean solution
The other was to make a threadsafe version of the same UI method, starting by doing an arraycopy of the contents and looping with the copy. I'm not sure if the arraycopy operation is unsafe (prone to ConcurrentModificationException) too, as it's hard to trigger the bug. So I'm not sure this is acceptable either.
I wrote a solution that uses AsyncTask.
Here's an Activity that calls the task:
public class CallingActivity extends Activity {
// method that does all the synchronization
private void doSync() {
// for each item you want to sync
int itemId;
new ItemUpdateAsyncTask(itemId, this).execute();
}
// this method is called for each synced item
public void syncItem(int itemId, Object syncedItem) {
// update list etc.
}
}
Now, the AsyncTask that updates the items in background:
public class ItemUpdateAsyncTask extends AsyncTask<Void, Void, Object> {
// the id of the item to be updated
private final int mItemId;
// reference to the activity that will be notified
// of the item update
private final WeakReference<CallingActivity> mCallingActivity;
public ItemUpdateAsyncTask(int itemId, CallingActivity callingActivity) {
super();
mItemId = itemId;
mCallingActivity = new WeakReference<>(callingActivity);
}
#Override
protected Object doInBackground(Void... params) {
// sync item in background
Object syncedItem;
return syncedItem;
}
#Override
protected void onPostExecute(Object syncedItem) {
CallingActivity callingActivity = mCallingActivity.get();
if (callingActivity != null) {
// update item in main thread
callingActivity.syncItem(mItemId, syncedItem);
}
}
}
I think it is easier to design this solution using AsyncTasks instead of Services because of the callback object passing. Services and Activities can only communicate with each other by means of Intents, which can carry only serializable data. As you can see, asynctasks can reference callback objects, which makes things easier. Or else, you would have to register a broadcast receiver in the activity and the service would have to send a broadcast with the updated item, which would need to be serializable. That's a viable solution as well, though.
the user can go between several activites during the time it is running
It is a point worth noting when you design an architecture like this, that does something in background and pushes the result to the caller. The caller may be e.g. an Activity that may be already finished and garbage-collected by the time that the background work finishes. That's the reason I wrapped the caller in a WeakReference.
So from what I've read, Android's AsyncTask is a great way to asynchronously load information from the Internet. However, I don't want to block up the UI and prevent the user from interacting with it.
A basic description of my problem.
Currently, I am using websockets in order to send/receive data from a web server. On events like a user entering the room, a song being added or removed from a playlist, a song being upvoted or downvoted, or one song ending and another one beginning, the UI must be updated in order to indicate changes. But ideally, these changes will be occurring very frequently, which means that constantly blocking the UI in order to refresh it would be cumbersome and annoying.
How would I update my UI without interrupting the user in their activities? Would AsyncTask suffice?
The asyncTask does not block the UI. It runs on a separate thread to send / receive the data from the web, and then returns the results. When you receive the results back, you can update the UI as you choose.
Your UI will not be stopped while the asyncTask is performing its background work. You can try it out by by building one in your activity and simply sleeping for some amount of time (let's say five seconds) in the doInBackground method. You will see that your UI is still functional during that five seconds.
Edit: You can do just about anything with the results you get back and it won't interrupt your UI either. If that's not the case, you'll probably want to look at optimizing what you are doing with your in memory objects. Anything not stored in memory should probably be retrieved or written to disk, database, or internet endpoint with an AsyncTask. As the commenter points out above, this is not the only way to use other threads, but it's easy and will probably work if you're making a reasonable web request and expect users to have a decent connection. You will just want to make sure you have timeouts and exceptions covered so that your app doesn't crash if the task takes longer than expected.
public class LoadCommentList extends AsyncTask<Integer, Integer, List<Comment>> {
private String commentSubject;
public LoadCommentList(commentSubject){
this.commentSubject = commentSubject;
}
// Do the long-running work in here
protected List<Comment> doInBackground(Integer... params) {
// the data producer is a class I have to handle web calls
DataProducer dp = DataProducer.getInstance();
// here, the getComments method makes the http call to get comments
List<Comment> comments = dp.getComments(commentSubject);
return comments;
}
// This is called each time you call publishProgress()
protected void onProgressUpdate(Integer... progress) {
// setProgressPercent(progress[0]);
}
// This is called when doInBackground() is finished
protected void onPostExecute(List<Comment> comments) {
// calls a method in the activity to update the ui
updateUI(comments);
}
}
There are cleaner examples actually using the Integer... params for example, but this is just something I had handy as an example.
I don't know where you read that but asyn task are worst way to make web service call this days. You should use Retrofit for service call, it is 8 Times faster and handle UI update smoothly.
Read more about this here :-
http://googleweblight.com/?lite_url=http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit&ei=qR4bQU5c&lc=en-IN&s=1&m=260&host=www.google.co.in&ts=1465531978&sig=APY536z0v15lfX3G6KY4nls4wf1kzttJdA
I'm writing an application for Android, which should get some data from the server as soon as it launches.
Between the start of the application and the response from the server (or timeout, if the server is down), the application should display a "waiting" animation.
Thereafter, the normal panel should be shown (if the server responded) or an error dialog box be displayed (if the server didn't respond).
What is the correct place to put this logic into?
MainActivity.onCreate or some other place?
if you want the data loaded only when the app starts for the first time, onCreate() is the right place. if you want to re-loaded every time the app comes into focus (i.e., the foreground), then onResume() is the right place. take a look at documentation on the activity lifecycle for details.
you'll want to take a look into AsyncTask, or Loader+AsyncTaskLoader to understand the right pattern for doing something in the background then updating the UI with the result.
As Jeffrey suggested at first you have to determine when you want to connect to the server? Depending on this you should connect to server in onCreate or onResume.
Now you must remember one thing that you can't do heavey tasks in your manin GUI thread. Else there is a good chance of ANR. So you have to implement this feature in a different thread. For this you can use different Thread, Handler or AsyncTask. You can find a nice doc here
I think it is a suitable situation to use AsyncTask. So here is an example with AsyncTask
private class ServerCommunication extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// show the connecting screen
// or you can do this before calling asyncTask
}
#Override
protected Void doInBackground(Void... params) {
// communicate with server
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// show the second screeen
}
}
and call it using
ServerCommunication pcd = new ServerCommunication();
pcd.execute();
This is just for suggesting the structure. You can definitely use neccessary paramenters or other method also.
I have a very sporadic failure in my app I'm trying to resolve. On entry to the app, at one point the main UI thread processing ends and passes control to a background thread to retrieve some data. When the data is retrieved, control passes back to the main UI thread to process it for display. However, on some rare occassions (it works 99% of the time), the AsyncTask seems to be failing to be called leaving the app in a poor static state forever waiting for the AsyncTask to complete.
Here's a snapshot of the code in the Activity:
//method call from main UI thread
private void fetchSomeData() {
Log.d("myTag", "In fecthSomeData()");
new ReadFileAsyncTask<DataModel>().execute(this);
}
Here's the ReadFileAsyncTask implementation:
public class ReadFileAsyncTask<A> extends AsyncTask<I_ReadFileListener<A>, Void, A>
{
I_ReadFileListener<A> listener;
#Override
#SuppressWarnings("unchecked")
protected A doInBackground(I_ReadFileListener<A>... params)
{
listener = params[0];
Log.d("mytag", "BACKGROUND: Loading " + listener.getFilename() + " from disk");
A fileContents = (A) FileUtils.readDataFromInternalStorage(listener.getContext(), listener.getFilename());
return fileContents;
}
#Override
protected void onPostExecute(A result)
{
Log.d("myTag", "FOREGROUND: Executing onFileRetrieved listener");
listener.onFileRetrieved(result);
}
}
Capturing the logs on this rare failure yeilds:
In fetchSomeData()
...
(Other log messages from other interactions with the activity such as menu creation and navigation initialization)
but, crucially, not the log statement from the second line of code in the doInBackground method. One thought I had was that this log statement was failing, but I'm not seeing any force stop messages, error in my logs or ACRA crash reports. The application is still active (I can navigate to other activities and back) so I'm at a loss as to what might stop this background thread from running properly. Any ideas?
Sadly AsyncTask is not suitable for critical code execution since, depending on the ThreadPool base and max size, your AsyncTask may never execute.
Moreover, the onPostExecute method could be called when the Activity it is referring (i.e. its creating context) has already been destroyed. You have no way to synchronize with it rather then maybe using join() on the AsyncThread from the UI Thread.
Even though I've seen doing this also in the Android Camera App it isn't a good idea to block the UI Thread waiting for an event since you coulg get an ANR (Application Not Running) notification.
Take a look at this: Is AsyncTask really conceptually flawed or am I just missing something?
Consider using IntentServices, HandlerThread or ThreadPoolExecutors if you need a possibly better way to synchronize your worker thread with your your UIThread.
From http://developer.android.com/training/run-background-service/create-service.html:
Also, an IntentService isn't affected by most user interface lifecycle events, so it continues to run in circumstances that would shut down an AsyncTask