Android handler讲解与实践

一、概述





   在我们的平常的开发中,我们通常是用 Handler 去在线程中更新 UI ,单其实 Handler 的主要用途是消息处理机制。

其实 handler 在我们 framework 中也很多地方用到了,比如,Activity 生命周期的回调,还有很多实践的传递机制,

都用到了 handler,那么现在我们就进入 handler 的世界吧。

为什么谷歌只允许我们在主线程中更新 UI 呢,这是因为如果是多线程更新 UI 的话,会造成界面错乱,而且,多线程

会导致很多问题。



二、handler 简介





1、handler 需要与一个带有 messagequeue 的线程进行绑定,它可以发送 message 或者 Runnable。

2、ActivityThread 会创建一个 looper,looper 会去创建一个 messagequeue ,这个 messagequeue
 
会去管理一些顶级应用,例如 broadcastreceiver 和 Activity 。





三、handler 的使用





其实 handler 的使用还是比较简单的,handler 主要就是去发送一个 message 或者一个 Runnable ,然后在 handler 的 handlemessage 中去处理就可以了。

我们先看第一种情况 handler 去发送 message:

我们首先需要创建一个 handler :


 	private Handler handler = new Handler(){
             @Override
             public void handleMessage(Message msg) {
            	super.handleMessage(msg);
             }
    	};



然后我们在用 handler去发送一个message :


 	Message message = new Message();
        handler.sendMessage(message);

这样就可以发送一个消息了,当发送这个消息之后,这个消息会被 handlerMessage 这个方法所接收,我们可以在 handlerMessage 中去处理 UI 和一些操作。

其实 handler 里面还有很多别的方法发送 message,首先,我们可以通过 handler.obtionMessage 这个方法去获取 Message ,当我们用这个方法去获取 message 的时

候,我们可以通过 message.sendToTarget,去发送message。如果我们想根据不同的message 去进行不同的操作,那我们怎么去判断 message 的不同呢,之前的方法

是 message 可以设置 arg1 和 arg2 这两个值去判断,现在的判断方法一般是根绝 message.what 去判断 message 的不同。最后 message 如果想传递数据的时候,我们

可以通过 message.setData 这个方法去向 handler 中传递一个 Bundle。handler 的通过 sendMessageDelayed 方法延时发送 message 下面我们看一个标准的  

sendMessage

	Message message = new Message();
        message.what = 0x123;
        Bundle bundle = new Bundle();
        bundle.putString("handler","handler");
        message.setData(bundle);
        handler.sendMessageDelayed(message,1000);   //延时一秒发送message



下面我们看一下 handler 发送 Runnable 的情况,handler 发送 Runnable 不需要在 handler 去更新 UI ,只需要在 Runnable 中的 Run 方法去修改 UI 就可以了。



下面看代码:
 	new Thread(){
            public void run(){
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("11111");
                    }
                });
            }
        }.start();   //这个 start 千万别忘记了



这样我们就可以很方便的在线程中去修改 UI 了。post 也有延时发送的方法:postDelayed。 我们可以在 runnable 中不断的调用 handler.postDelayed 方法,


这样就可以实现定时更新 UI 的效果。


最后我们介绍一下 handler 在创建的时候传递的 callback 方法。先看一下代码:


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


我们可以看到创建的 handler 里面有两个 handlerMessage 方法,一个是 handler.Callback 中的,一个是正常的 handlerMessage 方法,那么这个这个 Callback 

中的 handlerMessage 是什么时候调用的呢,我可以告诉大家,这个 handlerMessage 是在正常的 handlerMessage 方法之前调用的,这个方法是一个 boolean 方法

当返回 true 的时候,那么我们的 message 将不再向下传递,也就是我们的下面的 handler 将不会收到 message。这样 handler 的主要用法介绍的差不多了,下一节

将介绍 handler 的原理。




四、 handelr 的原理




我先用简单的一句话去描述一下 handler 的原理:

handler 负责发送消息,looper 负责接收 handler 发送的消息,并把消息回传给 handler 自己。messagequeue 负责存储消息。

之后我们通过源码去看一下当 handler 发送消息之后,怎么传递到 handlerMessage 的:

当我们一步一步点击 sendMessage 方法的时候可以看到最终到达这个方法:


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);
}


我们先不要管这个 mQueue 我们继续往下面走:


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


最后我们可以看到是将我们发送的 message 去存入了 messagequeue 中。这样发送就结束了。


上面我们可以看到 sendMessageAtTime 方法中有一个 mQueue ,这个在 handler 中的 mQueue 是哪里来的呢,通过源码我们可以看到:


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();
    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;
}



通过这段代码我们可以知道,这个 mQueue 是通过 mLooper 获取的,那么我们这个 mLooper 是在哪里获取的呢我们还看代码知道是通过,Looper.myLooper

去获取的,那么我们看一下 Looper.myLooper 方法:


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

那么我们的 sThreadLocal.get 方法又是怎么回去 Looper 的呢。这就需要追溯到我们的 ActivityThread 了。在我们的 ActivityThread 也就是我们的 main 线程创建

的时候回去调用 Looper.perparemain 方法。


public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    SamplingProfilerIntegration.start();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();     // 可以看到在 main 方法中调用了这个方法

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
  


那么这个 prepareMainLooper 方法又做了什么呢继续看代码:


public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}



从这两段代码中我们可以看到,在 ActivityThread 创建的时候回去 new Looper 然后再放到 sThreadLocal 中。然后再 new Looper 的时候去创建了一个

messageQueue,这样就能解释通了,当我们去sendMessage 的时候,我们会将发送的 message 存放到主 MessageQueue 中。

那么现在还是没有解决消息是怎么传递到 handlerMessage 中的。其实在我们 Activity 创建的时候会调用一个 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
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        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();
    }
}




这段代码中我们可以看到一个死循环,其实就是我们不断检查 MessageQueue 中是否有消息,如果有消息就会调用 dispathMessage 方法,那么我们看一下

dispathMessage 方法:


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



可以看到这个方法中先去检查了 message 有没有 Callback ,其实这个 callback 是一个 runnable ,也就是我们的 post 方法会走向这里,然后在去检查一下 handler

是否有 callback 如果有就走 callback。最后走向 handlerMessage 这个方法。这样也就成功的达到了我们 target.handlerMessage 方法中。 这样 handler 的原理基本上

就说完了,下面会讲一下 handler 与 线程的故事。



五、handler 与线程




1、首先我们说一下如何在线程中使用 handler 吧

在线程中创建 handler 需要先调用调用 Looper.perpare 方法,然后通过Looper.myLooper 方法去创建一个 Looper,然后创建handler 之后需要调用 Looper.loop 方法

这样其实就是让子线程中的 Handler 和 Looper 去关联。

在子线程中使用 handler 会重新创建一个 Looper。 handle = new Handler(thread.getLooper()) 可以让子线程和 handler 相关联。 也就是说我们一定要把我们的

Handler 和一个 Looper 相关联,下面看一下代码:
 class  myThread extends Thread{
        public Handler handler;
        public Looper looper;
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            looper = Looper.myLooper();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
           Looper.loop();
        }
    }


这样就完成了在线程中创建 handler。

2、主线程如何给子线程发送消息

在主线程和子线程中分别创建一个 handler ,然后在主线程中去调用子线程的 handler 发送消息,在子线程中通过调用主线程 handler 发送消息。

3、Android 在子线程中更新 UI 的方法:

runOnUiThread 是可以在线程中更新 UI 的,需要传递一个 Runnable。 这个是通过 Activity 中默认的 Handler 去更新 UI 的。

view.post(Runnable runnable) 也是可以的
	runOnUiThread(new Runnable() {
            @Override
            public void run() {

            }
        });


总结:

这篇主要介绍了 handler 的主要功能和原理,如有错误请指出,希望大家共同进步。







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值