If I leave a thread running when I quit my android app, can I get access to that thread when the app is restarted? I know that the thread is still associated with my app because I can kill it by going to settings-apps-force stop.
more details: my app connects to a device via bluetooth. when i rotate the tablet, it restarts the app, but if i don't stop all the threads, the old thread reconnects to the device and the app is not able to connect with a new thread.
I have fixed the basic problem by not allowing the app screen to rotate, and by killing the connect thread onDestroy(). but I would like to know how to re-connect with that sort of zombie thread just out of curiosity.
I can see threads that I don't recognize in Thread.enumerate(), but I don't know how to get access to those threads, other than seeing the name and their state.
The way I deal with this in my apps is to override an Activity's onRetainCustomNonConfigurationInstance() method, which allows you to retain an object through the restart that happens when the screen is rotated. Here's how I implement it.
I have an AsyncTask that performs a web request. The AsyncTask is in a separate file, and takes a reference to the calling Activity as a listener for some callbacks I have implemented. So the constructor for my web request AsyncTask is something like this:
private Callbacks listener;
public WebRequest(Callbacks listener) {
this.listener = listener;
}
I implement onRetainCustomNonConfigurationInstance() in my Activity like this:
#Override
public Object onRetainCustomNonConfigurationInstance() {
if(webRequest != null) {
webRequest.detachFromActivity();
return webRequest;
} else {
return null;
}
}
Now, when my screen is rotated, the Activity restarts, and if my AsyncTask is running, it will save a reference to it here. Notice that I also "detach" my task from this current Activity, which will now be destroyed. I accomplish this in my task by just making the listener (which is the current Activity) null. Like this:
public void detachFromActivity() {
listener = null;
}
Now when the Activity restarts, in onCreate(), I check to see if there was a retained reference to my running thread by calling getLastCustomNonConfigurationInstance() like this:
Object retainedRequest = getLastCustomNonConfigurationInstance();
if(retainedRequest != null) {
if(retainedRequest instanceof WebRequest) {
webRequest = (WebRequest) retainedRequest;
webRequest.setListener(this);
}
}
Since the reference to my running thread is passed as an Object, I need to retrieve it as an Object, then check if it's an instance of my AsyncTask, then cast it if it is.
The last step is to "reconnect" the callbacks to this NEW Activity, which was just created, so the task knows where to send the results. I use the setListener(this) method to do it in my task, like this:
public void setListener(Callbacks listener) {
this.listener = listener;
}
Now I can re-attach a reference to an old thread with a newly re-created Activity. You may not be using an AsyncTask, but the concept is the same and should work for any Thread, or any object you want, really.
Hope this helps!
Im not sure on your question, but what you are doing is kinda wrong. Screen rotation are UI changes and they should not affect your other code.
Check this answer for some guidance- http://stackoverflow.com/questions/5913130/dont-reload-application-when-orientation-changes
PS: NoChinDeluxes answer is also good for decoupling UI with other elements
The basic problem, as you have discovered, is that you have implemented your app in such a way that your bluetooth connection is logically bound to an Activity (i.e. the Activity is responsible for keeping track of the thread handling bluetooth activity).
To have the bluetooth connection reference survive a rotation, you will need to decouple it from the Activity. There are a number of ways to do this, depending on exactly what your requirements are.
You could, for instance, implement the bluetooth code as a Service.
There are other ways as well - for instance, take a look at Activity restart on rotation Android
Related
I have a MainActivity that uses fragments.
The onCreate of MainActivity completes its onCreate with the use of
welcomeFragment = new MyWelcomeFragment();
fr.beginTransaction().replace(R.id.mainContent, welcomeFragment).commit()
As a part of MyWelcomeFragment's on onResume, a thread is started to get updates from my webserver. If the user selects an action before the thread is completed and goes to MyNewsFragment, what happens to the thread that has yet to complete running in MyWelcomeFragment's thread stack?
Thread was created with: (myThread and handler are instance variables)
myThread = new Thread(new Runnable() {
#Override
public void run() {
sendDataToServer("");
handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
onTaskDone();
}
});
}
});
myThread.start();
Dalvik keeps all Thread references in the runtime so your thread will keep running unless it is terminated or completes (some reference). So depending on where you start your thread, you may be creating more than one. There is no clean way to cancel a Thread and in this case you may need to first cancel the http request inside sendDataToServer and use a shared flag to stop the thread.
In a bigger picture, I would suggest
move the networking method to Activity and handle it there since it has longer lifespan than
Fragment
use Android Volley to handle networking. With it you can manage inadvertent multiple requests to send data to your server. Since each request can be attached with tag, you can cancel any with a particular tag in the queue (in your case the one corresponding to sendDataToServer process) before starting a new one.
and finally use Publisher-Subsriber pattern which has already been made available by libraries like Otto or EventBus. This allows communication between Fragments or Activities while avoiding life cycle related problems. In a gist: a publisher emits events to subscribers registered to it and unlike listeners both publisher and subscriber are totally decoupled. In your case, when sendDataToServer completes, you will not know if the fragment containing onTaskDone is still around. If this method manipulates UI while the fragment has destroyed its view then you will definitely get an error. So onTaskDone should be wrapped inside a subscriber method whose parent fragment is registered to the http event publisher and deregistered as soon as its view is destroyed.
It'll keep running until run() method completes, which is probably for how long it takes for sendDataToServer("") takes to complete, as the handler should be fairly quick in comparison to network IO - or the thread is force interrupted.
Are you still interested in the result if the user switches fragments?
Are you keeping a reference to the welcome fragment? (Via either fragment manager or activity) - if so you could still access the result.
If the user goes back to welcome fragment, the previous thread reference will be lost.
Thread will keep on running till MyWelcomeFragment is alive and If you don't kill it in onPause().
I have a situation where I have multiple activities that deal with a single global list of objects.
From activity A, I can start an asynctask which sends an HTTP Get request to retrieve an XML message then parses the XML file into a series of objects.
My aim is to be able to refresh all views, only if the underlying data structure changes.
This should ideally mean that I can launch activity A, call an asynctask, move onto activity B, C D etc, and when the asynctask completes (no matter what activity I am on) the current view should be updated.
I've looked into broadcast listeners but I am not sure I am on the right direction with this, would it be possible for somebody to explain if this is possible?
Thanks
Since AsyncTask instances are dependent of the caller Activity, I think this is not the correct approach. In your case, as you need to recover the result within any Activity, I'd use an unbound Service so it might be accessed whenever you want, wherever you want.
You might also use a local BroadcastReceiver which would send a signal to all of your Activitys and, those registering the action that sent your AsyncTask/Service, would perform the necessary actions you may need. Be careful as if your Activity has not been started, obviously that Activity won't be able to process what you've sent since that receiver should be unregistered.
Just post a runnable to the handler of the current Activity.
Your activities can implement a common interface, where a function "updateUI" or whatever exists, and that way your runnable can be one line, and be agnostic as to the current active instance.
As you probably know the postexecute part of the asynctask runs on the UI thread of the activity that launches it. Since that activity may be dead, just post to the handler in the "doinbackground" part of the async task and keep postexecute blank.
interface Updateable{
public Handler getHandler();
public void updateUI();
}
From Async Thread
final Updateable curr_activity = ...
Handler handle = curr_activity.getHandler();
handle.post(new Runnable() {
#Override
public void run() {
curr_activity.updateUI();
}
});
I am running into a strange problem...
My application is meant to do some webservice calls on a separate thread. Once the webservice call is finished, it would navigate user to a different activity.
In the case when user press the home button or exit current activity it should terminate the webservice if the webservice call thread is still running. Hence I put a thread termination method in the OnPause state.
Here is the method block that is running inside the thread:
private Thread _webserviceThread;
void WebserviceCallThread(){
WebRestult result= WebserviceCall();
if(!result.containsError()){
RunOnUIThread(delegate{
transitionToActivityXYZ();
});
}
}
void RunThreadAction(){
_webserviceThread = new Thread(new ThreadStart(WebserviceCallThread));
_webserviceThread.Start();
}
protected override void OnPause(){
if(_webserviceThread != null && _webserviceThread.IsAlive){
_webserviceThread.Abort();
}
}
After the webservice call is done and begin the transition to another page, It gets to the OnPause state. However, in some strange cases, it would think that the thread is not finished in the OnPause state, even though the activity transition is the last line of the method.
Has anyone ran into this problem before? If so, how did you solve this problem?
Thanks!
I always use AsyncTask for this kind of thing. Not only does it abstract away the explicit thread handling and provide hooks to do everything you want here; it's also a nice way to represent a unit of work that can be used from other activities.
There's a simple example in this post part way down, but it doesn't use the generic parameters which are quite handy.
Why not use Task Parallel Library,
It is standard .NET, and with AsyncTask, it is only recommended for tasks that take less than few seconds. see the Documentation
AsyncTasks should ideally be used for short operations (a few seconds
at the most.) If you need to keep threads running for long periods of
time, it is highly recommended you use the various APIs provided by
the java.util.concurrent
Below is an example for how to use Task Parallel Library, taken from here
private void loginWithTaskLibrary()
{
_progressDialog.Show();
Task.Factory
.StartNew(() =>
_loginService.Login("greg")
)
.ContinueWith(task =>
RunOnUiThread(() =>
onSuccessfulLogin()
)
);
}
From the Activity, I am creating a Handler to fire off my AsyncTask every 45 seconds in order to refresh the content of my ListView's DataAdapter. The AsyncTask works great and keeps the user informed on the progress through ProgressUpdates and Toast messages.
Since the thread's doInBackground is fire and forget and not re-usable, I am having to create a new instance of the AsyncTask from my Hander that is firing off every 45 seconds. The problem is when the screen is rotated and and then I get concurrent messages going off because the Hander was recreated and created a new instance of the AsyncTask, so the friendly user progress through ProgressUpdates and Toast messages is overwhelming and makes utilizing the ListView difficult.
And please don't suggest this as a solution: android:screenOrientation="portrait" is not an option.
For something that has to run so frequently, should I just be using a custom Thread and not the AsyncTask class? ToDo: Not shown, I have to update the Adapter later from the Sensor's onSensorChanged event to update bearings on for each location in the ListView, I was going to run that on a separate AsyncTask class because I don't need to notify the user everytime the device bearing has changed.
Since the AsyncThread cannot be reused, am I doing this all wrong? In short, what is the best way to have the Activity refresh the ListView and keeping off the UI thread when doing so?
The problem is when the screen is rotated and and then I get concurrent messages going off because the Hander was recreated and created a new instance of the AsyncTask.
Reason quoting from API Activity - Configuration Changes:
Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate.
So every object has a activity-scope life cycle (i.e. Handler, AsyncTask and etc. defined within your activity class) is suffered by this activity recreation. However, you can bypass this activity recreation, as stated in the later paragraph of Activity - Configuration Changes section:
In some special cases, you may want to bypass restarting of your activity based on one or more types of configuration changes. This is done with the android:configChanges attribute in its manifest. For any types of configuration changes you say that you handle there, you will receive a call to your current activity's onConfigurationChanged(Configuration) method instead of being restarted. If a configuration change involves any that you do not handle, however, the activity will still be restarted and onConfigurationChanged(Configuration) will not be called.
Not related to topic, but as a good practice, you should always destroy used object (Handler, AsyncTask and etc.) properly when activity is about to finish (i.e. in onDestroy() method).
For something that has to run so frequently, should I just be using a custom Thread and not the AsyncTask class?
AsyncTask is pretty handy but not suit for periodic task, I would use ScheduledExecutorService or TimerTask in this case, check out my answer here for sample code.
Can you please post a bit of your code ? It may be useful to understand where your problem is.
As york has pointed it out, you should probably use TimerTask. It seems that it suit better with what you are trying to do.
If it is the creation of a new instance of the Handler that create the probleme you can try something like this :
private Handler mHandler = null;
#Override
public void onCreate(Bundle _savedInstanceState) {
super.onCreate(_savedInstanceState);
setContentView(R.layout.my_layout);
if (mHandler == null) {
// TODO create your handler here
}
}
EDIT :
You can test _savedInstanceState == null too.
_savedInstanceState is used to save the state of the activity so turning the phone shouldn't be a problem anymore.
However, if you leave the activity and then go back to it, it will create a new handler (except if you instanciate it as a static variable).
After an AsyncTask finishes, I usually do one of the following;
Call a method on a callback object/interface that I define in the activity and pass to My AsyncTask subclass constructor.
Provide the AsyncTask with a Handler and call myHandler.sendMessage() when the task is complete.
What I want to ask is what is considered best practice after the AsyncTask is complete. If the user has pressed the home button while the task is processing, the activity is no longer in the foregreound. As soon as the app tries some UI operation, as a response to the task beeing completed, the The OS throws a WindowManager$BadTokenException.
I can of course surround my UI code with a catch for BadTokenException. That seems hacky. Is there any way to tell if the activity is in front?
Maybe you will not like my answer, but I consider AsyncTask broken (for reasons like this one).
Edit: My initial answer recommended to use an IntentService and broadcast the result. This is as inefficient as sending a letter to yourself.
You can use an AsyncTaskLoader which works around the problems of AsyncTask, but the API of AsyncTaskLoader is far from perfect as well. In particular, you must ensure that loader id is unique, and be aware that results are cached for the id, not for the arguments. Also, the propagation of exception is as broken as with AsyncTask.
A more modern and safer way to approach the problem is to use Guava future.
It means you are using some where the context that is not appropriate. To clear you doubt about the exception see this Link.
Bad Token Exception
You can check if the activity is active or not. I usually make my AsyncTask subclass as static (to avoid memory leak) so I pass a reference of the activity (wrapped on a WeakReference, again to avoid memory leaks).
When onPostExecute is executing I do the necessary checks when using WeakReferences plus call Activity.isFinishing() for the activity, so I can check the activity is not in process of being destroy, to avoid execute UI changes on a dying Activity.
Define an object of your activity in your onStart() as a static member -
private static MyActivity mActivity = null;
public void onStart() {
mActivity = this;
}
public void onDestroy() {
mActivity = null;
}
Then on your AsyncTask method, do something like:
if (mActivity != null) {
mActivity.doSomething(); //make sure to use static member
}