Android实习生 —— 数据存储与共享
目录
前言一、使用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/
/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_APPENDMODE_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)
因为内部存储容量限制,有时候需要存储数据比较大的时候需要用到外部存储,使用外部存储分为以下几个步骤:
- 第一步:添加外部存储访问限权
- 第二步:检测外部存储的可用性
//获取外存储的状态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();不要关闭,不然单独调用查询操作会空指针 }
效果:
效果
文件路径
pc中使用Navicat可以操作数据表
- 第二步:实现插入数据的操作(两种方式任选其一)
- 使用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();//实例化ContentValuescv.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 avQuery = new AVQuery<>(TABLENAME); avQuery.findInBackground(new FindCallback() { @Override public void done(List 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包
导入jar包
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 avQuery = new AVQuery<>(TABLENAME); avQuery.findInBackground(new FindCallback() { @Override public void done(List 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文件
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学习规划打造,如有不好的地方请多多指教。
更多相关文章
- 一句话锁定MySQL数据占用元凶
- Android开发——Android(安卓)Studio下使用Cmake在NDK环境下移植
- android实现session保持
- Android(安卓)Activity间跳转与传递数据
- DICOM入门(四)——Android读取DCM文件图片
- Android(安卓)SharedPreferences保存Map集合
- cocos2d-x android 添加新场景报错: undefined reference to `vt
- Android录音--AudioRecord、MediaRecorder的使用
- Android恢复出厂设置流程分析