Android实习生 —— 数据存储与共享
![](https://img.haomeiwen.com/i4821697/aef2ce13dc8a3b0e.png)
目录
前言
一、使用SharedPreferences存储数据
1、适用范围
2、核心原理
3、app内部实现数据存储(Demo)
4、共享其他应用的SharedPreferences
5、关于android:sharedUserId
6、SharedPreferences总结
二、文件存储数据
1、功能介绍
2、存储方式
3、使用内部存储(Demo)
4、使用外部存储(Demo)
三、SQLite数据库存储数据
1、简介及特点
2、实现原理
3、通过SQLiteDatabase创建(Demo)
4、通过继承SQLiteOpenHelper类创建
5、两种方式的联系
四、使用ContentProvider存储数据
与SQLite数据库联系
详见:[Android实习生 —— 四大组件之ContentProvider]
五、网络存储数据
【附录】
Demo
前言
Android提供了5种方式来让用户保存持久化应用程序数据。
**
① 使用SharedPreferences存储数据
② 文件存储数据
③ SQLite数据库存储数据
④ 使用ContentProvider存储数据
⑤ 网络存储数据 **
我们可以根据需求选择对应的方式。文章根据相关Demo讲述各种方式的用法及优缺点说明,在文章末尾附录会有相关Demo的下载 。
通过以上方式还可以实现数据的在不同app间的数据共享。
一、使用SharedPreferences存储数据
1、适用范围
保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口令密码等。
2、核心原理
-
保存基于XML文件存储的key-value键值对数据。
-
通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data/<package name>/shared_prefs目录下。
-
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。
-
SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下:
Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。
Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写。
//以上三种写法均已过时,可以直接用数字代替,
//Context.MODE_PRIVATE = 0
//Context.MODE_WORLD_READABLE = 1
//Context.MODE_WORLD_WRITEABLE = 2
- Editor有如下主要重要方法:
SharedPreferences.Editor clear()
//清空SharedPreferences里所有数据
SharedPreferences.Editor putXxx(String key , xxx value):
//向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据
SharedPreferences.Editor remove()
//删除SharedPreferences中指定key对应的数据项
boolean commit()
//当Editor编辑完成后,使用该方法提交修改
3、app内部实现数据存储(Demo)
- 通过点击“保存用户名”,对登陆成功的用户进行用户名键值对的保存,让用户下次启动app时自动填充用户名。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText userName,userPass;
private CheckBox checkBox;
private Button ok,cancel;
private SharedPreferences pref;
private SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化SharedPreferences 及相关组建。
init();
//3、取出userInfo中的数据。
String name00=pref.getString("userName",null);
if (name00==null) {
checkBox.setChecked(false);
}else {
checkBox.setChecked(true);
//4、将取到的用户名赋给用户名编辑框。
userName.setText(name00);
}
}
private void init() {
userName = (EditText) findViewById(R.id.userName);
userPass = (EditText) findViewById(R.id.userPass);
checkBox = (CheckBox) findViewById(R.id.check);
ok = (Button) findViewById(R.id.join_btn);
cancel = (Button) findViewById(R.id.cancel_btn);
ok.setOnClickListener(this);
cancel.setOnClickListener(this);
//1、获取SharedPreferences对象,并把文件名设为"userInfo"。
pref =getSharedPreferences("userInfo", MODE_PRIVATE);
//2、获取SharedPreferences内部接口Editor用来编辑userInfo。
editor = pref.edit();
}
@Override
public void onClick(View v) {
//2.1:获取用户输入的用户名密码信息。
String name = userName.getText().toString();
String pass = userPass.getText().toString();
switch (v.getId()) {
case R.id.join_btn:
if ("admin".equals(name)&&"123456".equals(pass)){
if(checkBox.isChecked()){
//2.2.1:判断成功登入并对"保存用户名"打勾之后,
//将用户名的键值对添加到文件名为"userInfo"文件中并提交。
editor.putString("userName",name);
editor.commit();
}else{
//2.2.2若没打勾,则清空并提交。
editor.remove("userName");
editor.commit();
}
Toast.makeText(this,"登陆成功",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this,"登陆失败",Toast.LENGTH_SHORT).show();
}
break;
case R.id.cancel_btn:
userName.setText(null);
userPass.setText(null);
break;
}
}
-
效果
登入成功
登入成功
第二次打开自动填充用户名。
第二次打开app
【在DDMS中依次打开data/data/<包>/shared_prefs,可以看到此文件内容】
data/data/<包>/shared_prefs
文件内容
4、共享其他应用的SharedPreferences
- 在创建SharedPreferences时,指定MODE_WORLD_READABLE模式,表明该SharedPreferences数据可以被其他程序读取。
SharedPreferences pref =getSharedPreferences("userInfo", MODE_WORLD_READABLE);
- 创建其他应用程序对应的Context上下文引用:
Context otherAppContent = null;
try {
otherAppContent = createPackageContext("com.bb.sharedpr",CONTEXT_IGNORE_SECURITY);
//com.bb.sharedpr为我们要调用数据的包名
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
- 使用其他程序的Context获取对应的SharedPreferences
SharedPreferences read = otherAppContent.getSharedPreferences("userInfo",MODE_WORLD_READABLE);
- 如果是写入数据,使用Editor接口即可,所有其他操作均和前面一致。
5、关于android:sharedUserId
-
通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,才保障了程序运行的稳定。然后在有些时候,我们自己开发了多个APK并且需要他们之间互相共享资源,那么就需要通过设置shareUserId来实现这一目的。
-
通过SharedUserId,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以通过获取上下文来互相访问任意数据. 也可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。
-
而上面的两个工程中并没有对Android:sharedUserId属性进行设置。这个属性是在查资料时看到的:意思是说,在manifest.xml里面将两个应用程序的android:sharedUserId属性设为相同的就可以对SharedPreferences文件进行写。(此处并没有验证)
6、SharedPreferences总结
- 优点
SharedPreferences对象与SQLite数据库相比显得格外轻量级,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。 - 缺点
1、其职能存储boolean,int,float,long和String五种简单的数据类型。
2、无法进行条件查询等。
【所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。】
二、文件存储数据
1、功能介绍
Android文件系统和其他平台上的类似,使用File APIs可以读写文件。这部分内容需要你已经了解了Linux文件系统的基础,并且也了解了java.io包中的标准文件输入输出APIs。
2、存储方式
所有的Android设备都有两块文件存储区域:内部和外部存储。
-
内部存储:指设备自带的非易失性存储器。
- 永远可用,因为不可以拆卸。
- 文件默认情况下只对你的app可用,是私有的,无论是用户或者是其他app都不能共享访问你的数据。
- 当用户卸载你的app时,系统会自动移除app在内部存储上的所有文件。
-
外部存储:指可拆卸的存储介质,如卫星电视SD卡。
- 不一定一直可以访问,因为用户可以拆卸外部存储设备。
- 文件是全局可读的,没有访问限制,不受你的控制。可以和其他app共享数据,用户使用电脑也可以访问在外部存储中的文件。
- 当用户卸载你的app时,只有当你把文件存储在以 getExternalFilesDir().获得的路径下时,系统才会帮你自动移除。
3、使用内部存储(Demo)
Context提供了两个方法来打开数据文件里的文件IO流
FileInputStream openFileInput(String name);
FileOutputStream openFileInput(String name , int mode);
//name参数: 用于指定文件名称,不能包含路径分隔符“/” ,
//如果文件不存在,Android 会自动创建它。
这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。
MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,
写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取。
MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
模式可以连用 比如可读可写 就写成:
MODE_WORLD_READABLE+MODE_WORLD_WRITEABLE
除此之外,Context还提供了如下几个重要的方法:
getDir(String name , int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录
getFilesDir():获取该应用程序的数据文件夹得绝对路径
fileList():返回该应用数据文件夹的全部文件
我们将实现的Demo为将输入的文字写入文件,并读取出来。
- 第一步:创建和写入一个内部存储的私有文件:
public void WriteFiles(String content){
try {
//①调用Context的openFileOutput()函数,填入文件名和操作模式,它会返回一个FileOutputStream对象。
FileOutputStream fos = openFileOutput("a.txt",
MODE_PRIVATE);
//②通过FileOutputStream对象的write()函数写入数据。
fos.write(content.getBytes());
//③FileOutputStream对象的close ()函数关闭流。
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
- 第二步:读取一个内部存储的私有文件:
public String readFiles(){
String content = null;
try {
//① 调用openFileInput( ),参数中填入文件名,会返回一个FileInputStream对象。
FileInputStream fis= openFileInput("a.txt");
StringBuilder sb = new StringBuilder();
byte [] buffer = new byte[1024];
int len = 0;
//② 使用流对象的 read()方法读取字节
while ((len=fis.read(buffer))!=-1) {
sb.append(new String(buffer, 0, len));
}
content =sb.toString();
//③ 调用流的close()方法关闭流
fis.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return content;
}
第三步:MainActivity中实现流程:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edt = (EditText) findViewById(R.id.editText1);
but = (Button) findViewById(R.id.write);
contentvalue = (TextView) findViewById(R.id.contentvalue);
but.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
WriteFiles(edt.getText().toString());
contentvalue.setText(readFiles());
}
});
}
效果
-
app效果
-
文件路径
-
文件
【注意】保存内存缓存文件
有时候我们只想缓存一些数据而不是持久化保存,可以使用getCacheDir()去创建或打开一个文件,文件的存储目录( /data/data/包名/cache )是一个应用专门来保存临时缓存文件的内存目录。
File file = this.getCacheDir();
Log.i("info", file.toString();
当设备的内部存储空间比较低的时候,Android可能会删除这些缓存文件来恢复空间,但是你不应该依赖系统来回收,要自己维护这些缓存文件把它们的大小限制在一个合理的范围内,比如1MB.当你卸载应用的时候这些缓存文件也会被移除。
4、使用外部存储(Demo)
因为内部存储容量限制,有时候需要存储数据比较大的时候需要用到外部存储,使用外部存储分为以下几个步骤:
- 第一步:添加外部存储访问限权
<!-- 在AndroidManifest.xml中加入访问SDCard的权限-->
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 第二步:检测外部存储的可用性
//获取外存储的状态
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// 可读可写
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// 可读
} else {
// 可能有很多其他的状态,但是我们只需要知道,不能读也不能写
}
- 第三步:读写数据
public class MainActivity extends Activity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView= (TextView) findViewById(R.id.tv); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录 "/sdcard"
File saveFile = new File(sdCardDir,"a.txt");
//写数据
try {
FileOutputStream fos= new FileOutputStream(saveFile);
fos.write("bobobo".getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
//读数据
try {
FileInputStream fis= new FileInputStream(saveFile);
int len =0;
byte[] buf = new byte[1024];
StringBuffer sb = new StringBuffer();
while((len=fis.read(buf))!=-1){
sb.append(new String(buf, 0, len));
}
textView.setText(sb.toString());
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
-
效果
效果
外部路径
三、SQLite数据库存储数据
1、简介及特点
-
SQLite是轻量级嵌入式数据库引擎,且只利用很少的内存就有很好的性能。
-
在我们为移动设备开发应用程序时,使用SQLite作为复杂数据、大量数据的存储引擎。
-
SQLite它是一个独立的,无需服务进程,无需安装和管理配置,支持事务处理,可以使用SQL语言的数据库。
-
支持多种开发语言,C,PHP,Perl,Java,ASP.NET,Python。
-
在Content Provider 技术中就是使用SQLite数据库来操作数据的。
2、实现原理
-
直接通过SQLiteDatabase对象来创建一个数据库。
-
或者继承SQLiteOpenHelper类封装创建和更新数据库使用的逻辑。
它们都会在ddns 的file explorer 中的data/data/<包>/databases中创建这个数据库文件。
3、通过SQLiteDatabase创建(Demo)
- 第一步:创建数据库并插入数据
private SQLiteDatabase db;
public void init() {
db = openOrCreateDatabase("user.db", MODE_PRIVATE, null);
db.execSQL("create table if not exists usertb (_id integer primary key autoincrement, name text not null , age integer not null , sex text not null )");
db.execSQL("insert into usertb(name,sex,age) values('张三','女',18)");
db.execSQL("insert into usertb(name,sex,age) values('李四','男',19)");
db.execSQL("insert into usertb(name,sex,age) values('王五','女',22)");
//查询数据库并展示
query(findViewById(R.id.query));
}
query代码【关于查询代码的详情在本小节末尾会有详细说明】
public void query(View view) {
tv_id.setText("");
tv_name.setText("");
tv_sex.setText("");
tv_age.setText("");
Cursor cursor = db.rawQuery("select * from usertb", null);
if (cursor != null) {
while (cursor.moveToNext()) {
tv_id.append("\n" + cursor.getString(cursor.getColumnIndex("_id")));
tv_name.append("\n" + cursor.getString(cursor.getColumnIndex("name")));
tv_sex.append("\n" + cursor.getString(cursor.getColumnIndex("sex")));
tv_age.append("\n" + cursor.getString(cursor.getColumnIndex("age")));
}
cursor.close();
}
// db.close();不要关闭,不然单独调用查询操作会空指针
}
效果:
![](https://img.haomeiwen.com/i4821697/661a8fd8fb07f311.png)
![](https://img.haomeiwen.com/i4821697/e1106cafc1c095d1.png)
![](https://img.haomeiwen.com/i4821697/006d8c8ea1f5724d.png)
- 第二步:实现插入数据的操作(两种方式任选其一)
- 使用insert方法
public void add(View view) {
ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据
cv.put("name","新来的");
cv.put("sex","女");
cv.put("age","18");
db.insert("usertb",null,cv);//执行插入操作
// 使用直接执行语句添加
// db.execSQL("insert into usertb(name,sex,age) values('新来的','女',18)");
query(findViewById(R.id.query));
}
- 使用execSQL方式来实现
db.execSQL("insert into usertb(name,sex,age) values('新来的','女',18)");
-
效果
添加效果
-
第三步:实现删除数据的操作
同样有2种方式可以实现
String whereClause = "name=?";//删除的条件
String[] whereArgs = {"新来的"};//删除的条件参数
db.delete("user",whereClause,whereArgs);//执行删除
使用execSQL方式的实现
String sql = "delete from usertb where name='新来的'";//删除操作的SQL语句
db.execSQL(sql);//执行删除操作
- 第四步:实现修改数据的操作(将张三改成张三三)
同上,仍是2种方式
ContentValues cv = new ContentValues();//实例化ContentValues
cv.put("name","张三三");//添加要更改的字段及内容
String whereClause = "name=?";//修改条件
String[] whereArgs = {"张三"};//修改条件的参数
db.update("usertb",cv,whereClause,whereArgs);//执行修改
使用execSQL方式的实现
String sql = "update usertb set name = '张三三' where username='张三'";//修改的SQL语句
db.execSQL(sql);//执行修改
【关于查询操作】
查询操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询形式:
db.rawQuery(String sql, String[] selectionArgs);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
各参数说明:
table:表名称
colums:表示要查询的列所有名称集
selection:表示WHERE之后的条件语句,可以使用占位符
selectionArgs:条件语句的参数数组
groupBy:指定分组的列名
having:指定分组条件,配合groupBy使用
orderBy:y指定排序的列名
limit:指定分页参数
distinct:指定“true”或“false”表示要不要过滤重复值
Cursor:返回值,相当于结果集ResultSet
最后,他们同时返回一个Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。下面是Cursor对象的常用方法:
c.move(int offset); //以当前位置为参考,移动到指定行
c.moveToFirst(); //移动到第一行
c.moveToLast(); //移动到最后一行
c.moveToPosition(int position); //移动到指定行
c.moveToPrevious(); //移动到前一行
c.moveToNext(); //移动到下一行
c.isFirst(); //是否指向第一条
c.isLast(); //是否指向最后一条
c.isBeforeFirst(); //是否指向第一条之前
c.isAfterLast(); //是否指向最后一条之后
c.isNull(int columnIndex); //指定列是否为空(列基数为0)
c.isClosed(); //游标是否已关闭
c.getCount(); //总数据项数
c.getPosition(); //返回当前游标所指向的行数
c.getColumnIndex(String columnName);//返回某列名对应的列索引值
c.getString(int columnIndex); //返回当前行指定列的值
举例
db.update("stutb", values, "_id>?", new String[]{"3"});//把id>3的性别更新成"女"
db.delete("stutb", "name like ?", new String[]{"%丰%"});//删掉名字中带有"丰"的记录
//使用游标类 进行查询
Cursor c = db.query("stutb", null, "_id>?", new String[]{"0"}, null, null, "_id");
4、通过继承SQLiteOpenHelper类创建
Android 提供了 SQLiteOpenHelper,其是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。你只要继承 SQLiteOpenHelper 类根据开发应用程序的需要,封装创建和更新数据库使用的逻辑就行了。
- 第一步:写一个子类继承SQLiteOpenHelper并复写三个方法
public class DatabaseHelper extends SQLiteOpenHelper {
/**
* @param context 上下文环境(例如,一个 Activity)
* @param name 数据库名字
* @param factory 一个可选的游标工厂(通常是 Null)
* @param version 数据库模型版本的整数
*
* 会调用父类 SQLiteOpenHelper的构造函数
*/
// public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
// super(context, name, factory, version);
// }
//这里我们直接定义数据库名字根版本号
private static final String DATABASE_NAME = "stu.db";
private static final int VERSION = 1;
private static final String TABLE_NAME = "stutb";
// 步骤2:重载构造方法
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
/**
* 在数据库第一次创建的时候会调用这个方法
*
*根据需要对传入的SQLiteDatabase 对象填充表和初始化数据。
*/
//在这里进行建表
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table if not exists stutb(_id integer primary key autoincrement,name text not null,sex text not null,age integer not null)");
db.execSQL("insert into stutb(name,sex,age)values('张三','男',18)");
db.execSQL("insert into stutb(name,sex,age)values('张四','女',20)");
}
/**
* 当数据库需要修改的时候(两个数据库版本不同),Android系统会主动的调用这个方法。
* 一般我们在这个方法里边删除数据库表,并建立新的数据库表.
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号
}
@Override
public void onOpen(SQLiteDatabase db) {
// 每次成功打开数据库后首先被执行
super.onOpen(db);
}
}
- 第二步:继承SQLiteOpenHelper之后就拥有了以下两个方法
getReadableDatabase() 创建或者打开一个查询数据库
getWritableDatabase() 创建或者打开一个可写数据库
DatabaseHelper database = new DatabaseHelper(MainActivity.this);//传入一个上下文参数
SQLiteDatabase db = null;
db = database.getWritableDatabase();
//上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就可以查询或者修改数据库。
getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。
// 其中getReadableDatabase()方法则是先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后
// 会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象。
// getWritableDatabase() 方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,
// 使用的是getWritableDatabase() 方法就会出错。
- 第三步:实现查询代码
Cursor c = db.rawQuery("select * from stutb", null);
if (c!=null) {
String [] cols = c.getColumnNames();
while (c.moveToNext()) {
for (String ColumnName : cols) {
Log.i("info ", ColumnName+":"+c.getString(c.getColumnIndex(ColumnName)));
}
}
c.close();
}
db.close();
-
效果(命令行打印)
命令行打印结果
5、两种方式的联系
-
Context.openOrCreateDatabase 与 SQLiteDatabase.openOrCreateDatabase本质上完成的功能都一样,Context.openOrCreateDatabase最终是需要调用 SQLiteDatabase.openOrCreateDatabase来完成数据库的创建的。 也就是说, SQLiteDatabase类是android上对sqlite的最底层的封装,几乎所有的对数据库的操作最终都通过这个类来实现。
-
对数据表的操作方式不同,SQLiteOpenHelper是SQLiteDatabase的帮助类。要如你的代码执行,你需要通过类似SQLiteOpenHelper的getWritableDatabase方法获取到SQLiteDatabase实例才可以。创建表直接可以在SQLiteOpenHelper的onCreate方法中做,那里通过参数你可以得到一个SQLiteDatabase的实例。
四、使用ContentProvider存储数据
与SQLite数据库联系
1.SQLiteOpenHelper是将对数据库和表的创建、插入、更新、删除操作进行了简单的封装;
2.而ContentProvider可以说是一个对外的接口,除了可以实现对SQLiteOpenHelper的封装,还可以实现对文件操作、图片操作、对象操作等实现封装;
3.在多线程中使用SQLiteOpenHelper要考虑线程同步问题,而如果使用ContentProvider的话基本不用考虑;
4.使用ContentProvider存储数据可以实现不同app之间的数据共享。
详见:
Android实习生 —— 四大组件之ContentProvider
五、网络存储数据
一、网络保存数据介绍
可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中。用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法。
本文利用LeanCloud来进行网络数据的存储。
LeanCloud是一种简单高效的数据和文件存储服务。感兴趣的可以查看网址:https://leancloud.cn/。 的例子。
二、使用方法
1、上传数据
AVObject personObject = new AVObject(TABLENAME);
personObject.put(NAME, person.name);
personObject.put(AGE, person.age);
personObject.put(INFO, person.info);
personObject.saveInBackground(new SaveCallback() {
@Override
public void done(AVException e) {
if (e == null) {
Log.v(TAG, "put data success!");
} else {
Log.v(TAG, "put data failed!error:" + e.getMessage());
}
}
});
2、读取数据
AVQuery<AVObject> avQuery = new AVQuery<>(TABLENAME);
avQuery.findInBackground(new FindCallback<AVObject>() {
@Override
public void done(List<AVObject> list, AVException e) {
if (e == null) {
Log.v(TAG, "get data success!");
String message = "";
//倒着遍历后四条上传的数据
for (int i = list.size()-1; i >=list.size()-4; i--) {
String name = list.get(i).getString(NAME);
int age = list.get(i).getInt(AGE);
String info = list.get(i).getString(INFO);
message += "name:" + name + ",age:" + age + ",info:" + info + ".\n";
}
textView.setText(message);
}
}
});
三、小案例
1、导入jar包
![](https://img.haomeiwen.com/i4821697/8842d8f82c4df9f7.png)
2、添加NetworkDBManager类
package com.zhangmiao.datastoragedemo;
import android.util.Log;
import android.widget.TextView;
import com.avos.avoscloud.AVException;
import com.avos.avoscloud.AVObject;
import com.avos.avoscloud.AVQuery;
import com.avos.avoscloud.FindCallback;
import com.avos.avoscloud.SaveCallback;
import java.util.List;
/**
* Created by zhangmiao on 2016/12/22.
*/
public class NetworkDBManager {
private static final String TAG = "NetworkDBManager";
private final static String TABLENAME = "person";
private final static String NAME = "name";
private final static String AGE = "age";
private final static String INFO = "info";
public void putData(Person person) {
AVObject personObject = new AVObject(TABLENAME);
personObject.put(NAME, person.name);
personObject.put(AGE, person.age);
personObject.put(INFO, person.info);
personObject.saveInBackground(new SaveCallback() {
@Override
public void done(AVException e) {
if (e == null) {
Log.v(TAG, "put data success!");
} else {
Log.v(TAG, "put data failed!error:" + e.getMessage());
}
}
});
}
public void getData(final TextView textView) {
AVQuery<AVObject> avQuery = new AVQuery<>(TABLENAME);
avQuery.findInBackground(new FindCallback<AVObject>() {
@Override
public void done(List<AVObject> list, AVException e) {
if (e == null) {
Log.v(TAG, "get data success!");
String message = "";
////倒着遍历后四条上传的数据
for (int i = list.size()-1; i >=list.size()-4; i--) {
String name = list.get(i).getString(NAME);
int age = list.get(i).getInt(AGE);
String info = list.get(i).getString(INFO);
message += "name:" + name + ",age:" + age + ",info:" + info + ".\n";
}
textView.setText(message);
}
}
});
}
}
3、修改AndroidManifest.xml文件
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
4、MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private NetworkDBManager mNetworkDBManager;
private TextView mTableInfo;
private EditText et1,et2,et3;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v("MainActivity", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AVOSCloud.initialize(this, "yMNUazdBt872mNtC9aSakjYy-gzGzoHsz", "d4vw3VYdMCjLpsXRhHTBRutC");
mNetworkDBManager = new NetworkDBManager();
et1= (EditText) findViewById(R.id.name);
et2= (EditText) findViewById(R.id.age);
et3= (EditText) findViewById(R.id.sex);
Button networkGet = (Button) findViewById(R.id.network_get);
Button networkPut = (Button) findViewById(R.id.network_put);
mTableInfo = (TextView) findViewById(R.id.table_info);
networkGet.setOnClickListener(this);
networkPut.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.network_put:
Person person3 = new Person(et1.getText().toString(), Integer.parseInt(et2.getText().toString()), et3.getText().toString());
mNetworkDBManager.putData(person3);
Toast.makeText(this,"上传成功",Toast.LENGTH_SHORT).show();
break;
case R.id.network_get:
mNetworkDBManager.getData(mTableInfo);
break;
default:
Log.v("MainActivity", "default");
break;
}
}
}
【附录】
Demo
-
一、SharedPreferences
app内部实现数据存储
其他app实现共享数据
【先安装使用第一个并登入才可以使用第二个】 -
三、SQLite数据库存储数据
SQLiteDatabase直接操作数据库
继承SQLiteOpenHelper类操作数据库 -
四、ContentProvider
无 -
五、网络存储数据
网络存储
整理作者:汪博
少壮不努力,老大徒悲伤。
本文为Android学习规划打造,如有不好的地方请多多指教。
![](https://img.haomeiwen.com/i4821697/4edb9b76779471a3.png)