Android消息处理两大利器:Handler and Looper

虽然Google的诸多服务被某某机构所封,但是仍然阻挡不了我们开发者们释放那狂热的难以抗拒的Google情怀,为此,开发者们翻山越野,寻找各种手段也要找到你!Android是Google推出的,中国那么多Android开发者,而Android官网却难以访问,此问题是否会影响国内Android程序猿的生产力?吐槽到此,转入正题,以下为一篇来自国外的关于Android Handler、Looper的文章,半译半不译的文章如下(这不是对作者不尊重,只是为了更好地表达原文的中心思想,换了国人习惯的表达方式;因为生搬硬套过来的总觉得语句不通):

Android系统,正受到广大开发者的青睐,可以说,无处不Android(不夸张(⊙o⊙)哦);开发者们喜欢Android这个平台,可能有很多理由,例如:Android开源,社区很庞大,相关资源很多;但是,我想,Android的那丰富的framework也是广大开发者们喜欢的一个理由之一,而我喜欢Android API的原因之一就是它包含了非常丰富实用的小组件,而且,有很多的组件不只是适用于Android平台,例如:Java SE平台(当然,我得承认,在Java SE平台上开发中,我忽略了它们),在浩瀚的API中, 我选中了两个重要的类:Looper and Handler,简短地介绍一把;这两个类太让我们开发者受益了,使用它们,我们能干很多非常酷的事情。

基本原理

介绍之前,先熟悉一下基本原理:Android使用这两个类实现了一个并发模型,我称这个模型为——管道线程(Pipeline Thread)(注意:这个名字只是我取的,貌似wiki上,你是收不到的,但是你可以帮忙传播一下,传着传着,大家都这么叫了,(^__^) ),这个模型的工作细节如下:
1、管道线程维护着一个任务队列,这个队列是线程安全的
2、其他的线程会安全地将任务放进管道线程所维护的任务队列
3、管道线程不停地一个接一个地从这个队列取出任务并处理,如果,这个队列中没有任何任务,它将会阻塞直到队列中有任务
4、任务队列也可以叫消息队列,这个随便你
这种设计模型有一些非常有价值的特性,并且被广泛使用在很多平台上的Framework和应用程序中;我想很多熟悉Win32消息机制开发者对这种模型可能很熟悉;因为Google在设计Anroid的消息处理机制时,确实借鉴了win32消息处理机制;随后,我会实现一个Demo:基与管道线程模型,使用Handler和Looper,模拟一个后台下载队列,并且在UI中显示它的状态;在文章的最后,你能获得完整的源码包,不过,我觉得这篇文章的最大价值不在于最后的源码包,而在于深层的原理。

管道线程模型使用场景

在诸多UI framework中,到处有着管道线程模型的身影,例如:Swing(想想事件分发线程?)、SWT、Adobe Flex、Android;这种模型被用于处理一些UI事件(click、mouse movement、屏幕旋转等等),这种模型允许你去改变一个Button的文字说明,而不必担心用户同时点击这个Button。
另一方面,这种模型强制你不要在UI Thread中做一些耗时性的操作——如果在UI Thread中下载一个网络文件,我想每一个开发者都将会知道发生什么;在我们随后的Demo中,我将在一个独立的管道线程中执行耗时性的任务,这样就保证了UI Thread的自由运作。
其他的管道线程模型的应用:
1、执行发往远程服务器的请求(通常情况,你需要连续地执行请求)
2、上传图片到HTTP服务
3、缩放或者处理图片
4、下载文件(或者其他什么),与我的Demo所做内容一样
我想,很多朋友心里都会有一个疑问,为什么要使用管道线程模型,直接使用一个单独的线程不就好了嘛,但是,有经验地程序员稍微想想就会明白,管道线程的好处:它很好地控制了后端job的负荷和时序性问题。而且,我们也可以使用多个管道线程——我们可以称之管道线程池,这样,我们就能同时执行多个任务,并且都能很好地控制负载。
在我们的Demo中,我使用了一个管道线程, 并且每次只允许一个下载任务,多个下载任务将会被有序调度。

Looping and Handling

Looper:它因其作用而得名,它实现了一个Loop(循环)——获取下一个任务,并执行它,再获取下一个任务,并执行它,如此往复;Looper将一个普通的线程变成了管道线程
Handler:它的名字没啥好说的,可能是某些人无法想到更好的名字,只能用它了;它的作用是,让我们将任务从其他线程发往管道线程
下面是一段代码放入Thread’run方法的代码,作用如下:将这个Thread变为管道线程,并创建一个Handler,允许其他的线程把任务分配给这个管道线程

@Override
public void run() {
  try {
    // preparing a looper on current thread    
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  }
}

上述功能之后,其它的线程中将需要得到Handler对象的引用handler,并通过Handler类的方法,例如:postMessage(),发送消息(注:Handler有很多有趣的API,你可以去玩玩哦)。
下面一段代码将显示,其它的线程如何调度一个任务在管道线程中执行:

handler.post(new Runnable() {
  @Override
  public void run() {      
    // this will be done in the Pipeline Thread      
  }
});

在我们的案例中,当用户点击Button时(这个在UI Thread中被处理),我们将使用“习语”在管道线程中调度并执行下载任务;当下载线程通知Activity下载任务已经完成时,Activity将会使用绑定在UI Thread中的Handler,这样就能保证仅仅在UI Thread中刷新UI。
随便说一句,UI Thread将会隐式地拥有一个Looper,因次你能直接在Activity的onCreate方法中创建一个Handler:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Create the Handler. It will implicitly bind to the Looper
    // that is internally created for this thread (since it is the UI thread)
    handler = new Handler();
}

Demo实现细节剖析

如果你已经理解了Looper和Handler的核心思想了,我想,剩下的就是一些细节了。在我的Demo中,我主要创建了以下几个类:
1、DownloadTask:模拟了一个下载任务——我没有真实地去请求网络,不想浪费你手机的一丝流量,这样的话,也环保嘛。
2、DownloadThread:下载线程,提供一个方法,将DownloadTask放入队列,也提供了一个停止的方法
3、DownloadThreadListener:下载线程监听接口,在DownloadQueueActivity实现,当一个下载任务结束时,通过它通知UI Thread刷新UI,改变进度条
4、DownloadQueueActivity:主要的Activity,创建下载线程DownloadThread
注意:在我们的Demo中,当刷新UI中的进度条时,我们使用了一个单独的Handler,此Handler被创建在Activity中,因为我们要保证刷新UI总是UI Thread中,核心代码如下:

// note! this might be called from another thread
@Override
public void handleDownloadThreadUpdate() {
  // we want to modify the progress bar so we need to do it from the UI thread
  // how can we make sure the code runs in the UI thread? use the handler!
  handler.post(new Runnable() {
    @Override
    public void run() {
      // update the UI etc.
    }
  });
}

当然,使用DownloadThread中的handler也是可以的。想要了解更具体的,你需要去看附录Demo的完整源码和注释

总结

Loopers and Handlers,它们真得是很酷的两个类,能帮我们干很多漂亮的事情,如果,你想掀开他们那神秘的面纱,你可以去研究一下它们的源码(透露一下:其实核心就是个死循环)。

附件完整源码包
原文地址http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/

猜你喜欢

转载自blog.csdn.net/nieyinyin/article/details/45172605