Android多线程及Handler机制

来源:互联网 发布:淘宝上好的澳洲代购 编辑:程序博客网 时间:2024/05/20 01:07

—  
Android多线程编程是很常用的一种编程方式,使用方式看起来也相对固定,不过其中一些细节我之前由于没有注意到踩了不少坑,遂记录之。

线程的优先级

Android中的线程优先级是Linux进行设定的,设定范围依据数字,范围位:-20~19,这其中-20为最高优先级,而19则相对地为最低优先级。可能有人会问,线程优先级有什么作用,线程都是用来进行并发处理,有何区别?
其实就是为了保证使那些不太重要的线程过度抢占CPU资源,而导致重要的线程如音乐播放等阻塞。

为自己的线程设定优先级的代码如下:

Process.setThreadPriority

这里最好是用这种方法来进行优先级的设定,因为Java自带一种 Thread.setPriority方法,实践表明,上面的方法会显著影响线程的调度。
Android系统内置了一些常量字段来为程序员做一些选择,实际上其实也是设定了-20~19中的值,具体的字段感兴趣的朋友自行了解。

线程间消息传递

说到Android的多线程之间的传递,我想熟悉Android的朋友第一个想到的就是子线程与UI 线程之间的消息传递,由于Android不允许在子线程进行UI 的更新,所以若是在子线程进行耗时操作,完成后想更新UI,那么子线程需要传递信息给UI线程,在UI线程中进行更新。说到这里,多说一句,为什么不允许在子线程中更新UI呢?
实际上,不能在子线程中更新UI,最明显的原因就是Android本身禁止了这一行为,该操作是在:

ViewRootImpl.checkThead()

中完成的,该函数里只是简单地判断了下,当前线程是都是主线程,若不是的话,直接抛出异常。
这就是直接原因,更深层次的原因是Android系统中的view是线程不安全的,若是能在子线程中更新UI,那么会导致很多问题,比如:多个线程同时操作view,读写并行,那么此时,view的状态是不可预知的,会出现很多问题。那么,加个锁不就得了,这样想是没有错的,但是这里面有个效率的问题,加锁会影响效率是都知道的,view作为不次于四大组件的使用频率的组件,加锁会明显增加Android的效率。

回到主题,我们来看看Android提供了哪些方便在子线程中进行UI更新的方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable,long)

以上的方法都是一些Handler的封装,主要是为了方便程序员而准备的
Handler的具体使用的话,就是在子线程建立消息Message,然后在主线程中new一个对应的Handler,复写其:

public void handleMeaasge(Message msg)

在这个方法里做具体的处理

嗯,说起来确实是比较简单的,也是大家经常都会这么用的,不过有几个小细节值得注意:

在创建的时候,有两种方法:

Message msg =new Message()

mHandler.obtain()

两种方法,有没有想过要使用那一种方法呢?

这两种都会返回一个Message对象,其区别在于直接new的方法是去新创建一个Message对象,而obtain()方法则是去一个消息池中取出消息,然后进行使用,在使用的时候去消息池中获取消息,在使用完毕之后则调用recycle()方法将其回收到消息池中复用。

看到这里应该应该已经很明朗了,使用obtain()方法会减少资源消耗,从而提高效率,所以在大多数场景下,推荐大家使用obtain()方法区获取Meaasge对象。

已经拿到了消息了,那么就该发送了啊

msg.sendToTarget()

mHandler.sendMessage()

mHandler.postAtTime(runnable,long)

一一解释以上三种发送方法:

若是你获取的方法是使用obtain()方法,那么可以调用此方法,因为,这个Message本身在Handler中获取的,所以可以直接向该Handler发送消息。

第二种方法没有Message对象获取的限制,使用该Handler去发送Message。还有一点需要说明,若是不需要发送具体的数据,只是想去调用public void handleMeaasge(Message msg),可以发送一个空方法, sendEmptyMessage()此方法的参数是一消息标识符。

第三种方法是在指定时间去发送消息,对应的还有一个psotDelayed方法,该方法是延迟一定时间区发送消息,我认为本质上是没有区别的。

消息发送完毕后,就需要接收啊

这一部分我感觉没啥可说的,就是有个点必须注意,若是有多个线程向此handler中传入数据,那么必须注意 数据的同步问题,要加上锁。

Handler的工作原理

接下来来说说Handler的工作原理

其实Android的消息机制是由MessageQueue,Handler,Looper来共同完成的。

它们分工如下:

  • Handler则是负责将我们将要发送的消息发送到MessageQueue中。

  • 首先是MessageQueue负责消息的存取,虽说其是Queue,但是他的实现是由单链表实现的,单链表的存取效率比较高。

  • 然后Loooper负责将消息从MessageQueue中取出来,进行处理。

下面来具体分析下它们的工作过程:
第一步,Handler调用自己的send()方法,这个方法最终调用了MeeageQueue的enqueueMessage()方法,这个方法的话,就是向单链表中插入数据。

第二步,接下里介绍下MessageQueue的next()方法,这个方法在一个死循环之中,等待消息,一旦有消息,它就取出消息处理,并且将该条消息从MessageQueue中移除。

第三步,Looper的loop方法,这个方法也是在一个死循环之中,它等待消息的到来,一旦有消息,他就将消息取出来处理。
这么说,各位应该发现一个东西:MessageQueue虽然有方法,但是它除了是个数据结构之外,它几乎什么都不做。尽管它拥有两个很重要额方法,但是这两个方法分别由Handler,Looper来进行调用。

前面已经说了,对于Message的具体处理,交给了Looper来处理,Looper则是将消息通过msg.target.dispatchMseeage(msg)将消息转交给了Hnadler来处理,这里的Handler是msg.target。

到这里还没完,以后我们的经验都是直接在handleMessage(msg)中对msg进行处理的,但是在实现中并不是直接去执行handleMessage(msg)的,对message的处理顺序如下:

首先判断msg.callback是否为空,若是不为空,那么执行:handlecallback()
往下走
接下来判断callback是否为空(这个callback)实在构造Handler时传入的,不为空的话,则执行:callback.handleMessage()
最后才是执行我们最熟悉的Handler的方法handleMessage()

在分析完运行原理之后,我想各位对Android中Handler的有些代码部分疑惑已经解开了。不过我还是来说下

虽说我们平时在使用Handler的时候,只是很简单地在UI线程new出Handler,并且复写了其handleMessage()方法,然后直接在子线程中发送消息就好了,这样没问题。不过通过上文的分析,我们会发现,Looper,MessageQueue去哪儿了?没有Looper,MeeageQueue这个消息循环应该无法执行才对吧?确实也是这样的,没有Looper,MessageQueue是不可能执行的。原因在于这一步Android做了封装,因为UI线程的特殊性,所以Android在启动时就为该线程默认启动了Looper了,正因为Android替我们做了这一部分的工作,所以使用才如此简单。

那么好,如何显示进行Handler的使用过程呢?其实很简单
Looper.prepare()

Handler handler =new handler()

looper.loop()
还有一点,一个线程对应了一个Looper,其实也可以理解,几个线程使用一个Looper取消息,会出问题
3行代码就ok了,这其中第一句代码创建了Looper,而且在构造函数中创建了MessageQueue
第三句代码则是开启了死循环,等待消息的接收和处理。

好了,关于Handler的原理就总结到这里,其实这里面还有一些细节,比如,上面有说到一个线程对应了一个Looper,若是有多个线程向Handler发送消息处理,那么Hnadler怎么去获取各个不同线程的Looper的呢?在Android是使用了一个叫ThreadLocal的东西,感兴趣的朋友可以下去研究下。

0 0
原创粉丝点击