胖虎谈ImageLoader框架(三)

前言

从学校出来的这半年时间,发现很少有时间可以静下来学习和写博文了,为了保持着学习和分享的习惯,我准备每周抽出一部分时间为大家带来一个优秀的Android框架源码阅读后的理解系列博文。

期许:希望可以和大家一起学习好此框架,也希望大家看博文前最好是先了解下框架的基本使用场景和使用方法,有什么问题可以留言给我,交流学习。
当然,再好的博文,也不如自己看一遍源码!


这周为大家带来的是《胖虎谈ImageLoader框架》系列,分析优秀的框架源码能让我们更迅速地提升,大家共勉!!
源码包下载地址:http://download.csdn.net/detail/u011133213/9210765

希望我们尊重每个人的成果,转载请注明出处。
转载于:CSDN 胖虎 , http://blog.csdn.net/ljphhj


正文

继上2篇博文《胖虎谈ImageLoader框架(一)》 《胖虎谈ImageLoader框架(二)》
带来这篇《胖虎谈ImageLoader框架(三)》,上篇我们提到的几个涉及到的类,希望读者可以自己去阅读源码理解,此篇博文不希望在解释源码,希望提到一些那些类中涉及到的知识(线程池,下载器实现,ReentrantLock和Synchronized,Collections.synchronizedMap等)

(ps:读此博文前,希望网友已经阅读并理解了《胖虎谈ImageLoader框架(一)》 《胖虎谈ImageLoader框架(二)》再阅读此博文。)

1、Java线程池
1.1 什么情况下我们才使用线程池?
答:需要做大量的请求,并且每个请求的时间很短。

1.2 使用线程池的好处?
答:减少频繁建立和销毁线程对象所需时间和内存消耗,提升了效率。

1.3 开始学习Java中的线程池
(1) JAVA中线程池的类和继承实现关系UML图
这里写图片描述

(2) ThreadPoolExecutor / ScheduledThreadPoolExecutor

2.1 ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize: 核心线程数量
maximumPoolSize: 线程池中最大可容纳数量
keepAliveTime: 线程在队列中等待被执行的超时时间
unit: 线程池允许超时时间的单位
workQueue: 线程池采用的缓冲队列
threadFactory:创建线程的工厂类
handler: 拒绝线程任务的处理类

线程池的运行机制:

  1. 调用execute(Runnable)方法,添加一个线程任务到线程池中。
  2. ▲线程池判断当前池中存在线程数 < corePoolSize, 立即创建一个新的线程,并执行该Runnable对象的run()方法。▲如果>=corePoolSize, 会将该任务添加到workQueue队列中,等待执行。▲如果workQueue队列中已经满了,并且线程池中的线程数还 < maximumPoolSize, 那么会创建一个新的线程来放入线程池中来处理此任务。▲如果如果workQueue队列中已经满了,并且线程池中的线程数>=maximumPoolSize , 那么会调用handler的策略方法拒绝掉该任务。

学习这个类,主要还是学习一下这个类的参数的意义和运行的机制,当然咱们也要顺带把这个类里面涉及到的周边类也学习一下。这里面提到了 ThreadFactory(可以自定义一个,用来修改线程的name, group, priority, daemon status等) , 队列(SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue),拒绝任务的策略种类(1. AbortPolicy[直接抛RejectedExecutionException异常]、2.CallerRunsPolicy[再给一次尝试的机会]、3.DiscardPolicy[无作为]、4.DiscardOldestPolicy[舍弃队头任务,再尝试添加当前任务])

2.2 ScheduledThreadPoolExecutor
这个类继承于ThreadPoolExecutor,所以父类有的,它也一样,它的使用方式是 :
schedule(Runnable command, long delay, TimeUnit unit) , 可以设定一个延时执行的任务。

2、下载器实现

  1. ImageLoader中实现了三种的下载器:BaseImageDownloader、NetworkDeniedImageDownloader、SlowNetworkImageDownloader(都实现了接口ImageDownloader)
  2. ImageDownloader接口中,实现了Scheme类,用于判断传入的一个Uri是什么Schme和其他操作。
    Scheme分为:HTTP(“http”), HTTPS(“https”), FILE(“file”), CONTENT(“content”), ASSETS(“assets”), DRAWABLE(“drawable”), UNKNOWN("")。
    3.▲NetworkDeniedImageDownloader下载器,因为网络不能接触,所以就不处理"http"和"https" 这两种需要网络的Scheme。
private static class NetworkDeniedImageDownloader implements ImageDownloader {

		private final ImageDownloader wrappedDownloader;

		public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
			this.wrappedDownloader = wrappedDownloader;
		}

		@Override
		public InputStream getStream(String imageUri, Object extra) throws IOException {
			switch (Scheme.ofUri(imageUri)) {
				case HTTP:
				case HTTPS:
					throw new IllegalStateException();
				default:
					return wrappedDownloader.getStream(imageUri, extra);
			}
		}
	}

▲SlowNetworkImageDownloader下载器, 考虑到网络情况是属于比较差的,所以需要用到的InputStream来处理Scheme为"http"和"https"的Uri。

private static class SlowNetworkImageDownloader implements ImageDownloader {

		private final ImageDownloader wrappedDownloader;

		public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
			this.wrappedDownloader = wrappedDownloader;
		}

		@Override
		public InputStream getStream(String imageUri, Object extra) throws IOException {
			InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
			switch (Scheme.ofUri(imageUri)) {
				case HTTP:
				case HTTPS:
					return new FlushedInputStream(imageStream);
				default:
					return imageStream;
			}
		}
	}

BaseImageDownloader:此下载器最为重要,这个类里面写了ImageLoader处理各种数据源时是如何下载图片资源的。这边分别对所有的Scheme将对应的处理函数贴出来,有兴趣的网友再自行去看哈。这个其实可以抽出来作为自己的一个图片下载类,因为所有不同来源的图片下载方式都有。

	@Override
	public InputStream getStream(String imageUri, Object extra) throws IOException {
		switch (Scheme.ofUri(imageUri)) {
			case HTTP:
			case HTTPS:
				return getStreamFromNetwork(imageUri, extra);
			case FILE:
				return getStreamFromFile(imageUri, extra);
			case CONTENT:
				return getStreamFromContent(imageUri, extra);
			case ASSETS:
				return getStreamFromAssets(imageUri, extra);
			case DRAWABLE:
				return getStreamFromDrawable(imageUri, extra);
			case UNKNOWN:
			default:
				return getStreamFromOtherSource(imageUri, extra);
		}
	}

网络图片,Http和Https的下载处理:

protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
		String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
		HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
		conn.setConnectTimeout(connectTimeout);
		conn.setReadTimeout(readTimeout);
		return conn;
	}

protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
		HttpURLConnection conn = createConnection(imageUri, extra);

		int redirectCount = 0;
		while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
			conn = createConnection(conn.getHeaderField("Location"), extra);
			redirectCount++;
		}

		InputStream imageStream;
		try {
			imageStream = conn.getInputStream();
		} catch (IOException e) {
			// Read all data to allow reuse connection (http://bit.ly/1ad35PY)
			IoUtils.readAndCloseStream(conn.getErrorStream());
			throw e;
		}
		if (!shouldBeProcessed(conn)) {
			IoUtils.closeSilently(imageStream);
			throw new IOException("Image request failed with response code " + conn.getResponseCode());
		}

		return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
	}

来自本地文件,Scheme为"file"的下载处理:

protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
		String filePath = Scheme.FILE.crop(imageUri);
		if (isVideoFileUri(imageUri)) {
			return getVideoThumbnailStream(filePath);
		} else {
			BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE);
			return new ContentLengthInputStream(imageStream, (int) new File(filePath).length());
		}
	}

	@TargetApi(Build.VERSION_CODES.FROYO)
	private InputStream getVideoThumbnailStream(String filePath) {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
			Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.FULL_SCREEN_KIND);
			if (bitmap != null) {
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				bitmap.compress(CompressFormat.PNG, 0, bos);
				return new ByteArrayInputStream(bos.toByteArray());
			}
		}
		return null;
	}

来自ContentPorivder传入的Scheme为"content"的下载处理:

protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException {
		ContentResolver res = context.getContentResolver();

		Uri uri = Uri.parse(imageUri);
		if (isVideoContentUri(uri)) { // video thumbnail
			Long origId = Long.valueOf(uri.getLastPathSegment());
			Bitmap bitmap = MediaStore.Video.Thumbnails
					.getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null);
			if (bitmap != null) {
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				bitmap.compress(CompressFormat.PNG, 0, bos);
				return new ByteArrayInputStream(bos.toByteArray());
			}
		} else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo
			return ContactsContract.Contacts.openContactPhotoInputStream(res, uri);
		}

		return res.openInputStream(uri);
	}

来自Assets和Drawable中传入的Scheme为"assets"和"drawable"的下载处理:

	protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException {
		String filePath = Scheme.ASSETS.crop(imageUri);
		return context.getAssets().open(filePath);
	}
	protected InputStream getStreamFromDrawable(String imageUri, Object extra) {
		String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
		int drawableId = Integer.parseInt(drawableIdString);
		return context.getResources().openRawResource(drawableId);
	}

**3、ReentrantLock和Synchronized **
这两个的比较,我查阅了一下网上的文章,本想自己做一些理解和总结,看了一篇文章觉得剖析得非常好,偷懒直接贴上链接地址,如果您对此知识还不是完全熟悉,那么推荐您也可以看看。(http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html)

4、Collections.synchronizedMap类(线程安全)
此Map用于可能多线程共享使用的情况。源码如下,无非就是将自身作为一个互斥量,进行Map的各种操作时用synchronized关键字将其锁住,保证线程安全。

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

    /**
     * @serial include
     */
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }

        private transient Set<K> keySet;
        private transient Set<Map.Entry<K,V>> entrySet;
        private transient Collection<V> values;

        public Set<K> keySet() {
            synchronized (mutex) {
                if (keySet==null)
                    keySet = new SynchronizedSet<>(m.keySet(), mutex);
                return keySet;
            }
        }

        public Set<Map.Entry<K,V>> entrySet() {
            synchronized (mutex) {
                if (entrySet==null)
                    entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
                return entrySet;
            }
        }

        public Collection<V> values() {
            synchronized (mutex) {
                if (values==null)
                    values = new SynchronizedCollection<>(m.values(), mutex);
                return values;
            }
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return m.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return m.hashCode();}
        }
        public String toString() {
            synchronized (mutex) {return m.toString();}
        }

        // Override default methods in Map
        @Override
        public V getOrDefault(Object k, V defaultValue) {
            synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
        }
        @Override
        public void forEach(BiConsumer<? super K, ? super V> action) {
            synchronized (mutex) {m.forEach(action);}
        }
        @Override
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            synchronized (mutex) {m.replaceAll(function);}
        }
        @Override
        public V putIfAbsent(K key, V value) {
            synchronized (mutex) {return m.putIfAbsent(key, value);}
        }
        @Override
        public boolean remove(Object key, Object value) {
            synchronized (mutex) {return m.remove(key, value);}
        }
        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            synchronized (mutex) {return m.replace(key, oldValue, newValue);}
        }
        @Override
        public V replace(K key, V value) {
            synchronized (mutex) {return m.replace(key, value);}
        }
        @Override
        public V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
        }
        @Override
        public V computeIfPresent(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
        }
        @Override
        public V compute(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.compute(key, remappingFunction);}
        }
        @Override
        public V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.merge(key, value, remappingFunction);}
        }

        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

总结

其实这篇博文旨在将一些框架中引用到的知识点,可是平时可能被我忽视的或者少用到的,进行记录,方便我自身充电,与其说是在写博文交流,不如说是自身的补缺补漏,大牛们可以无视O(∩_∩)O!

在这里插入图片描述

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值