Android平台进行数据存储的五大方式
适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口 令密码等
核心原理:保存基于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数据能被其他应用程序读,写
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编辑完成后,使用该方法提交修改
代码如下:
private String print() {
StringBuffer buff = new StringBuffer();
BufferedReader reader= null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(PATH)));
String str;
while ((str = reader.readLine()) != null) {
buff.append(str + "/n");
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d("1111111", buff.toString());
return buff.toString();
}
2.Android本身的数据库(SQLite数据库)
SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。现在的主流移动设备像Android、iPhone等都使用SQLite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到SQLite来存储我们大量的数据,所以我们就需要掌握移动设备上的SQLite开发技巧 SQLiteDatabase类为我们提供了很多种方法,上面的代码中基本上囊括了大部分的数据库操作;对于添加、更新和删除来说,我们都可以使用
1 db.executeSQL(String sql);
2 db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集
除了统一的形式之外,他们还有各自的操作方法:
1 db.insert(String table, String nullColumnHack, ContentValues values);
2 db.update(String table, Contentvalues values, String whereClause, String whereArgs);
3 db.delete(String table, String whereClause, String whereArgs);
以上三个方法的第一个参数都是表示要操作的表名;insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称,系统将此列设置为NULL,不至于出现错误;insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列名,value代表该列要插入的值;update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;delete方法的参数也是一样
3.ContentProvider存储
Android这个系统和其他的操作系统还不太一样,我们需要记住的是,数据在Android当中是私有的,当然这些数据包括文件数据
和数据库数据 以及一些其他类型的数据。那这个时候有读者就会提出问题,难道两个程序之间就没有办法对于数据进行交换?
Android这么优秀的系统不会让这种情况发生 的。解决这个问题主要靠ContentProvider。一个Content
Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content
Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content
Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是
用数据库存储还是用文件 存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里
的数据打交道,可以读取程序的数据,也可以删除程序的数 据,当然,中间也会涉及一些权限的问题。
一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据
库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取
数据的操作基 本一样,只不过是采用URI来表示外界需要访问的“数据库”。
Content Provider提供了一种多应用间数据共享的方式,比如:联系人信息可以被多个应用程序访问。
Content Provider是个实现了一组用于提供其他应用程序存取数据的标准方法的类。 应用程序可以在Content
Provider中执行如下操作: 查询数据 修改数据 添加数据 删除数据
标准的Content Provider: Android提供了一些已经在系统中实现的标准Content
Provider,比如联系人信息,图片库等等,你可以用这些Content Provider来访问设备上存储的联系人信息,图片等等。
查询记录:
在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete,
modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”,
代表数据的路径,和一个可选的标识数据的ID。
以下是一些示例URI:
content://media/internal/images 这个URI将返回设备上存储的所有图片
content://contacts/people/ 这个URI将返回设备上的所有联系人信息
content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)
尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下
),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,参见下例:
MediaStore.Images.Media.INTERNAL_CONTENT_URI Contacts.People.CONTENT_URI
因此,如上面content://contacts/people/45这个URI就可以写成如下形式:
Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45);
然后执行数据查询: Cursor cur = managedQuery(person, null, null, null);
这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:
package com.wissen.testApp;
public class ContentProviderDemo extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
displayRecords();
}
private void displayRecords() {
//该数组中包含了所有要返回的字段
String columns[] = new String[] { People.NAME, People.NUMBER };
Uri mContacts = People.CONTENT_URI;
Cursor cur = managedQuery(
mContacts,
columns, // 要返回的数据字段
null, // WHERE子句
null, // WHERE 子句的参数
null // Order-by子句
);
if (cur.moveToFirst()) {
String name = null;
String phoneNo = null;
do {
// 获取字段的值
name = cur.getString(cur.getColumnIndex(People.NAME));
phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();
} while (cur.moveToNext());
}
}
}
修改记录: 我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法:
private void updateRecord(int recNo, String name) {
Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
ContentValues values = new ContentValues();
values.put(People.NAME, name);
getContentResolver().update(uri, values, null, null);
}
添加记录:
要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。 上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加:
private void insertRecords(String name, String phoneNo) {
ContentValues values = new ContentValues();
values.put(People.NAME, name);
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Log.d(”ANDROID”, uri.toString());
Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.NUMBER, phoneNo);
getContentResolver().insert(numberUri, values);
}
删除记录: Content Provider中的getContextResolver.delete()方法可以用来删除记录。
private void deleteRecords() {
Uri uri = People.CONTENT_URI;
getContentResolver().delete(uri, null, null);
}
创建Content Provider: 至此我们已经知道如何使用Content Provider了,现在让我们来看下如何自己创建一个Content Provider。 要创建我们自己的Content Provider的话,我们需要遵循以下几步:
1.创建一个继承了ContentProvider父类的类
2.定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如: public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);
3.创建你的数据存储系统。大多数ContentProvider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。
4.定义你要返回给客户端的数据列名。如果你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其他数据库一样。但是,你必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。
5.如果你要存储字节型数据,比如位图文件等,那保存该数据的数据列其实是一个表示实际保存文件的URI字符串,客户端通过它来读取对应的文件数据,处理这种 数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而 且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该 URI指向的文件资源,如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。
6.声明public static String型的变量,用于指定要从游标处返回的数据列。
7.查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。
8.在AndroidMenifest.xml中使用标签来设置Content Provider。
9.如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。
4.网络存储数据
我们可以调用WebService返回的数据或是解析HTTP协议实现网络数据交互。 具体需要熟悉java.net.*,Android.net.*这两个包的内容,在这就不赘述了,请大家参阅相关文档。 下面是一个通过地区名称查询该地区的天气预报,以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务。
package com.android.weather;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.os.Bundle;
public class MyAndroidWeatherActivity extends Activity {
//定义需要获取的内容来源地址
private static final String SERVER_URL =
"http://www.webservicex.net/WeatherForecast.asmx/GetWeatherByPlaceName";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HttpPost request = new HttpPost(SERVER_URL); //根据内容来源地址创建一个Http请求
// 添加一个变量
List<NameValuePair> params = new ArrayList<NameValuePair>();
// 设置一个地区名称
params.add(new BasicNameValuePair("PlaceName", "NewYork")); //添加必须的参数
try {
//设置参数的编码
request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
//发送请求并获取反馈
HttpResponse httpResponse = new DefaultHttpClient().execute(request);
// 解析返回的内容
if(httpResponse.getStatusLine().getStatusCode() != 404){
String result = EntityUtils.toString(httpResponse.getEntity());
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
```
5.文件存储数据
分为内部储存和外部存储。内部存储是应用程序使用Android为自己分配的内存空间,数据存储到“/data/data/程序包名/files”路径下的相应文件中。外部存储是使用手机sdcard的内存(
这个sdcard并不是我们经常说的那个可以拆卸替换的SD卡,那个SD卡我们称之为扩展卡),使用这部分内存要声明相应的权限。
1.assets(只读、不能修改或删除)
代码如下:
try {
//创建流
InputStream in = this.getAssets().open(“文件名.扩展名”);
//读出数据
Bitmap bm=BitmapFactory.decodeStream(in);
in.close();
in=null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
2.读写数据(读写手机自身内存)
位置:data/data/主包名/files目录下的 也可以使用context.getFilesDir().getAbsolutePath()获得内存根路径
存:
try {
OutputStream os=this.openFileOutput(“文件名”, MODE_PRIVATE);
os.write(“wa”.getBytes());
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
取:
try {
InputStream in=this.openFileInput(“文件名”);
//缓冲区
byte b[]=new byte[1024];
//创建缓存
ByteArrayBuffer ba=new ByteArrayBuffer(0);
int l;
//读
while((l=in.read(b))>-1)
{
ba.append(b,0,l);
}
//转化
String s=new String(ba.toByteArray());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}