Handler Looper 原理 详解

演示代码



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.what, new 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_1, new 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(null, MSG_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() != null) throw 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 == null) throw 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 == null) return;             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(null, false);

    }

    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 == null) throw 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)最终调用的方法。

来自为知笔记(Wiz)

时间: 2024-12-29 07:01:48

Handler Looper 原理 详解的相关文章

android消息机制原理详解

android消息机制原理详解 因为之前使用的是CSDN默认的文本编辑器,而且也因为懒得学用MarkDown来写博客,所以排版上有一些问题.就上一篇写的设计模式之抽象工厂模式提出了这个问题(一个android群的群友提出来的,没有在评论里评论),所以以后的文章都用MarkDown来写了. 好了,言归正传,这篇文章我来介绍一下android消息机制的原理 Android消息机制概述 说到Android的消息机制,Android初级工程师(不包括那些初学者)肯定会想到Handler.是的,Andro

IsPostBack原理详解

这个属性相信大家一定经常用吧  判断是否为回发 (切记这是判断是否回发  而听到很多人说这是判断是否第一次加载页面  还有的说是否为刷新 ) 很多人说做项目时  pageload事件里 都要加上 protectedvoid Page_Load(object sender, EventArgs e) { if (!IsPostBack) { } } 但是确不太理解原因   说加上这个肯定没错 可是 上篇的例子里 不就错了么? 所以 一定要理解原理~~ 为了把这个说清楚 这里不用asp.net页面

图像处理中的数学原理详解17——卷积定理及其证明

欢迎关注我的博客专栏"图像处理中的数学原理详解" 全文目录请见 图像处理中的数学原理详解(总纲) http://blog.csdn.net/baimafujinji/article/details/48467225 图像处理中的数学原理详解(已发布的部分链接整理) http://blog.csdn.net/baimafujinji/article/details/48751037 1.4.5   卷积定理及其证明 卷积定理是傅立叶变换满足的一个重要性质.卷积定理指出,函数卷积的傅立叶变

Java虚拟机工作原理详解

原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 [java] view plaincopy javac YourClassName.java 此时,你的java代码就被编译成字节码(.class).如果你是在Eclipse IDE或者其他开发工具中,你保存代码

kickstart安装系统原理详解

前言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装. 常规的办法有什么? 光盘安装系统===>一个服务器DVD内置光驱百千块,百台服务器都配光驱就浪费了,因为一台服务器也就开始装系统能用的上,以后用的机会屈指可数.用USB外置光驱,插来插去也醉了. U盘安装系统===>还是同样的问题,要一台一台服务器插U盘. 网络安装系统(ftp,http,nfs) ===>这个方法不错,只要服务器能联网就可以装系统了

Storm概念、原理详解及其应用(一)BaseStorm

本文借鉴官文,添加了一些解释和看法,其中有些理解,写的比较粗糙,有问题的地方希望大家指出.写这篇文章,是想把一些官文和资料中基础.重点拿出来,能总结出便于大家理解的话语.与大多数"wordcount"代码不同的是,并不会有如何运行第一storm代码等内容,只有在运行完代码后,发现需要明白:"知其然,并知其所以然". Storm是什么?为什么要用Storm?为什么不用Spark? 第一个问题,以下概念足以解释: Storm是基于数据流的实时处理系统,提供了大吞吐量的实

SVM -支持向量机原理详解与实践之四

SVM -支持向量机原理详解与实践之四 SVM原理分析 SMO算法分析 SMO即Sequential minmal optimization, 是最快的二次规划的优化算法,特使对线性SVM和稀疏数据性能更优.在正式介绍SMO算法之前,首先要了解坐标上升法. 坐标上升法(Coordinate ascent) 坐标上升法(Coordinate Ascent)简单点说就是它每次通过更新函数中的一维,通过多次的迭代以达到优化函数的目的. 坐标上升法原理讲解 为了更加通用的表示算法的求解过程,我们将算法表

SVM-支持向量机原理详解与实践之一

目录(?)[+] 前言 SVM机器学习与深度学习 人工智能领域 机器学习与深度学习 SVM简介 SVM原理分析 快速理解SVM原理 线性可分和线性不可分 函数间隔和几何间隔 超平面分析与几何间隔详解 二次最优化 SVM-支持向量机原理详解与实践 前言 去年由于工作项目的需要实际运用到了SVM和ANN算法,也就是支持向量机和人工神经网络算法,主要是实现项目中的实时采集图片(工业高速摄像头采集)的图像识别的这一部分功能,虽然几经波折,但是还好最终还算顺利完成了项目的任务,忙碌一年,趁着放假有时间好好

JSPatch实现原理详解&lt;二&gt;

本文转载至 http://blog.cnbang.net/tech/2855/ 距离上次写的<JSPatch实现原理详解>有一个月的时间,在这段时间里 JSPatch 在不断地完善和改进,代码已经有很多变化,有一些修改值得写一下,作为上一篇的补充. Special Struct 先说下 _objc_msgForward,在上一篇提到为了让替换的方法走 forwardInvocation,把它指向一个不存在的 IMP: class_getMethodImplementation(cls, @se