一起学Android之Storage
概述
在Android开发中,存储(Storage)的方式根据具体的需求不同而不同,例如数据对应用程序是私有的还是其他应用程序(和用户)可以访问的,以及保存数据需要多大的空间。
存储分类
主要的存储方式有以下几种(本文主要涉及前三种):
- Shared Preferences:以键值对( key-value pairs)的方式存储在程序内部;
- Internal Storage:将数据保存在设备内存中;
- External Storage:将公有数据保存在外部存储中(SD卡);
- SQLite Databases:保存结构化数据到私有数据库中;
- Network Connection:通过网络服务器,将数据存储在web上。
涉及知识点
涉及的知识点如下:
- SharedPreferences 共享目录接口,通过Context中的getSharedPreferences进行实例化。
- SharedPreferences.Editor 共享目录编辑器,需要以commit进行保存。
- FileOutputStream 文件输出流,用write方法将数据保存文件。
- FileInputStream 文件输入流,用于读取文件的内容,通过Context中的openFileInput进行实例化,用read方法进行读取内容。
- deleteFile Context中的方法,删除指定路径的文件
- Environment.getExternalStorageDirectory() 获取外部存储的根目录。
- Environment.getExternalStorageState() 获取外部存储卡的状态。
- File.separator 文件路径分割符,用斜杠(/)进行表示。
- BitmapFactory.decodeResource(getResources(), R.drawable.bg) 通过资源文件实例化对象。
- BitmapFactory.decodeFile(imgFile.getAbsolutePath() 通过具体文件的路径实例化对象。
- File 文件对象,mkdirs() 创建多层目录 mkdirs创建单个目录,exists() 判断文件或目录是否存在,createNewFile() 创建新文件。
Shared Preferences
此种方式,只能保存简单的数据类型如下图所示:

代码如下:
/**
* SharedPreferences保存
* @param v
*/
public void sharedSave(View v){
String name=etName.getText().toString().trim();
String age=etAge.getText().toString().trim();
if(TextUtils.isEmpty(name)||TextUtils.isEmpty(age)){
return;
}
//构造一个编辑器----笔
SharedPreferences.Editor editor = sp.edit();
//数据的存储---只能存储简单的数据
editor.putString("name",name);
editor.putString("age",age);
//提交--保存
editor.commit();
//清空
etName.setText("");
etAge.setText("");
} /**
* SharedPreferences读取
*/
public void sharedRedo(View v){
String name=sp.getString("name","");
String age=sp.getString("age","");
etName.setText(name);
etAge.setText(age);
}
Internal Storage
将数据内容转换为字节的方式保存在文件中,如下图所示:

代码如下:
/**
* 内部保存
* @param v
*/
public void internalSave(View v) {
String name = etName.getText().toString().trim();
String age = etAge.getText().toString().trim();
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(age)) {
return;
} try {
String content = "这是我的姓名:" + name + "这是我的年龄:" + age;
FileOutputStream fos = openFileOutput(sname, MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
etName.setText("");
etAge.setText("");
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 内部读取
* @param v
*/
public void internalRedo(View v) {
try {
FileInputStream fis = openFileInput(sname);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
String content = new String(buffer);
Toast.makeText(this, content, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 删除文件
* @param v
*/
public void internalDel(View v){
boolean del=deleteFile(sname);
if(del){
Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "删除失败", Toast.LENGTH_SHORT).show();
}
}
External Storage
在外部存储中,需要在AndroidManifest.xml中配置相关的读写权限,如下所示:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
保存在SD卡中,如下图所示:

代码如下:
/**
* 外部存储
* @param v
*/
public void externalSave(View v) { try {
Log.i(TAG, "externalSave: 开始");
Bitmap img = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
ByteArrayOutputStream bass = new ByteArrayOutputStream();
img.compress(Bitmap.CompressFormat.JPEG, 100, bass);
img.recycle();
Log.i(TAG, "externalSave: saveImg");
boolean saveOk = saveImg("dog.jpg", bass.toByteArray());
Log.i(TAG, "externalSave: saveImg:"+saveOk);
if (saveOk) {
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
}
bass.close();
Log.i(TAG, "externalSave: 结束");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "externalSave: 异常"+e.getMessage());
}
} /**
* 外部读取
* @param v
*/
public void externalRedo(View v) {
if (!isMounted()) {
return;
}
try {
Bitmap img = readImg("dog.jpg");
iv01.setImageBitmap(img);
} catch (Exception e) {
e.printStackTrace();
}
} private boolean saveImg(String fileName,byte[] data){
Log.i(TAG, "saveImg: 开始");
if(!isMounted()){
Log.i(TAG, "saveImg: 挂载失败");
return false;
}
Log.i(TAG, "saveImg: 路径:"+storedPath);
File dir=new File(storedPath);
if(!dir.exists()){
boolean f= dir.mkdirs();
if(f){
Log.i(TAG, "saveImg: 创建目录成功:"+storedPath);
}else{
Log.i(TAG, "saveImg: 创建目录失败:"+storedPath);
}
}
Log.i(TAG, "saveImg: 判断路径:ok");
try {
File file=new File(storedPath,fileName);
if(file.exists()){
Log.i(TAG, "saveImg: 判断文件:1");
file.delete();
}
Log.i(TAG, "saveImg: 判断文件:2");
file.createNewFile();
Log.i(TAG, "saveImg: 判断文件:ok");
FileOutputStream fos=new FileOutputStream(file);
fos.write(data);
fos.close();
return true;
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "saveImg: 异常:"+e.getMessage());
return false;
}
} public Bitmap readImg(String fileName){
if(!isMounted()){
return null;
}
File imgFile=new File(storedPath,fileName);
if(imgFile.exists()){
return BitmapFactory.decodeFile(imgFile.getAbsolutePath());
}
return null;
} private boolean isMounted(){
String state=Environment.getExternalStorageState();
return state.equals(Environment.MEDIA_MOUNTED);
}
备注
在进行外部存储时,如果默认关闭了APP的存储空间权限,没有前往设置开启应用权限,即使manifest中正常注册权限,该APP仍让无法读写文件。调试日志如下:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
未开启App的存储权限
04-02 21:21:59.173 30863-30863/com.hex.demostorage I/DemoStorage: externalSave: 开始
04-02 21:21:59.407 30863-30863/com.hex.demostorage I/DemoStorage: externalSave: saveImg
04-02 21:21:59.408 30863-30863/com.hex.demostorage I/DemoStorage: saveImg: 开始
04-02 21:21:59.412 30863-30863/com.hex.demostorage I/DemoStorage: saveImg: 路径:/storage/emulated/0/hex/images
04-02 21:21:59.412 30863-30863/com.hex.demostorage I/DemoStorage: saveImg: 判断路径:ok
04-02 21:21:59.412 30863-30863/com.hex.demostorage I/DemoStorage: saveImg: 判断文件:2
04-02 21:21:59.414 30863-30863/com.hex.demostorage I/DemoStorage: saveImg: 异常:No such file or directory
04-02 21:21:59.414 30863-30863/com.hex.demostorage I/DemoStorage: externalSave: saveImg:false
04-02 21:21:59.414 30863-30863/com.hex.demostorage I/DemoStorage: externalSave: 结束
开启App的存储权限
04-02 21:22:48.519 31306-31306/com.hex.demostorage I/DemoStorage: externalSave: 开始
04-02 21:22:48.754 31306-31306/com.hex.demostorage I/DemoStorage: externalSave: saveImg
04-02 21:22:48.755 31306-31306/com.hex.demostorage I/DemoStorage: saveImg: 开始
04-02 21:22:48.759 31306-31306/com.hex.demostorage I/DemoStorage: saveImg: 路径:/storage/emulated/0/hex/images
04-02 21:22:48.760 31306-31306/com.hex.demostorage I/DemoStorage: saveImg: 判断路径:ok
04-02 21:22:48.760 31306-31306/com.hex.demostorage I/DemoStorage: saveImg: 判断文件:2
04-02 21:22:48.761 31306-31306/com.hex.demostorage I/DemoStorage: saveImg: 判断文件:ok
04-02 21:22:48.763 31306-31306/com.hex.demostorage I/DemoStorage: externalSave: saveImg:true
04-02 21:22:48.787 31306-31306/com.hex.demostorage I/DemoStorage: externalSave: 结束
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
记录学习,记录成长!
一起学Android之Storage的更多相关文章
- Android开发学习之路-该怎么学Android(Service和Activity通信为例)
在大部分地方,比如书本或者学校和培训机构,教学Android的方式都基本类似,就是告诉先上原理方法,然后对着代码讲一下. 但是,这往往不是一个很好的方法,为什么? ① 学生要掌握这个方法的用途,只能通 ...
- 菜鸟学Android编程——简单计算器《一》
菜鸟瞎搞,高手莫进 本人菜鸟一枚,最近在学Android编程,网上看了一些视频教程,于是想着平时手机上的计算器应该很简单,自己何不尝试着做一个呢? 于是就冒冒失失的开撸了. 简单计算器嘛,功能当然很少 ...
- 学Android开发,入门语言java知识点
学Android开发,入门语言java知识点 Android是一种以Linux为基础的开源码操作系统,主要使用于便携设备,而linux是用c语言和少量汇编语言写成的,如果你想研究Android,就去学 ...
- DoNet屌丝学Android(一)——Android开发准备工作 & No HelloWord & (真机)调试
先乱扯淡一下吧,本人一.net屌丝,手持Android 4.2.2手机,Win7 x64本本,闲来无聊学习一下Android的开发,至于要开发啥玩意目前没有什么想法,就是想学学,搞不好是三分热度也有可 ...
- 从头学Android系列
从头学Android系列 http://blog.csdn.net/worker90/article/category/888358
- 学Android开发 这19个开发工具助你顺风顺水
学Android开发 这19个开发工具助你顺风顺水 要想快速开发一个Android应用,通常会用到很多工具,巧妙利用这些工具,能让我们的开发工作事半功倍,节省大量时间,下面大连Android开发培训小 ...
- 一步一步学android控件(之十五) —— DegitalClock & AnalogClock
原本计划DigitalClock和AnalogClock单独各一篇来写,但是想想,两个控件的作用都一样,就和在一起写一篇了. DegitalClock和AnalogClock控件主要用于显示当前时间信 ...
- 一步一步学android控件(之十六)—— CheckBox
根据使用场景不同,有时候使用系统默认的CheckBox样式就可以了,但是有时候就需要自定义CheckBox的样式.今天主要学习如何自定义CheckBox样式.在CheckBox状态改变时有时需要做一些 ...
- 【视频】零基础学Android开发:蓝牙聊天室APP(四)
零基础学Android开发:蓝牙聊天室APP第四讲 4.1 ListView控件的使用 4.2 BaseAdapter具体解释 4.3 ListView分布与滚动事件 4.4 ListView事件监听 ...
随机推荐
- c语言,数据结构,链表的一些操作总结
下面是自己的一些学习操作以及总结,能用我会很开心,有不足之处,欢迎大家提出宝贵的意见! c语言链表是一种基本的数据结构,与顺序表一样属于线性表,但是顺序表在内存中的存储单元是连续的,这样就对内存的要求 ...
- GPU渲染流水线的简单概括
GPU流水线 主要分为两个阶段:几何阶段和光栅化阶段 几何阶段 顶点着色器 --> 曲面细分着色器(可选)----->几何着色器(可选)----->裁剪-->屏幕 ...
- PAT1049:Counting Ones
1049. Counting Ones (30) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue The tas ...
- HTTP多线程下载+断点续传(libcurl库)
目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.lib ...
- SSM-SpringMVC-04:SpringMVC深入浅出理解HandleMapping(源码刨析)
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 先从概念理解,从中央调度器,携带参数request,调度到HandleMapping处理器映射器,处理器映射器 ...
- WPF MVVM UI分离之《交互与数据分离》
在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架. 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下面的问题: 删除操作,假如需要先执行一部分数据的处理,然后删除界 ...
- call(),apply()方法解析(一)
1.call()和apply()的作用是改变this指向,区别是传参列表不同(前者连续参数,后者为参数数组) 2.方法定义: function.apply(thisObj[, argArray]) f ...
- spring security4.2.2的maven配置+spring-security配置详解+java源码+数据库设计
最近项目需要添加权限拦截,经讨论决定采用spring security4.2.2!废话少说直接上干货! 若有不正之处,请谅解和批评指正,不胜感激!!!!! spring security 4.2.2文 ...
- 搭建 vue2 单元测试环境(karma+mocha+webpack3)
从网上找了很多例子关于单元测试,都是如何新建项目的时候的添加单元测试,用vue-cli中怎么添加,但是我的项目已经生成了,不能再一次重新初始化,这时如何添加单元测试,这里面遇到了好多坑,写在这里记录一 ...
- dqname_widnows.go
package nsqd // On Windows, file names cannot contain colons. func getBackendName(topicName, channel ...