In my app I have a service that plays audio, that service just has a single static MediaPlayer object. In onStart() from my main activity where I call MediaPlayer.isPlaying(); to determine which UI elements to show, I get an ANR message and a NullPointerException at the line where I call MediaPlayer.isPlaying();
I also call MediaPLayer.isPlaying(); when certain buttons are pressed, and that causes ANR as well, I discovered that after i took away the MediaPlayer.isPlaying(); call in onStart();, since I couldn't get past that without an ANR...
I found this similar question: MediaPlayer isPlaying() always returning false
Where someone says: "Also, while we're on the general MediaPlayer subject, note that isPlaying() can cause an ANR when called on the UI thread. In fact, Google recommends executing all MediaPlayer calls from a background handler thread. Which will also serialize execution of MediaPlayer calls."
I couldn't find anything in documentation about this. So how do I or what is the best way to create a short lived thread in these scenarios so I am able to check the status of my MediaPlayer? Or is there another way?
Related
I am learning Android app development from Udacity.com by Google engineers and they said,
"It is not a good idea to use 'AsyncTask' as it is not attached to an activity life cycle. The virtual machine will hold on to the activity object as long as the Asynctask is running, even after Android has called onDestroy( ) method for the activity and expect it to be discarded.
If you rotate your phone, the behavior is to destroy the current activity and instantiate a new one. The naive AsyncTask implementation now has two threads trying to do the same update. So it is not the best pattern for a potentially very long running background operation , such as fetching from web services. If you leave the app, the asyncTask will run as long as as the process is kept alive , but will run at a lower priority, and your process will be the first thing to be killed if the device needs more resources. "
1) If using AsyncTask is disadvantageous why was it created? What would have been the design philosophy or the cause to create it in spite of having services(or something similar to achieve same kind of functionality)?
2) What are the situations where Asynctask should be used for betterment compared to Services/similar options available in Android?
3) What are the situations/places Asynctask should never be used?
Please do not downvote this question. I searched Stackoverflow and I couldn't find a similar question.
Advantages of AsyncTask
Provides generic solution for all network calls
Publish progress to UI while executing.
Run Asynchronously
Easy to maintain and read.
Problems in AysncTask
When you rotate your screen, Activity gets destroyed, so AsyncTask will not have a valid reference to publish data from onPostExecute(). In order to retain it, you need to usesetRetainState(true) if calling from fragment or onConfigChanges() if calling from activity method of an activity.
If activity gets finished, AsyncTask execution will not cancelled automatically, you need to cancel them else they will keep on running in the background.
If any exception occurs while performing network task, you need to handle them manually.
Whereas AsycTask, Services, IntentService, Threads all run on different threads and all serve different purpose.
please read more detail here.
So you need to decide when to use which component while performing non UI operations.
At runtime, I am using a callback pattern to have generic code run and use the callback when finished to execute code on the GUI. So in my handler I use the runOnUiThread function against the callback.
These are all async http calls. At runtime if I interrupt and use the back button and go to another fragment for example, the system will swap out the fragments and run both callbacks (the new fragment one on the correct callback, and the old callback that should have ran on the old fragment on the new current fragment). This is wrong, the new fragment gets both callbacks but when it was initialized it was assigned to the other fragment...
This makes no sense and you can only observe the behavior by switching fragments at runtime before an async call finishes. I don't see how it's possible, in the code I check if callback is null so it should have been garbage collected and how it runs on the new callback I don't know how it's possible....there's no additional assignment happening to change this. It's almost like it's just looking in memory for the function signature and running it even though when it does it's on the wrong object.
Does anyone have any clue what's going on? I've surpressed it with an ugly piece code, I just don't know how this is possible?
I understand the obvious candidates are if I assigned it again somewhere, but the answer is no. When the fragment is created it creates an object, assigns itself as the callback, and processing begins so when the fragment is destroyed it should be too. But the async task is still executing which is fine, why it does a callback on the new thread on the main gui I guess is because of runOnUiThread, so that function is somehow changing what the callback object points to in memory?
Its your code :-)
There is no magic happening 'behind the scenes' in Android and no references are changed.
Many have problems like yours, and its always because they assume that the Android system is somehow cleaning up when a fragment or activity instance is replaced or removed. It isn't, and therefore the callbacks are still executed, on a obsolete fragment or activity.
runOnUiThread takes a Runnable and runs it when the UI thread have spare time (after invoking lifecycle methods and finished rendering). Its not magic either :-)
So I have a problem and I am wondering how to solve it if it can be solved at all.
I have an external library class called Messenger that defines the method sendMessage(String msg). I also have the following MessengerManager class
public class MessengerManager{
private Messenger messenger;
public MessengerManager(Context context){
messenger = new Messenger(context);
}
public void message(){
String msg = "12435";
messenger.sendMessage(msg);
// Do more stuff
.....
.....
.....
}
}
So the issue is that sendMessage runs asynchronously and starts a new activity. Probably using startActivity() (lets call this Activity A). Hence, the code after sendMessage ("Do more stuff") gets run immediately. However, I need to wait for the newly created activity (Activity A) to return to carry on with the "Do more stuff".
As sendMessage is a method defined in an external library, I cannot change it in any way nor do I have access to its source code.
So, in short, my problem is that sendMessage creates a new thread (using startActivity() probably) and I need to know when this newly created activity ends. However, I have no way of monitoring it since it is defined in an external library. Anyone know how to solve this?
AsyncTask should be what you are looking for.
Let your message() starts an AsyncTask that calls messenger.sendMessage(msg) in doInBackground(). If you care about the result of the AsyncTask, get it in onPostExecute().
http://developer.android.com/reference/android/os/AsyncTask.html
One admittedly ugly way to get around this is to call
Thread.sleep(int milliseconds)
after sendMessage(), and before you continue with the rest of your code. Of course, this will only be helpful if you can fairly accurately estimate how long it will take to get the callback.
The better alternative is as Kaifei is saying. But you won't want the "do more stuff" where it is now. Instead, put it in a new method, and call that method in onPostExecute(). That way, the code will not continue until the AsyncTask has returned.
Edit: Saw your addition about needing the created activity to finish. If that's the case, you're going to somehow need to communicate with that activity (which I can't speak to without seeing all the code). But if you have that, then before "do more stuff", insert
while(!activity.isFinished()) {}
and in that activity, have it set a finished variable to true when it is done (however you define done). So the main thread will run this loop until the activity on the second thread is done, and then the first thread will continue.
Double Edit: Another idea. Let's say that the other activity the user has to complete is some form. In your main activity, after you call sendMessage(), have some popup where the user selects "finished" when they have completed the new activity. Have the program only continue when "finished" has been selected. A bit annoying for the user, but if it's only a single additional tap, that should work.
As you mentioned, sendMessage() runs asynchronously. If it's written properly, it must also have provided a callback interface, whose method will be called when messaging is finished. Move the code in that callback method.
I searched through most answers here and all suggest we add a flag to the intent to kill old activities. The problem is my activity only receive intents from other app and has no control over it.
More specifically, my activity receive an intent to load a picture, then it uses Asynctask to load and do some complicated background processing of that picture, say, may be 2 minutes.
If the user at this moment back out (assuming that only onStop is called, not yet onDestroy) and share another picture to the app , this will start a new activity, and the previous activity cannot be accessed, but its Asynctask hold up the thread so that my new activity just freeze without starting its own Asynctask.
(I later tried the parallel thread executor, but this doesn't stop the old thread from running, thus consuming computational resources).
Any idea what I should do?
(I don't want to stop the task in onStop as this is to easy to be called. But I do want to stop the task if new picture is shared, since it is no longer needed.)
(The Asynctask will spit out a huge array of self-defined Objects declared in the main activity, and thus the activity gets immediate updates of the result from the background process, and the UI updates immediately after onPostExecute is called.)
EDIT:
It seems that your problem is that you're trying to do all this image processing work in asynctasks launched by your activity. Have you considered changing your app architecture to rely all this background processing to a service?
You can use the activity to show some UI information while you process the image in your service, or if you don't need this UI just simply communicate with the service (through broadcast, for example) to provide it the image and let the service show some information of the process through notifications.
If you don't want to keep your activity alive when you exit it you can use
android:noHistory="true" in your activity declaration at the manifest
More info here
This is the workaround, and seems it is working very well for the moment:
After getting a sharedpreference prefs, do this in onCreate:
prefs.edit().putBoolean("ForceClose", true).commit();
In the onPreExecute() of the AsyncTask, put:
prefs.edit().putBoolean("ForceClose", false).commit();
In the doInBackground() of the AsyncTask, constantly check for
if (prefs.getBoolean("ForceClose", false))
cancel(true);
and call finish() in onCancelled() to finish off the activity.
There are numerous posts about how to handle a configuration change during an AsyncTask, but none I have found give a clear solution regarding apps that are in background (onPause()) when an AsyncTask finishes and tries to dismiss a DialogFragment (compatibility library).
Here is the problem, if I have an AsyncTask running that should dismiss a DialogFragment in onPostExecute(), I get an IllegalStateException if the app is in the background when it tries to dismiss the DialogFragment.
private static class SomeTask extends AsyncTask<Void, Void, Boolean> {
public SomeTask(SomeActivity tActivity)
{
mActivity = tActivity;
}
private SomeActivity mActivity;
/** Set the view during/after config change */
public void setView(Activity tActivity) {
mActivity tActivity;
}
#Override
protected Boolean doInBackground(Void... tParams) {
try {
//simulate some time consuming process
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ignore) {}
return true;
}
#Override
protected void onPostExecute(Boolean tRouteFound) {
mActivity.dismissSomeDialog();
}
}
The Activity looks like this:
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
public class SomeActivity extends FragmentActivity {
public void someMethod() {
...
displaySomeDialog();
new SomeTask(this).execute();
...
}
public void displaySomeDialog() {
DialogFragment someDialog = new SomeDialogFragment();
someDialog.show(getFragmentManager(), "dialog");
}
public void dismissSomeDialog() {
SomeDialogFragment someDialog = (SomeDialogFragment) getFragmentManager().findFragmentByTag("dialog");
someDialog.dismiss();
}
....
}
Works fine UNLESS the app switches to background while SomeTask is still running. In that case, when SomeTask tries to dismissSomeDialog(), I get an IllegalStateException.
05-25 16:36:02.237: E/AndroidRuntime(965): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
All of the posts I've seen seem to point in some kludgy direction with elaborate workarounds. Isn't there some android way of handling this? If it were a Dialog instead of a DialogFragment, then the Activity's dismissDialog() would handle it correctly. If it were a real DialogFragment instead of one from the ACP, then dismissAllowingStateLoss() would handle it. Isn't there something like this for the ACP version of DialogFragment?
Fragments are saved as part of each Activity's state, so performing transactions after onSaveInstanceState() has been called technically doesn't make sense.
You definitely don't want to use commitAllowingStateLoss() to avoid the exception in this case. Consider this scenario as an example:
The Activity executes an AsyncTask. The AsyncTask shows a DialogFragment in onPreExecute() and starts executing its task on a background thread.
The user clicks "Home" and the Activity is stopped and forced into the background. The system decides that the device is pretty low on memory so it decides that it should also destroy the Activity too.
The AsyncTask completes and onPostExecute() is called. Inside onPostExecute() you dismiss the DialogFragment using commitAllowingStateLoss() to avoid the exception.
The user navigates back to the Activity. The FragmentManager will restore the state of its fragments based on the Activity's saved state. The saved state doesn't know about anything after onSaveInstanceState() has been called, so the request to dismiss the DialogFragment will not be remembered and the DialogFragment will be restored even though the AsyncTask has already completed.
Because of weird bugs like these that can occasionally happen, it's usually not a good idea to use commitAllowingStateLoss() to avoid this exception. Because the AsyncTask callback methods (which are called in response to a background thread finishing its work) have absolutely nothing to do with the Activity lifecycle methods (which are invoked by the system server process in response to system-wide external events, such as the device falling asleep, or memory running low), handling these situations require you to do a little extra work. Of course, these bugs are extremely rare, and protecting your app against them will often not be the difference between a 1 star rating and a 5 star rating on the play store... but it is still something to be aware of.
Hopefully that made at least some sense. Also, note that Dialogs also exist as part of the Activitys state, so although using a plain old Dialog might avoid the exception, you would essentially have the same problem (i.e. dismissing the Dialog wouldn't be remembered when the Activity's state is later restored).
To be frank, the best solution would be to avoid showing a dialog throughout the duration of the AsyncTask. A much more user-friendly solution would be to show a indeterminate progress spinner in the ActionBar (like the G+ and Gmail apps, for example). Causing major shifts in the user interface in response to asynchronous callbacks is bad for the user experience because it is unexpected and abruptly yanks the user out of what they are doing.
See this blog post on the subject for more information.
To get around the illegal state exception issue and essentially implement a dismissAllowingStateLoss() can be done using the following.
getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss();
This should solve the issue without the hacky code. The same can also be applied for show if you have threads communicating through a handler with the UI thread using dialog.show(); Which can cause an illegal state exception as well
getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss();
#joneswah is correct, given the posters question.
If you are using the support library, replace
getFragmentManager()
with
getSupportFragmentManager()
For future Googlers:
#Alex Lockwood raises good and valid concerns with this solution. The solution does solve the error and will work in most cases, but hints that there are issues with the approach in the original question, from a UX perspective.
The Activity should assume that the async task may not complete and that it will not perform onPostExecute(). Whatever UI action (ie, spinner, ideally not a dialog) is started to notify the user of the async operation, should have provisions to stop automatically either on a timeout or by tracking state and checking in onRestore/onResume type lifecycle events to ensure the UI is updated properly. Services may also be worth investigating.
You should cancel your AsyncTask in onPause() if the onPostExecute() is going to update the UI. You shouldn't try to update the UI while your activity has been paused.
Eg. in your onPause():
if (task != null) task.cancel(true);
If you want the changes from the task to persist to the next time, then store the data/changes in doInBackground() and then update the UI when your activity/fragment/dialog gets resumed.
If you don't want the changes from the task to persist, then don't store the changes until onPostExecute()
When Android stops your app because the user hit the back or home button, your dialogs are closed for you. Usually the trick is to preserve the dialogs between onStop()/onStart(). So unless you need to do more than just close the dialog, I'd say don't worry about it.
EDIT: On your activity that hosts the dialog, you may still want to close the dialog if it's still open inside onStop(). This helps prevent memory leaks. But this doesn't need to be triggered from AsyncTask.
Like i said above, the problem is what happens when you hit onStart() again and your AsyncTask is NOT finished yet. You'll need to figure out a way to determine that and re-open that dialog if needed.
After numerous redesigns I finally settled on an approach that seems to work. However, I haven't been able to find certain aspects of my design documented elsewhere in discussions of Android development. So I'd be interested in any feedback.
In summary, what I had to do is this:
onSaveInstanceState() - if SomeTask is running, I use a simple lock to block SomeTask from exiting doInBackground() during pause.
onResume() - I had to put in a switch statement to handle different resume situations. If launching app I do nothing as nothing is paused, if restarting after being hidden or after config change I release the lock so that the preserved SomeTask instance can resume where it left off, etc.
onDestroy() - I cancel SomeTask if it is running.
I'll put the code fragments for this solution in my original post.
getActivity().finish(); in the DialogFragment worked for me.