android线程

来源:互联网 发布:mac魔兽世界闪退 编辑:程序博客网 时间:2024/05/23 23:36

Threads


When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also the thread in which your application interacts with components from the Android UI toolkit (components from the android.widget and android.viewpackages). As such, the main thread is also sometimes called the UI thread.

The system does not create a separate thread for each instance of a component. All components that run in the same process are instantiated in the UI thread, and system calls to each component are dispatched from that thread. Consequently, methods that respond to system callbacks (such asonKeyDown() to report user actions or a lifecycle callback method) always run in the UI thread of the process.

For instance, when the user touches a button on the screen, your app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget that it should redraw itself.

When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.

Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:

  1. Do not block the UI thread
  2. Do not access the Android UI toolkit from outside the UI thread

    Worker threads

    Because of the single thread model described above, it's vital to the responsiveness of your application's UI that you do not block the UI thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

    For example, below is some code for a click listener that downloads an image from a separate thread and displays it in an ImageView:

    public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            Bitmap b = loadImageFromNetwork("http://example.com/image.png");            mImageView.setImageBitmap(b);        }    }).start();}

    At first, this seems to work fine, because it creates a new thread to handle the network operation. However, it violates the second rule of the single-threaded model: do not access the Android UI toolkit from outside the UI thread—this sample modifies the ImageView from the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.

    To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

    • Activity.runOnUiThread(Runnable)
    • View.post(Runnable)
    • View.postDelayed(Runnable, long)

    For example, you can fix the above code by using the View.post(Runnable) method:

    public void onClick(View v) {    new Thread(new Runnable() {        public void run() {            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");            mImageView.post(new Runnable() {                public void run() {                    mImageView.setImageBitmap(bitmap);                }            });        }    }).start();}

    Now this implementation is thread-safe: the network operation is done from a separate thread while theImageView is manipulated from the UI thread.

    However, as the complexity of the operation grows, this kind of code can get complicated and difficult to maintain. To handle more complex interactions with a worker thread, you might consider using aHandler in your worker thread, to process messages delivered from the UI thread. Perhaps the best solution, though, is to extend the AsyncTask class, which simplifies the execution of worker thread tasks that need to interact with the UI.

    注意区分UI线程和工作线程的工作内容;不要在工作线程中直接对UI线程的资源(控件)进行操作。

    2.Android线程之间的通信——Handler

       其实,Android线程之间的通信不只是Handler,还需要Message,MessageQueue,Looper的相互使用,Android线程通信模型如下:

    (1)Message:即要传递的消息;

    (2)MessageQueue:存放消息的队列;

    (3)Looper:用于创建MessageQueue以及循环使用其中的Message;

    (4)Handler:用于消息的传递了;

    ***********那么就必须保证UI线程不可以被阻塞,从而耗时操作必须要开启一个新的线程来处理!

    那么问题就来了,等耗时操作结束以后,如何把最新的数据反馈给用户呢?而我们目前工作Worker线程中,从而不可以进行UI更新。

    那么怎么办呢?必须要把最新的数据传给UI线程能处理的地方!现在就派到Handler出场了!可Handler到底干了啥呢?简要说明如下:

    Activity所在的UI线程在创建的时候,就关联了Looper和MessageQueue,那么我们又在UI线程里创建了自己的Handler,那么Handler是属于UI线程的,从而它是可以和UI线程交互的!
    UI线程的Looper一直在进行Loop操作MessageQueue读取符合要求的Message给属于它的target即Handler来处理!所以啊,我们只要在Worker线程中将最新的数据放到Handler所关联的Looper的MessageQueue中,然而Looper一直在loop操作,一旦有符合要求的Message,就第一时间将Message交给该Message的target即Handler来处理!所以啊,我们在创建Message的时候就应该指定它的target即Handler!
    但我们也可以,new Message() -- > mHandler.sendMessage(msg) ;这是特例!

    如果我们通过obtainMessage()方法获取Message对象,此时Handler就会自动设置Message的target。可以看源码!

    简单一点说就是:

    UI线程或Worker线程提供MessageQueue,Handler向其中填Message,Looper从其中读Message,然后交由Message自己的target即Handler来处理!!最终被从属于UI线程的Handler的handlMessag(Message msg)方法被调用!!

    这就是Android多线程异步处理最为核心的地方!!

    3、AsyncTask

    官方的描述(这个我就不翻译了):

    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.

    为了解决多线程执行任务的问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。其实说白了,这个工具类只是对线程和Handler的一个封装,具体内部如何实现我将会在提高篇解析。

    这个工具类使用很简单主要有以下四个步骤

    1、继承或者实现AsyncTask类(因为是abstract 修饰的抽象类)

    2、实现以下方法

    (1)onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。 
    (2)doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。 
       (3)onProgressUpdate(Progress...),publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。 
        (4)onPostExecute(Result), doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. 

    3、在UI线程中实例化定义好的AsyncTask或者其子类

    4、调用execute(Params...)开始执行任务

    为了正确使用AsyncTask类,必须遵循一下准则:

    1、AsyncTask实例必须在UI线程中创建

    2、execute方法必须在UI线程中调用

    3、不允许手动调用onPreExecute(), onPostExecute(Result)doInBackground(Params...), onProgressUpdate(Progress...)这几个方法 

    4、AsyncTaskexecute(Params...)执行方法只能执行一次,就是一个实例只能执行一次,执行多次会出现异常。需要说明AsyncTask不能完全取代线程,在一些逻辑较为复杂或者需要在后台反复执行的逻辑就可能需要线程来实现了。


0 0