Android 应用数据存储方式有五种,分别为
1 使用SharedPreferences存储数据
2 文件存储数据
3 SQLite数据库存储数据
4 使用ContentProvider存储数据
5 网络存储数据
1)使用SharedPreferences存储数据
SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceState保存一般使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。
它是什么样的处理方式呢? SharedPreferences类似过去Windows系统上的ini配置文件,但是它分为多种权限,可以全局共享访问,android123提示最终是以xml方式来保存,整体效率来看不是特别的高,对于常规的轻量级而言比SQLite要好不少,如果真的存储量不大可以考虑自己定义文件格式。xml 处理时Dalvik会通过自带底层的本地XML Parser解析,比如XMLpull方式,这样对于内存资源占用比较好。
它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。
其存储位置在/data/data/< >/shared_prefs目录下。
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。
实现SharedPreferences存储的步骤如下:
一、根据Context获取SharedPreferences对象
二、利用edit()方法获取Editor对象。
三、通过Editor对象存储key-value键值对数据。
四、通过commit()方法提交数据。
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数据能被其他应用程序读,写
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编辑完成后,使用该方法提交修改
//步骤1:获取输入值
String code = txtCode.getText().toString().trim();
//步骤2-1:创建一个SharedPreferences.Editor接口对象,lock表示要写入的XML文件名,MODE_WORLD_WRITEABLE写操作
SharedPreferences.Editor editor = getSharedPreferences("lock", MODE_WORLD_WRITEABLE).edit();
//步骤2-2:将获取过来的值放入文件 几种常见类型
editor.putString("code", code);
editor.putString("STRING_KEY", "string");
editor.putInt("INT_KEY", 0);
editor.putBoolean("BOOLEAN_KEY", true);
//步骤3:提交
editor.commit();
//步骤1:创建一个SharedPreferences接口对象
SharedPreferences read = getSharedPreferences("lock", MODE_WORLD_READABLE);
//步骤2:获取文件中的值
String value = read.getString("code", "");
2) 文件存储数据
关于文件存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。
文件可用来存放大量数据,如文本、图片、音频等。
默认位置:/data/data/< >/files/***.***。
代码示例:
public void save() {
try {
FileOutputStream outStream=this.openFileOutput("a.txt",Context.MODE_WORLD_READABLE);
outStream.write(text.getText().toString().getBytes());
outStream.close();
Toast.makeText(MyActivity.this,"Saved",Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
return;
}
catch (IOException e){
return ;
}
}
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。
创建的文件保存在/data/data//files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data//files目录就可以看到该文件。
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:
Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入: openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data//files),其他程序无法访问。
除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。
读取文件示例:
public void load()
{
try {
FileInputStream inStream=this.openFileInput("a.txt");
ByteArrayOutputStream stream=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int length=-1;
while((length=inStream.read(buffer))!=-1) {
stream.write(buffer,0,length);
}
stream.close();
inStream.close();
text.setText(stream.toString());
Toast.makeText(MyActivity.this,"Loaded",Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e){
return ;
}
}
对于读写SDcard上的文件上篇已讲,这里就不多余了。
3 )SQLite数据库存储数据
SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite 称这为“弱类型”(manifest typing.)。 此外,SQLite 不支持一些标准的 SQL 功能,特别是外键约束(FOREIGN KEY constrains),嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 还有一些 ALTER TABLE 功能。 除了上述功能外,SQLite 是一个完整的 SQL 系统,拥有完整的触发器,交易等等。
Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper 类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。
SQLiteOpenHelper 的子类,至少需要实现三个方法:
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class SqliteDBHelper extends SQLiteOpenHelper {
// 步骤1:设置常数参量
private static final String DATABASE_NAME = "diary_db";
private static final int VERSION = 1;
private static final String TABLE_NAME = "diary";
// 步骤2:重载构造方法
public SqliteDBHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
/*
* 参数介绍:context 程序上下文环境 即:XXXActivity.this
* name 数据库名字
* factory 接收数据,一般情况为null
* version 数据库版本号
*/
public SqliteDBHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
//数据库第一次被创建时,onCreate()会被调用
@Override
public void onCreate(SQLiteDatabase db) {
// 步骤3:数据库表的创建
String strSQL = "create table "
+ TABLE_NAME
+ "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";
//步骤4:使用参数db,创建对象
db.execSQL(strSQL);
}
//数据库版本变化时,会调用onUpgrade()
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
创建表
SQLiteDatabase没有提供创建表的方法,所以要靠execSQL()方法来实现。看名字也知道execSQL()用于直接执行sql的。
String sql="create table t_user (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL,password TEXT NOT NULL)";
db.execSQL(sql);
使用SQLiteDatabase的insert(String table, String nullColumnHack, ContentValues values)方法插入数据。ContentValues 类,类似于java中的Map,以键值对的方式保存数据。
ContentValues values=new ContentValues(); values.put("name", "liangjh"); values.put("password", "123456"); db.insert("t_user", "id", values);
删除数据就比较直接了。使用SQLiteDatabase的delete(String table, String whereClause, String[] whereArgs)实现。如果不想把参数写在whereArgs里面,可以直接把条件写在whereClause里面。
// 方式1 直接将条件写入到条件里面(个人觉得容易被注入,但其实数据都在客户端,没啥安全性可言) db.delete("t_user", "id=1", null); // 方式2 条件分开写,感觉比较安全 db.delete("t_user", "name=? and password =?", new String[]{"weiyg","112233"});
values.put("password", "111111"); // 方式1 条件写在字符串内 db.update("t_user", values, "id=1", null); // 方式2 条件和字符串分开 db.update("t_user", values, "name=? or password=?",new String[]{"weiyg","123456"});
查
// Cursor c = db.rawQuery("select * from t_user", null); // db.rawQuery("select * from t_user where id=1", null); // db.rawQuery("select * from t_user where id=?", new String[]{"1"}); // 使用query() Cursor c = db.query("t_user", new String[]{"id","name"}, "name=?", new String[]{"weiyg"}, null, null, null); c.moveToFirst(); while(!c.isAfterLast()){ String msg=""; for(int i=0,j=c.getColumnCount();i<j;i++){ msg+="--"+c.getString(i); } Log.v("SQLite", "data:"+msg); c.moveToNext(); }
无论何时,打开的数据库,记得关闭。
db.close()
另外使用beginTransaction()和endTransaction()可以设置事务。
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.chinasoft.dbhelper.SqliteDBHelper;
public class DiaryDao {
private SqliteDBHelper sqliteDBHelper;
private SQLiteDatabase db;
// 重写构造方法
public DiaryDao(Context context) {
this.sqliteDBHelper = new SqliteDBHelper(context);
db = sqliteDBHelper.getWritableDatabase();
}
// 读操作
public String execQuery(final String strSQL) {
try {
System.out.println("strSQL>" + strSQL);
// Cursor相当于JDBC中的ResultSet
Cursor cursor = db.rawQuery(strSQL, null);
// 始终让cursor指向数据库表的第1行记录
cursor.moveToFirst();
// 定义一个StringBuffer的对象,用于动态拼接字符串
StringBuffer sb = new StringBuffer();
// 循环游标,如果不是最后一项记录
while (!cursor.isAfterLast()) {
sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"
+ cursor.getString(2) + "/" + cursor.getString(3) + "/"
+ cursor.getString(4)+"#");
//cursor游标移动
cursor.moveToNext();
}
db.close();
return sb.deleteCharAt(sb.length()-1).toString();
} catch (RuntimeException e) {
e.printStackTrace();
return null;
}
}
// 写操作
public boolean execOther(final String strSQL) {
db.beginTransaction(); //开始事务
try {
System.out.println("strSQL" + strSQL);
db.execSQL(strSQL);
db.setTransactionSuccessful(); //设置事务成功完成
db.close();
return true;
} catch (RuntimeException e) {
e.printStackTrace();
return false;
}finally {
db.endTransaction(); //结束事务
}
}
}
实例:
public class SQLiteActivity extends Activity {
public DiaryDao diaryDao;
//因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory);
//所以要确保context已初始化,我们可以把实例化Dao的步骤放在Activity的onCreate里
@Override
protected void onCreate(Bundle savedInstanceState) {
diaryDao = new DiaryDao(SQLiteActivity.this);
initDatabase();
}
class ViewOcl implements View.OnClickListener {
@Override
public void onClick(View v) {
String strSQL;
boolean flag;
String message;
switch (v.getId()) {
case R.id.btnAdd:
String title = txtTitle.getText().toString().trim();
String weather = txtWeather.getText().toString().trim();;
String context = txtContext.getText().toString().trim();;
String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date());
// 动态组件SQL语句
strSQL = "insert into diary values(null,'" + title + "','"
+ weather + "','" + context + "','" + publish + "')";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"添加成功":"添加失败";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
case R.id.btnDelete:
strSQL = "delete from diary where tid = 1";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"删除成功":"删除失败";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
case R.id.btnQuery:
strSQL = "select * from diary order by publish desc";
String data = diaryDao.execQuery(strSQL);
Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show();
break;
case R.id.btnUpdate:
strSQL = "update diary set title = '测试标题1-1' where tid = 1";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"更新成功":"更新失败";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
}
}
}
private void initDatabase() {
// 创建数据库对象
SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this);
sqliteDBHelper.getWritableDatabase();
System.out.println("数据库创建成功");
}
}
列出几个ORM框架:ORMLite、greendao、ormndroid、androrm、ActiveAndroid
4)使用ContentProvider存储数据
ContentProvider(内容提供者)是Android的四大组件之一,管理android以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。
一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到ContentProvider。而且Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
注意ContentProvider它也只是一个中间人,真正操作的数据源可能是数据库,也可以是文件、xml或网络等其他存储方式。
- 在A程序中定义一个ContentProvider,重载其增删查改等方法;
- 在A程序中的AndroidManifest.xml中注册ContentProvider;
- 在B程序中通过ContentResolver和Uri来获取ContentProvider的数据,同样利用Resolver的增删查改方法来获得和处理数据。
建立DBOpenHelper类:
在上面创建了一个person表, 实现person的增删改查, OK!数据库已建好,接下来就是要实现ContentProvider来提供统一的访问接口: 最后不要忘记在manifest里注册- public class ResolverDemoActivity extends Activity {
- /** Called when the activity is first created. */
- private SimpleCursorAdapter adapter;
- private ListView listView;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- listView=(ListView) this.findViewById(R.id.listView);
- ContentResolver contentResolver = getContentResolver();
- Uri selectUri = Uri.parse("content://cn.com.karl.personProvider/person");
- Cursor cursor=contentResolver.query(selectUri, null, null, null, null);
- adapter = new SimpleCursorAdapter(this, R.layout.item, cursor,
- new String[]{"_id", "name", "age"}, new int[]{R.id.id, R.id.name, R.id.age});
- listView.setAdapter(adapter);
- listView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- ListView lView = (ListView)parent;
- Cursor data = (Cursor)lView.getItemAtPosition(position);
- int _id = data.getInt(data.getColumnIndex("_id"));
- Toast.makeText(ResolverDemoActivity.this, _id+"", 1).show();
- }
- });
- Button button = (Button) this.findViewById(R.id.insertbutton);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- ContentResolver contentResolver = getContentResolver();
- Uri insertUri = Uri.parse("content://cn.com.karl.personProvider/person");
- ContentValues values = new ContentValues();
- values.put("name", "wangkuifeng");
- values.put("age", 23);
- Uri uri = contentResolver.insert(insertUri, values);
- Toast.makeText(ResolverDemoActivity.this, "添加完成", 1).show();
- }
- });
- }
- }
-
前面介绍的几种存储都是将数据存储在本地设备上,除此之外,还有一种存储(获取)数据的方式,通过网络来实现数据的存储和获取。
我们可以调用WebService返回的数据或是解析HTTP协议实现网络数据交互。
具体需要熟悉java.net.*,Android.net.*这两个包的内容,在这就不赘述了,请大家参阅相关文档。
别忘记了在配置文件中设置访问网络权限: <uses-permission android:name="android.permission.INTERNET" />网络上有很多第三方框架 Retrofit,okhttp,volley,Android async http,下面有链接详细介绍Android开发目前流行第三方框架。
参考:
http://www.cnblogs.com/ITtangtang/p/3920916.html
http://www.2cto.com/kf/201309/241039.html
http://blog.youkuaiyun.com/amazing7/article/details/51437435
http://www.2cto.com/kf/201606/518021.html
http://www.jb51.net/article/85722.htm
http://blog.youkuaiyun.com/chuyuqing/article/details/39995607
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1127/2066.html
http://blog.youkuaiyun.com/amazing7/article/details/51324022
http://blog.youkuaiyun.com/wangkuifeng0118/article/details/7028953
https://www.oschina.net/news/73836/15-android-general-popular-frameworks(Android常用框架大全)