1.handler.post(new Runnable() ..这种模式,虽然是在子线程中启动的,但是实际是执行在UI线程中的 ,如下:
private Handler handler = new Handler();
private ExecutorService executorService = Executors.newFixedThreadPool(5);// 引入线程池来管理多线程
private void loadImage3(final String url, final int id) {
executorService.submit(new Runnable() {
public void run() {
try {
final Drawable drawable = Drawable.createFromStream(
new URL(url).openStream(), "image.png");
// 模拟网络延时
SystemClock.sleep(2000);
handler.post(new Runnable() {
public void run() {
((ImageView) MainActivity.this.findViewById(id))
.setImageDrawable(drawable);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
}[/mw_shl_code]
这里我们象第一步一样使用了 handler.post(new Runnable() { 更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { 来确保下载是在线程池的线程中。
2. android销毁线程示例
- package com.tutor.thread;
- 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();
- }
- }
3. 子线程访问UI主线程的方式:
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask { protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }
1>可以通过泛型指定它的类型:参数,进度值,任务的结果值。2>doInBackGround()方法自动在工作线程中只想能够。3> onPreExecute(),onPostExecute(),onProgressUpdate()方法都在UI线程中执行。4> doInBackground()方法返回的值被当作参数传递给onPostExecute()方法。5>你能够在doInBackground()方法里任何时候调用publishProgress()方法在UI线程中去执行onProgressUpdate()方法。
5.在Android中有多种方法可以实现其他线程与Main线程通讯,我们这里介绍常见的两种。
1) 使用AsyncTask
AsyncTask是Android框架提供的异步处理的辅助类,它可以实现耗时操作在其他线程执行,而处理结果在Main线程执行,对于开发者而言,它屏蔽掉了多线程和后面要讲的Handler的概念。你不了解怎么处理线程间通讯也没有关系,AsyncTask体贴的帮你做好了。不过封装越好越高级的API,对初级程序员反而越不利,就是你不了解它的原理。当你需要面对更加复杂的情况,而高级API无法完成得很好时,你就杯具了。所以,我们也要掌握功能更强大,更自由的与Main线程通讯的方法:Handler的使用。
2) 使用Handler
这里需要了解Android SDK提供的几个线程间通讯的类。
2.1 Handler
Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。
2.2 Looper
Looper负责管理线程的消息队列和消息循环
2.3 Message
Message是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息。
2.4 MessageQueue
MessageQueue是消息队列,先进先出,它的作用是保存有待线程处理的消息。
它们四者之间的关系是,在其他线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件添加到Main线程的MessageQueue中,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调Handler的handlerMessage()方法。
除了以上两种常用方法之外,还有几种比较简单的方法
3) Activity.runOnUiThread(Runnable)
4) View.post(Runnable)
View.postDelayed(Runnable, long)
5) Handler.post
Handler.postDelayed(Runnable, long)
3) 利用线程池提高性能
这里我们建议使用线程池来管理临时的Thread对象,从而达到提高应用程序性能的目的。
线程池是资源池在线程应用中的一个实例。了解线程池之前我们首先要了解一下资源池的概念。在JAVA中,创建和销毁对象是比较消耗资源的。我们如果在应用中需要频繁创建销毁某个类型的对象实例,这样会产生很多临时对象,当失去引用的临时对象较多时,虚拟机会进行垃圾回收(GC),CPU在进行GC时会导致应用程序的运行得不到相应,从而导致应用的响应性降低。
资源池就是用来解决这个问题,当你需要使用对象时,从资源池来获取,资源池负责维护对象的生命周期。
了解了资源池,就很好理解线程池了,线程池就是存放对象类型都是线程的资源池。
6. Android研究院之游戏开发多线程(十六) http://www.xuanyusong.com/archives/344
7. 多线程Looper的使用:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
new Thread() {
@Override
public void run() {
//如果handler不指定looper的话
//默认为mainlooper来进行消息循环,
//而当前是在一个新的线程中它没有默认的looper
//所以我们须要手动调用prepare()拿到他的loop
//可以理解为在Thread创建Looper的消息队列
Looper.prepare();
Toast.makeText(LooperActivity.this, "收到消息",Toast.LENGTH_LONG).show();
//在这里执行这个消息循环如果没有这句
//就好比只创建了Looper的消息队列而
//没有执行这个队列那么上面Toast的内容是不会显示出来的
Looper.loop();
//如果没有 Looper.prepare(); 与 Looper.loop();
//会抛出异常Can't create handler inside thread that has not called Looper.prepare()
//原因是我们新起的线程中是没有默认的looper所以须要手动调用prepare()拿到他的loop
}
}.start();
}
};
8.Handler注意事项
Handler注意点1:在那个线程创建的Handler,Handler就属于哪个线程(就和这个线程的loop绑定)。
Handler注意点2:Handler调用Post(Runnable)函数时,不会启动另外一个线程,就是当前线程。
这个在做UI更新中要特别注意,这也是很多人问为什么调用了post方法UI线程还是阻塞了的原因,因为这样只是用Handler实现了异步,没有多线程(Handler要结合Thread实现异步)。
Handler注意点3:在子线程中创建Handler,默认是没有MessageQueue的。
解决方法:1)用主线程的Handler(将主线程创建的Handler实例作为参数传入子线程中)来发送消息到主线程的MessageQueue。
2)子线程创建Handler,获取主线程的MessageQueue,即获得主线程的Looper,(方法new Handler(Loop.getMainLooper()))