文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Android】数据存储

2022-06-06 14:15

关注

Content1. 保存数据到文件1.1. 内部、外部存储区1.2. 在内部存储区 (internal) 存放文件1.3. 在外部存储区 (external) 存放文件2. 使用共享首选项(Shared Preferences)3. 序列化3.1. XML序列化3.2. XML解析4. SQLite数据库4.1. SQLite的命令行操作4.2. 在SQL数据库中存放数据4.3. 数据库的事务 摘自老师文档 1. 保存数据到文件 1.1. 内部、外部存储区

Internal 内部存储区

非易失内存,保存应用的私有文件

External 外部存储区

可移除的存储媒介,world-readable 世界可读,不具有保密性

一些设备把持久的存储空间分为了 intenal 和 external 分区,所以即使没有可移除的存储媒介,也有两种存储空间,并且不管是不是可移除的,API 的行为也是一致的

app 默认安装到 Internal 存储区,可通过清单文件中的

android:installLocation
属性更改

卸载 app 时,系统会移除internal中你的app的所有文件 (Internal)
系统移除你app在通过 getExternalFilesDir() 方法取到的目录中的文件 (External)

简单来说:Internal 中的都删,External 中 public 文件保留,private 文件删除 1.2. 在内部存储区 (internal) 存放文件 内部存储区目录通常为
data/data/app_name/
对于内部存储区的文件的读写,不需要向系统申请权限

. 打开目录

获取合适的目录作为 File 对象
getFilesDir() 		返回一个代表 internal 目录的 File 对象
getDir(name,mode) 	在 internal 目录中创建或者打开一个目录
getCacheDir() 		返回一个用于存放你的 app 临时 缓存文件的 internal 目录
File file = new File(getApplicationContext().getFilesDir(), filename);

. 写文件

文件设置为 MODE_PRIVATE 后,别的 app 就无法存取你的内部存储 主要用
fos.write(content.getBytes());
将字符串 content 写入文件
public void save(View view) {
     EditText et_input = (EditText) findViewById(R.id.et_input);
     String content = et_input.getText().toString();
     String filename = "data.txt";
     FileOutputStream fos;
     try {
         fos = openFileOutput(filename, Context.MODE_PRIVATE);
         fos.write(content.getBytes());
         fos.close();
         Toast.makeText(this, getString(R.string.success), Toast.LENGTH_SHORT).show();
     } catch (IOException e) {
         e.printStackTrace();
     }
 }

. 写一个缓存文件

public File getTempFile(Context context, String url) {
    File file = null;
    try {
        String filename = Uri.parse(url).getLastPathSegment();	//从 URL 中提取文件名
        assert filename != null;	//断言,这里为 true 后边的语句才会执行
        file = File.createTempFile(filename, null, context.getCacheDir());
        //在指定目录中创建前缀为 filename,后缀为 null 的空文件
    } catch (IOException e) {
        e.printStackTrace();
    }
    return file;
}

. 打开一个已存在的文件

主要,读文件内容到 buffer 中
byte[] buffer = new byte[fis.available()];      //测试 byte 数组的大小
fis.read(buffer);
content = new String(buffer);
还有
//按字符读
int ch;
ch = fis.read();
while(ch != -1) {
	fileContent += (char)ch;
	ch = fis.read();
}
详细
public void getInfo(View view) {
    String filename = "data.txt";
    FileInputStream fis;
    EditText et_input = (EditText) findViewById(R.id.et_input);
    String content = null;
    try {
        fis = openFileInput(filename);
        byte[] buffer = new byte[fis.available()];      //测试 byte 数组的大小
        fis.read(buffer);
        content = new String(buffer);
        et_input.setText(content);
        fis.close();
        Toast.makeText(this, getString(R.string.success), Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
1.3. 在外部存储区 (external) 存放文件 外部存储区分为 可移除 (存储介质) 和 不可移除 (内部分区) 两种
存储介质:如SD卡
内部分区:设备将内部内存分配做外部存储 所有应用都能读取放在 外部存储 的文件,并且用户可以移除 可以存储两种类型的文件
1.public文件 属于公共,卸载 app 无影响
2.private 文件,属于该 app,卸载 app 会删除这些文件

. 获取外部存储的访问权限

写权限隐含读权限 读取 私有文件 无需权限

在 manifest 中声明权限


. 校验外部存储可用

因为外部存储可能不可用 例如移除SD卡 检验是否可写
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}
检验是否可读
和可写一样,不过 if 语句中多了
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)

. 保存文件到公共目录

用 getExternalStoragePublicDirectory 方法
public File getExternalDir(String directoryName) {
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), directoryName);
	//File file = Environment.getExternalStoragePublicDirectory(directoryName);
    if (!file.exists()) {
    	if(!file.mkdirs()) {
        	Log.e(LOG_TAG, "Directory not created");
    	}
    }
    return file;
}
外部目录中若包含
.nomedia
空文件,媒体扫描程序就无法读取你的媒体文件 可用
file.getAbsolutePath()
获取文件绝对路径 (外部存储位置)

. 保存应用私有文件

用 getExternalFilesDir 方法创建目录
public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory.
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e("LOG_TAG", "Directory not created");
    }
    return file;
}
读写 私有文件 无需权限

注:不管 public 还是 private ,用类似 DIRECTORY_PICTURES 这样的 API 常量来创建能够确保文件被系统正确对待

若设备同时有两个外部存储目录,需用 getExternalFilesDirs 方法同时访问两个位置
返回的 File 数组中,第一条认为是主存储,满了或不可用才会用下一个

. 保存缓存文件

用 getExternalCacheDir 方法,同上,有 getExternalCacheDirs 方法 通常需要手动删除 为节省文件空间并保持应用性能,你应该在应用的整个生命周期内仔细管理您的缓存文件并移除其中不再需要的文件

. 查询剩余空间

方法 getFreeSpace 或 getTotalSpace

. 删除文件

文件的 delete 方法 internal storage 中的文件还可以这样删
myContext.deleteFile(fileName);
2. 使用共享首选项(Shared Preferences) 一个Shared Preferences对象指向一个保存键值对的文件,并且提供了简单的方法来读写它们 SharedPreferences 类提供了一个通用框架,能够保存和检索原始数据类型的永久性键值对 用来保存任何原始数据 (基本类型),该数据会跨多个用户会话永久保留

. 获取Shared Preferences的handle (创建)

getSharedPreferences 通过名字区分 getPreferences 只需一个共享 preference 文件时
Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences(
        getString(R.string.preference_file_key), Context.MODE_PRIVATE);
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

. 写入Shared Preferences

edit 获取 editor,写入用 putString 等,提交用 commit 方法
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(getString(R.string.saved_high_score), newHighScore);
editor.commit();

. 从Shared Preferences中读取数据

用 getString、getInt 方法
int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
int highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);

. 例 保存登录账号密码
即,关闭应用后再次打开会

工具类
public class Utils {
	public static boolean saveUserInfo(Context context, String account, String password) {
		SharedPreferences sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE);
		SharedPreferences.Editor editor = sharedPreferences.edit();
		editor.putString("username", account);
		editor.putString("password", password);
		return editor.commit();
	}
	public static Map getUserInfo(Context context) {
		SharedPreferences sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE);
		Map userMap = new HashMap();
		userMap.put("username", sharedPref.getString("username", null));	//若取不到,null作为默认值取出
		userMap.put("password", sharedPref.getString("password", null));
		return userMap;
	}
}
初始化显示
Map userInfo = Utils.getUserInfo(this);
if(userInfo != null) {
	etAccount.setText(userInfo.get("username"));
	etPassword.setText(userInfo.get("password"));
}
登录并保存
public void login(View view) {
	String number = etAccount.getText().toString().trim();
	String password = etPassword.getText().toString();
	if(TextUtils.isEmpty(number)) {		//系统提供的类 TextUtils
		Toast.makeText(this, "请输入账号", Toast.LENGTH_SHORT).show();
		return;
	}
	if(TextUtils.isEmpty(password)) {
		Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
		return;
	}
	Toast.makeText(this, "登陆成功", Toast.LENGTH_SHORT).show();
	boolean isSaveSuccess = Utils.saveUserInfo(this, number, password);
	if(isSaveSuccess) {
		Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
	} else {
		Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
	}
}
3. 序列化 序列化是将对象状态转换为可保存或传输的格式的过程,可将对象序列化成不同的格式 3.1. XML序列化 把对象的成员变量转化为XML格式,用Xml序列化器(XmlSerializer类),序列化之后的对象以XML文件的形式保存

. XML序列化过程描述

1.创建文件
2.打开文件输出流
3.创建相应的序列化器
File file = new File(Environment.getExternalStorageDirectory(),"Person.xml");
FileOutputStream fos = new FileOutputStream(file);
XmlSerializer serializer = Xml.newSerializer();
然后
1.设置文件编码方式:serializer.setOutput(fos,"utf-8");
2.写入xml文件的开始标签:serializer.startDocument("utf-8",true); 第一个参数设置文档的编码格式,第二个参数设置是否是一个独立的文档,一般设置为true
3.依次写入各元素 (如果有多个元素则可以使用迭代的方式写入,如果标签是嵌套的,则在写入顺序上也是嵌套的):
a) 写入开始标签:serializer.startTag(null,"Persons"); 命名空间,没有可以用null;标签名
b) 如果该标签有属性:serializer.attribute(null,"id",1); 命名空间;属性名;属性值
c) 写入元素内容:serializer.text(person.getName()); 该参数为实例对象中的某个属性值
d) 写入结束标签:serializer.endTag(null,"Persons"); 命名空间,一般为null;结束标签的标签名
4.文档的写入结束:serializer.endDocument()
5.通过 serializer.flush() 将流写入文件中
最后,关闭输出流:fos.close()
3.2. XML解析 DOM解析 只能解析较小的文件 内存消耗大 SAX解析 逐行扫描,可以解析超大的 速度快且占用内存少,不过只能读无法增删改XML数据 PULL解析 和SAX差不多 是Android内置的,最常用

. PULL解析过程

1.创建解析器
Xml.newPullParser() 或 XmlPullParserFactory.newInstance().newPullParser()
2.设置解析器的 xml 来源
abstract void setInput(Reader in) 该方法的源为字符流
abstract void setInput(InputStream inputStream, String inputEncoding) 字节流;编码
3.获取当前事件类型,解析器状态
parser.getEventType()
4.循环处理 xml
在循环体中根据解析器状态决定操作,然后通过调用解析器的next()方法获取下一个事件,并转入下一次循环 4. SQLite数据库 SQLite 是一款轻型的数据库,遵守 ACID,占用资源很低,处理速度快于 Mysql 4.1. SQLite的命令行操作 命令行工具 sqlite3,用于输入和执行 SQL 命令
首先打开虚拟机,
adb shell
sqlite3 数据库名		//打开或创建数据库
//若省略数据库名,则创建临时数据库,退出时删除该库
点命令,是一类由 sqlite3 直接执行的系统命令
点命令:1.命令名和点之间没有空白符 2.必须在同一行 3.不能在普通SQL语句中 4.不接受注释
.databases 列出数据库
.tables 列出数据库中的表
chcp 65001
把控制台的字符编码切换为
utf-8
的编码 4.2. 在SQL数据库中存放数据

. 定义 schema 与 contract

schema ,一种数据库组织结构的正式声明. contract 类,一些常量的容器,它用一种系统化并且自动生成文档的方式显式指定 schema 的布局.
实现 BaseColumns 接口有助于数据库和 framework 相容.

. 使用SQL Helper创建数据库

在后台线程中调用比较耗时的 getReadableDatabase 和 getWriteableDatabase

实现 SQLiteOpenHelper 类的例子

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";
    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}
//实例化
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

注:其中 onCreate 方法并没有实际创建或打开数据库,实际是在调用 getReadableDatabase 或 getWriteableDatabase 时.

. 对数据库中的数据进行操作

首先使用 helper 获取 SQLiteDatabase 类对象
SQLiteDatabase db = helper. getWritableDatabase();

. 把信息放入数据库

public long insert,返回插入行的ID
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);
long newRowId;
newRowId = db.insert(FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values);

. 从数据库中读取信息

public Cursor query,返回 Cursor 对象,其中存放查询的结果集
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );
//cursor 起始位置在 -1 处
cursor.moveToFirst();			//将读取点放在入口位置,read position
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);
List itemIds = new ArrayList();
while(cursor.moveToNext()) {
  long itemId = cursor.getLong(
   	cursor.getColumnIndexOrThrow(FeedEntry._ID));
	itemIds.add(itemId);
}
cursor.close();

. 从数据库中删除信息

public int delete,返回被删的行数
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };
db.delete(table_name, selection, selectionArgs);

. 更新一个数据库

public int update,返回受影响的行数
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);
4.3. 数据库的事务

所谓事务,就是针对数据库的一组操作,具有原子性
若有语句没能成功执行,则已执行的语句发生回滚

   db.beginTransaction();
   try {
     ...
     db.setTransactionSuccessful();
   } finally {
     db.endTransaction();
   }

End.

Earnest~ 原创文章 95获赞 190访问量 2万+ 关注 私信 展开阅读全文
作者:Earnest~


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯