Handler vs AsyncTask vs Thread

2018-12-31 06:36发布

I got slightly confused about the differences between Handlers, AsyncTask and Threads in Android. I've read quite a few blogs and questions here in stackoverflow.

Handler are background threads that provide you to communicate with the UI. Updating a progressbar for instance should be done via Handler. Using Handlers you have the advantage of MessagingQueues, so if you want to schedule messages or update multiple UI elements or have repeating tasks.

AsyncTask are similar, infact they make use of Handler, but doesn't run in the UI thread, so it's good for fetching data, for instance fetching web services. Later you can interact with the UI.

Thread however can't interact with the UI, provide more "basic" threading and you miss all the abstractions of AsyncTask.

However I would like to have a socket connection run in a service. Should this be run in a handler or a thread, or even an AsyncTask? UI interaction is not necessary at all. Does it make a difference in terms of performance which I use?

Meanwhile the documentation has been majorly improved.

12条回答
姐姐魅力值爆表
2楼-- · 2018-12-31 07:01

If you look at the source code of AsyncTask and Handler, you will see their code is written purely in Java. (Of course, there are some exceptions, but that is not an important point.)

So there is no magic in AsyncTask or Handler. They just make your job easier as a developer.

For example: If Program A calls method A(), method A() could run in a different thread with Program A.You can easily verify it using:

Thread t = Thread.currentThread();    
int id = t.getId();

Why you should use a new thread? You can google for it. Many many reasons.

So, what is the difference between Thread, AsyncTask, and Handler?

AsyncTask and Handler are written in Java (internally they use a Thread), so everything you can do with Handler or AsyncTask, you can achieve using a Thread too.

What can Handler and AsyncTask really help you with?

The most obvious reason is communication between the caller thread and the worker thread. (Caller Thread: A thread which calls the Worker Thread to perform some task. A Caller Thread does not necessarily have to be the UI thread). Of course, you can communicate between two threads in other ways, but there are many disadvantages (and dangers) due to thread safety issues.

That is why you should use Handler and AsyncTask. They do most of the work for you, you just need to know what methods to override.

The difference between Handler and AsyncTask is: Use AsyncTask when Caller thread is a UI Thread. This is what android document says:

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers

I want to emphasize on two points:

1) Easy use of the UI thread (so, use when caller thread is UI Thread).

2) No need to manipulate handlers. (means: You can use Handler instead of AsyncTask, but AsyncTask is an easier option).

There are many things in this post I haven't said yet, for example: what is UI Thread, or why it's easier. You must know some method behind each kind and use it, you will completely understand why..

@: when you read the Android document, you will see:

Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue

They may seem strange at first. Just understand that each thread has each message queue (like a to do list), and the thread will take each message and do it until the message queue is empty (just like you finish your work and go to bed). So, when Handler communicates, it just gives a message to caller thread and it will wait to process. Complicated? Just remember that Handler can communicate with the caller thread in a safe way.

查看更多
高级女魔头
3楼-- · 2018-12-31 07:01
public class RequestHandler {

    public String sendPostRequest(String requestURL,
                                  HashMap<String, String> postDataParams) {

        URL url;

        StringBuilder sb = new StringBuilder();
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode = conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                sb = new StringBuilder();
                String response;
                while ((response = br.readLine()) != null){
                    sb.append(response);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

}
查看更多
残风、尘缘若梦
4楼-- · 2018-12-31 07:08

Thread:

You can use new Thread for long running background tasks without impacting UI Thread. From java Thread, you can't update UI Thread.

Since normal Thread is not much useful for Android architecture, helper classes for threading have been introduced.

You can find answers to your queries in Threading performance documentation page.

Handler:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue.

There are two main uses for a Handler:

  1. To schedule messages and runnables to be executed as some point in the future;

  2. To enqueue an action to be performed on a different thread than your own.

AsyncTask:

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.

Drawbacks:

  1. By default, an app pushes all of the AsyncTask objects it creates into a single thread. Therefore, they execute in serial fashion, and—as with the main thread—an especially long work packet can block the queue. Due to this reason, use AsyncTask to handle work items shorter than 5ms in duration.

  2. AsyncTask objects are also the most common offenders for implicit-reference issues. AsyncTask objects present risks related to explicit references, as well.

HandlerThread:

You may need a more traditional approach to executing a block of work on a longer running thread (unlike AsyncTask, which should be used for 5ms workload) , and some ability to manage that workflow manually. A handler thread is effectively a long-running thread that grabs work from a queue, and operates on it.

ThreadPoolExecutor:

This class manages the creation of a group of threads, sets their priorities, and manages how work is distributed among those threads. As workload increases or decreases, the class spins up or destroys more threads to adjust to the workload.

If workload is more and single HandlerThread is not suffice, you can go for ThreadPoolExecutor

However I would like to have a socket connection run in a service. Should this be run in a handler or a thread, or even an AsyncTask? UI interaction is not necessary at all. Does it make a difference in terms of performance which I use?

Since UI interaction is not required, you may not go for AsyncTask. Normal threads are not much useful and hence HandlerThread is best option. Since you have to maintain socket connection, Handler on main thread is not useful at all. Create a HandlerThread and get a Handler from looper of HandlerThread.

 HandlerThread handlerThread = new HandlerThread("SocketOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());
 requestHandler.post(myRunnable); // where myRunnable is your Runnable object. 

If you want to communicate back to UI thread, you can use one more Handler to process response.

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Foreground task is completed:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

in your Runnable, you can add

responseHandler.sendMessage(msg);

More details about implementation can be found here:

Android: Toast in a thread

查看更多
无与为乐者.
5楼-- · 2018-12-31 07:08

Let me try and answer the question here with an example :) - MyImageSearch [Kindly refer the image here of the main activity screen - containing an edit text / search button / grid view]

MyImageSearch

Description of MyImageSearch - Once user enters the details on the edit text field and clicks on the search button, we will search images on the internet via the web services provided by flickr (you only need to register there to get a key/secret token) - for searching we send an HTTP Request and GET JSON Data back in response containing the url's of individual images which we will then use to load the grid view.

My Implementation - In the main activity I will define a inner class which extends the AsyncTask to send the HTTP Request in doInBackGround Method and fetch the JSON Response and update my local ArrayList of FlickrItems which I am going to use to update my GridView via the FlickrAdapter (extends the BaseAdapter) and call the adapter.notifyDataSetChanged() in the onPostExecute() of AsyncTask to reload the grid view. Note that here the HTTP Request is a blocking call because of which I have done it via the AsyncTask. And, I can cache the items in adapter to increase the performance or store them on SDCard. The grid that I will be inflating in the FlickrAdapter contains in my implementation a progressbar and image view. Below you can find the code for mainActivity which I used.

Answer to the Question Now - So once we have the JSON data for fetching individual Images we can implement the logic of getting the images in background via Handlers or Threads or AsyncTask. We should note here that since my images once downloaded must be displayed on the UI/main thread we cannot simply use threads as it is since they don't have access to the context. In the FlickrAdapter, the choices I could think of:

  • Choice 1: Create a LooperThread [extends thread] - and keep on downloading images sequentially in one thread by keeping this thread open [looper.loop()]
  • Choice 2: Make use of a Thread Pool and post the runnable via myHandler which contains reference to my ImageView, but since the views in Grid View are recycled, again the problem might arise where image at index 4 is displayed at index 9 [download may take more time]
  • Choice 3 [I used this]: Make use of a Thread Pool and send a message to myHandler, which contains data related to ImageView's index and ImageView itself, so while doing handleMessage() we will update the ImageView only if currentIndex matches the index of the Image we tried to download.
  • Choice 4: Make use of AsyncTask to download the images in background, but here I will not have access to the number of threads I want in the thread pool and it varies with different android version, but in Choice 3 I can make of conscious decision of the size of thread pool depending on device configuration being used.

Here the source code:

public class MainActivity extends ActionBarActivity {

    GridView imageGridView;
    ArrayList<FlickrItem> items = new ArrayList<FlickrItem>();
    FlickrAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageGridView = (GridView) findViewById(R.id.gridView1);
        adapter = new FlickrAdapter(this, items);
        imageGridView.setAdapter(adapter);
    }

    // To avoid a memory leak on configuration change making it a inner class
    class FlickrDownloader extends AsyncTask<Void, Void, Void> {



        @Override
        protected Void doInBackground(Void... params) {
            FlickrGetter getter = new FlickrGetter();

            ArrayList<FlickrItem> newItems = getter.fetchItems();

            // clear the existing array
            items.clear();

            // add the new items to the array
            items.addAll(newItems);

            // is this correct ? - Wrong rebuilding the list view and should not be done in background
            //adapter.notifyDataSetChanged();

            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

            adapter.notifyDataSetChanged();
        }

    }

    public void search(View view) {
        // get the flickr data
        FlickrDownloader downloader = new FlickrDownloader();
        downloader.execute();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

I hope my answer though long will help in understanding some of the finer details.

查看更多
琉璃瓶的回忆
6楼-- · 2018-12-31 07:10

AsyncTask is designed to perform not more than few seconds operation to be done in background (not recommended for megabytes of file downloading from server or compute cpu intensive task such as file IO operations ). If you need to execute a long running operation, you have been strongly advised to use java native threads. Java gives you various thread related classes to do what you need. Use Handler to update the UI Thread.

查看更多
浅入江南
7楼-- · 2018-12-31 07:13

As the Tutorial on Android background processing with Handlers, AsyncTask and Loaders on the Vogella site puts it:

The Handler class can be used to register to a thread and provides a simple channel to send data to this thread.

The AsyncTask class encapsulates the creation of a background process and the synchronization with the main thread. It also supports reporting progress of the running tasks.

And a Thread is basically the core element of multithreading which a developer can use with the following disadvantage:

If you use Java threads you have to handle the following requirements in your own code:

  • Synchronization with the main thread if you post back results to the user interface
  • No default for canceling the thread
  • No default thread pooling
  • No default for handling configuration changes in Android

And regarding the AsyncTask, as the Android Developer's Reference puts it:

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. 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 package such as Executor, ThreadPoolExecutor and FutureTask.

Update May 2015: I found an excellent series of lectures covering this topic.

This is the Google Search: Douglas Schmidt lecture android concurrency and synchronisation

This is the video of the first lecture on YouTube

All this is part of the CS 282 (2013): Systems Programming for Android from the Vanderbilt University. Here's the YouTube Playlist

Douglas Schmidt seems to be an excellent lecturer

Important: If you are at a point where you are considering to use AsyncTask to solve your threading issues, you should first check out ReactiveX/RxAndroid for a possibly more appropriate programming pattern. A very good resource for getting an overview is Learning RxJava 2 for Android by example.

查看更多
登录 后发表回答