say for example I have this code in my activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Thread cThread = new Thread(new Runner());
cThread.start();
}
private NotifyMe(){
//do something here
}
and this is my Runner class:
public class TCPClient implements Runnable {
public void run(){
//call NotifyMe() [THIS IS MY QUESTION]
}
}
I have a thread on my activity that runs the Runner Class.
Once the thread start, I would like to call the NotifyMe() function
that is located at the activity. Is this possible?
Please let me know if you don't understand my question.
You can add a Constructor to the TCPClient that takes a reference to the activity, change the notifyMe method to public and then call the notifyMe method on the activity object that is stored in the thread.
The problem you would get with this is that activities may be closed, paused, destroyed while your thread is running. To check if the activity is still active use the isFinishing() method from the activity.
This solution is somewhat dangerous if your activity uses a lot of memory because the reference to the activity in the thread will let the garbage collector not reclaim the memory used by the drawables of the UI in the activity etc. until the thread is executed and can be garbage collected as well. If your activity is not that heavy in memory that should be ok. If it is or if you want to access the data from the thread from multiple activities have a look at this question.
A more or less unrelated note if you have a very small thread that won't run the whole time your app is running use a AsyncTask. This will allow you to simply put a single operation into the background.
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.
}
}
}
For example, if we consider the snippet code below:
public class HandlerExample extends AppCompatActivity {
private Handler mLeakyHandler = new Handler();
private TextView myTextBox;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_samples);
myTextBox = (TextView) findViewById(R.id.tv_handler);
// Post a message and delay its execution for 10 seconds.
mLeakyHandler.postDelayed(new Runnable() {
#Override
public void run() {
myTextBox.setText("Done");
}
}, 1000 * 10);
}
}
When the Activity gets destroyed, whether by a configuration change or another reason, the Runnable will not. The same goes for Asynctasks.
My question is, what makes it not being destroyed even if it's declared in that Activity?
Consider that I am asking about the why.
Because Activity class runs on UI Thread and Runnable, AsyncTask etc run on Background Threads that are separate from the ui thread. And ending the Activity won't end other threads unless you specifically instruct it to do so.
Runnable Description:
The Runnable interface should be implemented by any class whose
instances are intended to be executed by a thread.
AsyncTask Description:
AsyncTask enables proper and easy use of the UI thread. This class
allows you to perform background operations and publish results on the
UI thread without having to manipulate threads and/or handlers.
and also you can learn more about Threads by reading the documentation.
What makes it not being destroyed even if it's declared in that Activity?
When new Handler() is called on a thread (the main thread in your case), the Handler is associated with the Looper's message queue of the thread, sending to and processing Runnables and messages from the queue. Those Runnables and messages have a reference to the target Handler. Even though Activity's onDestroy() method isn't a "destructor", i.e. when the method returns the Activity's instance won't be immediately killed (see), the memory cannot be GC-ed because of an implicit reference to the Activity as being an outer class of the Runnable. The Activity won't be GC-ed for the delay time, i.e. until the Runnable be de-queued from the Looper's message queue and processed.
References:
This Handler class should be static or leaks might occur: IncomingHandler
How to Leak a Context: Handlers & Inner Classes
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).
I have a bindservice in onCreate that takes a long time, I tried to solve this problem by moving it to a worker thread, but it still causes the UI to wait. What can I do
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
new Thread(){
public void run(){
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
Intent serviceIntent = new Intent(getApplicationContext(), Recognizer.class);
bindService(serviceIntent, mConnection,Context.BIND_AUTO_CREATE);
}
}.start();
}
From your description, it seems that calling bindService within UI thread is not the root cause of ANR. You should instead, call bindService without spawning user thread. One main reason is that, this causes memory leak.
When there is configuration change, as long the the thread still alive, it doesn't give chance to Activity for performing garbage collection. This is because your user thread is still holding reference to Activity.
The ANR root cause should come from Recognizer or mConnection. If you can post the implementation details of them, this can help us to help you.
Ask yourself the following questions
Are you performing any I/O intensive task in mConnection's onServiceConnected?
Are you performing any I/O intensive task in Recognizer's onCreate?
...
As others have said, the problem is not in your Activity, but in your Service. Your onBind() call is taking too long and onBind() is called on the main (UI) Thread. You are doing too much work in the onBind() call. What you need to do is to move your initialization of your Service out of the onBind() call and into a separate background thread. To prevent the client from using the Service before it has been initialized, you'll need to keep a flag for each bound client which indicates if the background thread has completed the initalization. If the client tries to use the Service before it is ready (ie: completely initialized) then you can return some error code or throw a suitable exception.
Remember that lifecycle calls in all Android components (Activity, Service, BroadcastReceiver, ContentProvider) all run on the main (UI) thread and must run quickly.
The problem is not in your Activity. You said that your bind service takes long time. So you must create thread inside the onBind method of your service.
public class ClientService extends Service
{
#Override
public IBinder onBind(final Intent intent)
{
new Thread(){
public void run(){
// Your code here
}
}.start();
}
}
I know that AsyncTask are not preferred for long process. Their main objective is to take the load off from UI thread and do stuff in background. Later on completion update the respective UI thread.
I am aware of the memory leaks i.e when UI needs to be updated after doInBackground is done and there's a possibility that the activity is destroyed.
My question is can I use AsyncTask just like a simple Thread?
What happens to AsyncTask if Activity or Application dies, which started it?
Requirments
I dont need to update any UI.
I dont want my task to be associated with Activity(which starts it).
First Question :
Yes you can.Its totally depends on your logic.
Second Question :
The thread will be in the background though the application is killed by the user or by the system.
To resolve the second scenario use the following technique
Just make sure that you are finishing your AsyncTask before application or Activity closes
AsyncTask yourAsyncTask
#Override
public void onDestroy(){
//you may call the cancel() method but if it is not handled in doInBackground() method
if(yourAsyncTask!=null)
if (yourAsyncTask != null && yourAsyncTask.getStatus() != AsyncTask.Status.FINISHED)
yourAsyncTask.cancel(true);
super.onDestroy();
}
If you only need the 'doInBackground' just use a normal thread.
new Thread("threadName", new Runnable(){ #Override run(){ } }).start();
The whole reason to use an AsyncTask is to have the facilities of preExecute and postExecute, so you don't need to mess with handlers.
It remain started in background even the application is killed or crash.
First, a general note, as stated by the Android Docs:
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 pacakge such as Executor, ThreadPoolExecutor and FutureTask.
To answer your questions:
Yes - you can use Async task as if it were just a background thread - an Async task is merely a wrapper of Thread and Handler that allows the thread to seamlessly communicate with the UI thread. Warning! If you plan to update the UI thread, or otherwise reference an activity or fragment in the callbacks that reference the UI thread (i.e. onProgressUpdated and/or onPostExecute) you should explicitly check that the activity or fragment is still in a state from which it can be referenced and used. For example - here's the right and wrong way to do it when launching an AsyncTask from a fragment:
Create your task with a ref to the activity so you can do something when it's done:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
Fragment mFragment;
public DownloadFilesTask(Fragment fragment){
mFragment = fragment;
}
WRONG:
protected void onPostExecute(Long result) {
// if the fragment has been detached, this will crash
mFragment.getView().findView...
}
RIGHT:
protected void onPostExecute(Long result) {
if (mFragment !=null && mFragment.isResumed())
... do something on the UI thread ...
}
}
If the Activity dies while an AsyncTask is executed, it will continue to run. Using the techniques listed above, you can avoid crashes by checking the lifecycle of the context that started the task.
Finally, if you have a very long-running operation that does not require the UI thread at all, you should look into using a Service. Here's a blurb:
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use
My question is can I use AsyncTask just like a simple Thread?
Yes AsyncTask is android background thread , the task will be done in the background .
AsyncTask automatically creates a new Thread for you, so everything you do in doInBackground() is on another thread.
What happens to AsyncTask if Activity or Application dies, which
started it?
The AsyncTask is related to application , if application destroy or finish then all related AsyncTask of that application will be terminated .