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();
}
}
}