Gallery从网上读取图片,可取消的多线程demo

应朋友要求,用BlockingQueue,Executors写了一个线程竞争较小的demo,从网络上读取图片,然后存于本地。

当Gallery设置图片的时候,首先从缓存读取,若缓存不存在图片,则从SD卡上读取,若SD卡上不存在,则从网络获取图片,经过一整天的代码编写,重构后代码如下:

package net.liuyx.test;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.Toast;

public class Main extends Activity {
	private List<String> urls = new ArrayList<String>();
	private Map<Integer, Bitmap> adminImages = new HashMap<Integer, Bitmap>();
	private Map<Integer, Bitmap> syncGalleryImgs = new ConcurrentHashMap<Integer, Bitmap>();
	private BlockingQueue<Bitmap> putInSDcardQueue;
	private ExecutorService exec;
	private GalleryAdapter adapter;
	private Future<?> downloadFuture = null;
	private ImgState state = ImgState.INTERNET;

	private enum ImgState {
		INTERNET {
			void print() {
				Log.v("liuyx", "既不存在于本地SD卡上,也不存在于本地内存上,需要您下载");
			}
		},
		CACHE {
			void print() {
				Log.v("liuyx", "存在本地内存上");
			}
		},
		SDCARD {
			void print() {
				Log.v("liuyx", "存在SD卡上");
			}
		};
		abstract void print();
	}

	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 0x110:
				adapter.notifyDataSetChanged();
				break;
			}
			super.handleMessage(msg);
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		initGallery();
		initUrls();
		initAdminImgs();
		if (state == ImgState.INTERNET)
			downloadImgs();

		putInSDcardQueue = new ArrayBlockingQueue<Bitmap>(urls.size());
	}

	/**
	 * 初始化Gallery,设置Adapter
	 */
	private void initGallery() {
		Gallery gallery = (Gallery) findViewById(R.id.gallery);
		adapter = new GalleryAdapter();
		gallery.setAdapter(adapter);
	}

	/**
	 * 若图片还没有从网上下载完成,初始化默认显示图片(这里设置为android自带的默认图片)
	 */
	public void initAdminImgs() {
		Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.ic_launcher);
		for (int i = 0; i < urls.size(); i++) {
			adminImages.put(i++, bitmap);
		}
	}

	/**
	 * 将需要下载的URL添加到List容器内
	 */
	private void initUrls() {
		urls.add("http://fujian.xabbs.com/forum/201109/02/160646nn9hjjiimixvkxhe.jpg");
		urls.add("http://img1.cache.netease.com/catchpic/A/A9/A9D98040B397C366AE93E67871346561.jpg");
		urls.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");
		urls.add("http://i1.sinaimg.cn/ent/m/c/2010-01-18/U1819P28T3D2847679F326DT20100118115712.jpg");
		urls.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
	}

	/**
	 * 使用Executors框架开启线程下载图片
	 */
	private void downloadImgs() {
		exec = Executors.newCachedThreadPool();
		cancel();
		for (int i = 0; i < urls.size(); i++) {
			downloadFuture = exec.submit(new DownloadImgTask(urls.get(i), i));
		}
		state = ImgState.CACHE;
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		exec.shutdownNow();
	}

	@Override
	protected void onPause() {
		super.onPause();
		cancel();
	}

	private void cancel() {
		Future<?> newFuture = downloadFuture;
		if (newFuture != null)
			downloadFuture.cancel(true);
	}

	private class DownloadImgTask implements Runnable {
		final String uri;
		final int index;

		DownloadImgTask(String uri, int index) {
			this.uri = uri;
			this.index = index;
		}

		@Override
		public void run() {
			InputStream in = null;
			try {
				in = getImgInputStream();
				final Bitmap bitmap = BitmapFactory.decodeStream(in);
				syncGalleryImgs.put(index, bitmap);
				sendMsg();
				putInSdcard(in, bitmap);
			} catch (IOException e) {
				Log.v("liuyx", "the " + index + " imgs has not download!");
			} finally {
				closeInStream(in);
			}
		}

		/**
		 * 将bitmap放在BlockingQueue中
		 * 
		 * @param bitmap
		 */
		private void putInSdcardToQueue(Bitmap bitmap) {
			try {
				putInSDcardQueue.put(bitmap);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		private void putInSdcard(InputStream in, Bitmap bitmap)
				throws FileNotFoundException {
			if (!isSDCardExist()) {
				Toast.makeText(Main.this, "SD卡不存在,请重新确认", 1).show();
				Log.v("liuyx", "SD卡不存在,请重新确认");
				return;
			}
			putInSdcardToQueue(bitmap);
			saveFiletoSDcard(in);
			if (state != ImgState.CACHE)
				state = ImgState.SDCARD;
		}

		private boolean isSDCardExist() {
			return ((Environment.getExternalStorageState() != null) && !Environment
					.getExternalStorageState().equals(""));
		}

		private void saveFiletoSDcard(InputStream in) {
			try {
				File file = prepareFileInSDcard();
				saveFiletoSDcard(in, file);

			} catch (IOException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		/**
		 * 
		 * @param in
		 * @param file
		 * @throws IOException
		 * @throws InterruptedException
		 */
		private void saveFiletoSDcard(InputStream in, File file)
				throws IOException, InterruptedException {
			Bitmap bitmap = putInSDcardQueue.take();
			final OutputStream out = new FileOutputStream(file);
			final BufferedOutputStream bos = new BufferedOutputStream(out);
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = in.read(buffer)) != -1) {
				bos.write(buffer, 0, len);
			}
			bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
			bos.flush();
			bos.close();
			in.close();
		}

		/**
		 * 为保存的文件取个名字
		 * 
		 * @return
		 */
		private File prepareFileInSDcard() {
			File dir = Environment.getExternalStorageDirectory();
			String path = dir.getAbsolutePath() + "/images/";
			File file;
			synchronized (this) {
				String fileName = path + getImgNameInSDcard();
				file = new File(fileName);
			}
			return file;
		}

		private String getImgNameInSDcard() {
			return "image" + index + ".jpg";
		}

		private InputStream getImgInputStream() throws IOException {
			final URL url = new URL(uri);
			final HttpURLConnection conn = (HttpURLConnection) url
					.openConnection();
			conn.setRequestMethod("GET");
			conn.connect();
			return conn.getInputStream();
		}

		private void closeInStream(InputStream in) {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		private void sendMsg() {
			mHandler.sendEmptyMessage(0x110);
		}
	}

	private Bitmap bitmapForSDCard = null;

	class GalleryAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return urls.size();
		}

		@Override
		public Object getItem(int position) {
			return urls.get(position);
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			ImageView iv = new ImageView(getApplicationContext());
			iv.setLayoutParams(new Gallery.LayoutParams(getWidth(), getHeight()));
			setImgForAdapter(iv, position);
			return iv;
		}

		private void setImgForAdapter(ImageView iv, int position) {
			switch (state) {
			case CACHE:
				iv.setImageBitmap(getBitmap(position));
				break;
			case SDCARD:
				iv.setImageBitmap(bitmapForSDCard);
				break;
			case INTERNET:
				break;
			default:
				iv.setImageBitmap(getDefaultImg(position));
				break;
			}
			// if (isGalleryImgExistAtCache(position)) {
			// iv.setImageBitmap(getBitmap(position));
			// } else if (isGalleryImgExistAtSDcard(position))
			// iv.setImageBitmap(bitmapForSDCard);
			// else {
			// iv.setImageBitmap(getDefaultImg(position));
			// }
		}

	}

	/**
	 * 
	 * @param position
	 * @return 缓存里的默认图片
	 */
	private Bitmap getDefaultImg(int position) {
		return adminImages.get(position);
	}

	/**
	 * 
	 * @param position
	 * @return 判断缓存是否存在图片
	 */
	// private boolean isGalleryImgExistAtCache(int position) {
	// Log.v("liuyx", "从内存上读取");
	// Bitmap bitmap = syncGalleryImgs.get(position);
	// return bitmap != null;
	// }

	/**
	 * 
	 * @param position
	 * @return 判断图片是否在SD卡上
	 */
	// private boolean isGalleryImgExistAtSDcard(int position) {
	// Log.v("liuyx", "从SD卡上读取");
	// String fileName = Environment.getExternalStorageDirectory()
	// .getAbsolutePath() + "/image/" + "iamge" + position + ".jpg";
	// FileInputStream in = getInputStream(fileName);
	// bitmapForSDCard = BitmapFactory.decodeStream(in);
	// return bitmapForSDCard != null;
	// }
	/**
	 * 
	 * @param fileName
	 *            SD卡上的文件名称
	 * @return 返回该文件的输入流
	 */
	// private FileInputStream getInputStream(String fileName){
	// FileInputStream in = null;
	// try {
	// in = new FileInputStream(new File(fileName));
	// } catch (FileNotFoundException e) {
	// e.printStackTrace();
	// }
	// return in;
	// }

	private Bitmap getBitmap(int position) {
		return syncGalleryImgs.get(position);
	}

	private Display getDisplay() {
		Display display = Main.this.getWindowManager().getDefaultDisplay();
		return display;
	}

	private int getWidth() {
		return getDisplay().getWidth();
	}

	private int getHeight() {
		return getDisplay().getHeight();
	}
}
代码应该算是比较简洁,可能有点小bug,希望朋友们,能指出来,共同进步哈,谢谢

你可能感兴趣的:(Gallery从网上读取图片,可取消的多线程demo)