一、Android数据存储方式
1、Android支持的数据存储方式:
(1)SharedPrefrence存储
(2)手机内部文件存储
(3)手机外部文件存储
(4)Sqlite数据库存储
(5)远程服务器存储
二、SharedPreferences存储
1、 说明
(1)SP存储专门用来存储一些单一的小数据
(2)存储数据的类型: boolean, float, int, long, String
(3)数据保存的路径: /data/data/packageName/shared_prefs/yyy.xml
(4)可以设置数据只能是当前应用读取, 而别的应用不可以
(5)应用卸载时会删除此数据
2、相关API
SharedPreferences: 对应sp文件的接口
context. getSharedPreferences (String name, int mode): 得到SP对象
name: 文件名(不带.xml)
mode: 生成的文件模式(是否是私有的,即其它应用是否可以访问)
Editor sp.edit() : 得到Editor对象
Xxx sp.getXxx(name,defaultValue): 根据name得到对应的数据
Editor : 能更新sp文件的接口
Editor put(name, value) : 保存一个键值对, 没有真正保存到文件中
Editor remove(name)
commit(): 提交, 数据真正保存到文件中了
存储:
//1. 得到sp对象
sp=getSharedPreferences("haitao", Context.MODE_PRIVATE);
//2. 得到editor对象
SharedPreferences.Editor editor=sp.edit();
//3. 得到输入的key/value
String key=et_main_key.getText().toString();
String value=et_main_value.getText().toString();
//4. 使用editor保存key-value
editor.putString(key,value).commit();
//5. 提示
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
读取:
//1. 得到输入的key
String key=et_main_key.getText().toString();
//2. 根据key读取对应的value
String value= sp.getString(key,"默认值");
//3. 显示
if(value!=null){
et_main_value.setText(value);
}else{
Toast.makeText(this, "value为空", Toast.LENGTH_SHORT).show();
}
三、手机内部file存储
1、说明
(1)应用运行需要的一些较大的数据或图片可以用文件保存的手机内部
(2)文件类型: 任意
(3)数据保存的路径: /data/data/projectPackage/files/
(4)可以设置数据只能是当前应用读取, 而别的应用不可以
(5)应用卸载时会删除此数据
2、 相关API
(1)读取文件
FileInputStream fis = openFileInput("logo.png");
(2)保存文件
FileOutputStream fos = openFileOutput("logo.png", MODE_PRIVATE)
(3)得到files文件夹对象
File filesDir = getFilesDir();
(4)操作asserts下的文件
得到AssetManager : context.getAssets();
读取文件: InputStream open(filename);
(5)加载图片文件
Bitmap BitmapFactory.decodeFile(String pathName) // .bmp/.png/.jpg
四、手机外部file存储
1、说明
应用运行用到的数据文件(如图片)可以保存到sd卡中
文件类型: 任意
数据保存的路径:
路径1: /storage/sdcard/Android/data/packageName/files/
路径2: /storage/sdcard/xxx/
路径1 :其它应用可以访问,应用卸载时删除
路径2 : 其它应用可以访问, 应用卸载时不会删除
必须保证sd卡挂载在手机上才能读写, 否则不能操作
2、区分getExternalStorageDirectory()和getExternalFilesDir()
(1)App专属文件 vs App独立文件
app专属文件就是那些只有该app才可以使用的文件,例如专属格式的电子书,app独立文件就是那些不依赖于某特定app的文件,例如照片。
(2)App独立文件
这类文件当我们删除应用之后,还应该保留在手机上的,例如拍照的照片,不应该随着删除应用而被删除掉。对于这类文件,Android给我们提供了特定的目录,这些目录都是以DIRECTORY开头的,例如:DIRECTORY_MUSIC , DIRECTORY_PICTURES.
访问这些文件夹有两种方式:
第一种:
File sdCard = Environment.getExternalStorageDirectory();
这个sdCard的路径为mnt/sdcard/ 即为SD卡根路径,我们可以指定访问的文件夹名
File sdCard = Environment.getExternalStorageDirectory();
File directory_pictures = new File(sdCard, "Pictures");
Log.i(TAG,"directory_pictures="+directory_pictures);
第二种:
File directory_pictures = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
Log.e(TAG, "directory_pictures="+directory_pictures);
第二种方法是一个更加方便的访问Android给我们提供好的一些公共目录的方法,第一种方式更加灵活,可以自己指定目录。
(3)App专属文件
这类文件应该是随着app删除而一起删除的,它们可以被存储在两个地方:internal storage 和 external storage 。 internal storage就是手机自带的一块存储区域,通常很小;external storage就是通常所说的SD卡,通常很大,有16GB,32GB等。
internal storage很小,所以你就应该很正确的使用它,因为SD卡有可能会被用户卸下,换成新的,所以SD卡不是任何时间都可用的,因此我们必须将一些重要的数据库文件以及一些用户配置文件存放在internal storage中。将一些大的图片或文件等缓存放到external storage中。
存储在internal storage
这是你app私有的目录,你的shared preference文件,数据库文件,都存储在这里。目录为data/data/< package name >/files/
访问方法为:
File filesDir = getFilesDir();
Log.i(TAG,"file_dir="+filesDir);
存储在external storage
这类文件不应该存在SD卡的根目录下,而应该存在mnt/sdcard/Android/data/< package name >/files/…这个目录下。这类文件应该随着App的删除而一起删除。例如一种格式的电子书,只有该app才可以打开,如果用户删除了该app,那么留下来的电子书就成为了一种无法打开的垃圾文件,所以应该随着该app一起删除掉。
获得这个路径的方法:
File externalFilesDir = getExternalFilesDir(null);
Log.i(TAG, "externalFileDir = "+externalFilesDir);
记得添加权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
这样路径就访问到了,我们注意到上面的getExternalFilesDir(null)参数传入的为null,这样默认访问的是files文件夹,我们可以指定子文件夹
File externalFilesDir = getExternalFilesDir("Caches");
Log.i(TAG, "externalFileDir = "+externalFilesDir);
有些时候我们的手机没有安装SD卡,所以我们使用前需要判断一下:
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { //SD卡已装入 }
总结
两种方式的区别在于:
1、自己创建一个文件夹用来存放文件,删除的时候系统找不到自己创建的目录,所以删除不了。
2、获取sdk路径用的是系统的这个方法getExternalStorageDirectory(),上面的用的是getExternalFilesDir()
3、其它地方都是一样的,只是路径换了。
五、Sqlite数据库存储
1、说明
应用运行需要保存一系列有一定结构的数据, 比如说公司员工信息
文件类型: .db
数据保存的路径: /data/data/projectPackage/databases/xxx.db
默认情况下其它应用不能访问, 当前应用可以通过ContentProvider提供其它应用操作
应用卸载时会删除此数据
2、Sqlite数据库客户端
3、Sqlite数据库命令行
adb shell 进入系统根目录
cd data/data/…/databases : 进入包含数据库文件的文件夹下
sqlite3 contacts2.db : 使用sqlite3命令连接指定的数据库文件, 进入连接模式
.help : 查看命令列表
.tables : 查看所有表的列表
执行insert/delete/update/select语句
.exit : 退出数据库连接模式
Ctrl + C : 直接退出sell模式
4、数据类型
Sqlite支持的数据类型与Mysql相似, 常用的数据类型
INT/INTEGER : 整数
FLOAT/DOUBLE : 小数
CHAR/VARCHAR/TEXT : 字符串文本
BLOB : 文件
DATE/ DATETIME : 日期/日期时间
5、Sqlite建表
Sqlite操作数据库的sql语句基本与mysql一样, 但需要注意下面2个点:
最大的不同在于创建表时可以不用指定字段类型, Sqlite可以适时的 自动转换, 但除varchar类型外最好指定类型
Sqlite中的主键名称建议使用_id
create table employee (
_id integer primary key autoincrement, /*主键,自增长*/
name varchar, /*字符串*/
salary double, /*小数*/
birthday date /*日期, 可直接插入日期格式字符串*/
)
6、Sqlite的CRUD语句
/*插入*/
INSERT INTO employee (name,salary,birthday) VALUES('Tom', 8000, '1988-09-21');
/*删除*/
DELETE FROM employee WHERE _id=2
/*更新*/
UPDATE employee SET name='Jack',salary=salary+1000 WHERE _id=1
/*查找*/
SELECT * FROM employee where _id=3
7、相关API
SQLiteOpenHelper: 数据库操作的抽象帮助类
SQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) : 构造方法, 指定数据库文件名和版本号
abstract void onCreate(SQLiteDatabase db) : 用于创建表
abstract void onUpgrade() : 用于版本更新
SqliteDatabase getReadableDatabase() : 得到数据库连接
8、相关API
SqliteDatabase: 代表与数据库的连接的类
long insert(): 用于执行insert SQL, 返回id值
int update(): 用于执行update SQL
int delete(): 用于执行delete SQL
Cursor query(): 用于执行select SQL, 返回包含查询结果数据的Cursor
void execSql(sql) : 执行sql语句
beginTransaction(): 开启事务
setTransactionSuccessful(): 设置事务是成功的
endTransaction(): 结束事务, 可能提交事务或回滚事务
openDatabase(String path, CursorFactory factory, int flags): 得到数据库连接
9、相关API
Cursor : 包含所有查询结果记录的结果集对象(光标,游标)
int getCount() : 匹配的总记录数
boolean moveToNext() : 将游标移动到下一条记录的前面
Xxx getXxx(columnIndex) : 根据字段下标得到对应值
int getColumnIndex(columnname): 根据字段名得到对应的下标
/**
* SQLite数据库帮助类
*/
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context, int version){
super(context,"haitao",null,version);
}
/**
* 什么时候才会创建数据库文件?
* 1). 数据库文件不存在
* 2). 连接数据库
*
* 什么时候调用?
* 当数据库文件创建时调用(1次)
* 在此方法中做什么?
* 建表
* 插入一些初始化数据
*/
@Override
public void onCreate(SQLiteDatabase db) {
//建表
String sql="create table person(_id integer primary key autoincrement, name varchar,age int)";
db.execSQL(sql);
//插入一些初始化数据
db.execSQL("insert into person (name, age) values ('Tom1', 11)");
db.execSQL("insert into person (name, age) values ('Tom2', 12)");
db.execSQL("insert into person (name, age) values ('Tom3', 13)");
}
//当传入的版本号大于数据库的版本号时调用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.e("TAG", "DBHelper onUpgrade()");
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/*
* 创建库
*/
public void testCreateDB(View v){
DBHelper dbHelper=new DBHelper(this,1);
//获取连接
SQLiteDatabase database=dbHelper.getReadableDatabase();
Toast.makeText(this,"数据库创建成功",Toast.LENGTH_SHORT).show();
}
/*
* 更新库
*/
public void testUpdateDB(View v){
DBHelper dbHelper=new DBHelper(this,2);
SQLiteDatabase database=dbHelper.getReadableDatabase();
Toast.makeText(this,"数据库更新成功",Toast.LENGTH_SHORT).show();
}
/*
* 添加记录
*/
public void testInsert(View v){
DBHelper dbHelper=new DBHelper(this,2);
SQLiteDatabase database=dbHelper.getReadableDatabase();
//2. 执行insert insert into person(name, age) values('Tom', 12)
ContentValues value=new ContentValues();
value.put("name","Tom");
value.put("age",12);
long insertId=database.insert("person",null,value);
//3. 关闭
database.close();
// 4. 提示
Toast.makeText(this,"添加记录成功"+insertId,Toast.LENGTH_SHORT).show();
}
public void testUpdate(View v){
DBHelper dbHelper=new DBHelper(this,2);
SQLiteDatabase database=dbHelper.getReadableDatabase();
//执行update update person set name=Jack, age=13 where _id=4
ContentValues value=new ContentValues();
value.put("name","Jack");
value.put("age",13);
int updateCount=database.update("person",value,"_id=?",new String[]{"4"});
database.close();
Toast.makeText(this,"修改记录成功"+updateCount,Toast.LENGTH_SHORT).show();
}
public void testDelete(View v){
// 1. 得到连接
DBHelper dbHelper=new DBHelper(this,2);
SQLiteDatabase database=dbHelper.getReadableDatabase();
// 2. 执行delete delete from person where _id=2
int deleteCount=database.delete("person","_id=2",null);
// 3. 关闭
database.close();
// 4. 提示
Toast.makeText(this,"删除记录成功"+deleteCount,Toast.LENGTH_SHORT).show();
}
public void testQuery(View v){
// 1. 得到连接
DBHelper dbHelper=new DBHelper(this,2);
SQLiteDatabase database=dbHelper.getReadableDatabase();
// 2. 执行query select * from person
Cursor cursor=database.query("person",null,null,null,null,null,null);
//cursor = database.query("person", null, "_id=?", new String[]{"3"}, null, null, null);
//得到匹配的总记录数
int count=cursor.getCount();
while(cursor.moveToNext()){
//id
int id=cursor.getInt(0);
//name
String name=cursor.getString(1);
//age
int age=cursor.getInt(2);
//int age = cursor.getInt(cursor.getColumnIndex("age"));
Log.e("tag", id+"-"+name+"-"+age);
}
// 3. 关闭
cursor.close();
database.close();
// 4. 提示
Toast.makeText(this, "count=" + count, Toast.LENGTH_SHORT).show();
}
/*
* 测试事务处理
* update person set age=16 where _id=1
* update person set age=17 where _id=3
*
* 一个功能中对数据库进行的多个操作: 要就是都成功要就都失败
* 事务处理的3步:
* 1. 开启事务(获取连接后)
* 2. 设置事务成功(在全部正常执行完后)
* 3. 结束事务(finally中)
*/
public void testTransaction(View v){
// 1. 得到连接
SQLiteDatabase database=null;
try{
DBHelper dbHelper=new DBHelper(this,2);
database=dbHelper.getReadableDatabase();
//1. 开启事务(获取连接后)
database.beginTransaction();
//执行update update person set age=16 where _id=1
ContentValues value=new ContentValues();
value.put("age",16);
int updateCount =database.update("person",value,"_id=?",new String[]{"1"});
//出了异常
boolean flag = true;
if(flag) {
throw new RuntimeException("出异常啦!!!");
}
//执行update update person set age=17 where _id=3
value = new ContentValues();
value.put("age", 17);
int updateCount2 = database.update("person", value , "_id=?", new String[]{"3"});
Log.e("TAG", "updateCount2="+updateCount2);
//2. 设置事务成功(在全部正常执行完后)
database.setTransactionSuccessful();
}catch(Exception e){
e.printStackTrace();
Toast.makeText(this, "出异常啦!!!", Toast.LENGTH_SHORT).show();
}finally {
//3. 结束事务(finally中)
if(database!=null){
database.endTransaction();
database.close();
}
}
}
}
<LinearLayout
android:layout_width="368dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="创建数据库"
android:onClick="testCreateDB"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="更新数据库"
android:onClick="testUpdateDB"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="插入记录"
android:onClick="testInsert"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="更新记录"
android:onClick="testUpdate"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删除记录"
android:onClick="testDelete"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查询记录"
android:onClick="testQuery"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试事务处理"
android:onClick="testTransaction"/>
</LinearLayout>