每次在Android中遇到Handler,Runnable,Thread,HandlerThread,Message,Looper,MessageQueue及多线程的时候,我就一阵眩晕,感觉这些知识好难懂,今天就我的理解写一些总结,大部分内容都是从别人的博客中的理解。
首先,看看Java中的多线程是如何实现的,在Java中实现多线程有两种方式,当然这两种方式放在Android也是能运行的:
class MyThread extends Thread { public void run() { // 这里写上线程的内容 } public static void main(String[] args) { // 使用这个方法启动一个线程 new MyThread().start(); } }
class MyThread implements Runnable{ public void run() { // 这里写上线程的内容 } public static void main(String[] args) { // 使用这个方法启动一个线程 new Thread(new MyThread()).start(); } }
前者是继承Thread类,后者是实现了Runnable接口,一般鼓励使用第二种方式,因为Java只允许单继承,但允许实现多个接口。
接下来,看看线程的状态,线程状态一共5种,分别是:
新生状态(New): 当一个线程的实例被创建即使用new关键字和Thread类或其子类创建一个线程对象后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive);
就绪状态(Runnable): 通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,但还没有被分配到CPU即不一定会被立即执行,此时处于线程就绪队列,等待系统为其分配CPCU,等待状态并不是执行状态; 此时线程是活着的(alive);
运行状态(Running): 一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive);
阻塞状态(Blocked):通过调用join()、sleep()、wait()或者资源被暂用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)
死亡状态(Dead):当一个线程的run()方法运行完毕或被中断或被异常退出,该线程到达死亡(dead)状态。此时可能仍然存在一个该Thread的实例对象,当该Thread已经不可能在被作为一个可被独立执行的线程对待了,线程的独立的call stack已经被dissolved。一旦某一线程进入Dead状态,他就再也不能进入一个独立线程的生命周期了。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。
现在开始Android的消息队列模式,Activity、Looper、Handler,Thread之间的的关系,那么一个线程怎样把消息放入主线程的消息队列呢?答案是通过Handle对象,只要Handler对象以主线程的Looper创建,那么调用Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler的handleMessage接口来处理消息,见下图:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
由上图可以看到Handler负责将Message放入到MessageQueue中,一个MessafeQueue可以对应多个Handler,那么Handler和Runnable是什么关系呢。
我们还是通过几个例子来具体看下:
package com.xue.threaddemo; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; public class ThreadDemo extends Activity { private static final String TAG = "ThreadDemo"; private int count = 0; private Handler mHandler = new Handler(); private Runnable mRunnable = new Runnable() { public void run() { //为了方便 查看,我们用Log打印出来 Log.e(TAG, Thread.currentThread().getName() + " " +count); count++; setTitle("" +count); //每2秒执行一次 mHandler.postDelayed(mRunnable, 2000); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //通过Handler启动线程 mHandler.post(mRunnable); } @Override protected void onDestroy() { //将线程销毁掉 mHandler.removeCallbacks(mRunnable); super.onDestroy(); } }
由上面代码可以看出handler通过mHandler.post(mRunnable)方法启动一个Runnable,然后执行Runnable方式中的的run()方法的代码。下面看看另外一个例子:
package com.xue.handlerdemo; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { private int title = 0; private Handler handler =new Handler(){ public void handleMessage(Message msg) { if(msg.what==1){ updateTitle(); } }; }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Timer timer = new Timer(); timer.scheduleAtFixedRate(new MyTask(), 1, 5000); } class MyTask extends TimerTask{ @Override public void run() { // TODO Auto-generated method stub Message message =new Message(); message.what=1; handler.sendMessage(message); } } public void updateTitle(){ setTitle("Welcome to Xue's APP " + title); title ++; } }
上面代码中终于有Message出现了,在继承自TimerTask的类中,实例化一个Message对象,然后handler.sendMessage(message)把message传递给Handler,前面讲过Handler就是用来处理message的,上面代码中用sendmessage()发送消息,在Handler中用handleMessage(Message msg)接收消息,那么这个实例化的message在接收之前放在消息队列中,实例化的Message除了有what参数外常用的还有arg1,agr2,obj,前面两个是int类型,obj是object类型。下来看一个MARS老师的例子:
package mars.barhandler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; public class TestBarHandler extends Activity { /** Called when the activity is first created. */ //声明控件变量 ProgressBar bar = null; Button startButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //根据控件的ID得到代表控件的对象,并为按钮设置监听器 bar = (ProgressBar)findViewById(R.id.bar); startButton = (Button)findViewById(R.id.startButton); startButton.setOnClickListener(new ButtonListener()); } //当点击startButton按钮时,就会执行ButtonListener的onClick方法 class ButtonListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub bar.setVisibility(View.VISIBLE); updateBarHandler.post(updateThread); } } //使用匿名内部类来复写Handler当中的handleMessage方法 Handler updateBarHandler = new Handler(){ @Override public void handleMessage(Message msg) { bar.setProgress(msg.arg1); Bundle bundle = msg.getData(); updateBarHandler.post(updateThread); System.out.println("test---->" + bundle.getString("test")); } }; //线程类,该类使用匿名内部类的方式进行声明 Runnable updateThread = new Runnable(){ int i = 0 ; @Override public void run() { System.out.println("Begin Thread" + i); i = i + 10 ; //得到一个消息对象,Message类是有Android操作系统提供 Message msg = updateBarHandler.obtainMessage(); //将msg对象的arg1参数的值设置为i,用arg1和arg2这两个成员变量传递消息,优点是系统性能消耗较少 msg.arg1 = i ; Bundle bundle = new Bundle(); bundle.putString("test", "test bundle"); msg.setData(bundle); try { //设置当前显示睡眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //将msg对象加入到消息队列当中 if( i > 100){ //如果当i的值为100时,就将线程对象从handler当中移除 updateBarHandler.removeCallbacks(updateThread); System.out.println(">>>>>>"); }else{ updateBarHandler.sendMessage(msg); System.out.println("<<<<<<"); } } }; }
这个例子非常经典,点击开始按钮后,updateBarHandler.post(updateThread)通过post方法handler启动了一个runnable,在继承自Runnable的类中终于又出现Message啦,这次不仅简单的让msg.arg1=i而且还加入了Bundle类型的数据msg.setData(bundle);然后当然是sendMessage了,相应的在Handler中就会有handlerMessage接收消息,这儿可以注意到第一个例子没有使用Message机制,所以第一个例子仅仅是使用post方法启动了个runnable,而在runnable没有使用sendMessage方法发送消息,故Handler没有加入handlMessage方法,仅仅实例化了个Handler()。回到本例子中,Handler接收到消息后,bar.setProgress(msg.arg1)更新了UI,而且接收了Runnable发送的参数,然后再用post方法循环到Runnable一直到i>100,updateBarHandler.removeCallbacks(updateThread)这行代码非常重要,作用是把线程对象从handler当中移除,那么不移除会有什么后果,不移除的话即时关闭这个应用,线程不会停,依然在执行。
许多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,遇到的时候总是感觉一阵眩晕,我的理解还是较为浅薄,望海涵。
由于不满意自己对Handler的理解,贴上一篇博文,这里面讲的非常详细:http://52android.blog.51cto.com/2554429/470542