Android缓存机制——一般存储实现

平、Android缓存机制

Android缓存分为内存缓存和文书缓存(磁盘缓存)。在初,各大图片缓存框架流行之前,常用的内存缓存方式是软引用(SoftReference)和弱引用(WeakReference),如大部分底使用方式:HashMap<String
url, SoftReference<Drawable>> imageCache;这种形式。从Android
2.3(Level
9)开始,垃圾回收器更倾向于回收SoftReference或WeakReference对象,这令SoftReference和WeakReference变得无是那实用有效。同时,到了Android
3.0(Level
11)之后,图片数据Bitmap被放到了内存的堆积区域,而堆区域的内存是出于GC管理的,开发者也尽管无欲进行图片资源的假释工作,但马上为令图片数的获释无法预知,增加了招OOM的或是。因此,在Android3.1后,Android推出了LruCache这个内存缓存类,LruCache中之靶子是青出于蓝引用的。

次、二级缓存工作机制

所谓二级缓存实际上并无复杂,当Android端需要取多少时仍取网络被的图样,我们率先由内存中查找(按键查找),内存中从不的双重从磁盘文件要sqlite中错过寻找,若磁盘中吗从不才经网络获取;当得来自网络的数目,就以key-value对的道先缓存到内存(一级缓存),同时缓存到文件或者sqlite中(二级缓存)。注意:内存缓存会招致堆内存泄露,所有一级缓存通常如严格控制缓存的大大小小,一般控制在网内存的1/4。

其三、离线缓存

离线缓存就是在网通畅的场面下用自服务器收到的多少保存及本地,当网络断开之后一直读取本地文件中的数量。本质就是是一旦控制好文件之贮存、读取。

咱俩的应用程序一般会出以下几栽类型的数额:

file-普通的文书存储
database-数据库文件(.db文件)
sharedPreference-配置数据(.xml文件)
cache-图片缓存文件

使内数据的有着路线:

/data/data/com.xxx.xxx/cache –
应用内缓存(注:对应措施getCacheDir())
/data/data/com.xxx.xxx/databases – 应用内数据库
/data/data/com.xxx.xxx/shared_prefs – 应用内安排文件
/data/data/com.xxx.xxx/files –
应用内文件(注:对应措施getFilesDir())
SD卡的文件(开发者自定义之)

无是搭还是外置SD卡,获取路径的措施是同等:

取得SD卡根目录:Environment.getExternalStorageDirectory().getAbsolutePath();
表Cache路径:/mnt/sdcard/android/data/com.xxx.xxx/cache
一般存储缓存数据(注:通过getExternalCacheDir()获取)
外表File路径:/mnt/sdcard/android/data/com.xxx.xxx/files
存储长时是的数据 (注:通过getExternalFilesDir(String type)获取,
type为特定项目,可以是以下任何一样栽
Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_PODCASTS,
Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_ALARMS,
Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PICTURES,
or Environment.DIRECTORY_MOVIES. )



季、Android常用存储:

Android平台开展多少存储的五好措施,分别如下:

  • 1 使用SharedPreferences存储数据

  • 2 文件存储数据

  • 3 SQLite数据库存储数据

  • 4 使用ContentProvider存储数据

  • 5 网络存储数据

以下介绍了面前片栽之下,数据库可使用库比较便利,如:greenDao;
其他不过基本上介绍。主要针对缓存做牵线。

(一)、SharedPreferences详解

应用 SharedPreferences 保存key-value对的步子一般是如此:
采用Activity类的getSharedPreferences方法取得到 SharedPreferences
对象,指定文件称与走访权限
获得SharedPreferences.Editor对象,并以该目标的
putXxx方法保存key-value对。
通过SharedPreferences.Editor的commit方法保存(提交)key-value对。

(1)获取SharedPreferences的少种方式:

1 调用Context对象的getSharedPreferences()方法
2 调用Activity对象的getPreferences()方法

(2)两栽方法的分别:

  • 调用Context对象的getSharedPreferences()方法获得的SharedPreferences对象好叫同一应用程序下的旁组件共享.

  • 调用Activity对象的getPreferences()方法获得的SharedPreferences对象只是会以该Activity中使用.

(3)SharedPreferences的季种操作模式:

Context.MODE_PRIVATE

Context.MODE_APPEND

Context.MODE_WORLD_READABLE

Context.MODE_WORLD_WRITEABLE
  • Context.MODE_PRIVATE:为默认操作模式,代表该公文是损公肥私出数据,只能为用本身访问,在拖欠模式下,写副的情会挂原文件之始末

  • Context.MODE_APPEND:模式会检查文件是否在,存在就往文件增加内容,否则就创造新文件.

  • Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来决定其他应用是否发权力读写该文件.

  • MODE_WORLD_READABLE:表示目前文件可以被外应用读取.

  • MODE_WORLD_WRITEABLE:表示目前文件可以让另外应用写入.

(4)将数据保存到SharedPreferences:

SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE);
sp.edit().putString("name", "小张").putInt("age", 11).commit();

或下的写法也得

SharedPreferences preferences=getSharedPreferences("user",Context.MODE_PRIVATE);

Editor editor=preferences.edit();

String name="xixi";

String age="22";

editor.putString("name", name);

editor.putString("age", age);

editor.commit();

(5)从SharedPreferences获取数据:

SharedPreferences preferences=getSharedPreferences("user", Context.MODE_PRIVATE);

String name=preferences.getString("name", "defaultname");

String age=preferences.getString("age", "0");

(二)、文件存储(File)详解

文件存储是 Android
中不过核心的如出一辙栽多少存储方,它不对准存储的始末进行其它的格式化处理,所有数据都是本封不动的保留及文件中的。它比符合用来存储一些大概的文件数据或者二进制数据。如果你想采取文件存储方来保存有比较复杂的文件数据,就用定义一效好的格式规范,方便于之后以文件再次分析出。

(1)Context 提供的文书存储方(openFileOutput、openFileInput)

  • Context
    类中提供了一个openFileOutput()计,可以用于将数据存储到指定的文书中。
  • 其一艺术接收两只参数,第一独参数是文件创建时利用的名称,注意这里指定的公文称非可以分包路径,因为具备文件都是默认储存到
    /data/data/<包名>/files/ 目录下之。
  • 仲单参数是文件的操作模式,主要发生一定量种植模式可选,MODE_PRIVATE(覆盖原文)
    和 MODE_APPEND(追加内容) 。
  • openFileOutput() 方法返回的凡一个 FileOutputStream
    对象,得到这个目标下虽好采用 Java 流的主意拿数据写入到文件中了。

(I)将同段落文本内容保留至文件中:

    public void save() {
        String data = "hello to save";
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            //设置文件名称,以及存储方式
            out = openFileOutput("data", Context.MODE_PRIVATE);
            //创建一个OutputStreamWriter对象,传入BufferedWriter的构造器中
            writer = new BufferedWriter(new OutputStreamWriter(out));
            //向文件中写入数据
            writer.write(data);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

只顾:文件默认会储存到/data/data/package/files中;

  • 接近于用数据存储到文件中,Context 类中尚提供了一个
    openFileInput()主意,用于自文本中读取数据。

  • openFileInput()
    方法就收到一个参数,即如果读取的文书称,然后系统会自行到
    /data/data/<包名>/files/ 目录下来加载是文件,并回到一个
    FileInputStream 对象。

(II)展示什么由文本中读取文本数据:

    public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            //设置将要打开的存储文件名称
            in = openFileInput("data");
            //FileInputStream -> InputStreamReader ->BufferedReader
            reader = new BufferedReader(new InputStreamReader(in));
            String line = new String();
            //读取每一行数据,并追加到StringBuilder对象中,直到结束
            while ((line = reader.readLine()) != null) {
                content.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return content.toString();
    }
(III)四种文件保留之模式。

Context.MODE_PRIVATE
为默认操作模式,代表该文件是损公肥私出数据,只能吃以本身访问,在拖欠模式下写副的内容会挂原文件之情节。
Context.MODE_APPEND
检查文件是否在,存在即朝文件增加内容,否则即创办新文件。
MODE_WORLD_READABLE 代表目前文件可以为另外应用读取。
MODE_WORLD_WRITEABLE 代表手上文件可以于其他使用写入。

(2)内部存储:一般IO流操作File

留意内部存储不是内存。内部存储在系统受到非常非常的一个职务,如果您想用文件存储于中存储着,那么文件默认只能吃你的使用访问到,且一个运所创造的拥有文件都于与应用包名相同的目录下。也就是说应用创建于内存储的文件,与这利用是关联起来的。当一个采取卸载之后,内部存储着之这些文件也于删去。从技术上来讲要你在创立中存储文件之时节用文件属性设置成可读,其他app能够访问自己用之数据,前提是他明白您这以的包名,如果一个文本之性是私房(private),那么即使知道包名其他应用也无法访问。
内部存储空间大点儿,因而显得可贵,另外,它也是系统自身和系应用程序主要的多寡存储所在地,一旦中存储空间耗尽,手机呢就无法采取了。所以对于其中存储空间,我们若尽量避免使用。Shared
Preferences和SQLite数据库都是储存在其间存储空间达到之。内部存储一般用Context来收获和操作。

getFilesDir()得你app的内存储空间,相当给您的采取在里存储上的彻底目录。

应用设置后还见面当Android 根目录生成
/data/data/packagename,当前使读博不待读写权限

注意:
微开发者可能看到过使用的彻底目录为 /data/user/0/packagename
的场面,这里解释一下,Android 4.2
版本添加了同等设备好登录不同用户之意义(由于专利原因仅仅限于平板计算机,手机不支持是功能),所以为了区别不同用户在平应用中之安装以及存储的数量,添加了该系列之路子,该路线指向
/data/data/packagename

  • getFileDir() 方法赢得的是欠目录下 files 文件夹的 File 对象
  • getChacheDir() 方法得到的凡欠目录下 cache 文件夹的 File 对象
  • 一直调用ContextWrapper的 openFileOutput(String name,int mode)
    也会见当该目录下 files
    文件夹下创造相应的公文,并且是私密的。可以修改也任何应用可看的,通过
    openFileOutput 方法的 mode 参数来完成

注意:

  1. 欠目录只有 root 权限下可以查阅,会趁机以卸载删除

  2. 应用程序详情中祛数据会将 packagename 下所生多少以及坐存储、外置
    SD 卡存储空间中 /Android/data/packagename 的漫天目录删除删除

  3. 应用程序详情中排除缓存会将 packagename/cache
    目录下所发数据以及坐存储、外置 SD 卡存储空间中
    /Android/data/packagename/cache 的全部目录删除

设若是要创造一个文本,如下

File file = newFile(context.getFilesDir(), filename);`

安卓还呢我们提供了一个简便方法
openFileOutput()`来读写用在其间存储空间及之文书,下面是一个通向文件被形容副文本的事例:(参考以上)

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try{
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch(Exception e) {
e.printStackTrace();
}

里头存储的别一些操作:

A.列出有的都开立的文件,这个或许无爱想到,Context还是生诸如此类的点子。

String[] files = Context.fileList();
for(String file : files) {
Log.e(TAG, "file is "+ file);
}

B.删除文件,能创造就要能够去,当然也会见提供了去文件之接口,它为非常简单,只待提供文件称

if(Context.deleteFile(filename)) {
Log.e(TAG, "delete file "+ filename + " sucessfully“);
} else {
Log.e(TAG, "failed to deletefile " + filename);
}

C.创建一个索引,需要传入目录名称,它回到 一个文书对象用到操作路径

File workDir = Context.getDir(dirName, Context.MODE_PRIVATE);
Log.e(TAG, "workdir "+ workDir.getAbsolutePath();

总一下文本有关操作,可以得出以下三独特点:

  1. 文件操作就需要为函数提供文件称,所以程序自己只是需要保障文件称即可;
  2. 无用好失去创造文件对象与输入、输出流,提供文件称就是好回File对象或输入输出流
  3. 对于路径操作返回的都是文本对象。

(3)外部存储:IO(读写sdcard上的文本)

手机打带 ROM 的蕴藏路径

1、getExternalCacheDir() 方法,获取内置存储卡中
/Android/data/packagename/cache
目录的不二法门,4.4和以后读写不待权限,会趁机以卸载删除
2、getExternalFilesDir() 方法,获取内置存储卡中
/Android/data/packagename/files
目录的路线,4.4事后读写不欲权限,会趁着以卸载删除,该方式参数为
“null”
时未负定子文件夹,指定时创建子文件夹保存文件。创建的文本其他以只要出读写权限也得读取,如果一旦私密的哪怕运中存储。
3、Environment.getExternalStorageDirectory()
方法赢得的是放置存储目录的根路径目录,读写得权限,不会见趁以卸载删除
4、Environment.getExternalStoragePublicDirectory()
方法取得的凡坐存储目录根路径下的特定类型文件的官目录,读写得权限,不见面就以卸载删除
5、注意,内置存储着 /Android/data/packagenaem/ 路径的读写 Android
版本不同,需要权限不同,所以开时应始终宣称读写权限

其间读写步骤按如下进行:

1、调用Environment的getExternalStorageState()方法判断手机及是否插了sd卡,且应用程序具有读写SD卡的权,如下代码用回到true

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

2、调用Environment.getExternalStorageDirectory()方法来获得标存储器,也便是SD卡的目,或者下”/mnt/sdcard/”目录

3、使用IO流操作SD卡上之公文

注意点:手机当就插入SD卡,对于模拟器而言,可透过mksdcard命令来创造虚拟存储卡
得于AndroidManifest.xml上部署读写SD卡的权柄

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

(I)文件写操作函数

    private void write(String content) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定义File类对象
            if (!file.getParentFile().exists()) { // 父文件夹不存在
                file.getParentFile().mkdirs(); // 创建文件夹
            }
            PrintStream out = null; // 打印流对象用于输出
            try {
                out = new PrintStream(new FileOutputStream(file, true)); // 追加文件
                out.println(content);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close(); // 关闭打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用户
            Toast.makeText(this, "保存失败,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
    }

II)文件读操作函数

    private String read() {

        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定义File类对象
            if (!file.getParentFile().exists()) { // 父文件夹不存在
                file.getParentFile().mkdirs(); // 创建文件夹
            }
            Scanner scan = null; // 扫描输入
            StringBuilder sb = new StringBuilder();
            try {
                scan = new Scanner(new FileInputStream(file)); // 实例化Scanner
                while (scan.hasNext()) { // 循环读取
                    sb.append(scan.next() + "\n"); // 设置文本
                }
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (scan != null) {
                    scan.close(); // 关闭打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用户
            Toast.makeText(this, "读取失败,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
        return null;
    }

五、总结的片段使用代码:

常用存储:
有点之数据体,参数,值(存储于shared_prefs)
网数据json(这种多少一般不特别,存储在文件被file(.txt等等) )
图、音乐、视频(建议数据较生之贮存在SD卡中,图片(有框架的之所以框架))

(一)小的数额(SharedPrefs)

工具类

public class AppConfig {
    private static AppConfig appConfig;

    private SharedPreferences preferences;

    /**
     * App根目录.
     */
    public String APP_PATH_ROOT;

    private AppConfig() {
        preferences = MyApplication.getInstance().getSharedPreferences("test", Context.MODE_PRIVATE);

        APP_PATH_ROOT = FileUtil.getRootPath(MyApplication.getInstance()).getAbsolutePath() + File.separator +
                "test";
    }

    public synchronized static AppConfig getInstance() {
        if (appConfig == null)
            appConfig = new AppConfig();
        return appConfig;
    }

/*
    public void initialize() {
        IOUtils.createFolder(APP_PATH_ROOT);
    }*/

    public void putInt(String key, int value) {
        preferences.edit().putInt(key, value).commit();
    }

    public int getInt(String key, int defValue) {
        return preferences.getInt(key, defValue);
    }

    public void putString(String key, String value) {
        preferences.edit().putString(key, value).commit();
    }

    public String getString(String key, String defValue) {
        return preferences.getString(key, defValue);
    }

    public void putBoolean(String key, boolean value) {
        preferences.edit().putBoolean(key, value).commit();
    }



    public boolean getBoolean(String key, boolean defValue) {
        return preferences.getBoolean(key, defValue);
    }

    public void putLong(String key, long value) {
        preferences.edit().putLong(key, value).commit();
    }

    public long getLong(String key, long defValue) {
        return preferences.getLong(key, defValue);
    }

    public void putFloat(String key, float value) {
        preferences.edit().putFloat(key, value).commit();
    }

    public float getFloat(String key, float defValue) {
        return preferences.getFloat(key, defValue);
    }


}

(二)文件存储——网络数据(string–>txt)

仓储数据

/**
     * 存储文件:.txt
     *
     * @param fileName 文件名称
     * @param content  存储的文件内容
     */

    public static void wirteCache(String fileName, String content) {
        //设置缓存
        File file = new File(MyApplication.getInstance().getCacheDir(), fileName + ".txt");
        if (file.exists()) {
            boolean isdelete = file.delete();
            if (isdelete) {
                Log.e("delete", "success");
            }
        } else {
            byte[] bytes = content.getBytes();
            FileOutputStream output = null;
            try {

                output = new FileOutputStream(file);
                output.write(bytes, 0, bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

读取数据

/**
     * 读取缓存数据
     *
     * @param fileName
     * @return
     */


    public static String readCache(String fileName) {
        String result = null;
        // 读取文件
        File file = new File(MyApplication.getInstance().getCacheDir() + File.separator
                + fileName + ".txt");
        if (file.exists()) {
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                byte[] buffer;
                buffer = new byte[in.available()];
                int count = 0;
                while ((count = in.read(buffer)) > 0) {
                    result = new String(buffer);
                }
            } catch (Exception e) {

                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            //不存在缓存
            return "";
        }
        return result;
    }

(三)文件存储——图片,音乐,视频

存储

  /**
     * 文件缓存策略
     *
     * @param url        文件路径
     * @param fileName   文件名称
     * @param fileFormat 文件格式
     */
    public static void downFile(String url, String fileName, String fileFormat) {
        try {
            String path = "";
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                //判断sd卡是否存在,不存在,存储在应用内部
                path = Environment.getExternalStorageDirectory() + "/";
                Log.e(TAG, "Storage file");
            } else {
                path = MyApplication.getInstance().getFilesDir() + "/";
                Log.e(TAG, "APP file");
            }
            File fileDir = new File(path + "test");
            if (!fileDir.exists()) {
                fileDir.mkdir();
            }
            File file = new File(fileDir.getAbsolutePath() + "/" + fileName + "." + fileFormat);
            if (file.exists()) {
            } else {
                URL myURL = new URL(url);
                URLConnection conn = myURL.openConnection();
                conn.connect();
                InputStream is = conn.getInputStream();
                int fileSize = conn.getContentLength();
                if (fileSize <= 0)
                    throw new RuntimeException("can not know the file`s size");
                if (is == null)
                    throw new RuntimeException("stream is null");
                FileOutputStream fos = new FileOutputStream(file.getPath());
                byte buf[] = new byte[1024];
                while (true) {
                    // 循环读取
                    int numread = is.read(buf);
                    if (numread == -1) {
                        break;
                    }
                    fos.write(buf, 0, numread);
                }
                try {
                    is.close();
                } catch (Exception ex) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

读取

 /**
     * 获取文件地址
     *
     * @param fileName   文件名称
     * @param fileFormat 文件格式
     * @return
     */
    public static String getDownFilePath(String fileName, String fileFormat) {
        String path = "";
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            //判断sd卡是否存在,不存在,存储在应用内部
            path = Environment.getExternalStorageDirectory() + "/";
        } else {
            path = MyApplication.getInstance().getFilesDir() + "/";
            Log.e(TAG, "cache file");
        }
        // 读取文件
        File file = new File(path + "test" + File.separator
                + fileName + "." + fileFormat);
        if (file.exists()) {
            return file.getAbsolutePath();
        } else {
            return "";
        }
    }
网站地图xml地图