Android Handler使用

在Android开发的过程中,经常会有这样的场景:从网络获取数据然后更改某个控件的属性。例如,从网络API获取Json,解析某个字段的值,将它设定到对应的TextView中。在很多人刚开始入门Android的时候,可能会将网络请求解析设定TextView放在一起处理。当然这样是不行的。为什么呢?这个就需要引入线程和进程的概念。

进程和线程

说到进程,其实是需要大致了解一些计算机的CPU原理的。CPU是一台电脑的处理核心,假设现在的CPU是单核的,所有的任务处理都是需要通过它来进行的。它通过时间将任务分成一个又一个的时间片进行处理。对我们用户来看,好像上网、听歌、处理Word好像是一直都在运行。但对于CPU来说,它可能处理了一会上网的任务,又处理了一会听歌的任务,然后又处理了一下Word的任务。因为CPU速度很快,现在操作系统也有很好的调度策略。导致对于我们用户来说,好像任何的任务都是连续不间断的。上面所说的上网、听歌、Word实际上代表这一个或者多个应用程序,每一个应用程序在操作系统里面都有一个与之对应的进程。我们在Windows的任务管理器看到的类似「Chrome.exe」这样的信息,就代表这是Chrome浏览器的一个进程。

随着技术和硬件的发展,现在的CPU都是多核心的,你肯定会想了。那我只需要把上网任务交付给一个核心工作,把听歌任务交付给另外一个核心工作不就可以提高速度嘛。Bingo!这个没有错。但CPU因为制造工艺的问题,普通人很难用到动辄40核80核的CPU。因此,在多核心的CPU处理任务的过程中,实际上还是核单核心的顺序执行没有什么差别,在一些调度算法上面有一些改进。

而且,可能对于同样一个上网任务,它还包含很多子任务,例如在网络上听歌,看视频,打字输入等等。对待这样的问题,就需要引入线程的概念了。

线程,其实就是将进程内部分为了很多块,每一块可以独立的进行工作。同时线程有一个很好的特性,线程之间可以共享该进程的资源。这样当然又会出现一个问题,优先处理哪一个线程呢?JVM是通过线程的优先级来抢占CPU的资源。另外需要注意的是,线程是CPU调度的基本单位。通过多线程的方式,将一个进程分解成多个线程,就实现了可以在浏览器一个Tab听歌,一个Tab看视频,站在用户的角度看,貌似它们是相互独立且并行的。

如果有兴趣可以看看阮一峰写的-《进程与线程的一个简单解释》廖学峰-《进程和线程》。两位都是大神,文章写的深入浅出。

什么是Handler

说了上面那么多,我们在回过来看看最开始的那个问题的原因。上面的场景实际上是有两个子场景网络请求更新UI。Android的UI线程是不安全的,另外网络请求也是一个耗时的操作,所以要是将网络请求也写入在UI线程里面的话,会造成用户在UI线程上等待网络请求的结果,造成阻塞。因此,正确的做法是对于网络请求,我们新开一个线程去处理,将得到的结果传递给UI线程中去。

Handler就充当了这样一个桥梁。它具有两个功能发送消息接收并处理消息。也就是说,我可以在子线程通过UI线程的mHandler,发送一条Message给UI线程,mHandler在UI线程收到这个Message后,做相应的处理。是不是很方便,系统就已经帮你实现了这样一个工具。

这一篇文章着重讲讲如何使用,那么接下来我们一起看看,如何使用Handler。

如何使用Handler

同样是上面这个场景。我们在一个Activity中放置一个TextView,通过新开一个子线程,每隔1s更新一下TextView的text。

在UI线程创建Handler

public Handler mUIHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        if (msg.what = 1){
            mTextView.setText("" + msg.arg1)
        }
    }
}

这里在主线程中New了一个Handler来接收从其他地方传递过来的Message对象。

创建子线程,发送消息

class ChildThread extends Thread {
        @Override
        public void run() {
            while (count <= 100) {
                count++;
                mUIHandler.sendEmptyMessage(1);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

我们可以创建一个子类得到一个子线程,同时在子线程中每隔1s通过mUIhandler发送一条消息到主线程去处理。这样就完成了我们的需求。

原理

对于其中的原理,我将会在下一篇文章里面来详细的讲一讲。尤其是Message、Looper、MessageQueue和Handler之间的关系