线程对象Android 开发之多线程处理、Handler 详解

发一下牢骚和主题无关:

    

    每日一道理
毅力,是千里大堤一沙一石的凝聚,一点点地累积,才有前不见头后不见尾的壮丽;毅力,是春蚕吐丝一缕一缕的环绕,一丝丝地坚持,才有破茧而出重见光明的辉煌; 毅力,是远航的船的帆,有了帆,船才可以到达成功的彼岸。

    

Android开发过程当中为什么要多线程

我们创立的Service、Activity以及Broadcast均是一个主线程处置,这里我们可以懂得为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载须要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以斟酌使用Thread线程来解决。

  Android中使用Thread线程会遇到哪些问题

对于从事过J2ME开发的程序员来讲Thread比较简单,直接匿名创立重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来讲UI控件都没有设计成为线程安全类型,所以须要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处置机制。

postInvalidate()方法

对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处置,其中还供给了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒,如下:

void

postInvalidate()

void

postInvalidate(int left, int top, int right, int bottom)

void

postInvalidateDelayed(long delayMilliseconds)

void

postInvalidateDelayed(long delayMilliseconds, int left, int top, int right, int bottom)

Handler

当然推荐的方法是通过一个Handler来处置这些,可以在一个线程的run方法中调用handler对象的postMessage或sendMessage方法来实现,Android程序外部维护着一个消息队列,会轮训处置这些,如果你是Win32程序员可以很好懂得这些消息处置,不过相对于Android来讲没有供给PreTranslateMessage这些干预外部的方法。

消息的处置者,handler负责将须要传递的信息封装成Message,通过调用handler对象的obtainMessage()来实现。将消息传递给Looper,这是通过handler对象的sendMessage()来实现的。继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法对其停止处置。

 

Handler主要接受子线程发送的数据,并用此数据配合主线程更新UI.
      当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,停止事件分发,比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。  如果此时须要一个耗时的操作,例如:联网读取数据, 或者读取当地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死景象,如果5秒钟还没有实现的话,,会收到Android系统的一个错误提示  "强制关闭".  这个时候我们须要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的.这个时候,Handler就出现了,来解决这个复杂的问题, 由于Handler运行在主线程中(UI线程中),  它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包括数据)  ,把这些消息放入主线程队列中,配合主线程停止更新UI。


Handler一些特点:handler可以分发Message对象和Runnable对象到主线程中,每一个Handler实例,都会绑定到创立他的线程中(一般是位于主线程),
      它有两个作用: (1)安排消息或Runnable在某个主线程中某个地方执行

                           (2)安排一个动作在不同的线程中执行
        Handler平分发消息的一些方法
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)
      以上post类方法答应你排列一个Runnable对象到主线程队列中,sendMessage类方法,答应你安排一个带数据的Message对象到队列中,等待更新.

Handler实例
    // 子类须要继承Hendler类,并重写handleMessage(Message msg) 方法,用于接受线程数据
     // 以下为一个实例,它实现的功能为 :通过线程修改界面Button的内容

public class MyHandlerActivity extends Activity {

    Button button;

    MyHandler myHandler;

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentview(R.layout.handlertest);

        button = (Button) findViewById(R.id.button);

        myHandler = new MyHandler();

        //当创立一个新的Handler实例时,它会绑定到当前线程和消息的队列中,开始分发数据

        // Handler有两个作用, (1) :定时执行Message和Runnalbe对象

        // (2):让一个动作,在不同的线程中执行.

        //它安排消息,用以下方法

        // post(Runnable)

        // postAtTime(Runnable,long)

        // postDelayed(Runnable,long)

        // sendEmptyMessage(int)

        // sendMessage(Message);

        // sendMessageAtTime(Message,long)

        // sendMessageDelayed(Message,long)

        //以上方法以 post扫尾的答应你处置Runnable对象

        //sendMessage()答应你处置Message对象(Message里可以包括数据,)

        MyThread m = new MyThread();

        new Thread(m).start();

    }

    /**

     *接受消息,处置消息 ,此Handler会与当前主线程一块运行

     * */

    class MyHandler extends Handler {

        public MyHandler() {

        }

        public MyHandler(Looper L) {

            super(L);

        }

        //子类必须重写此方法,接受数据

        @Override

        public void handleMessage(Message msg) {

            // TODO Auto-generated method stub

            Log.d("MyHandler", "handleMessage......");

            super.handleMessage(msg);

            //此处可以更新UI

            Bundle b = msg.getData();

            String color = b.getString("color");

            MyHandlerActivity.this.button.append(color);

        }

    }

    class MyThread implements Runnable {

        public void run() {

            try {

                Thread.sleep(10000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            Log.d("thread.......", "mThread........");

            Message msg = new Message();

            Bundle b = new Bundle();//存放数据

            b.putString("color", "我的");

            msg.setData(b);

            MyHandlerActivity.this.myHandler.sendMessage(msg); //向Handler发送消息,更新UI

        }

    }
}

  Looper

其实Android中每一个Thread都随着一个Looper,Looper可以帮助Thread维护一个消息队列,昨天的问题 Can't create handler inside thread 错误 一文中提到这一观点,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还供给了一个Thread继承类HanderThread可以帮助我们处置,在HandlerThread对象中可以通过getLooper方法获得一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行须要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。

Looper是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创立是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象 
创立一个Looper对象时,会同时创立一个MessageQueue对象。除了主线程有默许的Looper,其他线程默许是没有MessageQueue对象的,所以,不能接受Message。如须要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。 
Looper从MessageQueue中取出Message然后,交由Handler的handleMessage停止处置。处置实现后,调用Message.recycle()将其放入Message Pool中。

Message

对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的    sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处置器。对于Handler类供给了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处置后才返回。

Message:消息对象,Message Queue中的存放的对象。一个Message Queue中包括多个Message。 Message实例对象的取得,通常使用Message类里的静态方法obtain(),该方法有多个重载版本可供选择;它的创立并不一定是直接创立一个新的实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例,则才用给定的参数创立一个Message对象。调用removeMessages()时,将Message从Message Queue中删除,同时放入到Message Pool中。除了上面这种方式,也可以通过Handler对象的obtainMessage()获得一个Message实例。

final boolean

sendEmptyMessage(int what)

final boolean

sendEmptyMessageAtTime(int what, long uptimeMillis)

final boolean

sendEmptyMessageDelayed(int what, long delayMillis)

final boolean

sendMessage(Message msg)

final boolean

sendMessageAtFrontOfQueue(Message msg)

boolean

sendMessageAtTime(Message msg, long uptimeMillis)

final boolean

sendMessageDelayed(Message msg, long delayMillis)

MessageQueue

是一种数据结构,见名知义,就是一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。 
创立一个线程的时候,并不会自动创立其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue停止管理。主线程创立时,会创立一个默许的Looper对象,而Looper对象的创立,将自动创立一个Message Queue。其他非主线程,不会自动创立Looper,要须要的时候,通过调用prepare函数来实现。 
java.util.concurrent对象分析

对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,斟酌到Android为我们已经设计好的Task机制,我们这里Android开发网对其不做过多的赘述。

Task以及AsyncTask

在Android中还供给了一种有别于线程的处置方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处置这些异步任务。 当然涉及到同步机制的方法和技巧还有很多,斟酌时间和篇幅问题不再做过多的描述。

文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。

--------------------------------- 原创文章 By
线程和对象
---------------------------------

原文地址:https://www.cnblogs.com/jiangu66/p/3104983.html