Handler发送sendMessage和postRunnable的区别

 

 

 

先从平时的应用入手吧。试想这样一个场景,我们有一个下载文件的需求,而且我们在界面上要显示下载的状态:未下载,下载中,已下载。这个时候我们该怎么办?首先可以在界面上放一个Button,显示未下载,然后设置点击事件,点击后显示下载中,并开启一个线程去下载(这里用线程sleep代替,实际未下载)。等下载完成后,再把Button上的文字改为已下载。我们来试一下:

 

public class MainActivity extends AppCompatActivity {
    private Button mButton;
    private Thread mThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.bt_1);
        initThread();
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mButton.setText("开启下载线程下载中");
                mThread.start();    
		mButton.setClickable(false);
            }
        });
    }

    private void initThread() {
        mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mButton.setText("已下载完成");
            }
        });
    }
}

编译运行,一切正常,然后我们点击一下按钮:肏,crash掉了。好吧,淡定,淡定,我们看一下log:

03-10 16:15:22.707 10592-11189/com.example.gray_dog3.handlertest E/AndroidRuntime: FATAL EXCEPTION: Thread-302

                                                                                   Process: com.example.gray_dog3.handlertest, PID: 10592

                                                                                   android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread    that created a view hierarchy can touch its views.

                                                                                   at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7177)

                                                                                   at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1065)

 好吧,用我过了四级的英语水平给翻译下吧,错误的线程调用异常,发生Crash的地点是ViewRootImpl中调用checkThread方法中。温馨提示是:只有创建View的线程才能对摸摸它创建的View。。。好吧,顺着代码一瞅,果然,我再mThread中摸了一把在UI线程中创建的mButton。所以报了这个错。为了更直观的看到报错原因,我们直接跳到源码ViewRootImpl的checkThread方法,看它做了什么。

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

就是这货了,但是Android为什么要这样搞呢?我们仔细看这句话,只有创建了View的线程才能对这个View进行操作。而我们一般View都是为了显式在UI上的。Android正是为了防止我们在非UI线程去操作这些UI上的控件,才加了限制的。因为UI体验对用户来说是最直观的,如果谁都有权限去搞一发,那UI要么很乱,要么控制很复杂。但是总不能难搞就不让搞把?实际上是可以的,Android为我们提供一个工具,来让我们进行线程间的消息传递,就是我们接下来要讲的主角:Handler。

对于Handler来说上面的问题可以轻易解决,看代码:

 

public class MainActivity extends AppCompatActivity {
    private Button mButton;
    private Thread mThread;
    private Handler mHandler;
    private int a=3,b=5;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                 mButton.setText("已下载完成");
            }
        };
        initThread();
        mButton = (Button) findViewById(R.id.bt_1);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mButton.setText("开启下载线程下载中");
                mThread.start();
                mButton.setClickable(false);
            }
        });

    }

    private void initThread() {
        mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mHandler.sendMessage(Message.obtain());
            }
        });
    }
}
 

在上面的代码中,我们只是在主线程里创建一个Handler,重写它的handleMessage方法,然后子线程里拿到这个Handler,下载结束后用Handler给主线程发一个消息。然后主线程收到消息后,再去改变Button上显示的文字。

上面只是Handler最普通的一个应用场景,发送消息,接收消息。实际上这也是它所有的能做的事。有人说还有postRunnable的吗?怎么能说是只有发送和就收消息呢?好,我们带着这个疑问来看下Handler究竟是什么:

 

首先看Android源码注释,

 A Handler allows you to send and process {@link Message} and Runnable

 * objects associated with a thread's {@link MessageQueue}.  Each Handler

 * instance is associated with a single thread and that thread's message

 * queue.  When you create a new Handler, it is bound to the thread /

 * message queue of the thread that is creating it -- from that point on,

 * it will deliver messages and runnables to that message queue and execute

 * them as they come out of the message queue.

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值