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.
}
}
}
Related
I know how to save states and restore them, but I just get confused when I have to do work with the Web services and to update UI. For times I was using the AsyncTask but then I came to point where I loose my activity/fragment context for example when I rotate the device. So in this way, I am thinking how other apps are handling such situations.
If I use the IntentService and call my web service from there, then I came to think that for each web service I have to make IntentService differently, and update the UI of each activity and fragment I have to make the BroadcastReceiver for each activity and fragments.
So what is a good practice for calling web service from the activity and the fragments?
How can I Update UI when the service return arrives (or call next service based on first services results)?
If you want your data to be instantly available through configuration changes (which you do), then you probably want to use Loaders.
It gives the developer a mechanism of loading data asynchronously for an activity or fragment. Since loaders are specifically designed to solve the issue of async loading, one does not have to spend too much time designing async tasks to handle all different scenarios efficiently.
Good article about Loaders https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832
Try using retrofit. It's a great networking libraries for Android apps and it's easy to use.
The entire network call + JSON/XML parsing is completely handled by it (with help from Gson for JSON parsing). Documentation is great and the community is huge.
check out this sample.
I noticed a comment you made:
...and my webservices are soap and I cant change them
The way I'm currently calling my web service, which is also SOAP, is via an Intent. I do this by passing in the data that I'm submitting to the Web service with putExtra then receiving it on my WebService, as you probably do right now. I then get the result from that web call and process it inside an AsyncTask, the async task will then utilize EventBus to post to Results as needed which are received on my MainThread via ThreadMode.Main.
So with that said, I highly recommend the use of a library called EventBus from Greenrobot.
You greatly simplify communication between Activities and Fragments, You can get started immediately using a default EventBus instance available from anywhere in your code. For example, you can do the following.
EventBus.getDefault().post(new ModelForOtherActivityToSee(data));
In the model, you can include anything you want, and react accordingly when received.
The best part is that when received, EventBus handles how the data will be executed by either running ASYNC, MAIN, BACKGROUND
ASYNC - Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long-running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
Background - Subscribers will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.
MAIN -Subscribers will be called in Android’s main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly (synchronously like described for ThreadMode.POSTING). Event handlers using this mode must return quickly to avoid blocking the main thread.
An example of receiving an event broadcasted from EventBus:
//ThreadMode can be ASYNC, MAIN, BACKGROUND
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModelForOtherActivityToSee eventModel) {
/* Do something with eventModel received, this runs on UI thread */
};
Full example on how to use EventBus:
1 - Open your build.gradle for the app and set your dependency for EventBus:
dependencies { compile 'org.greenrobot:eventbus:3.0.0'}
2 - Create your first model to use in publishing an EventBus, I will use a very simplistic example of a model:
package com.myapp.models.eventbusmodels;
public final class EventBusMyModel {
private final String dataRaw
public EventBusMyModel(final String rawData) {
this.dataRaw = rawData;
}
public String getRawData() {
return this.dataRaw;
}
}
3 - Now all that's left is pushing out a broadcast by using from anywhere.
EventBus.post(new EventBusModel("My Data here"));
4 - To enable Activities/Fragments to receive events from EventBus you must attach and detach, this is what I mean. From inside an Activity on the onResume() and onStop() overrides:
public class SomeActivity {
#Override
protected void onResume() {
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
}
#Override
protected void onStop() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onStop();
}
}
5 - The final thing to do is receive that broadcast, you can receive it in Any Fragment, Activity, or in all your fragments/activities. Here's an example from inside the SomeActivity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void eventThisNameDoesNotMatter(final EventBusMyModel resultModel) {
String receivedData = resultModel.getRawData();
//Do whatever with receivedData. Since we are on ThreadMode.MAIN, this is on the UI thread.
}
My activity is finished while AsynchTask is inside doinBackground() method.
When it reaches postExecute() the activity used to update the UI is already destroyed,but its TextView is still accessible in onPostExecute().
Can any one explain how this is happening?
Android Activities have a specific lifecyle. When they go past their Destroy lifecycle state, they are no longer usable by Android and will be eventually garbage collected.
However, when you pass an Activity reference, or any widget reference for that matter, to an AsyncTask, this prevents the GC from dispossing the activity and its related objects (e.g. view and widgets). Furthermore, your AsyncTask, which is executed in a separate thread with its own state, will hold a reference one or more objects that are invalid. And since you hold a reference in your AsyncTask, they cannot be freed by the GC.
That is why you must be very careful with using AsyncTasks with Activity reference. Also, refrain from using AsyncTasks for updating your UI, there are other ways of doing this.
Before starting new activity call cancel on AsyncTask
asynTask.cancel(true)
and in your asynctasks doInBackground method check if AsyncTask is cancelled or not
#Override
protected String doInBackground(String... urls) {
for( !isCancelled()) {
//do some stuff
}
}
Your activity can get destroyed for several reasons such as
System purging your activity due to memory needs
You rotate the phone. Then the Activity gets killed and recreated
You explicitly navigate back from the Activity
Your logic needs to handle such scenarios when executing an AsyncTask. Once your Activity starts an AsyncTask, it gets executed in another thread and delivers the results back to your activity once it gets finished. Your activity might get killed while the background AsyncTask is doing the work. A good practice is to call the cancel(boolean) method once your activity gets killed (onDestroy) and if you don't wish to continue with the results of the AsyncTask.
The question expects an explaination on how it happens.
Think of this simple java code.
public class myClass{
public static void main(String[] args) {
int n=2;
MyThread t=new MyThread(n);
t.start();
n=4;
System.out.println("n from main "+n);
}
}
class MyThread extends Thread{
int n;
public MyThread(int n){
this.n=n;
}
#Override
public void run(){
try{
Thread.sleep(2000);
}catch(Throwable e){
System.out.println("error :"+e);
}
System.out.println("n from thread "+n);
}
}
And the output would be,
n from main 4
n from thread 2
This happens because the instance of the integer n you pass to the thread holds its value. This is the same thing that happens in your AsyncTask. This behavior will be changed if you make n a static variable and made a reference to it inside the new thread.
AsyncTask (same as standard java thread) doesn't respect lifecycle of the host Activity. It's up to the developer to implement proper cancelling (as #mrDroid showed).
You may use AsyncTaskLoader, so you don't have to write code that handles activity configuration changes.
Be aware that even without direct reference to the host Activity (as mentioned #George Metaxas) a non-static inner AsyncTask may lead to memory leaks (because inner class holds implicit reference to the outer class).
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);
}
}
I am calling a class from my Activity. The class I am calling doesn't extend a Service or a Activity. It is just a worker class that I am using to process a BufferedReader object. I am concerned that if the app loses focus during the processing of the BufferedReader I could be left with messy data. So what happens when my Activity calls a class to perform database entries and the Activity loses focus. I am thinking of making my worker class a Service so that I will have access to the OnPause and OnResume methods. Not sure what I should be doing. Does anyone else have any clues about this.
You have to leverage the Android Activity Lifecycle.
class MyActivity extends Activity {
private MyWorkerClass myWorkerClass;
void onCreate() {
myWorkerClass = new MyWorkerClass();
myWorkerClass.startLoading();
}
void onPause() {
// You know the activity is going into the background
// so act appropriately in your worker class
myWorkerClass.stopLoading();
}
}
The issues you describe above are very generic, so explaining any specific implementation wouldn't help.
If you wanted your worker class to complete the work even if your Activity goes into onPause then yes you would want to do the work in another Thread and possibly in a Service. You can still have your worker class though for nice OO & adhering to the SRP.
You can create an IntentService if its a simple one-time task, or extend Service class if this is a continuous task. Also, have a look at AsyncTaskLoader.
If app process is being terminated, you need to make sure you don't leave the IO medium in a inconsistent state, or with corrupt data. So, use transactions in case of databases, write to a temporary file and replace at end in case of file IO, use confirmation requests in case of remote IO.
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