Android--Handler使用应运及消息机制处理原理分析

来源:互联网 发布:ibarn网盘系统源码 编辑:程序博客网 时间:2024/04/28 15:49


最近开通了一个小微博,欢迎大家关注,每天分享一些上班路上看的小知识点

点击打开链接

一、Handler是什么 ?

handler是android给我们提供的一套用来更新UI的一套机制,也是一套消息处理机制,我们可以发送消息,也可以通过它处理消息

二、Handler的基本使用方法:

使用方法一 (普通更新UI方法)

//在子线程中使用,用来更新UI new Thread(){            @Override            public void run() {                super.run();                SystemClock.sleep(3000);                mHandler.post(new Runnable() {                    @Override                    public void run() {                        textView.setText("三秒后更新文字");                    }                });            }        }.start();
这个可以在主线种中直接调用


使用方法二(循环消息更新UI)

private UpdateTextViewRuunable mRuunable = new UpdateTextViewRuunable();    private int number = 1;    private class UpdateTextViewRuunable implements  Runnable{        @Override        public void run() {            number++;            mTextView.setText("一秒更新一次"+number);            mHandler.postDelayed(mRuunable,1000);        }    }

然后再在主线程中进行调用 :

mHandler.postDelayed(mRuunable,1000);

也就是开启循环



注:

 我们会发现当我们的应用程序退出的时候,这个线程还是在始终执行着,所以我们要在我们的应用程序退出的时候,停止循环操作
 也就是移除相关的消息发送

 @Override    protected void onDestroy() {        super.onDestroy();        mHandler.removeCallbacks(mRuunable);    }




使用方法三(通过Message消息来传递数据)

创建保存数据的信息对象

class Person{    public String name;    public int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

通过handler发送消息并传递相关数据

new Thread(){            @Override            public void run() {                super.run();                SystemClock.sleep(3000);                mHandler.post(new Runnable() {                    @Override                    public void run() {                        //   textView.setText("三秒后更新文字");                        Message message = Message.obtain();                        message.arg1 = 10;                        message.arg2 = 12;                        message.obj = new Person("zhaong san",102);                        mHandler.sendMessage(message);                    }                });            }        }.start();

在handler中接收发送的消息和相关数据

private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            final int arg1 = msg.arg1;            final int arg2 = msg.arg2;            final Person obj = (Person) msg.obj;            mTextView.setText("arg1 is "+arg1+"\n+arg2 is"+arg2+"\n person is "+obj.toString());        }    };



特别说明:

消息发送说明

在上面的发送消息的时候 ,我们使用的是mHandler的sendMessage方法,在这里发送消息,我们也可以使用

message.sendToTarget();

方法进行消息发送,message.sendToTarget方法简析

   /**     * Sends this Message to the Handler specified by {@link #getTarget}.     * Throws a null pointer exception if this field has not been set.     */    public void sendToTarget() {        target.sendMessage(this);    }
可以看到  message.sendToTarget(); 进行消息发送,实际上是调用了target的sendMessage方法进行消息发送
而这里使用到的target则是我们的一个Handler对象

创建消息对象说明

在这里我们是通过Message.obtain方法来获取一个消息 对象的,这里我们可以简单看一下其源码

 /**     * Return a new Message instance from the global pool. Allows us to     * avoid allocating new objects in many cases.     */    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }

Message.obtain()在这里获取到一个Message对象,可以简单看一下其原理,实际上它是取出系统中存在的一个空的message对象,
如果这个对象为空,那么再进行创建

当然我们也可以直接使用new Message来创建一个新的消息对象


三、拦截Handler发送的消息


private  Handler mHandlerTwo = new Handler(new Handler.Callback() {        @Override        public boolean handleMessage(Message msg) {            return true;        }    }){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);        }    };

在Handler接收发送过来的消息的时候会先回调callback中的handleMessage方法,然后再回调handler的handleMessage方法
当我们在callback的handleMessage方法返回为true时候,就会在callback中进行消息拦截,之后就不会再回调handleMessage方法了


四、Handler原理分析

* Handler主要是分析消息发送,默认情况下就是把消息发送给了自己

*Looper
    内部包含一个消息队列,也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列

    Looper.Looper方法,就是一个死循环,不断的从MessageQueue中取消息,如有消息就进行消息处理,没有消息就阻塞

* 总结:
    Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自已
    MessageQueue是一个存储消息的容器

* 在默认情况下,我们的应用程序是由ActivityThread来创建的,在ActivityThread中创建我们所有的Activity,并回调我们
Activity的构造方法,ActivytyThread也会默认去创建main线程(也就是主线程),同时也会默认创建一个Looper对象,而在
Looper中也会默认创建一个message对象

*源码分析:

* 我们查看ActivityThread类的main方法,可以看到在这里会首先调用方法  Looper.prepareMainLooper();

* 然后再查看Looper的prepareMainLooper方法

/**     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

 可以看到 在这里首先调用了prepare方法
/** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */    public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));    }

* 而在prepare方法中,使用到了sThreadLocal.get()方法
* sThreadLocal主要是用于在我们的线程运行中保存一些变量信息,其主要有set 方法 和get方法 ,set方法就是将我们  创建的变量放到ThreadLocal中
* 而get方法就是将变量拿出来

* 在这里我们默认的UI线程去调用它的get方法,默认情况下是等于null的,所以在这里 set了一个Looper对象

而我们再查看new Loopre的操作

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mRun = true;        mThread = Thread.currentThread();    }

可以看到,在我们创建Looper对象的时候,会同时创建一个MessageQueue对象,而在这里的MessageQueue就是我们所有消息的一个消息队列

走到这里,在我们的UI线程中就创建了一个Looper对象和一个MessageQueue对象


然后当我们在应用程序中创建Handler的时候

/**     * Default constructor associates this handler with the {@link Looper} for the     * current thread.     *     * If this thread does not have a looper, this handler won't be able to receive messages     * so an exception is thrown.     */    public Handler() {        this(null, false);    }


 public Handler(Callback callback, boolean async) {       ...        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

*在这里,无参数的构造会调用有参数的构造方法

*当执行到有两个参数的构造方法中后,首先会Looper.myLooper()方法获取一个Looper对象,

    /**     * Return the Looper object associated with the current thread.  Returns     * null if the calling thread is not associated with a Looper.     */    public static @Nullable Looper myLooper() {        return sThreadLocal.get();    } 

*可以看到这里调用了ThreadLocal.get方法,而上面我们分析到,主线程在进行创建应用程序的过程中会将一个Looper对象存放在ThreadLocal中,
在这里我们通过get方法将其取到,这样,Handler 与Looper 以及 MessageQuquene关联到一起了

*然后呢在我们的Handler的两个参数的构造方法中就可以通过方法  mQueue = mLooper.mQueue; 来拿到这里的MessageQueue消息队列

*当我们调用  mHandler.sendMessage(message);方法进行消息发送的时候

public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }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);    }

我们可以看到最终是调用的sendMessaeAtTime方法;在这里handler会通过当前线程拿到一个MessageQueue消息队列,然后调用下一个方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

在这里我们可以看到 msg.target是一个标识,是指消息要发送给谁,在这里,指向Handler对象本身,然后
调用了queue.euqueueMessage方法将消息放到这个消息队列中去



而在我们的Looper对象中loop方法中


public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            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.recycleUnchecked();        }    }

可以看到在这里首先是通过myLooper方法到获取到当前的Looper对象

public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

然后再通过Looper对象来拿到我们创建的MessageQueue


然后就是在for循环中不断的取消息,如果取出的消息为null,直接return,如果取出的消息如果不为空,则调用msg.target.dispatchMessage(msg);处理消息


msg.target就是指向消息发送的方向,而在这里默认设置的是Handler本身
而在msg.target.dispatchMessage(msg);方法中


public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

可以看到在这里首先会去调用callback的handleMessage方法,当这个方法返回true的时候,这里会直接return,也就是说不再会执行本身的handleMessage方法
如果返回 的是false,那么会继续执行本身的handleMessage方法,这就是我们刚刚谈到的拦截handler接收的消息处理过程


Handler主要是封装了消息发送,默认情况下就是把消息发送给了自己

Looper
    内部包含一个消息队列,也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列

    Looper.Looper方法,就是一个死循环,不断的从MessageQueue中取消息,如有消息就进行消息处理,没有消息就阻塞

总结:

    Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自已
    MessageQueue是一个存储消息的容器

    在默认情况下,我们的应用程序是由ActivityThread来创建的,在ActivityThread中创建我们所有的Activity,并回调我们
    Activity的构造方法,ActivytyThread也会默认去创建main线程(也就是主线程),同时也会默认创建一个Looper对象,而在
    Looper中也会默认创建一个message对象



  

创建一个与子线程绑定的Handler

class  CustomThread extends  Thread{    //定义一个Handler    public Handler mHandler;    @Override    public void run() {        super.run();        Looper.prepare();        mHandler = new Handler(){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);            }        };        Looper.loop();    }}

在使用的时候可直接创建CustomThread对象,然后通过对象来调用我们的线程Handler


创建一个子线程,并指定其中的一个Looper对象


class  CustomThread extends  Thread{    //定义一个Handler    public Handler mHandler;    private Looper mLooper;    @Override    public void run() {        super.run();        Looper.prepare();        mLooper = Looper.myLooper();        mHandler = new Handler(){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);            }        };        Looper.loop();    }}

在主线程中进行调用

 final CustomThread customThread = new CustomThread();        customThread.start();        customThread.mHandler.sendEmptyMessage(0);        final Handler handler = new Handler(customThread.mLooper){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);            }        };        handler.sendEmptyMessage(0);

这样直接调用的时候,程序会抛出空指针异常,也就 是说当我们在主线程中调用 子线程(CustomThread)的Looper的时候,当我们的Looper对象还没有被创建出来的时候,然后我们主线程中的另一个Handler就使用到了,所以..


为了解决这样的问题,我们可以使用

private HandlerThread mHandlerThread;private Handler mHandler;mHandlerThread = new HandlerThread("custumThread");        mHandlerThread.start();        mHandler = new Handler(mHandlerThread.getLooper()){            @Override            public void handleMessage(Message msg) {                System.out.println("Thread.currentThread name is "+Thread.currentThread());            }        };        mHandler.sendEmptyMessage(1);

然后我们在主线程中执行

可以在控制台上看到

因为我们在new HandlerThread的时候就 定义了我们线程的名字

我们可以看一下在这里使用到的mHandlerThread.gtLooper方法

public Looper getLooper() {        if (!isAlive()) {            return null;        }                // If the thread has been started, wait until the looper has been created.        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }

调用这个方法的时候,首先会去判断下当前线程是否为空

因为我们当前的HandlerThread其实就是一个线程

然后我们在getLooper方法中可以看到接下来就是判断当Looper对象为空的时候,使当前线程处于等待状态

在HandlerThread的run方法中

public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }

可以看到这里创建了一个Looper对象,并且唤醒了我们的线程


主线程与子线程进行通信

public class ThreadMainToChildActivity extends Activity  {    //创建主线程的Handler    private Handler mMainHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            System.out.println("主线程中Handler执行的方法");            final Message message = Message.obtain();            //向子线程中发送消息            mChildHandler.sendMessageDelayed(message, 1000);        }    };    private HandlerThread mChildThread;    private Handler mChildHandler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main_thread_to_child);        mChildThread = new HandlerThread("childThread");        mChildThread.start();        //创建子线程的Handler        mChildHandler = new Handler(mChildThread.getLooper()){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                System.out.println("子线程中执行的方法");                final Message message = Message.obtain();                //主线程Handler发送消息                mMainHandler.sendMessageDelayed(message,1000);            }        };        mMainHandler.sendEmptyMessage(1);    }}

运行



Android自定义控件ImageViwe(一)——依据控件的大小来设置缩放图片显示
   点击查看分析文档
    
 Android自定义ImageView(二)——实现双击放大与缩小图片
    点击打开链接
    
 Android自定义控件ImageViwe(三)——随手指进行图片的缩放
   点击打开链接
    
 Android自定义控件ImageViwe(四)——多点触控实现图片的自由移动  
    点击打开链接
    
 Android ListView分组排序显示数据
    点击打开链接
    
 Android自定义下拉刷新功能的ListView
    点击打开链接
    
 Android音乐播放器高级开发
    点击打开链接
    
 Android自定义控件之流式布局
 点击打开链接

 Android自定义下拉刷新功能的ListView
 点击打开链接




0 0
原创粉丝点击