1 Android多线程同Java差异

语法格式上来说并没有太多区别,区别在于Android存在UI线程(主线程)和非UI线程(子线程)。
UI线程:即程序主线程,主要负责处理UI相关的事件
Android线程之间存在区别的主要原因是:UI线程会被阻塞。当非常耗时的操作放在UI线程内会引起线程的阻塞。这是Android不允许的。Android3.0版本之后明确规定网络编程内容不允许放在UI线程内,否则会报NetworkOnMainThreadException异常。

1.2解决方法

Android提供相应的线程之间通信方法。即耗时的操作放在子线程中,如果需要对UI界面进行修改,则传回对应的消息,由UI线程处理这些消息。

2 Android多线程通信

2.1 Handler类

简介:协助处理UI线程同非UI线程之间的消息传递。
作用:a.在新线程中发送消息 b.在主线程中接受和处理消息
作用范围:Handler主要负责处理较为复杂的线程间通信以及消息处理。
常用方法:

方法

说明

boolean sendMessage(Message msg)

自定义消息对象发送至主线程

boolean sendEmptyMessage(int what)

发送整型数值至主线程

2.1.1 具体细节

1 使用方法
a.在UI线程内创建Handler子类实例
b.重写Handler类handleMessage(Message msg)方法。当有其他线程向这个自定义子类发送消息时,这个方法会被调用,通过识别具体参数执行相应的UI操作。
2 Message简介
顾名思义,“消息对象”多线程之间通信消息的载体。
尽管Message有自己的默认构造方法,但是通常使用Message.obtain()或者Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源
相关属性:

属性

类型

说明

arg1

int

存放整型数据

arg2

int

存放整型数据

obj

Object

存放发送给接收器的Object类型的任意对象

replyTo

Messager

用来指定此Message发送到何处的可选Messager对象

what

int

用户自定义的消息代码

除此之外,Message还可以通过setData(Bundler data)方法将一个Bundler对象放入消息中。
3 Bundler对象
一个Bundler对象可以封装多个键值对,键只能是字符串类型,值可以任意。Bundler类提供了大量的putxxx()方法(设置数据)和getxxx()方法(获取数据),类似Map。
4 sendEmptyMessage实例

public class MainActivity extends Activity {
 TextView text;
 Button btn;
 Handler handler;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  	super.onCreate(savedInstanceState);
  	setContentView(R.layout.activity_main);
  	text = (TextView)findViewById(R.id.text);
  	btn = (Button)findViewById(R.id.btn);
  	handler = new Handler() {
   		public void handleMessage(Message msg) {
    			super.handleMessage(msg);
    			text.setText(msg.what + "");
   		}
  	};
  	btn.setOnClickListener(new View.OnClickListener() {
   	@Override
   		public void onClick(View arg0) {
    			new Thread(new TimeThread()).start();
   		}
  	});
 }
 
 class TimeThread implements Runnable{
  @Override
  public void run() {
   	for(int i = 10; i >= 0; i--) {
    		try {
     		Thread.sleep(1000);
    		} catch (InterruptedException e) {
     		e.printStackTrace();
    		}
    		handler.sendEmptyMessage(i);
   	}
  }
 }
 
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  	// Inflate the menu; this adds items to the action bar if it is present.
  	getMenuInflater().inflate(R.menu.main, menu);
  	return true;
 	}
}

以上实例主要通过Handler对象传递数值实现一个倒计时功能,可以发现what属性可以传送任何自定义值
5 sendMessage实列
代码同上边相似,只不过TimeThread类和handleMessage()方法有了变动

class TimeThread implements Runnable{
  @Override
  public void run() {
   	for(int i = 10; i >= 0; i--) {
    	try {
     	Thread.sleep(1000);
    } catch (InterruptedException e) {
     	e.printStackTrace();
    }
    	Message msg = Message.obtain();
    	Bundle b = new Bundle();
    	//测试不同键值对
    	b.putInt("key1", i);
    	b.putString("key2", "string" + i);
    	msg.setData(b);
    	handler.sendMessage(msg);
   	}
  }
 }
handler = new Handler() {
   public void handleMessage(Message msg) {
    	super.handleMessage(msg);
    	text.setText(msg.getData().getInt("key1") 
      	+ "\n" + msg.getData().getString("key2"));
   	}
  };

2.2 AsyncTask类

简介:AsyncTask类是Android提供的一个轻量级的基于多线程的进行后台异步工作处理的类。在后台工作比较简单,只需要向UI线程传递一些简单数据,可以不用像Handler那样麻烦(不需要创建Handler、Thread类),只需要使用AsyncTask类即可。
方法:创建一个AsyncTask子类,重写其相关方法,然后再UI线程中使用execute()方法运行这个自定义类即可。

3 Handler消息传递机制

3.1 Looper、MessageQueue、Handler

1.相关分析
通过上边学习,可以发现线程间的工作顺序基本上是子线程将数据传递给UI线程,UI线程再将数据呈现到UI界面上。这里需要说明并非所有的子线程都需要将信息传递到UI线程,只有在子线程的结果或者影响需要在UI界面上显示时才需要将具体数据传递到UI线程。上边实例也可以发现,子线程传递过去的所有值都可以被完整的显示在界面上,这是因为Handler通过一个MessageQueue队列来保存相关值。
每一个线程中只能存在一个Looper和一个MessageQueue。除UI线程外,当其他线程涉及到线程之间数据传递保存相关数值时,需要自己创建Looper对象和MessageQueue,因为MessageQueue是封装在Looper内的,所以不需要我们进行操作。
2.操作步骤
子线程中创建Handler实例必须先创建Looper对象
a.创建Looper对象。通过调用Looper中prepare()方法为当前线程创建Looper对象
b.创建Handler子类的实例,重写handlerMessage()方法此时这个方法主要用来处理其他线程发送过来的数据信息
c.调用Looper的loop()方法启动Looper
3.实例
通过Toast提示框在子线程中显示UI界面输入框的内容。

public class MainActivity extends Activity {
 //组件
 Button btn;
 EditText input;
 LooperThread run;

 protected void onCreate(Bundle savedInstanceState) {
  	super.onCreate(savedInstanceState);
  	setContentView(R.layout.activity_main);
  	btn = (Button)findViewById(R.id.send);
  	input = (EditText)findViewById(R.id.input);
  	run = new LooperThread();
  	run.start();
  }
public void clic(View views) {//通过xmlonClick方法调用
  	Message msg = new Message();
  	Bundle b = new Bundle();
  	String str = input.getText().toString();
  	b.putString("key", str);
  	msg.setData(b);
  	run.handle.sendMessage(msg);
 }
 class LooperThread extends Thread{
  public Handler handle;
  @Override
  public void run() {
   	Looper.prepare();
   	handle = new Handler() {
    	public void handleMessage(Message msg) {
     		String str = msg.getData().getString("key");
     		Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
    		}
   	};
   	Looper.loop();
  	}	
  }
 }