分享

android 多线程积累

 风雪夜归人_95 2014-04-04
android中有主线程、子线程、服务。我认为这三个概念是不应该被分开来看的。它们之间总有着千丝万缕的联系。而我也曾一度在三个概念见徘徊不定。把费时操作最开始写在主线程中,然后想写在服务中,最后发现服务也是在主线程中,又开子线程。如果刚开始就搞清楚它们之间的使用场景,也许就不会那么纠结了。
 
一、基础知识:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消 息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该 线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。
                                        

Handler类
Handler类可以对运行在不同线程中的多个任务进行排队,并使用Message和Runnable对象安排这些任务。Handler对象的四个构造方法:
 1)Handler()
  这个handler将自动与当前运行线程相关联,即这个handler与当前运行的线程使用同一个消息队列,并可以处理该队列中的消息。直接利用handler对象post一个runnable对象,相当于直接调用runnable对象的run函数,不会创建一个新线程,而是在原线程中直接调用run方法,runnalbe对象和主用户界面线程的ID是相同实例。一般在handleMessage中处理更新UI界面的操作。
 2)Handler(Looper looper)
    这个handler对象将与参数所表示的Looper相关联。但此时线程应该为一个特殊类HandlerThread类(一个有Looper的Thread类)
 3) Handler (Handler.Callback callback)
    这个handler和当前所在线程关联并且返回一个可以处理messages的返回接口。但是如果当前线程没有looper,则handler不能接收messages并抛出异常。
 4)Handler (Looper looper, Handler.Callback callback)
  
 
异步任务
AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用。
AsyncTask是个抽象类,使用时需要继承这个类,然后调用execute()方法。注意继承时需要设定三个泛型Params,Progress和Result的类型,如AsyncTask<Void,Inetger,Void>:
Params是指调用execute()方法时传入的参数类型和doInBackgound()的参数类型
Progress是指更新进度时传递的参数类型,即publishProgress()和onProgressUpdate()的参数类型
Result是指doInBackground()的返回值类型
上面的说明涉及到几个方法:doInBackgound() 这个方法是继承AsyncTask必须要实现的,运行于后台,耗时的操作可以在这里做
publishProgress() 更新进度,给onProgressUpdate()传递进度参数
onProgressUpdate() 在publishProgress()调用完被调用,更新进度
 
wait() 和 notify机制
synchronized(obj) {
 while(!condition) {
 obj.wait();
 }
 obj.doSomething();
 }

synchronized(obj) {
 condition = true;
 obj.notify();
 }
需要注意的概念是:
对象x不能是基本类型,应该为可引用类型或者javabean
 
# 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {…} 代码段内。
 
# 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {…} 代码段内唤醒A。
 
# 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
 
# 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
 
# obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
 
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
 
二、实际使用遇到的问题
1、在子线程中使用runOnUiThread时,其中若要使用子线程中定义的数据时,eclipse都会提示将该变量的定义为final;而如果在主线程中调用,不会报错,则发现其值会变为0.0。(runOnUiThread方法是一种比较简单的在主线程中工作的地方)
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多