安卓的存储方式有5种,分别为:
- SharedPrefences;
- SQLite数据库
- 文件存储
- 网络存储
- ContentProvider
1. 数据存储的类型和存放位置
应用程序一般有一下几种类型:
file-普通的文件存储
database-数据库文件(.db文件)
sharedPreference-配置数据(.xml文件)
cache-图片缓存文件
应用数据的存放位置:
com.xxx.xxx 为自定义的包名
/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卡的文件(开发者自定义的)
2. SharedPreferences存储数据
1)使用范围
Android 提供了一个轻量级的存储类 SharedPreferences
,使用 XML
格式保存一些键值对数据
当我们的应用想要保存用户的一些偏好参数,比如是否自动登陆,是否记住账号密码,是否在 Wifi
下才能 联网等相关信息,我们这些信息为用户的偏好 设置
2)SharedPrefences 的使用步骤
首先实例化一个
SharedPreferences
对象SharedPreferences sp = mContext.getSharedPreferences("filename", Context.MODE_PRIVATE);
mContext
就是上下文,如果在Activity
可以使用Activity.this
或者getApplicationContext()
方法获得
filename
就是要保存的文件名,不要加上.xml
扩展名,因为系统自己会加上
Context.MODE_PRIVATE
私有的文件模式,一般都用Context.MODE_PRIVATE
调用
sp.edit()
获取sp
对象的写编辑器SharedPreferences.Editor editor = sp.edit();
调用
editor.putXxxx()
写数据操作 ,Xxx
表示类型editor.putString("username","imyufei")
调用
editor.commit()
提交和保存数据调用
sp.getXxxx()
去处Xxx
类型的数据调用
sp.remove(key)
可以删除key
对应的数据
其中文件读取的模式有4种, 其中2 种是废弃的:
模式 | 说明 |
---|---|
Context.MODE_PRIVATE | 默认的操作模式,表示该文件是私有文件,只能够被应用本身访问,写入的内容会覆盖原有的数据 |
Context.MODE_APPEND | 会检查文件是否存在,如果存在则把内容追加到文件末尾,否则新建一个文件进行写 |
Context.MODE_WORLD_READABLE 废弃 | 当前文件可以被其它 APP 读取 |
Context.MODE_WORLD_WRITEABLE废弃 | 当前文件可以被其它 APP 写入 |
这些模式可以使用 竖线(|) 叠加,比如
openFileOutput("site.txt",Context.MODE_WORLD_READABLE|MODE_WORLD_WRITEABLE)
Editor有如下主要重要方法:
- SharedPreferences.Editor clear():清空SharedPreferences里所有数据
- SharedPreferences.Editor putXxx(String key , xxxvalue): 向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据
- SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项
- boolean commit(): 当Editor编辑完成后,使用该方法提交修改
3)具体实现例子
1. 自定义一个帮助读取的类
package com.example.newdemo.storagefile;
import android.content.Context;
import android.content.SharedPreferences;
public class SharedPrehelper {
private Context context;
public SharedPrehelper(Context context){
this.context = context;
}
public void save(String key, String value){
// 实例化一个 sp 对象
SharedPreferences sharedPreferences = context.getSharedPreferences("my_sp", Context.MODE_PRIVATE);
// 调用 sp.edit() 获取 sp 对象的写编辑器
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, value);
editor.commit();
}
// 获取数据
public String read(String key){
SharedPreferences sharedPreferences = context.getSharedPreferences("my_sp", Context.MODE_PRIVATE);
return sharedPreferences.getString(key, "");
}
}
2. 设置主activity
package com.example.newdemo.storagefile;
import androidx.appcompat.app.AppCompatActivity;
import android.app.backup.SharedPreferencesBackupHelper;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.newdemo.R;
import com.example.newdemo.util.ToastManager;
public class SharedPreferencesActivity extends AppCompatActivity {
private Button btn_sp;
private EditText sp_edit;
private Context context;
private SharedPrehelper sharedPrehelper;
private TextView sp_textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);
btn_sp = findViewById(R.id.btn_sp);
sp_edit = findViewById(R.id.sp_edit);
sp_textview = findViewById(R.id.sp_textview);
context = getApplicationContext();
sharedPrehelper = new SharedPrehelper(context);
// 保存输入的数据
btn_sp.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
String value = sp_edit.getText().toString();
sharedPrehelper.save("name", value);
ToastManager.showMsg(context, "数据已经保存了---");
}
});
}
@Override
protected void onStart() {
super.onStart();
// 获取
String value = sharedPrehelper.read("name");
sp_textview.setText(value);
ToastManager.showMsg(context, "textView 在显示--");
}
}
找到保存的文件:
在 /data/data/com.xxx.xxx/shared_prefs - 应用内配置文件
Android 4.2 以后已经不能读取其它 APP 的 SharedPreference,如需要读取其他app 文件,需使用ContentProvider
4)自定义一个帮助类
SharedPreference
都要自行实例化 SharedPreference
相关的类,会比较麻烦
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Map;
public class SPHelper {
/**
* 保存在手机里的SP文件名
*/
private String FILE_NAME;
public SPHelper(String FILE_NAME){
this.FILE_NAME= FILE_NAME;
}
/**
* 保存数据
*/
public static void put(Context context, String key, Object obj) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (obj instanceof Boolean) {
editor.putBoolean(key, (Boolean) obj);
} else if (obj instanceof Float) {
editor.putFloat(key, (Float) obj);
} else if (obj instanceof Integer) {
editor.putInt(key, (Integer) obj);
} else if (obj instanceof Long) {
editor.putLong(key, (Long) obj);
} else {
editor.putString(key, (String) obj);
}
editor.commit();
}
/**
* 获取指定数据
*/
public static Object get(Context context, String key, Object defaultObj) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
if (defaultObj instanceof Boolean) {
return sp.getBoolean(key, (Boolean) defaultObj);
} else if (defaultObj instanceof Float) {
return sp.getFloat(key, (Float) defaultObj);
} else if (defaultObj instanceof Integer) {
return sp.getInt(key, (Integer) defaultObj);
} else if (defaultObj instanceof Long) {
return sp.getLong(key, (Long) defaultObj);
} else if (defaultObj instanceof String) {
return sp.getString(key, (String) defaultObj);
}
return null;
}
/**
* 删除指定数据
*/
public static void remove(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
editor.commit();
}
/**
* 返回所有键值对
*/
public static Map<String, ?> getAll(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
Map<String, ?> map = sp.getAll();
return map;
}
/**
* 删除所有数据
*/
public static void clear(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
/**
* 检查key对应的数据是否存在
*/
public static boolean contains(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
return sp.contains(key);
}
}
3. SQLite存储数据
SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。现在的主流移动设备像Android、iPhone等都使用SQLite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到SQLite来存储我们大量的数据,所以我们就需要掌握移动设备上的SQLite开发技巧
创建一个帮助类
public class MsDBOpenHelper extends SQLiteOpenHelper {
public MsDBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,
int version) {super(context, name, factory, version); }
@Override
//数据库第一次创建时被调用
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE lang(id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(64))");
}
//软件版本号发生改变时调用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
数据库的增删改查的例子实现:
xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".storagefile.SharedPreferencesActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入编程语言"
android:id="@+id/sql_edit"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入需要删除的id"
android:id="@+id/sql_id"/>
<LinearLayout
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="插入"
android:id="@+id/sql_insert"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="查询"
android:id="@+id/sql_query"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="修改"
android:id="@+id/sql_modify"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="删除"
android:id="@+id/sql_delete"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="操作结果"
android:id="@+id/sp_result"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sp_resultshow"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="输出表的数据: "
android:id="@+id/sp_data"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sp_datashow"/>
</LinearLayout>
java代码实现
package com.example.newdemo.storagefile;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.example.newdemo.R;
import com.example.newdemo.util.ToastManager;
public class SqliteActivity extends AppCompatActivity implements View.OnClickListener{
private Button sql_insert;
private Button sql_query;
private Button sql_modify;
private Button sql_delete;
private EditText sql_edit;
private EditText sql_id;
private TextView sp_resultshow;
private TextView sp_datashow;
private SQLiteDatabase db;
private MyDBopenHelper myDBopenHelper;
private StringBuilder sb;
private Context context;
private int i = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);
context = SqliteActivity.this;
myDBopenHelper = new MyDBopenHelper(context, "my_database.db", null, 1);
bindView();
queryAll();
}
@Override
public void onClick(View v) {
db = myDBopenHelper.getWritableDatabase();
switch(v.getId()){
// 插入数据
case R.id.sql_insert:
ContentValues value = new ContentValues();
value.put("name", sql_edit.getText().toString());
db.insert("lang", null, value);
sp_resultshow.setText("插入成功");
ToastManager.showMsg(context, "插入数据---");
break;
case R.id.sql_delete:
// 依据id 进行删除
db.delete("lang", "id = ?", new String[]{sql_id.getText().toString()});
sp_resultshow.setText("删除成功");
ToastManager.showMsg(context, "删除数据---");
break;
case R.id.sql_query:
// 依据id 进行查询
sb = new StringBuilder();
Cursor cursor = db.query("lang", null, "id = ?",
new String[]{sql_id.getText().toString()}, null, null, null);
if (cursor.moveToFirst()){
do{
int pid = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
sb.append("id " + pid + ": " + name +'\n');
}while (cursor.moveToNext());
}
cursor.close();
sp_datashow.setText(sb.toString());
break;
case R.id.sql_modify:
ContentValues contentValues = new ContentValues();
// 将更新的数据保存
contentValues.put("name", sql_edit.getText().toString());
// 更新id 值
db.update("lang", contentValues, "id = ?", new String[]{sql_id.getText().toString()});
sp_resultshow.setText("更新成功");
break;
}
queryAll();
}
// 查询数据库里所有的数据
public void queryAll(){
db = myDBopenHelper.getWritableDatabase(); // 获取数据库
sb = new StringBuilder();
Cursor cursor = db.query("lang", null, null, null, null, null, null);
if (cursor.moveToFirst()){
do {
int pid = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
sb.append("id " + pid + ": " + name +'\n');
}while (cursor.moveToNext());
}
cursor.close();
sp_datashow.setText(sb.toString());
}
public void bindView(){
sql_insert = findViewById(R.id.sql_insert);
sql_query = findViewById(R.id.sql_query);
sql_modify = findViewById(R.id.sql_modify);
sql_delete = findViewById(R.id.sql_delete);
sp_resultshow = findViewById(R.id.sp_resultshow);
sp_datashow = findViewById(R.id.sp_datashow);
sql_edit = findViewById(R.id.sql_edit);
sql_id = findViewById(R.id.sql_id);
sql_insert.setOnClickListener(this);
sql_query.setOnClickListener(this);
sql_modify.setOnClickListener(this);
sql_delete.setOnClickListener(this);
}
}
1)增加数据
insert()方法,参数一表名,参数二是在未指定添加数据的情况下给某些可为空的列自动赋值为NULL,设置为null即可,参数三是ContentValues对象。
SQLiteDatabase db = myDBopenHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name","The Book Name");
values.put("author","chen");
values.put("pages",100);
values.put("price",200);
db.insert("Book",null,values);
2)删除数据
delete()方法,参数一是表名,参数二、三是去约束删除某一行或某几行的数据,不指定默认删除所有
SQLiteDatabase db = myDBopenHelper.getWritableDatabase();
db.delete("Book","pages> ?",new String[]{"100"});
或者直接使用sql语法
String sql = "delete from user where username='Jack Johnson'";//删除操作的SQL语句
db.execSQL(sql);//执行删除操作
3)修改数据
update()方法,参数一是表名,参数二是ContentValues对象,参数三、四是去约束更新某一行或某几行的数据,不指定默认更新所有。
SQLiteDatabase db = myDBopenHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",120);
db.update("Book",values,"name= ?",new String[]{"The Book Name"});
4)查找数据
query()方法,参数一是表名,参数二是指定查询哪几列,默认全部,参数三、四是去约束查询某一行或某几行的数据,不指定默认查询所有,参数五是用于指定需要去group by的列,参数六是对group by的数据进一步的过滤,参数七是查询结果的排序方式
SQLiteDatabase db = myDBopenHelper.getWritableDatabase();
Cursor cursor = db.query("Book",null,null,null,null,null,null);
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex("name");
String author = cursor.getString(cursor.getColumnIndex("author");
int pages = cursor.getString(cursor.getColumnIndex("pages");
double price = cursor.getString(cursor.getColumnIndex("price");
}while(cursor.moveToNext());
}
cursor.close():
5)使用SQL语句操作数据库
//添加数据
db.execSQL("insert into Book(name,author,pages,price) values(?,?,?,?) "
,new String[]{"The Book Name","chen",100,20});
//更新数据
db.execSQL("update Book set price = ? where name = ?",new String[]
{"10","The Book Name"});
//删除数据
db.execSQL("delete from Book where pages > ?",new String[]{"100"});
//查询数据
db.execSQL("select * from Book",null);
4.文件存储
文件存储是 Android 中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动的保存到文件当中的。它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,方便于之后将文件重新解析出来。
1)内部存储
注意内部存储不是内存。内部存储位于系统中很特殊的一个位置,如果你想将文件存储于内部存储中,那么文件默认只能被你的应用访问到,且一个应用所创建的所有文件都在和应用包名相同的目录下。也就是说应用创建于内部存储的文件,与这个应用是关联起来的。当一个应用卸载之后,内部存储中的这些文件也被删除。
内部存储的写入步骤:
第一, 获取文件输出流对象 FileOutputStream
第二, 使用FileOutputStream类的openFileOutput(String name, int mode)方法
第三, 调用FileOutputStream对象的write()方法写入文件
第四, 调用flush()方法,因为write()方法是写入缓冲区的,调用flush()方法将缓冲中的数据写入到文件,清空缓存
第五, close()方法用于关闭FileOutputStream
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();
}
一些方法:
- getFileDir() 方法得到的是该目录下 files 文件夹的 File 对象
- getChacheDir() 方法得到的是该目录下 cache 文件夹的 File 对象
- 直接调用ContextWrapper的 openFileOutput(String name,int mode) 也会在该目录下 files 文件夹下创建相应的文件,并且是私密的。可以修改为其他应用可访问的,通过 openFileOutput 方法的 mode 参数来完成
2)外部存储(SD卡)
外部内存的读取步骤:
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"/>
文件写操作函数
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();
}
}