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方法是一种比较简单的在主线程中工作的地方)
|
|