I have an application that runs a background thread which periodically performs a task. The UI thread moves through several different activities.
The tutorial I used can be found at this blog, the gist of it is the following:
Create a class that extends Thread
public final class JSONThread extends Thread {
Define a method in this class that adds a task to the MessageQueue, prompting executing when able.
public synchronized void enqueueJSON(final JSON.JSON task) {
However, after creating the initial object in my main activity, navigating to another activity obviously loses the Object bound to my Thread. I am no longer able to call methods on that Object (hence unable to add to queue).
I am unsure if this is caused by a wrong decision in architecture on my part or by overseeing the obvious solution. Any ideas? Note that I am trying to avoid AsyncTask for this purpose, since a pool of five threads for a simple task seems a little too much.
You need to store a Thread object as member of some other object with lifetime longer than Activity.
Two ideas for you:
a) It could be a member of Application (http://developer.android.com/reference/android/app/Application.html)
You may have problems with this, if you don't have a Service running. There is no guarantee that your application won't be killed (as example if any system dialog will pop up on top of your activities)
b) It could be a member of Service
(http://developer.android.com/reference/android/app/Service.html)
You should be using a service, not a thread. A service will remain in the background so long as there is an activity bound to it, and it won't be reset when an activity exits.
Related
As I understand - both Runnable and Service are intended to run piece of code in background. My code structure is this:
BaseManager.class which is implemented as Singleton and using BaseManager.getInstance() will return single instance in application. Also, when first initialized it automatically creates SmallerAndCompletelyDifferentManager.class - has a dependency.
SmallerAndCompletelyDifferentManager.class - creates a Runnable that runs every 2 seconds.
Now, I've two scenerios:
SCENERIO A: I create initialize BaseManager.class in Activity first and use it wherever I need. The Runnable that is inside SmallerAndCompletelyDifferentManager.class runs okay, but as I understand is attached to Activity - if Activity dies, so will the Runnable which I can not afford.
SCENERIO B: I create a foreground service and initialize BaseManager.class. Does this mean that now the Runnable will work as intended - even if application is in background and Activity has been destroyed?
Am I getting this right or no? The overall plan is to make sure that Runnable survives in background at all costs.
As I understand - both Runnable and Service are intended to run piece
of code in background
This is not correct.
Service is an application component that can be perform long-running operations in the background. Here background means you do something behind the scene (or background) when users interact with the app, or when users switch to another apps.
Runnable is a block of code that can be run, that why it has the name "Runnable", it means something can be run/execute. The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread.
In Android there a two types of thread, the first one is main/UI thread and another one is background thread. Here background means when you do something in a thread rather than main/UI thread.
Back to your case
In scenario 1: The Activity creates Runnable and keep a reference to it. When you destroy the activity (by press Back button or call finish() method), the activity will be destroyed, and the runnable will be released.
In scenario 2: The foreground service creates Runnable and keep a reference to it. When you destroy activity or switch to another apps, the service is still alive (and runnable as well) until you kill service by calling (stopSelft() or stopService() method). Because when using a foreground service, it will tell the system that the app is doing something important and it shouldn’t be killed.
As I understand - both Runnable and Service are intended to run piece
of code in background.
To be more specific, Service runs on a main thread. It is your responsibility to put the work on a separate thread if you are planning to make a CPU-intensive work inside the service. You can do so by putting it inside a Runnable or a Thread.
Am I getting this right or no? The overall plan is to make sure that
Runnable survives in background at all costs.
Scenario B could work but my suggestion is to modify your BaseManager into a service class.
I have an app which makes rest calls and represents the data in a GridView.
The main activity uses two fragements, a UI Fragment and a retained worker fragment. The worker fragment contains an inner AsyncTask that performs the REST calls.
Everything was working great I had no crashes etc, until I tried to do an update on a regular interval.
In order to perform the interval I added a handler. The handler is a member of the worker fragment. Within the worker fragment I have the LoadAPI method which calls the asynctask.
public void loadAPI(){
final String myURL = "http://x.com/"
handler.post(new Runnable() {
public void run(){
new APITask().execute(myURL);
handler.postDelayed(this,10000);
}
});
}
The problem is when there is a config change, my activity is destroyed, and onPostExecute crashes when it references the main activities listener. But I have implemented onAttach in my worker fragment. onCancel seems an ugly option, as multiple asynctasks can get called, and I don't have a named instance. I suppose I could keep a list of asynctasks in the worker fragment and cancel them onDestroy (It's ok to lose the latest update) but I think I am doing something wrong here. What is frustrating is the worker frag and asynctask were working fine until I did a continuous polling and I can't figure out why the introduction of the handler is causing this behavior.
My api tasks will take anywhere from 50 milisecond to 5 seconds.
Retained fragments will not get recreated during config changes like rotations, but they will still get destroyed and recreated when system will kill your app because it is in background for example.
so to be safe you should at least:
Never put your async task inside fragment as inner class, if you want to have it inside your fragment class body, then make it static. Otherwise AsyncTask will keep internal reference to your fragment and will prevent it from being garbage collected, and whats more bad is that in onPostExecute you will access your destroyed fragment.
When creating your asynctask, pass a reference to fragment to it, and store this reference inside WeakReference<>, ex:
private WeakReference<DataFragment> fragmentRef;
then in onPostExecute, before using fragment check if fragmentRef.get() returns non-null.
If you need continuous data updates, then consider using IntentService, or even WakefulIntentService. It will be slightly more difficult to report data updates progress from service to activity - but it can be managed with broadcasts. Also, if you want to do data updates from background then you will have to use service, together with alarms - then WakeFullIntentService (or regular service) is the way to go: https://github.com/commonsguy/cwac-wakeful for further reading.
Ok first of all android is really confusing. The scenario is I have about two runnable classes which are created from a Login View and if logged in it will create another view which will have other data and even more activities can be created from there
Now I can pass the the Login view context when creating a thread for the runnable class and edit out UI elements in them like this:
((Activity)someContext).runOnUiThread(new Runnable(){
public void run()
{
TextView txtErr = (TextView) ((Activity)someContext).findViewById(R.id.errMsg);
txtErr.setText("Some message");
}
});
But the issue is there will be more activities that will be created and the runnable class is created at the time of logging in, and I can't keep passing contexts.
Is there a better way for accessing the UI elements of different activities from different threads?
P.S: the threads which will be accessing the UI elements doesn't extend Activity and are running in a separate thread.
EDIT
I think I need to make my question more clear... I am developing a client app for a messenger... The process goes this way... User clicks on login button which creates a thread in a separate class named ClientThread for handling socket connection and keeping the connection alive till the user logs out or connection drops. The ClientThread class loops till the socket is connected and whenever some data is received the data is passed to another thread in a class named ProcessDataThread which do the parsing of data and will update the UI accordingly.
Now in a response from server if the user is logged in I want to create an activity from that class and keep a context to that activity in ProcessDataThread as I will be updating UI on further responses from server. And if login fails ProcessDataThread will display a message on the main activity saying login failed, now I was able to achieve the later by passing the context from the MainActivity to the two threads when clicked on Login like this:
global_constants.clientObject = new ClientThread(this);
global_constants.clientThread = new Thread(global_constants.clientObject);
global_constants.clientThread.start();
And then from ClientThread to ProcessDataThread
global_constants.updateConversationHandler.post(new ProcessDataThread(SharedBuff, cntxt));
But how will I create more activities from a non-activity class and do all update them or find a UI element etc...
Not sure if I understand you, but it sounds like you are trying to control the view of an activity from outside of the Activity. This sounds hacky to me. I'd let each Activity manage its own UI.
A good way of doing decoupled communication between objects is the observer pattern, aka an "event bus" or "event dispatcher" system. An example of how to do this on Android is here: http://www.therealjoshua.com/2012/03/event-dispatching-sending-messages/
Basically, the code that's generating the error should dispatch a message. The Activity can listen for this message, and then update its own UI as needed.
EDIT
Thanks for the clarification. I think the observer pattern can still help here. Basically, your data processing threads shouldn't know anything about the UI. Just have them post an event for the error, optionally with additional info on the error. If you want, your event dispatcher class could even make the actual event calls on the UI thread itself using a Runnable like you showed, so that the listener can always assume that they are being called on the UI thread, if this is important for your design. This way you don't have to pass the context to the thread at all (at least not for purposes of updating the UI) - let the worker thread just be responsible for the work, and the activity can be responsible for its own UI.
Another option you could use is an android Handler (see http://developer.android.com/reference/android/os/Handler.html)
In this case, the work is still done in another thread, but the Activity receives a handleMessage callback from the thread at the appropriate time. I haven't used this myself but from the documentation it looks like it can get the job done for what you need.
In either case IMO, the responsibility for updating the UI should lie with the Activity, not the worker thread.
I have seen some discussion here on Stack Overflow related to using Activity.onRetainNonConfigurationInstance() to maintain a background thread started by one instance of an Activity and pass it to the next instance of the Activity which results, for example, when the phone's orientation changes from portrait to landscape.
The discussions do not specify exactly what can be done with the thread wrapped in the Object returned from onRetainNonConfigurationInstance().
For example:
1. Is there a way to actually keep the background thread running using this technique?
2. Do you need to somehow pause the thread when the previous instance of Activity is going away and then restart it again in the new instance?
Can anyone provide a short example?
Any details would be appreciated.
You can return anything you want to onRetainNonConfigurationInstance(). If you have a Thread that you want passed from one instance of the Activity to another, you can either return it directly, or put it inside another object that you return from onRetainNonConfigurationInstance(). You don't need to pause the thread or interact with it in any way. It just keeps running as if nothing happened.
The only thing you need to be concerned about is how the Thread interacts with the Activity (if at all). If the thread will call the Activity back (to indicate progress or something like that) then you somehow need to give the thread a reference to the new Activity, as the old Activity will be dead.
What do you want to do in your background thread?
EDIT (add more details about threads/activities):
Threads have their own lifetimes which are completely disconnected from Activities. If you create a Thread in an Activity and start it, it will run to completion no matter what your Activity does. The only thing that will stop the thread explicitly is if Android decides to kill your process (which it may do if your process contains no active activities).
The thread will continue to run. For an example of what you can do with this, you can check out the android Ignition project and its IgnitedAsyncTask (and related examples).
The idea is that you will maintain a reference to your thread (usually an AsyncTask) somewhere in your Activity, and occasionally your thread (again, especially if it's an AsyncTask) will require a reference to a Context in order to perform some kind of UI update upon the conclusion of its background task. You will need to make sure that the Context (and anything derived from it--like a TextView or the like) to which your thread has a reference is non-null, or else it will crash.
You might use getLastNonConfigurationInstance() to set your Activity's reference to the thread, and then call a setter on the thread to set its Context reference (to avoid any related null pointer crash).
I have a class that fetches data in response to button presses in the main activity. Unfortunately, I keep running into problems because this class is not an Activity or a Service. For example, without a Context I cannot translate a resource id into a string:
getString(R.string.example_string); // Doesn't work
Should I make this class into a Service and have the main Activity stop the class when it is closed? Should I pass the Context from the Activity into this class like this?
MyClass c = new MyClass(this);
Or is there some better way to handle this problem?
This issue also comes up when I try to send a Toast from this class.
Update: Erich and Janusz pointed me in the direction of the AsyncTask class which works perfectly, except that it creates a new thread and never kills that thread. This means that ever time the user presses a button, another thread is added and the old ones just sit there.
If you have a background action whose lifecycle is decoupled from your activity, I would use a Service. In that case, the Service will have its own Context, so you won't need to pass it in. If, however, you need to perform a background action in response to a UI event (and optionally post the results back into the UI thread), I would recommend you use an AsyncTask.
I agree with Erich, if you only have a something small like posting a change to a web backend or loading something from the phone memory to show it on screen use a Async Task. If the task will exit very "quick" (some seconds) you can make an anonymous class inside your activity. This will enable you to use a implicit reference to the outer activity inside the task and you can get your context from there.
If the task is running for a longer time you can pass down the context. If you are passing down the context try to not pass this from the activity use this.getApplicationContext() this will minimize the number of references to your activity and enable the garbage collector to clean up properly.