Android换肤功能设计与实现(6)——网络加载及图片内存管理(2)

   

    在上一篇博客中主要通过对内存的提前申请及Bitmap加载方式,来提高网络访问效率及本地程序稳定性,取得效率与速度上的一个平衡。近期对网络加载进行了进一步优化,先将主要思路及提升点一一列出,期待大家共同讨论,提出更优化的方案。

    首先,是Bitmap具体的网络加载方式及多线程访问的优化。在终端应用中,机器自身的运算速度及网络速度的限制很大程度上限制了终端应用的实现方式,同时终端较高的用户体验需求,决定了应用要具有较高的响应速度,较流畅的体验。这就要求在访问网络下载图片资源时,随着界面的切换,必须要即时中断前一网络请求,启动新的网络请求。而我们知道,在终端访问级别已经做到单线程下载单张Bitmap的程度,如何做到即时中断上一请求,快速响应新请求,就是一个必然要面对的问题。由于在终端访问中,一般都是单线程访问单Bitmap,从粒度角度来讲不可能再小,也不可能在加载Bitmap后,根据条件判断是否线程终止。从实现的角度来看有两种实现方案,从宏观角度来说直接终止请求线程或者从微观角度即时终止Bitmap的网络访问,线程自然结束。我们知道Thread运行后,要想控制其结束,就要使用Thread.interrupt方法,并实现想关的方法,回调该方法,释放所分配的Bitmap空间,否则必然造成内存泄露。虽然比较麻烦,但并不是不可实现。但在正常情况下,为了减少线程启动、关闭造成的线程损耗,对于网络访问,一般采用线程池的方式。而JAVA提供的ThreadPoolExecutor,帮助我们解决了线程池所要面对的绝大部分问题,但是要想做到及时关闭线程就力不从心了,由于线程池未提供直接操作具体线程的接口,我们不可以直接操作线程,只能通过ThreadPoolExecutor.shutdown方法,被动的调用Thread的interrupt方法,开始的思路是控制ThreadPoolExecutor的请求队列,及重写Thread,Runnable,在Thread的interrupt中回调重写的Runnable中的onInterrupt方法,释放Bitmap等相关资源。但在实际操作中,却发现由于线程池的实现策略,不可能回调自己重写的Runnable的onInterrupt方法,线程池永远都是将提交的RUNNABLE重新封装为Task,通过ThreadFactory申请新的Thread(worker)来执行该Task,由于其所遵循标准接口,在Thread不可能直接操作到我们提交的Runnable对象,只有重写JAVA线程池,这样实现的代价及成本就有些太高,同时该方案每次都需要终止线程池,这样线程池所提供的反复利用的优势也荡然无存,果断放弃该方案,转而从微观角度入手,最理想的情况是随时中断单张Bitmap的下载,随着中断的发生,线程资源及时释放,快速响应下一网络访问请求。这就必然要求我们改变Bitmap加载方式,强化控制Bitmap的加载的能力,做到及时中断、及时响应。之前我们采用的是BitmapFactory.decodeStream(new URL().openStream());的方式,由于该方式是边下载边解码,效率不高同时可控性不好,主要的思路是将下载与解码完全分离,由于主要消耗时间的是下载过程,只要下载过程可被中断,即可。思路清晰后就着手改变Bitmap的加载方式了。下载缓存方式,将网络图片流首先缓存到byte数组中,完成下载的过程,在解码时直接使用BitmapFactory.decodeByteArray()来实现快速的图片解码。在下载过程中保存所下载的网络流在mIs(InputStream)中,需要中断网络访问时直接close掉该输入流,由于所有的内存空间都在JAVA层分配,所以这部分空间会即时释放,不会造成内存泄露。同时close掉输入流后网络下载立即终止,线程资源释放,响应下一网络请求。通过这种方式,实现bitmap下载与解码的分离,提高对耗时操作的可控性。可以大幅提高线程响应速度,减少不必要的损耗。

    public static final byte[] input2byte(InputStream inStream)   
                throws IOException {   
        {
            
            int inx = 0;
           synchronized (mSwapStream) {
               /*获取缓存所在序号*/
               inx = mCurInx = (++mCurInx %CORE_THREAD_SIZE);
               mIs[inx] = inStream;
            }
           /*减少拷贝次数*/
           ByteArrayOutputStream os = new ByteArrayOutputStream(480*800*4+5);
           
            int rc = 0;   
            try{
            while ((rc = inStream.read(mBuffArr[inx], 0, 1024*4)) > 0) {   
                os.write(mBuffArr[inx], 0, rc);   
            }   
            }
            catch( IOException io){
                io.printStackTrace();
            }
            byte[] in2b = os.toByteArray();
            return in2b;
            
        }
       } 

    上面解决了网络加载即时释放、快速响应请求的问题,由于终端网络访问的代价实在太过昂贵,(从访问速度来说,2G/3G/WIFI速度无法保证,下载速度缓慢,体验不佳,从上网价格来看,拜可敬的中移动所赐,流量同样宝贵)这样在终端网络访问中就一定要减少网络访问,同时要保证良好地用户体验。如何实现这些需求那,以空间换时间是最好的方案了,我们从网络上辛苦下载的图片用完就释放是很浪费空间的,即使之前使用软链接提高内存缓存图片,实现小图片快速访问,但内存毕竟有限且宝贵,更多的资源还是被不断重复加载,重复释放了。而目前的手机,其实SD卡空间非常充足的,与宝贵的网络、内存资源比起来,SD卡空间是最经济实惠的考虑,尤其是我们在终端应用的特性,所访问的资源相对固定,所以建立网络资源的本地缓存成了最具应用价值的考虑,实际操作及效果也同样验证了这种方案,直接上代码。

    /**
     * @param name 正常链接地址
     * @return 返回重定向SDCARD地址
     * */
    private String getCache(String name){
        /*如果缓存命中,则返回其Bitmap*/
        name = name.replace('/', '1');
        name = name.replace(':', '1');
        File bitmap = new File(CACHE_DIR+name);
        if(bitmap.exists()){
            return "file://"+bitmap.getAbsolutePath();
        }
        return null;
    }
    /*name 直接取其Url地址*/
    private void saveBitmap(Bitmap image,String name){
        
        name = name.replace('/', '1');
        name = name.replace(':', '1');
//        name = "1";
        File bitmapFile = new File(CACHE_DIR + name);  
        if(!bitmapFile.exists())  
        {  
            try  
            {  
                bitmapFile.createNewFile();  
            }  
            catch (IOException e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
        FileOutputStream fos;  
        try  
        {  
            fos = new FileOutputStream(bitmapFile);  
            image.compress(Bitmap.CompressFormat.PNG,   
                    100, fos);  
            fos.close();  
        }  
        catch (FileNotFoundException e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        catch (IOException e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } 
    }

    简单来说就是在SD卡中建立永久二级缓存,在内存缓存未命中,优先放问二级缓存,二级缓存未命中再去网络加载,加载完后即时保存在SD卡中。由于SD卡具有永久保存的特性,只要不进行手动删除,其加速性及网络访问是永久有效的,这样就不用进行费时费力代价高昂的网络访问了。

     ——欢迎转载,请注明出处http://blog.csdn.net/zyplus——


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值