生产者-消费者模型在Android开发中的应用
来源:互联网 发布:apache 配置php7 linux 编辑:程序博客网 时间:2024/04/29 15:04
话说生产者-消费者模型可是并发模型中的一个经典案例了,你可能会问,这种并发情况下,一般服务端程序用的比较多把,Android端的应用程序哪有什么并发情况。虽然事实如此,但是构建生产者-消费者模型,是线程间协作的思想,工作线程的协助是为了让UI线程更好的完成工作,提高用户体验。比如,下面的一种情况:
这个是我们平常开发中很常见的一种情景,大量的图片资源的访问,因为图片访问是一个网络耗时的任务,如果完全交由UI线程去处理,显然用户体验不佳,只能在适配器(Adapter)中的getView()方法做网络异步请求。很多人,都通过第三方框架来实现异步的效果,虽然图片加载的处理要比我们好很多,但是用户体验的效果还是不佳。在比如说像图片的那样,如何做到异步加载,这都是由工作线程协助UI线程去完成的,使用生产者-消费者模型则有助于提高的用户体验。
1.生产者-消费者模型的构造
在这里,我可以提前准备一个队列或者集合,作为缓冲区,把用户拖动作为生产者,因为用户一拖动就会调度getView()方法,那么我们在getView()方法就像缓冲区存放网络请求的任务进去。那么,消费者就是我们的工作线程,我们在工作线程将任务取出,并且加载到内存中,由Hander来切换到UI线程中,完成更新。更为主要的是,如果将任务放入队列中或者什么时间取出任务;从队列中取出哪个任务;什么时候执行任务;怎样执行任务……这个决策权完全由我们掌握,这样,我们就把UI线程的压力给释放出来了。
2.实现模型
- 用Vector来实现队列的效果,当然我们的选择也有很多。
- 第二步我们要做的是,实现GridView/ListView的滚动监听
<code class="hljs java has-numbering">mGridView.setOnScrollListener(<span class="hljs-keyword">new</span> OnScrollListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onScrollStateChanged</span>(AbsListView view, <span class="hljs-keyword">int</span> scrollState) { <span class="hljs-comment">// TODO Auto-generated method stub</span> <span class="hljs-keyword">switch</span> (scrollState) { <span class="hljs-keyword">case</span> OnScrollListener.SCROLL_STATE_FLING: isTouch = <span class="hljs-keyword">false</span>; isFlying = <span class="hljs-keyword">true</span>; <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> OnScrollListener.SCROLL_STATE_IDLE: isTouch = <span class="hljs-keyword">false</span>; isFlying = <span class="hljs-keyword">false</span>; <span class="hljs-keyword">if</span> (adapter != <span class="hljs-keyword">null</span>) { adapter.notifyDataSetChanged(); } <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: isFlying = <span class="hljs-keyword">false</span>; isTouch = <span class="hljs-keyword">true</span>; <span class="hljs-keyword">break</span>; } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onScroll</span>(AbsListView view, <span class="hljs-keyword">int</span> firstVisibleItem, <span class="hljs-keyword">int</span> visibleItemCount, <span class="hljs-keyword">int</span> totalItemCount) { <span class="hljs-comment">// TODO Auto-generated method stub</span> } });</code>
这样我们就知道,用户使用的状态了。
- 其次,我们要在Adapter中启动一条工作线程,充当消费者。
<code class="hljs java has-numbering"><span class="hljs-keyword">if</span>(<span class="hljs-keyword">null</span> == workThread){ workThread = <span class="hljs-keyword">new</span> Thread(){ <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() { <span class="hljs-keyword">while</span>(isLoad){ <span class="hljs-comment">//这里停顿主要是为了达到瀑布流的效果</span> <span class="hljs-keyword">try</span> { Thread.sleep(<span class="hljs-number">100</span>); } <span class="hljs-keyword">catch</span> (InterruptedException e) { <span class="hljs-comment">// TODO Auto-generated catch block</span> e.printStackTrace(); } <span class="hljs-keyword">if</span>(! vector.isEmpty() ){ <span class="hljs-comment">//这里仿照微信里面的倒序加载图片</span> String url = vector.remove(vector.size()-<span class="hljs-number">1</span>); <span class="hljs-keyword">if</span>(mHandler != <span class="hljs-keyword">null</span>) <span class="hljs-comment">//消耗任务并且更新UI</span> mHandler.sendMessage(mHandler.obtainMessage(UPDATE_IMAGE, url)); } <span class="hljs-keyword">else</span>{ <span class="hljs-keyword">try</span> { <span class="hljs-comment">//如果队列为空,则等待</span> <span class="hljs-keyword">synchronized</span> (workThread) { workThread.wait(); } } <span class="hljs-keyword">catch</span> (InterruptedException e) { e.printStackTrace(); } } } } }; workThread.start(); } }</code>
在Adapter的getView()方法,放入任务,生产者所做的事情。
<code class="hljs mel has-numbering"><span class="hljs-comment">//标记ImageView,为了后续执行完任务后,将图片放到这个ImageView上</span>iv.setTag(Images.imageThumbUrls[position]); ImageContainer <span class="hljs-keyword">image</span> = ImageMaps.get(Images.imageThumbUrls[position]); <span class="hljs-keyword">if</span>(<span class="hljs-keyword">image</span> != null){ <span class="hljs-comment">//这里防止内存中缓存的图片已回收</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">image</span>.getBitmap() != null){ iv.setImageBitmap(<span class="hljs-keyword">image</span>.getBitmap()); } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">if</span>(!isFlying){ <span class="hljs-comment">//这里防止getView()方法多次调用,导致放入重复的任务</span> <span class="hljs-comment">//或者可以使用Set来存放任务</span> <span class="hljs-keyword">if</span>(!<span class="hljs-keyword">vector</span>.contains(Images.imageThumbUrls[position])) { <span class="hljs-comment">//放任务</span> <span class="hljs-keyword">vector</span>.add(Images.imageThumbUrls[position]); } <span class="hljs-comment">//通知消费者去取任务</span> synchronized (workThread) { workThread.notify(); } } }</code>在Handler中,将任务交给Volley处理,并且更新UI。
<code class="hljs java has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span>{</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> WeakReference<MainActivity> mActivity; <span class="hljs-keyword">public</span> <span class="hljs-title">MyHandler</span>(MainActivity activity) { mActivity = <span class="hljs-keyword">new</span> WeakReference<MainActivity>(activity); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) { <span class="hljs-keyword">if</span>(msg.what == UPDATE_IMAGE){ <span class="hljs-keyword">try</span> { String url = (String) msg.obj; <span class="hljs-comment">//找出我们标记的ImageView</span> SquareImageView iv = (SquareImageView)mActivity.get().mGridView.findViewWithTag(url); <span class="hljs-comment">//更新UI之后,我们再将从网络上获取的图片资源,再放到一层硬缓存中</span> <span class="hljs-comment">//目的是为了不再加载已经加载过的图片</span> mActivity.get().ImageMaps.put(url, mActivity.get().imageLoader.get( url, ImageLoader.getImageListener(iv, R.mipmap.aio_image_default, R.mipmap.aio_image_fail)) ); <span class="hljs-comment">//告诉我们的硬缓存,现在应用程序已经占有多少内存空间了</span> <span class="hljs-comment">//如果达到指定的空间,则清理部分图片</span> mActivity.get().ImageMaps.setHasHoldMemory( ((<span class="hljs-keyword">int</span>) Runtime.getRuntime().totalMemory())/<span class="hljs-number">1024</span>/<span class="hljs-number">1024</span>); } <span class="hljs-keyword">catch</span> (Exception e) { e.printStackTrace(); } } } }</code>在此之前,指定一层硬缓存(不一定要加,可以选择加上)。选择继承的是LinkedHashMap,内部采用了LRU算法,即不常用的实体,最先被清理。
<code class="hljs axapta has-numbering"><span class="hljs-comment">/** * Created by Nipuream on 2016/5/19 0019. */</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LruHashMap</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">LinkedHashMap</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>>{</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> maxMemory = <span class="hljs-number">1024</span>*<span class="hljs-number">10</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> totalMemory = <span class="hljs-number">0</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> isRemoveOldest = <span class="hljs-keyword">true</span>; <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> setMaxMemory(<span class="hljs-keyword">int</span> max){ maxMemory = max; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> setRemoveOldest(<span class="hljs-keyword">boolean</span> isRemoveOldest){ <span class="hljs-keyword">this</span>.isRemoveOldest = isRemoveOldest; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> setHasHoldMemory(<span class="hljs-keyword">int</span> totalMemory){ <span class="hljs-keyword">this</span>.totalMemory = totalMemory; } <span class="hljs-keyword">protected</span> <span class="hljs-keyword">boolean</span> removeEldestEntry(Map.Entry eldest) { <span class="hljs-comment">//如果应用程序现在占据的内存空间加上10MB已经要大于系统指定给我们最大的内存空间</span> <span class="hljs-comment">//那赶紧清理老的图片</span> <span class="hljs-keyword">if</span>(isRemoveOldest){ <span class="hljs-keyword">if</span>((totalMemory + <span class="hljs-number">10</span>) > maxMemory){ <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>; }<span class="hljs-keyword">else</span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } } <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; }}</code>到此我们的生产者-消费者模型就已经构建完了,这样一来,用户的体验就会提升一个档次,不相信的可以下载代码,运行下,哈哈。当然,如果你觉得代码繁琐,还有种更为简单的方式,我们可以采用java并发库里面的工具ConcurrentLinkedQueue来充当我们的队列,当然这个队列就是真的队列,采用的是先进先出的行为,我们就不能对任务的取出的顺序进行操作了,也就达不到倒序加载或者随机加载了的效果了。但是代码非常的简洁。
<code class="hljs cpp has-numbering"><span class="hljs-comment">//队列</span><span class="hljs-keyword">private</span> ConcurrentLinkedQueue<String> <span class="hljs-built_in">queue</span> = <span class="hljs-keyword">new</span> ConcurrentLinkedQueue<String>();<span class="hljs-comment">//生产者</span> <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">queue</span>.contains(Images.imageThumbUrls[position])){ <span class="hljs-built_in">queue</span>.offer(Images.imageThumbUrls[position]); }<span class="hljs-comment">//消费者,这里虽然不是原子操作,但是考虑到只有一个线程对它操作,所以就没有同步了。</span> <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">queue</span>.isEmpty()){ String url = <span class="hljs-built_in">queue</span>.poll(); <span class="hljs-keyword">if</span>(mHandler != null){ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_IMAGE, url)); } }</code>
ConcurrentLinkedQueue队列的特点就是内部采用了CAS原子操作,是一种非阻塞的同步队列,所以就没必要我们对其加锁了。所以对ConcurrentLinkedQueue不熟悉的话,可以看这篇文章并发编程网脑补下。
如果对文章代码逻辑不清楚的也可以下载我的源码参考下,代码地址链接如下:
生产者-消费者在Android开发中的应用
本文转载自:http://blog.csdn.net/YanghuiNipurean/article/details/51454344
- 生产者-消费者模型在Android开发中的应用
- 生产者-消费者模型在Android开发中的应用
- 生产者-消费者模型在Android开发中的应用
- Linux中的生产者消费者模型
- Java Monitor Object在生产者消费者问题中的应用
- java并发之Condtion在生产者消费者中的应用
- Handler-Looper中的生产者消费者模型
- Java多线程中的生产者消费者模型
- Android平台多线程实现生产者-消费者模型
- 信号量在linux0.11下的实现及其在生产者,消费者应用中的体现
- 生产者-消费者模型
- 生产者/消费者模型
- 生产者 消费者模型
- 生产者消费者模型
- 生产者&&消费者模型
- 模拟生产者/消费者模型
- 生产者消费者模型
- 生产者消费者模型
- Android framework系统默认设置修改
- MongoDB基本用法(增删改高级查询、mapreduce)
- 史上最简单的JQ选项卡
- Android studio 中设置作者和创建日期等注释模板
- 转载集合
- 生产者-消费者模型在Android开发中的应用
- 四层和七层负载均衡的区别
- popupWindow弹出自定义布局+弹出布局的位置控制
- Android利用LocalSocket实现Java端进程与C端进程之间的IPC
- 树莓派配置
- 项目分工及人员管理
- Android控件ToggleButton的使用与修改
- 多个 Android Drawable shape 组合画田字格
- 瀑布流StaggeredGridView