安卓handler机制和RunOnUi方法都能修改主线程,两者的区别

来源:互联网 发布:基金买卖模拟软件 编辑:程序博客网 时间:2024/04/29 12:47

今天一个哥们说他去公司面试,碰到这个问题:安卓handler机制和RunOnUi方法都能修改主线程,两者的区别。

拿给我看,也是不甚了解,所以这里我们先看下app的运行机制:


从图中我们可以看到,当我们从外部调用组件的时候,Service 和 ContentProvider 是从线程池那里获取线程,而Activity 和BroadcastReceiver是直接在主线程运行。

为什么要使用Handlers?

    因为,我们当我们的主线程队列,如果处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的消息,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信,关系如下图;

     mainthread

下面是Handler,Message,Message Queue 之间的关系图

Handlers

这个图有4个地方关系到handlers

1, 主线程(Main thread)

2, 主线程队列(Main thread queue)

3,Hanlder

4,Message

   上面的四个地方,主线程,和主线程的队列我们无需处理,所以,我们主要是处理Handler 和 Message 之间的关系.

   我们每发出一个Message,Message就会落在主线程的队列当中,然后,Handler就可以调用Message绑定的数据,对主线程的组件进行操作.

好了,看完了相关信息handler的相关信息,我们再来看下,我们这次运行代码的时候,是怎么利用handler更新主界面UI的

   流程:事件响应→开启线程→new Message();→new Bundle();→bundle.putString("event", "Handler更改UI");→msg.setData(bundle);→handler.sendMessage(msg);→new handler(msg.getdata→getString→更改UI)

代码如下:

handler事件响应:

findViewById(R.id.button1).setOnClickListener(new OnClickListener() {<span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public void onClick(View v) {<span style="white-space:pre"></span>Log.e("事件ing。。。开始", "进入点击事件");<span style="white-space:pre"></span>UIThread thread = new UIThread();<span style="white-space:pre"></span>thread.start();<span style="white-space:pre"></span>UIhandler = new UIHandler();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>});


handler内容:

/** * 继承Handler的子类,必须先实现handlerMessage这个方法 *  * @author 阿程 *  */private class UIHandler extends Handler {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.e("事件ing。。。", "handler收到传递过来的消息");Bundle bundle = msg.getData();Log.e("事件ing。。。", "接受传递过来的msg里面的包裹内容");String str = bundle.getString("event");Log.e("事件ing。。。", "打开包裹。接受传递过来的东西");UITxt.setText(str);Log.e("事件ing。。。结束", "主界面UI做出相应的变化");}}/** * Massage发送信息调用handler实例 *  * @author Administrator *  */private class UIThread extends Thread {@Overridepublic void run() {try {Thread.sleep(3000);Log.e("事件ing。。。", "模拟耗时操作3秒和runOnUiThread方法做对比");} catch (InterruptedException e) {e.printStackTrace();}Log.e("事件ing。。。", "捆绑信息到msg,并发送msg");Message msg = new Message();Log.e("事件ing。。。", "先创建出来msg");Bundle bundle = new Bundle();Log.e("事件ing。。。", "创建包裹");bundle.putString("event", "Handler更改UI");Log.e("事件ing。。。", "将信息装入包裹");msg.setData(bundle);Log.e("事件ing。。。", "将包裹装入msg");MainActivity.this.UIhandler.sendMessage(msg);Log.e("事件ing。。。", "主界面UI发送msg");}}
咱贴上代码运行耗时截图:

(为了相对直观,咱都模拟了耗时操作)


RunOnUi事件响应:

findViewById(R.id.button2).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e("事件go。。。开始", "进入点击事件");new RunOnUiThread().start();}});

RunOnUi线程:
/** * 创建实现RunOnUi更改主界面的线程实例 *  * @author Administrator *  */private class RunOnUiThread extends Thread {@Overridepublic void run() {// 模拟耗时的操作。try {Log.e("事件go。。。", "模拟耗时操作3秒和runOnUiThread方法做对比");Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("事件go。。。", "runOnUiThread开始");// 更新主线程UIMainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {UITxt.setText("runOnUi更改UI");Log.e("事件go。。。结束", "runOnUiThread结束");}});}}
代码运行耗时截图:



相信这上面的一番对比 大家都明白了不少。下面贴出总得源码:

package com.example.handler_runonuithread;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends Activity {EditText UITxt;UIHandler UIhandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);UITxt = (EditText) findViewById(R.id.editText1);findViewById(R.id.button1).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e("事件ing。。。开始", "进入点击事件");UIThread thread = new UIThread();thread.start();UIhandler = new UIHandler();}});findViewById(R.id.button2).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e("事件go。。。开始", "进入点击事件");new RunOnUiThread().start();}});}/** * 继承Handler的子类,必须先实现handlerMessage这个方法 *  * @author 阿程 *  */private class UIHandler extends Handler {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.e("事件ing。。。", "handler收到传递过来的消息");Bundle bundle = msg.getData();Log.e("事件ing。。。", "接受传递过来的msg里面的包裹内容");String str = bundle.getString("event");Log.e("事件ing。。。", "打开包裹。接受传递过来的东西");UITxt.setText(str);Log.e("事件ing。。。结束", "主界面UI做出相应的变化");}}/** * Massage发送信息调用handler实例 *  * @author Administrator *  */private class UIThread extends Thread {@Overridepublic void run() {try {Thread.sleep(3000);Log.e("事件ing。。。", "模拟耗时操作3秒和runOnUiThread方法做对比");} catch (InterruptedException e) {e.printStackTrace();}Log.e("事件ing。。。", "捆绑信息到msg,并发送msg");Message msg = new Message();Log.e("事件ing。。。", "先创建出来msg");Bundle bundle = new Bundle();Log.e("事件ing。。。", "创建包裹");bundle.putString("event", "Handler更改UI");Log.e("事件ing。。。", "将信息装入包裹");msg.setData(bundle);Log.e("事件ing。。。", "将包裹装入msg");MainActivity.this.UIhandler.sendMessage(msg);Log.e("事件ing。。。", "主界面UI发送msg");}}/** * 创建实现RunOnUi更改主界面的线程实例 *  * @author Administrator *  */private class RunOnUiThread extends Thread {@Overridepublic void run() {// 模拟耗时的操作。try {Log.e("事件go。。。", "模拟耗时操作3秒和runOnUiThread方法做对比");Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("事件go。。。", "runOnUiThread开始");// 更新主线程UIMainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {UITxt.setText("runOnUi更改UI");Log.e("事件go。。。结束", "runOnUiThread结束");}});}}}

至于布局文件中,只有三个控件,两个按钮一个文本框,这里不再贴上代码。


但是,你以为这样就完了嘛?   

NO,not only this!


首先,我们看上面我们的handler控制界面UI的流程:

事件响应→开启线程→new Message();→new Bundle();→bundle.putString("event", "Handler更改UI");→msg.setData(bundle);→handler.sendMessage(msg);→new handler(msg.getdata→getString→更改UI)

具体就是这样,那RunOnUi的流程呢?

爬源码所得如下:

事件响应→开启线程→runOnUiThread→判断是否当前线程为主UI线程(是就立刻执行,不是就通过handler.post()发送到动作序列中,等到是主UI线程再立刻执行)


官方源码介绍如下:

<strong><span style="color:#ff0000;">/**     * 在UI线程里面执行指定的动作. 如果当前线程是UI线程,则立刻执行该动作,     * 如果当前线程不是UI线程,则将该动作发送到动作序列中,等UI线程为当前线程时再执行。     * @action 传递的参数为action     * @param action the action to run on the UI thread     */    public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }</span></strong>

相信看到这里哥们都明白啥意思了,弄哎暗着了,我也滚去困瞌睡了。

0 0
原创粉丝点击