Android 线程池

时间:2019-11-11
本文章向大家介绍Android 线程池,主要包括Android 线程池使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Handler+Runnable模式

我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式。

这里为何不是新开线程的原因请参看这篇文章:Android Runnable 运行在那个线程 这里的代码其实是在UI 主线程中下载图片的,而不是新开线程。

我们运行下面代码时,会发现他其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。

  1 /**
  2  * Handler+Thread+Message模式
  3  */
  4 public class ThreadPoolActivity extends Activity {
  5 
  6     protected static final String TAG = "@@@ThreadPool1Activity";
  7 
  8     @Override
  9     protected void onCreate(Bundle savedInstanceState) {
 10         super.onCreate(savedInstanceState);
 11 
 12         setContentView(R.layout.activity_thread1);
 13 
 14         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
 15         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif",
 16                 R.id.imageView2);
 17         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
 18         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif",
 19                 R.id.imageView4);
 20         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
 21     }
 22 
 23     private Handler mHandler = new Handler();
 24 
 25     private void loadImage(final String url, final int id) {
 26 
 27         mHandler.post(new Runnable() {
 28 
 29             @Override
 30             public void run() {
 31 
 32                 Drawable tDrawable = null;
 33 
 34                 try {
 35 
 36                     tDrawable = Drawable.createFromStream(
 37                             new URL(url).openStream(), "image.png");
 38                 } catch (Exception e) {
 39 
 40                     Log.e(TAG, e.getMessage());
 41                 }
 42 
 43                 if (tDrawable == null) {
 44 
 45                     Log.e(TAG, "null drawable");
 46                 } else {
 47 
 48                     Log.e(TAG, "not null drawable");
 49                 }
 50 
 51                 // 为了测试缓存而模拟的网络延时
 52                 SystemClock.sleep(2000);
 53 
 54                 ((ImageView) ThreadPoolActivity.this.findViewById(id))
 55                         .setImageDrawable(tDrawable);
 56             }
 57         });
 58     }
 59 }
 60 
 61 Handler+Thread+Message模式
 62 这种模式使用了线程,所以可以看到异步加载的效果。
 63 /**
 64  * Handler+Thread+Message模式
 65  */
 66 public class ThreadPoolActivity1 extends Activity {
 67 
 68     protected static final String TAG = "@@@ThreadPool1Activity";
 69 
 70     @Override
 71     protected void onCreate(Bundle savedInstanceState) {
 72         super.onCreate(savedInstanceState);
 73 
 74         setContentView(R.layout.activity_thread1);
 75         
 76         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
 77         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif",
 78                 R.id.imageView2);
 79         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
 80         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif",
 81                 R.id.imageView4);
 82         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
 83     }
 84 
 85     private Handler mHandler = new Handler() {
 86 
 87         @Override
 88         public void handleMessage(Message msg) {
 89             ((ImageView) ThreadPoolActivity1.this.findViewById(msg.arg1))
 90                     .setImageDrawable((Drawable) msg.obj);
 91         }
 92     };
 93 
 94     private void loadImage(final String url, final int id) {
 95 
 96         Thread tThread = new Thread() {
 97 
 98             @Override
 99             public void run() {
100 
101                 Drawable tDrawable = null;
102 
103                 try {
104 
105                     tDrawable = Drawable.createFromStream(
106                             new URL(url).openStream(), "image.png");
107                 } catch (Exception e) {
108 
109                     Log.e(TAG, e.getMessage());
110                 }
111 
112                 if (tDrawable == null) {
113 
114                     Log.e(TAG, "null drawable");
115                 } else {
116 
117                     Log.e(TAG, "not null drawable");
118                 }
119 
120                 // 为了测试缓存而模拟的网络延时
121                 SystemClock.sleep(2000);
122 
123                 Message message = mHandler.obtainMessage();
124                 message.arg1 = id;
125                 message.obj = tDrawable;
126                 mHandler.sendMessage(message);
127             }
128         };
129 
130         tThread.start();
131     }
132 }

Handler+ExecutorService(线程池)+MessageQueue模式
能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。
这个例子是使用线程池。Android 拥有与 Java 相同的 ExecutorService 实现,我们就来用他。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

/**
 * Handler+Thread+Message模式
 */
public class ThreadPoolActivity2 extends Activity {

    protected static final String TAG = "@@@ThreadPool1Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_thread1);

        loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
        loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif",
                R.id.imageView2);
        loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
        loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                R.id.imageView4);
        loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
    }

    private Handler handler = new Handler();

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    // 引入线程池来管理多线程
    private void loadImage(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) ThreadPoolActivity2.this
                                    .findViewById(id))
                                    .setImageDrawable(drawable);
                        }
                    });
                } catch (Exception e) {
                    
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

Handler+ExecutorService(线程池)+MessageQueue+缓存模式
下面比起前一个做了几个改造:
把整个代码封装在一个类中
为了避免出现同时多次下载同一幅图的问题,使用了本地缓存

/**
 * @ClassName: AsyncImageLoader
 * @author Xiao JinLai
 * @Date 2015-3-15 下午4:39:07
 * @Description:图片缓存类
 */
public class AsyncImageLoader {

    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
    public Map<String, SoftReference<Drawable>> mImageCache = new HashMap<String, SoftReference<Drawable>>();

    private ExecutorService mEService = Executors.newFixedThreadPool(5); // 固定五个线程来执行任务
    
    private final Handler mHandler = new Handler();

    /**
     * 
     * @param imageUrl
     *            图像url地址
     * @param callback
     *            回调接口 <a
     *            href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\""
     *            target="\"_blank\"">@return</a> 返回内存中缓存的图像,第一次加载返回null
     */
    public Drawable loadDrawable(final String imageUrl,
            final ImageCallback callback) {
        
        // 如果缓存过就从缓存中取出数据
        if (mImageCache.containsKey(imageUrl)) {
            
            SoftReference<Drawable> softReference = mImageCache.get(imageUrl);
            
            if (softReference.get() != null) {
                
                return softReference.get();
            }
        }
        
        // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
        mEService.submit(new Runnable() {
            
            public void run() {
                
                try {
                    
                    final Drawable drawable = loadImageFromUrl(imageUrl);

                    mImageCache.put(imageUrl, new SoftReference<Drawable>(
                            drawable));

                    mHandler.post(new Runnable() {
                        public void run() {
                            
                            callback.imageLoaded(drawable);
                        }
                    });
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        return null;
    }

    // 从网络上取数据方法
    protected Drawable loadImageFromUrl(String imageUrl) {
        
        try {
            
            // 测试时,模拟网络延时,实际时这行代码不能有
            SystemClock.sleep(2000);

            return Drawable.createFromStream(new URL(imageUrl).openStream(),
                    "image.png");

        } catch (Exception e) {
            
            throw new RuntimeException(e);
        }
    }

    // 对外界开放的回调接口
    public interface ImageCallback {
        
        // 注意 此方法是用来设置目标对象的图像资源
        public void imageLoaded(Drawable imageDrawable);
    }
}

说明:
final 参数是指当函数参数为 final 类型时,你可以读取使用该参数,但是无法改变该参数的值。参看:Java 关键字 final、static使用总结 
这里使用 SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的,更详细的可以参看:内存优化的两个类:SoftReference 和 WeakReference
前段调用:

/**
 * Handler+ExecutorService(线程池)+MessageQueue+缓存模式
 */
public class ThreadPoolActivity3 extends Activity {

    protected static final String TAG = "@@@ThreadPoolActivity3";

    private AsyncImageLoader mAILoader = new AsyncImageLoader();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_thread1);

        loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
        loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif",
                R.id.imageView2);
        loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
        loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif",
                R.id.imageView4);
        loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
    }

    // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
    private void loadImage(final String url, final int id) {

        // 如果缓存过就会从缓存中取出图像,ImageCallback 接口中方法也不会被执行
        Drawable tCacheImage = mAILoader.loadDrawable(url,
                new AsyncImageLoader.ImageCallback() {

                    @Override
                    public void imageLoaded(Drawable imageDrawable) {

                        // 请参见实现:如果第一次加载url时下面方法会执行
                        ((ImageView) findViewById(id))
                                .setImageDrawable(imageDrawable);
                    }
                });

        if (tCacheImage != null) {

            ((ImageView) findViewById(id)).setImageDrawable(tCacheImage);
        }
    }
}

原文地址:https://www.cnblogs.com/zx-blog/p/11835790.html