android消息处理机制

几个概念的介绍:

Looper:Handler和MessageQueue之间的桥梁。系统会为主线程(UI线程)创建一个Looper对象,就是说对于主线程可以直接使用Looper对象,实际上主线程的工作就是不断的从其Looper对象的消息队列中取出消息并执行。系统并没有为子线程创建Looper对象,如果子线程要使用Looper对象必须先创建,有两种创建方式:

public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
从prepare方法的定义中可以看出,prepare方法就是将Looper对象放到ThreadLocal中,ThreadLocal会为每一个访问其中保存的变量的线程创建一个变量的副本,每一个线程对该变量的访问都是独立的。(具体怎么实现的,还有点纠结)。这两个方法的返回值都为void,他们只是为当前线程关联一个Looper对象。还有一点要注意的是,Looper类是final型的,也就是说无法通过继承创建自己的Looper类。loop()方法用于循环取出消息队列中的消息,并进行处理,quit()方法结束循环。

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg); //重点

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }
myLooper()方法返回与当前线程关联的Looper对象,里面有好多代码不太懂,但是最重要的一句话msg.target.dispatchMessage(msg);可以看出loop()方法会将消息队列中取出的消息交给消息中的Handler对象来处理,这也就是为什么说Looper是Handler和MessageQueue之间的桥梁。调用Message的recycle()方法会将处理完的Message对象释放到系统的线程池中。


Message:用于描述各种数据,换句话说,Message对象可以用来存放各种类型的数据这里的数据是要交由Handler中的handleMessage方法来处理的。Message有几个重要的成员变量:Handler、Bundle(键值对的形式携带数据)、Runnable,arg1、arg2用来携带int类型的数据,what用于标记是哪个Message。Object用来携带对象。关于Message对象的实例化系统提供了new Message()方法,但是考虑到效率和内存,推荐使用Handler.obtainMessage(),这个方法还有几个接收不同类型参数的重载方法,都是用来在创建Message对象的时候为Message对象中的成员变量赋值,具体内容可以看API,这里就不说了。


MessageQueue:顾名思义,消息队列,被封装到Looper中,具体怎么工作的不用管,只需要知道他是以队列的方式来存储消息的。


Handler:用于处理Runnable对象和Message对象。可以将Runnable对象和Message对象发布到消息队列中,还可以对他们进行相应的处理。使用默认构造器new Handler()创建的Handler对象与当前所在线程的Looper关联,当然也可以使用new Handler(Looper)来在在创建Handler对象的同时指定与之相关联的Looper对象。也就是说,在创建Handler对象的时候必须保证已经有一个Looper与当前线程相关联。前面已经说了,系统会为主线程关联一个Looper对象,这也就是说,在主线程中可以直接new一个Handler,但是对于子线程,系统并没有为其关联一个Looper,因此在子线程中创建Handler之前必须先为子线程关联一个Looper对象,否则会抛出异常。


看了前面几个关键的类之后,可能还是有点迷糊,现在用一个简单的图来描述一下Android的消息机制的工作流程:

android消息处理机制_第1张图片

工作流程:

1、在线程中关联Looper对象(主线程中这个工作由系统完成)和Handler对象,并覆盖其中的handleMessage(Message msg)方法(如果你仅仅只是要处理Runnable对象的话可以不覆盖这个方法,直接new就行了);

2、在线程中(可以与上面的线程是同一个也可以不同)调用Handler上面的发布消息的方法(各种post和send),如果是发布Message对象,当然要先创建(obtainMessage),消息都存在MessageQueue中(MessageQueue被封装在Looper中,不需要自己创建);

3、调用Looper上的loop()方法开始工作,循环从MessageQueue中取出消息并交个Handler的handleMessage方法处理。(对于主线程这个工作也是由系统完成)

说明:

1、一个线程至多有一个Looper与之关联(主线程必定有一个,子线程就看我们自己了),一个Looper必定只有一个MessageQueue;

2、一个Looper可以与多个Handler关联,但是一个Handler只能与一个Looper关联;

3、一个Handler可以处理多个消息,即使这些消息来自不同的对象;


下面看几个例子。

在主线程中对Message对象的处理

前面已经说过,系统会为主线程关联一个Looper对象,因此下面的代码中没有调用prepare方法。

package com.zju.test_handler6;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

	Handler handler;
	static int count = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				switch (msg.what) {
				case 1:
					Log.v("adb", "msg.what = " + msg.what);
					break;
				case 2:
					Log.v("adb", "msg.what = " + msg.what);
					break;
				default:
					Log.v("adb", "click too much");
				}
			}
		};

		Button button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		// 在创建Message对象的时候给what变量赋值,what用于标记Message对象
		Message msg = handler.obtainMessage(count++);
		msg.sendToTarget();
		//handler.removeMessages(2);
	}
}
连续点击三次Button的运行结果:


如果把最后一行的注释取消掉,则不会打印出第二行,这说明不仅可以通过Handler来发布消息,也可以通过Handler来从消息队列中删除消息。


在主线程中对Runnable对象的处理:

package com.zju.test_handler4;

import com.example.test_handler4.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	static Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		handler = new Handler();

		Button post = (Button) findViewById(R.id.post);
		Button postAtTime = (Button) findViewById(R.id.postAtTime);
		Button postDelayed = (Button) findViewById(R.id.postDelayed);
		Button postAtFrontOfQueue = (Button) findViewById(R.id.postAtFrontOfQueue);

		post.setOnClickListener(new Listeners());
		postAtTime.setOnClickListener(new Listeners());
		postDelayed.setOnClickListener(new Listeners());
		postAtFrontOfQueue.setOnClickListener(new Listeners());
	}
}

class Threads implements Runnable {

	int num;

	public Threads(int id) {
		this.num = id;
	}

	@Override
	public void run() {
		switch (num) {
		case 1:
			Log.v("adb", "Thread num = " + num);
			Log.v("adb", "Thread id = " + Thread.currentThread().getId());
			Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
			Log.v("adb", "");
			break;
		case 2:
			Log.v("adb", "Thread num = " + num);
			Log.v("adb", "Thread id = " + Thread.currentThread().getId());
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
			Log.v("adb", "");
			break;
		case 3:
			Log.v("adb", "Thread num = " + num);
			Log.v("adb", "Thread id = " + Thread.currentThread().getId());
			Log.v("adb", "执行run之后系统时间为:" + System.currentTimeMillis());
			Log.v("adb", "");
			break;
		}
	}
}

class Listeners implements OnClickListener {

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		int whitch = v.getId();
		Log.v("adb", "执行run之前系统时间为:" + System.currentTimeMillis());
		switch (whitch) {
		case R.id.post:
			// 把一个Runnable对象发布到MessageQueue中
			Threads thread_1 = new Threads(1);
			MainActivity.handler.post(thread_1);
			//MainActivity.handler.removeCallbacks(thread_1);
			break;
		case R.id.postAtTime:
			// 控制Runnable对象中的run方法运行指定的时间
			MainActivity.handler.postAtTime(new Threads(2), 2000);
			break;
		case R.id.postDelayed:
			//控制Runnable对象中的run方法延迟指定的时间后运行
			MainActivity.handler.postDelayed(new Threads(3), 2000);
			break;
			/*
			 * 这个方法没有搞太明白,也基本用不到
		case R.id.postAtFrontOfQueue:
			MainActivity.handler.postAtFrontOfQueue(new Threads(4));
			break;
			*/
		}
	}
}
界面就是四个Button,依次点击前三个Button的运行结果:

android消息处理机制_第2张图片

由于这个例子没有涉及到对Message的处理,故没有覆盖Handler中的handleMessage方法。调用Handler中的方法发布Runnable对象时,有几种不同的方式,每一种发布方式的作用在注释中说了,运行结果也进行了验证。需要说明的是,使用Handler发布的Runnable对象仍旧属于主线程(结果中线程id都是一样的),尽管Runnable对象中的run方法执行了。这也说明:Handler只是将Runnable对象发布到主线程的消息队列中,在loop方法中,取出该对象并交由Handler执行Runnable中的run方法。也正因为如此,在使用主线程的Looper的时候,最好不要在Handler中处理耗时操作。跟处理Message对象时一样,如果把代码中第一个case 中的注释删掉,第一个Runnable对象的run方法就不会执行。


当然,最常见的还是上面的两个例子的结合:

package com.zju.test_handler5;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	static Handler handler = new Handlers();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 几个按钮控制Message携带不同类型的数据
		Button int_button = (Button) findViewById(R.id.button1);
		Button object_button = (Button) findViewById(R.id.button2);
		Button bundle_button = (Button) findViewById(R.id.button3);

		int_button.setOnClickListener(new Listeners());
		object_button.setOnClickListener(new Listeners());
		bundle_button.setOnClickListener(new Listeners());
	}
}

class Listeners implements OnClickListener {

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		int whitch = v.getId();
		switch (whitch) {
		case R.id.button1:
			MainActivity.handler.post(new Threads(1));
			break;
		case R.id.button2:
			MainActivity.handler.post(new Threads(2));
			break;
		case R.id.button3:
			MainActivity.handler.post(new Threads(3));
			break;
		}
	}
}

//定义自己的Handler对象
class Handlers extends Handler {

	// 在这个方法里面处理接收到的消息
	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		switch (msg.what) {
		case 1:
			Log.v("adb", "msg_1.arg1 = " + msg.arg1);
			Log.v("adb", "msg_1.arg2 = " + msg.arg2);
			break;
		case 2:
			Log.v("adb", "msg_2.obj = " + msg.obj);
			break;
		case 3:
			Log.v("adb", "msg_3.bundle = " + msg.getData().getString("name"));
			break;
		}
	}
}

class Threads implements Runnable {

	int num;

	public Threads(int num) {
		this.num = num;
	}

	// 这样做是为了给不同的Runnable对象定义不同的行为
	@Override
	public void run() {
		switch (num) {
		case 1:
			Message msg_1 = MainActivity.handler.obtainMessage(1);
			// 用于标记是哪个Message
			msg_1.what = num;
			// 携带int型数据
			msg_1.arg1 = 12;
			msg_1.arg2 = 13;
			//msg_1.sendToTarget();
			MainActivity.handler.sendMessage(msg_1);
			break;
		case 2:
			Message msg_2 = MainActivity.handler.obtainMessage(1);
			msg_2.what = num;
			// 携带Object
			msg_2.obj = new Class_to_send();
			//msg_2.sendToTarget();
			MainActivity.handler.sendMessage(msg_2);
			break;
		case 3:
			Message msg_3 = MainActivity.handler.obtainMessage(1);
			msg_3.what = num;
			// 用Bundle携带数据(键值对形式,可以携带各种进本类型的数据和IBinder对象)
			Bundle bundle = new Bundle();
			bundle.putString("name", Thread.currentThread().getName());
			msg_3.setData(bundle); // 把Bundle放到msg_3中
			//msg_3.sendToTarget();
			MainActivity.handler.sendMessage(msg_3);
			break;
		}
	}
}

class Class_to_send {
}
在上面的代码中,我写了两钟发布消息的方法,两种方法的效果是一样的,依次点击三个按钮的运行结果:


这个例子很常见的一种用法就是用来避免直接在子线程中操作UI,我们经常说的,不能在子线程中更新UI,是因为系统没有为View提供同步锁,为了避免死锁,系统就禁止在子线程中直接更新UI。这个例子提供了一种思路:在Runnable对象的run方法更新UI信息,然后把更新后的UI信息用一个Message对象保存起来并发布到主线程的消息队列中,然后在主线程的Handler对象(与主线程的Looper对象关联的Handler对象,不一定是在主线程中定义的)的handleMessage方法中更新UI。从前面的例子中我们知道,Runnable对象的run方法还是在定义它的线程中执行的,并没有另开一个线程(因为自始至终没有调用线程的start方法),这样就是说,如果Runnable对象时定义在主线程中的,自然可以在run方法中更新UI信息,如果Runnable对象是定义在子线程中的,也可以通过Handler在run方法实现对UI信息的更新,因为Handler对消息的处理是异步的。


上面的几个例子都是在主线程中进行的操作,下面再看几个在子线程中进行操作的例子,这个时候就要为子线程关联自己的Looper了。

为子线程关联Looper:

package com.zju.test_handler7;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Thread sub_Thread = new Sub_Thread();
		sub_Thread.start();
	}
}

class Sub_Thread extends Thread {

	@Override
	public void run() {
		// 为当前线程关联一个Looper,Looper中有一个MessageQueue,所以不用再创建了
		Looper.prepare();//static方法可以直接通过类名调用
		
		// 使用默认构造器,与当前线程的Looper关联,发布的消息在当前线程的消息队列中
		Handler sub_thread_handler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				Log.v("adb", "sub_msg.what = " + msg.what);
				Log.v("adb", "current thread name = "
						+ Thread.currentThread().getName());
				//结束工作,非static方法,通过Looper独享调用
				Looper.myLooper().quit();
			}
		};
		

		// 与主线程的Looper关联,发布的消息在主线程的消息队列中
		Handler main_thread_handler = new Handler(Looper.getMainLooper()) {

			@Override
			public void handleMessage(Message msg) {
				Log.v("adb", "main_msg.what = " + msg.what);
				Log.v("adb", "current thread name = "
						+ Thread.currentThread().getName());
			}
		};

		Message sub_msg = sub_thread_handler.obtainMessage(1);
		sub_thread_handler.sendMessage(sub_msg);

		Message main_msg = main_thread_handler.obtainMessage();
		// 发送空消息就必须指定what,不然的话,如果发送了很多空消息就不知道谁是谁了
		main_thread_handler.sendEmptyMessage(2);
		
		//Looper开始工作了,loop方法会一直执行知道调用quit方法,因此这个方法最好放在线程的最后
		Looper.loop();
	}
}
运行结果:


从结果中可以验证上面的说法:Handler对象与哪个线程的Looper关联(使用默认构造器时与Handler所在的线程的Looper对象关联),handleMessage就会在哪个线程中处理消息,而跟Handler在哪个线程中无关。在上面的代码中消息是从子线程中发布出去的,但是第二个Handler对消息的处理是在主线程红完成的,这也就实现了从子线程到主线程的通信。前面说的在子线程中更新UI信息的原理跟这个例子是一样的。


一个Looper与多个Handler关联:

package com.zju.test_handler8;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 创建两个与主线程关联的Handler对象
		Handler handler_1 = new Handler_1();
		Handler handler_2 = new Handler_2();

		Message msg_1 = handler_1.obtainMessage(1);
		Message msg_2 = handler_2.obtainMessage(2);

		msg_1.sendToTarget();
		msg_2.sendToTarget();
	}
}

class Handler_1 extends Handler {

	@Override
	public void handleMessage(Message msg) {
		if (msg.what == 1)
			Log.v("adb", "handler_1处理消息");
	}
}

class Handler_2 extends Handler {

	@Override
	public void handleMessage(Message msg) {
		if (msg.what == 2)
			Log.v("adb", "handler_2处理消息");
	}
}
在上面的代码中,我是将两个Handler都与主线程的Looper关联,当然你也可以讲多个Handler与其他线程的Looper关联,原理都是一样的。


一个Message被多个Handler处理:

package com.zju.test_handler9;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		handler_1.post(run);
		handler_2.post(run);
	}
	
	Handler handler_1 = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			Log.v("adb", "handler_1" + msg.toString());
		}
	};
	
	Handler handler_2 = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			Log.v("adb", "handler_2 " + msg.toString());
		}
	};
	
	class handler_3 implements Handler.Callback{

		@Override
		public boolean handleMessage(Message msg) {
			// TODO Auto-generated method stub
			return false;
		}
		
	}
	
	Runnable run = new Runnable() {
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Message msg = handler_1.obtainMessage();
			//为Message关联一个Handler对象
			msg.setTarget(handler_2);
			msg.sendToTarget();
		}
	};
}
前面说了,哪Handler发布的Message就由哪个Handler来处理,现在在补充一点:Handler也可以处理不是由自己发布的Message,调用Message.setTarget(Handler)方法就行了。


写了这么多,逻辑可能有点混乱,如果有什么错误或者补充,欢迎讨论。

你可能感兴趣的:(Android笔记,Android消息处理机制)