图片的三级缓存和二次采样使用的案例

本文介绍了一种用于改善用户体验的三级缓存技术,并详细解释了如何通过内存、文件和网络来实现图片加载的优化。同时,文章还探讨了二次采样的原理及其在避免程序内存溢出方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先跟大家介绍一下三级缓存,当然缓存的好处就是可以让用户体验更好,通过首次访问时将数据存储起来,以后需要就直接在本地获取数据,减少不必要的网络访问次数,也减少了流量的开销。但我们大部分应用都运用了此技术,所以三级缓存运用面比较广的。

所谓三级缓存其实是一种加载图片文件的策略,简单的来说就是 “内存——文件——网络”,顾名思义,当你需要加载一张图片,首先去内存中去取,原因是内存的响应速度最快,其次是外存(即SD卡),如果两者都没有你需要的图片,那就只有发送网络请求获取图片,但需要注意的是当网络获取到图片时首先存入SD卡中,当需要此图片时,户首先去缓存中去取,但是缓存还没有存入数据,接着去sd卡中去获取数据此时sd卡中已经有数据,在此时将sd卡中的数据存入缓存中去的同时将数据取出来使用。当下一次需要使用该图片时,就可以在缓存中直接获取图片了。

三级缓存想必大家已经有了一定的了解了下面来说说二次采样

二次采样使为了避免程序oom,当我们需要加载一张获取多张图片时,由于程序分配的内存有限,当图片过大过多时,内存就就会溢出,程序就直接over了,所以二次采样时为了优化程序。

我们来说说二次采样的原理,首先我们获取一张高清图片获取此图片的宽(sourceWidth)和高(sourceHeight),然后自己把需要的图片的宽(width)和高(height)通过参数缩放比率来进行调整,与原图进行比较,通过BitmapFactory内部的解码器参数的设定, 通过参数缩放(simpleSize)来不断调整大小,当有一条边和规定的边相等时,将边框绘制成功,第一次采样完成,第二次采样就是通过得到的边框来设置彩色格式RGB_565(如果想了解采样格式,可以看我的下一篇博客,里面有彩色格式的介绍),然后就采样好的图片解码填充边框。二次采样就完成了!

说了这么多下面就给大家展示一个小案例,看完案例你应该就明白了。

下面是布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="demo.liuchen.com.android25_lrucache.MainActivity">

    <Button
        android:id="@+id/btn_getImg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="Click"
        android:text="获取图片"/>

    <ImageView
        android:id="@+id/image_Show"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>


</LinearLayout>
下面是MainActivity:

package demo.liuchen.com.android25_lrucache;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 图片的三级缓存:LruCache--SDCard——Internet
 */
public class MainActivity extends AppCompatActivity {
//    private String url = "http://p8.qhimg.com/t0113125382f3c4141a.png";
    private String url = "http://t2.27270.com/uploads/tu/201606/32/k5xnewfzvz0.jpg";
    private ImageView imageView;
    private LruCache<String,Bitmap> lruCache;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what==1){
                Bitmap bitmap = (Bitmap) msg.obj;
                imageView.setImageBitmap(bitmap);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.image_Show);
        //获取运行时内存
        Long MaxSize = Runtime.getRuntime().maxMemory();

        //初始化LruCache
        lruCache = new LruCache<String, Bitmap>((int) (MaxSize/8)){
            //key 从内存中取出或存入的对象的名字
            //value 存取的对象
            //return 返回对象的缓存
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }
    public void Click(View view) {
    //当用户需要图片是先到缓存中去取
        Bitmap bitmap = lruCache.get(getName(url));
        if (bitmap!= null){
            Log.e("TAG","在缓存中获取");
            imageView.setImageBitmap(bitmap);
        }else {
            //在文件中查找

            Bitmap bitmap1 = getBitmapFromSDCard(getName(url));
            if (bitmap1!= null){
                Log.e("TAG","在文件中获取");
                imageView.setImageBitmap(bitmap1);
                //如果图片在SD卡中找到,将图片放入缓存
                Log.e("TAG","文件中存在放入缓存");
                lruCache.put(getName(url),bitmap1);
            }else {

                getBitmapFromNet();
            }
        }
    }
        //从网络获取
    private void getBitmapFromNet() {
        Log.e("TAG","在网络中获取");
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL path = new URL(url);
                    HttpURLConnection httpURLConn = (HttpURLConnection) path.openConnection();
                    if (httpURLConn.getResponseCode() == 200){
                        final Bitmap bitmap = BitmapFactory.decodeStream(httpURLConn.getInputStream());
                        //将图片放入缓存中
                        lruCache.put(getName(url),bitmap);
                        //将bitmap存入当前文件的文件中
                        SaveBitmapToSDCard(getName(url),bitmap);
                        Message message = Message.obtain();
                        message.obj=1;
                        message.obj=bitmap;


                       handler.sendMessage(message);

                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
	//将Bitmap存入sd卡中
    private void SaveBitmapToSDCard(String name, Bitmap bitmap) {
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(new File(getExternalCacheDir(),name)));
            if (name.endsWith("png")||name.endsWith("PNG")){
                //把bitmap存入SdCard中
                Log.e("TAG","把bitmap存入SdCard中");
                bitmap.compress(Bitmap.CompressFormat.PNG,100,bos);
            }else {
                bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }


    }
	//从sd卡中获取图片
    private Bitmap getBitmapFromSDCard(String name) {
      //  Log.e("TAG","从SD卡中获取图片");
	//如果需要原图就可以不调用二次采用方法
  //   Bitmap bitmap = BitmapFactory.decodeFile(getExternalCacheDir().getAbsolutePath()+ File.separator+name);
        //在SD卡中取出的Bitmap进行二次采样,传出去后会将二次采样好的图片放入缓存中
        Bitmap bitmap = BitmapUtils.getBitmap(getExternalCacheDir().getAbsolutePath()+File.separator+name,100,100);
        return  bitmap;
    }

    //获取Bitmap为Url的名字
    public String getName(String url){
       String name = url.substring(url.lastIndexOf("/")+1,url.length());
        return name;
    }


}
下面是二次采样的封装类,方便调用。
package demo.liuchen.com.android25_lrucache;

/**
 * Created by Administrator on 2016/9/23.
 */

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * Bitmap二次采样工具类
 */
public class BitmapUtils {
    /**
     *
     * @param filePath  bitmap的路径
     * @param destWidth 希望采样后Bitmap的宽
     * @param destHeight 希望采样后Bitmap的高
     *
     * @return  只有边框的bitmap
     */
    public static Bitmap getBitmap(String filePath, int destWidth, int destHeight) {
        //第一采样
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        int outWidth = options.outWidth;
        int outHeight = options.outHeight;
        int sampleSize = 1;
        while ((outWidth / sampleSize > destWidth) || (outHeight / sampleSize > destHeight)) {
            sampleSize *= 2;
        }
        //第二次采样
        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        return BitmapFactory.decodeFile(filePath, options);
    }
}
    }


    //获取Bitmap为Url的名字
    public String getName(String url){
       String name = url.substring(url.lastIndexOf("/")+1,url.length());
        return name;
    }


}

//当然访问了网路和sd卡记得在Manifest清单文件加上权限:
<uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

//好了案例就完成了,如果有问题欢迎评论和指正,也可以加好友一起交流学习。


下面是以上案例的代码需要的可以自行下载:http://download.youkuaiyun.com/detail/mr_condingson/9765051
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值