Android 应用开发 之通过AsyncTask与ThreadPool【转】

http://www.tuicool.com/articles/AVvAfy



  在加载大量数据的时候,经常会用到异步加载,所谓异步加载,就是把耗时的工作放到子线程里执行,当数据加载完毕的时候再到主线程进行UI刷新。在数据量非常大的情况下,我们通常会使用两种技术来进行异步加载,一是通过AsyncTask来实现,另一种方式则是通过ThreadPool来实现,今天我们就通过一个例子来讲解和对比这两种实现方式。

    本文原创,如需转载,请注明转载地址http://blog.csdn.net/carrey1989/article/details/12002033

    项目的结构如下所示:


     在今天这个例子里,我们用到了之前一篇文章中写过的一个自定义控件,如果有同学感兴趣的话可以 点击这里 来先研究下这个控件的实现,为了配合异步加载的效果,我针对这个控件做了一点修改,下面会对修改的地方进行解释。

     接下来我们就分别针对ThreadPool和AsyncTask两种实现方式进行讲解,我会顺着实现的思路贴出关键的代码,在文章最后会贴出实现效果和源码下载,感兴趣的同学可以下载下来对比来看。

     首先来讲解ThreadPool(线程池)的实现方式。

     我们首先需要来实现一个线程池管理器,这个管理器内部包含一个独立的轮询子线程,它的工作是不时的检查工作队列,如果队列中有未执行的任务,就将任务交给线程池来执行。此外,线程池管理器还负责管理线程池和维护任务队列。具体实现代码如下:

package com.carrey.asyncloaddemo;

import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.util.Log;

/**
 * 线程池管理类
 * @author carrey
 *
 */
public class ThreadPoolManager {

  private static final String TAG = "ThreadPoolManager";
  
  /** 线程池的大小 */
  private int poolSize;
  private static final int MIN_POOL_SIZE = 1;
  private static final int MAX_POOL_SIZE = 10;
  
  /** 线程池 */
  private ExecutorService threadPool;
  
  /** 请求队列 */
  private LinkedList<ThreadPoolTask> asyncTasks;
  
  /** 工作方式 */
  private int type;
  public static final int TYPE_FIFO = 0;
  public static final int TYPE_LIFO = 1;
  
  /** 轮询线程 */
  private Thread poolThread;
  /** 轮询时间 */
  private static final int SLEEP_TIME = 200;
  
  public ThreadPoolManager(int type, int poolSize) {
    this.type = (type == TYPE_FIFO) ? TYPE_FIFO : TYPE_LIFO;
    
    if (poolSize < MIN_POOL_SIZE) poolSize = MIN_POOL_SIZE;
    if (poolSize > MAX_POOL_SIZE) poolSize = MAX_POOL_SIZE;
    this.poolSize = poolSize;
        
    threadPool = Executors.newFixedThreadPool(this.poolSize);
    
    asyncTasks = new LinkedList<ThreadPoolTask>();
  }
  
  /**
   * 向任务队列中添加任务
   * @param task
   */
  public void addAsyncTask(ThreadPoolTask task) {
    synchronized (asyncTasks) {
      Log.i(TAG, "add task: " + task.getURL());
      asyncTasks.addLast(task);
    }
  }
  
  /**
   * 从任务队列中提取任务
   * @return
   */
  private ThreadPoolTask getAsyncTask() {
    synchronized (asyncTasks) {
      if (asyncTasks.size() > 0) {
        ThreadPoolTask task = (this.type == TYPE_FIFO) ? 
            asyncTasks.removeFirst() : asyncTasks.removeLast();
        Log.i(TAG, "remove task: " + task.getURL());
        return task;
      }
    }
    return null;
  }
  
  /**
   * 开启线程池轮询
   * @return
   */
  public void start() {
    if (poolThread == null) {
      poolThread = new Thread(new PoolRunnable());
      poolThread.start();
    }
  }
  
  /**
   * 结束轮询,关闭线程池
   */
  public void stop() {
    poolThread.interrupt();
    poolThread = null;
  }
  
  /**
   * 实现轮询的Runnable
   * @author carrey
   *
   */
  private class PoolRunnable implements Runnable {

    @Override
    public void run() {
      Log.i(TAG, "开始轮询");
      
      try {
        while (!Thread.currentThread().isInterrupted()) {
          ThreadPoolTask task = getAsyncTask();
          if (task == null) {
            try {
              Thread.sleep(SLEEP_TIME);
            } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
            }
            continue;
          }
          threadPool.execute(task);
        }
      } finally {
        threadPool.shutdown();
      }
      
      Log.i(TAG, "结束轮询");
    }
    
  }
}
     注意在上面的代码中,我们自定义了任务单元的实现,任务单元是一系列的Runnable对象,最终都将交给线程池来执行,任务单元的实现代码如下:

     ThreadPoolTask.java: 

package com.carrey.asyncloaddemo;
/**
 * 任务单元
 * @author carrey
 *
 */
public abstract class ThreadPoolTask implements Runnable {

  protected String url;
  
  public ThreadPoolTask(String url) {
    this.url = url;
  }
  
  public abstract void run();
  
  public String getURL() {
    return this.url;
  }
}
     ThreadPoolTaskBitmap.java: 
package com.carrey.asyncloaddemo;

import com.carrey.customview.customview.CustomView;

import android.graphics.Bitmap;
import android.os.Process;
import android.util.Log;
/**
 * 图片加载的任务单元
 * @author carrey
 *
 */
public class ThreadPoolTaskBitmap extends ThreadPoolTask {
  
  private static final String TAG = "ThreadPoolTaskBitmap";

  private CallBack callBack;
  
  private CustomView view;
  
  private int position;
  
  public ThreadPoolTaskBitmap(String url, CallBack callBack, int position, CustomView view) {
    super(url);
    this.callBack = callBack;
    this.position = position;
    this.view = view;
  }

  @Override
  public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
    
    Bitmap bitmap = ImageHelper.loadBitmapFromNet(url);
    
    Log.i(TAG, "loaded: " + url);
    
    if (callBack != null) {
      callBack.onReady(url, bitmap, this.position, this.view);
    }
  }

  public interface CallBack {
    public void onReady(String url, Bitmap bitmap, int position, CustomView view);
  }
}
     上面代码中的回调实现位于MainActivity.java中,在任务单元的run方法中主要做的事情就是从服务器得到要加载的图片的Bitmap,然后调用回调,在回调中会将得到的图片加载到UI界面中。

     在加载服务器图片的时候,用到了ImageHelper这个工具类,这个类主要的功能就是提供获得服务器图片地址和解析图片的方法,具体代码如下:

package com.carrey.asyncloaddemo;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * 工具类,用于获得要加载的图片资源
 * @author carrey
 *
 */
public class ImageHelper {
  
  private static final String TAG = "ImageHelper";
  
  public static String getImageUrl(String webServerStr, int position) {
    return "http://" + webServerStr + "/" + (position % 50) + ".jpg";
  }
  
  /**
   * 获得网络图片Bitmap
   * @param imageUrl
   * @return
   */
  public static Bitmap loadBitmapFromNet(String imageUrlStr) {
    Bitmap bitmap = null;
    URL imageUrl = null;
    
    if (imageUrlStr == null || imageUrlStr.length() == 0) {
      return null;
    }
    
    try {
      imageUrl = new URL(imageUrlStr);
      URLConnection conn = imageUrl.openConnection();
      conn.setDoInput(true);
      conn.connect();
      InputStream is = conn.getInputStream();
      int length = conn.getContentLength();
      if (length != -1) {
        byte[] imgData = new byte[length];
        byte[] temp = new byte[512];
        int readLen = 0;
        int destPos = 0;
        while ((readLen = is.read(temp)) != -1) {
          System.arraycopy(temp, 0, imgData, destPos, readLen);
          destPos += readLen;
        }
        bitmap = BitmapFactory.decodeByteArray(imgData, 0, imgData.length);
      }
    } catch (IOException e) {
      Log.e(TAG, e.toString());
      return null;
    }
    
    return bitmap;
  }
}
     到这里准备的工作基本就完成了,接下来的工作就是启动线程池管理器并向任务队列添加我们的加载任务了,这部分工作我们放在GridView的Adapter的getView方法中来执行,其中关键的代码如下:
holder.customView.setTitleText("ThreadPool");
        holder.customView.setSubTitleText("position: " + position);
        poolManager.start();
        String imageUrl = ImageHelper.getImageUrl(webServerStr, position);
        poolManager.addAsyncTask(new ThreadPoolTaskBitmap(imageUrl, MainActivity.this, position, holder.customView));
     下面我们来接着讲解AsyncTask的实现方式。

     相对线程池的实现方式,AsyncTask的实现方式要简单一些

     我们首先来定义好我们的AsyncTask子类,在其中我们将在doInBackground中加载图片数据,在onPostExecute中来刷新UI。代码如下:

package com.carrey.asyncloaddemo;

import com.carrey.customview.customview.CustomView;

import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import android.util.Pair;

public class AsyncLoadTask extends AsyncTask<Integer, Void, Pair<Integer, Bitmap>> {

  private static final String TAG = "AsyncLoadTask";
  
  /** 要刷新的view */
  private CustomView view;
    
  public AsyncLoadTask(CustomView view) {
    this.view = view;
  }
  
  @Override
  protected void onPreExecute() {
    super.onPreExecute();
  }
  
  @Override
  protected Pair<Integer, Bitmap> doInBackground(Integer... params) {
    int position = params[0];
    String imageUrl = ImageHelper.getImageUrl(MainActivity.webServerStr, position);
    Log.i(TAG, "AsyncLoad from NET :" + imageUrl);
    Bitmap bitmap = ImageHelper.loadBitmapFromNet(imageUrl);
    return new Pair<Integer, Bitmap>(position, bitmap);
  }
  
  @Override
  protected void onPostExecute(Pair<Integer, Bitmap> result) {
    if (result.first == view.position) {
      view.setImageBitmap(result.second);
    }
  }

}

     在Adapter中调用AsyncTask异步加载的代码如下:
holder.customView.setTitleText("AsyncTask");
        holder.customView.setSubTitleText("position: " + position);
        new AsyncLoadTask(holder.customView).execute(position);
   通过对比可以发现,ThreadPool相比AsyncTask,并发能力更强,加载的速度也更快,AsyncTask在加载过程中明显变现出顺序性,加载的速度要慢一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值