码迷,mamicode.com
首页 > 其他好文 > 详细

Handler Looper 原理 详解

时间:2016-06-14 16:08:38      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:


演示代码
技术分享
public class MainActivity extends ListActivity {
    private TextView tv_info;
    private CalThread calThread;//一个子线程
    private boolean ifLooperPrepare = false;//是否在子线程中的创建Handler【前】初始化Looper
    private boolean ifLooperLoop = false;//是否在子线程中创建Handler【后】执行loop方法
    private boolean sendMessageToUi = false;//是否将子线程中的消息转发到主线程中
    private boolean sendMessageToZi = false;//是否将主线程中的消息转发到子线程中
    public static final int MSG_WHAT_1 = 1;
    @SuppressLint("HandlerLeak")
    private Handler uiHandler = new Handler() {//在主线程中,系统已经初始化了一个Looper对象,所以我们直接创建Handler对象就可以进行信息的发送与处理了
        public void handleMessage(Message msg) {
            Toast.makeText(MainActivity.this"主线程收到消息:" + msg.obj, Toast.LENGTH_SHORT).show();
            tv_info.setText("主线程收到消息:" + msg.obj);
            if (sendMessageToZi) {
                Message newMsg = Message.obtain(null, msg.whatnew SimpleDateFormat("  HH:mm:ss", Locale.getDefault()).format(new Date()));
                calThread.getThreadHandler().sendMessageAtTime(newMsg, SystemClock.uptimeMillis() + 4500);// 从开机到现在的毫秒数(不包括手机睡眠的时间)
            }
        }
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = { "在子线程中的创建Handler前,必须先通过Looper.prepare()初始化Looper",//
                "若要子线程能收到消息,在子线程中的创建Handler后,必须调用Looper.loop()",//
                "开启子线程,并在子线程中创建Handler \n注意:此线程会一直阻塞在Looper.loop()"//
                "在主线程中,通过子线程的Handler向子线程发消息"//
                "将子线程中的收到的消息(不)转发到主线程",//
                "将主线程中的收到的消息(不)转发到子线程",//
                "演示Handler的post方法" };
        for (int i = 0; i < array.length; i++) {
            array[i] = i + "、" + array[i];
        }
        tv_info = new TextView(this);
        tv_info.setTextColor(Color.BLUE);
        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
        tv_info.setPadding(20, 10, 20, 10);
        getListView().addFooterView(tv_info);
        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1new ArrayList<String>(Arrays.asList(array))));
        calThread = new CalThread();
    }
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
        case 0:
            ifLooperPrepare = true;
            break;
        case 1:
            ifLooperLoop = true;
            break;
        case 2:
            calThread.start();
            break;
        case 3:
            String obj = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date());
            calThread.getThreadHandler().sendMessage(Message.obtain(nullMSG_WHAT_1, obj));//这种方式时,Message.obtain的第一个参数可直接设为null
            //注意,并不是说Message.obtain的第一个参数Handler没有用(其作用是指定msg.target),而是后面调用sendMessage方法时重新指定了当前Handler为msg.target
            break;
        case 4:
            sendMessageToUi = !sendMessageToUi;
            break;
        case 5:
            sendMessageToZi = !sendMessageToZi;
            break;
        case 6:
            uiHandler.post(new Runnable() {//其实这个Runnable并没有创建什么线程,而是发送了一条消息,当Handler收到此消息后回调run()方法
                        @Override
                        public void run() {
                            tv_info.setText("演示Handler的post方法");
                        }
                    });
            break;
        }
    }
    /** 定义一个线程,用于执行耗时的操作  */
    class CalThread extends Thread {
        private Handler ziHandler;
        public Handler getThreadHandler() {
            return ziHandler;
        }
        @SuppressLint("HandlerLeak")
        public void run() {
            if (ifLooperPrepare) {
                Looper.prepare();//①为当前线程创建唯一的Looper对象  ②在它的构造方法中会创建一个的MessageQueue对象
                //prepare()方法只能被调用一次,否则抛异常;这保证了在一个线程中只有一个Looper实例,同时一个Looper实例也只有一个MessageQueue实例
            }
            ziHandler = new Handler() {//任何线程都可通过此Handler发送信息!
                //在初始化Handler之前必须先调用Looper.prepare(),否则报RuntimeException: Can‘t create handler inside thread that has not called Looper.prepare()
                @Override
                public void handleMessage(Message msg) {
                    Toast.makeText(MainActivity.this"子线程收到消息:" + msg.obj, Toast.LENGTH_SHORT).show();//在子线程中是可以弹土司的,因为土司机制比较特殊!
                    //在子线程中创建Handler的目的完全是为了和其他线程(包括UI线程)通讯,绝对不是(也不能)更新UI,若要更新UI,还必须通过UI线程才可以!
                    if (sendMessageToUi) {
                        uiHandler.sendMessageDelayed(Message.obtain(msg), 2500);//注意,不能直接把消息转发出去,否则IllegalStateException: This message is already in use
                    }
                }
            };
            if (ifLooperLoop) {
                Looper.loop();//启动一个死循环,不断从MessageQueue中取消息,没有消息则阻塞等待,有则通过将消息传给当前Handler去处理。此方法必须在prepar之后运行
            }
        }
    }
}

简介
Handler 、Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?
异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
其实Looper负责的就是创建一个MessageQueue,然后在一个无限循环体中不断从该MessageQueue中读取消息,而消息是由Handler(一个或多个发送过来

基本使用过程:
  • ①在主线程中创建Handler并重写handleMessage()方法
  • ②在子线程利用此Handler发送消息,消息会被发送到主线程的MessageQueue中
  • ③一旦MessageQueue中有新消息,主线程中的 Looper 就会发现此消息,然后就会调用Handler的handleMessage()方法处理此消息

相关名词
  • UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
  • Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
  • Message:Handler接收与处理的对象
  • MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
  • Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!

Looper详解
首先看其prepare()方法
public static final void prepare() {
        if (sThreadLocal.get() != nullthrow new RuntimeException("Only one Looper may be created per thread");
        sThreadLocal.set(new Looper(true));
    }
ThreadLocal可以在一个线程中存储变量,可以看到,我们将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否存储过Looper对象,存储过(即.get()不为null)则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例

下面看其构造方法:
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }
在构造方法中,创建了一个消息队列MessageQueue。

然后我们看loop()方法:
public static void loop() {
        final Looper me = myLooper();//方法体为 return sThreadLocal.get();  即此方法直接返回了sThreadLocal存储的Looper实例
        if (me == nullthrow new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");//如果me为null则抛出异常,也就是说looper方法必须在prepar()之后运行。
        final MessageQueue queue = me.mQueue;//拿到该looper实例中的消息队列
        Binder.clearCallingIdentity(); // Make sure the identity of this thread is that of the local process, and keep track of what that identity token actually is.  
        final long ident = Binder.clearCallingIdentity();
        //无限循环
        for (;;) {
            Message msg = queue.next(); // might block,取出一条消息,如果没有消息则阻塞等待
            if (msg == nullreturn;
            Printer logging = me.mLogging; // This must be in a local variable, in case a UI event sets the logger  
            if (logging != null) logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
            msg.target.dispatchMessage(msg);//Msg的target其实就是handler对象
            if (logging != null) logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            final long newIdent = Binder.clearCallingIdentity();// Make sure that during the course of dispatching the identity of the thread wasn‘t corrupted.  
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent)
                        + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);
            }
            msg.recycle();//释放消息占据的资源
        }
    }

Looper主要作用:
  • 1、与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
  • 2、执行loop()方法,不断从MessageQueue中取消息,交给消息的target属性的dispatchMessage方法去处理。

大家可能还会问,在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢?
这是因为在Activity的启动代码中,已经默默的在当前UI线程中调用了Looper.prepare()和Looper.loop()方法。
而如果我们要想在子线程中创建Handler,则必须先调用Looper.prepare()方法,Handler创建后再调用Looper.loop()方法。

Handler详解
使用Handler之前,我们都是初始化一个实例,我们可以在声明的时候直接初始化,或者在onCreate中初始化。
我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(当然可以在任何线程)是怎么发送到MessageQueue中的。
    public Handler() {//我们一般都是使用此无参的构造方法
        this(nullfalse);
    }
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
            }
        }
        mLooper = Looper.myLooper();//获取当前线程保存的Looper实例
        if (mLooper == nullthrow new RuntimeException("Can‘t create handler inside thread that has not called Looper.prepare()");//在初始化Handler之前必须先通过Looper.prepare()方法创建Looper的实例
        mQueue = mLooper.mQueue;//获取这个Looper实例中保存的MessageQueue(消息队列)
        mCallback = callback;
        mAsynchronous = async;
    }

然后看我们最常用的发送消息相关的几个方法
    public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) delayMillis = 0;
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
辗转反侧最后都是调用了sendMessageAtTime方法,在此方法内部又直接获取MessageQueue,然后调用了enqueueMessage方法
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) msg.setAsynchronous(true);
        return queue.enqueueMessage(msg, uptimeMillis);
    }
enqueueMessage中首先为meg.target赋值为this,最后调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。【上面我们说了,Looper的loop方法会取出每个msg然后交给msg.arget.dispatchMessage(msg)去处理消息】,也就是说Looper的loop方法会把取出的每个msg通过当前的handler的dispatchMessage回调处理

下面我们去看一看handler中的这个dispathMessage方法
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) handleCallback(msg);
        else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) return;
            }
            handleMessage(msg);
        }
    }

可以看到,最后调用了handleMessage方法,可实际上这是一个空方法,为什么呢?因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。


Handler的全部方法
构造方法
技术分享
其他方法
技术分享
获取消息
技术分享
移除消息和回调
技术分享
发送消息
技术分享

post方法
技术分享

有时候为了方便,我们会直接写如下代码:

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTxt.setText("yoxi");//在run方法中可以更新UI
        }
    });  
其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
    public final boolean post(Runnable r) {
        return sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();//得到了一个Message对象
        m.callback = r;//将我们创建的Runable对象作为callback属性,赋值给了此Message
        return m;
    }  
后面的步骤就和handler.sendMessage一样了
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:如果msg.callback不为null,则执行callback回调,也就是我们定义的Runnable。

总结
  • 1、首先Looper.prepare()在本线程中保存一个Looper实例,然后在该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
  • 2、Looper.loop()方法会让当前线程进入一个无限循环,不断从MessageQueue中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
  • 3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。
  • 4、Handler的sendMessage方法,会给msg.target赋值为handler自身,然后加入MessageQueue中。
  • 5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。





Handler Looper 原理 详解

标签:

原文地址:http://www.cnblogs.com/baiqiantao/p/5583908.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!