注:这是《Android入门与实践》的其中一章内容
在Android中,可供选择的存储方式有SharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)和网络。
一.SharedPreferences方式
Android提供用来存储一些简单的配置信息的一种机制,例如,一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储,
使得我们可以很方便的读取和存入.
1)程序要实现的功能:
我们在Name文本框中输入wangwu,在Password文本框中输入123456,然后退出这个应用。我们在应用程序列表中找到这个应用,重新启动,可以看到其使用了前面输入的Name和Password
2)实现的代码
布局
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="SharedPreferencesdemo"
- />
-
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="name"
- >
- </TextView>
-
- <EditText
- android:id="@+id/name"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text=""
- >
- </EditText>
-
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="password"
- >
- </TextView>
-
- <EditText
- android:id="@+id/password"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:password="true"
- android:text=""
- >
- </EditText>
- </LinearLayout>
主要实现代码
- packagecom.demo;
-
- importandroid.app.Activity;
- importandroid.content.SharedPreferences;
- importandroid.os.Bundle;
- importandroid.widget.EditText;
-
- publicclassSharedPreferencesDemoextendsActivity{
-
- publicstaticfinalStringSETTING_INFOS="SETTING_Infos";
- publicstaticfinalStringNAME="NAME";
- publicstaticfinalStringPASSWORD="PASSWORD";
- privateEditTextfield_name;
- privateEditTextfiled_pass;
-
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
-
- field_name=(EditText)findViewById(R.id.name);
- filed_pass=(EditText)findViewById(R.id.password);
-
-
- SharedPreferencessettings=getSharedPreferences(SETTING_INFOS,0);
- Stringname=settings.getString(NAME,"");
- Stringpassword=settings.getString(PASSWORD,"");
-
-
- field_name.setText(name);
- filed_pass.setText(password);
- }
-
- @Override
- protectedvoidonStop(){
- super.onStop();
-
- SharedPreferencessettings=getSharedPreferences(SETTING_INFOS,0);
- settings.edit()
- .putString(NAME,field_name.getText().toString())
- .putString(PASSWORD,filed_pass.getText().toString())
- .commit();
- }
-
- }
SharedPreferences保存到哪里去了?
SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data/<package
name>/shared_prefs下,以上面这个为例,可以看到一个叫做SETTING_Infos.xml的文件
注意:Preferences只能在同一个包内使用,不能在不同的包之间使用。
二.文件存储方式
在Android中,其提供了openFileInput 和 openFileOuput 方法读取设备上的文件,下面看个例子代码,具体如下所示:
String FILE_NAME = "tempfile.tmp"; //确定要操作文件的文件名
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); //初始化
FileInputStream fis = openFileInput(FILE_NAME); //创建写入流
上述代码中两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出异常。需要提醒的是,如果调用
FileOutputStream 时指定的文件不存在,Android 会自动创建它。另外,在默认情况下,写入的时候会覆盖原文件内容,如果想把
新写入的内容附加到原文件内容后,则可以指定其模式为Context.MODE_APPEND。
三.SQLite数据库方式
SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。
1)实现的功能
在这个例子里边,我们在程序的主界面有一些按钮,通过这些按钮可以对数据库进行标准的增、删、改、查。
2)实现代码
所用到的字符文件
- <?xmlversion="1.0"encoding="utf-8"?>
- <resources>
- <stringname="app_name">SQLite数据库操作实例</string>
- <stringname="button1">建立数据库表</string>
- <stringname="button2">删除数据库表</string>
- <stringname="button3">插入两条记录</string>
- <stringname="button4">删除一条记录</string>
- <stringname="button5">查看数据库表</string>
- </resources>
布局代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
-
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/app_name"
- />
- <Button
- android:text="@string/button1"
- android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- ></Button>
- <Button
- android:text="@string/button2"
- android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- ></Button>
- <Button
- android:text="@string/button3"
- android:id="@+id/button3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- ></Button>
- <Button
- android:text="@string/button4"
- android:id="@+id/button4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- ></Button>
- <Button
- android:text="@string/button5"
- android:id="@+id/button5"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- ></Button>
-
- </LinearLayout>
主要代码
- packagecom.sqlite;
-
- importandroid.app.Activity;
- importandroid.content.Context;
- importandroid.database.Cursor;
- importandroid.database.SQLException;
- importandroid.database.sqlite.SQLiteDatabase;
- importandroid.database.sqlite.SQLiteOpenHelper;
- importandroid.os.Bundle;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.view.View.OnClickListener;
- importandroid.widget.Button;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- publicclassSQLiteDemoextendsActivity{
-
- OnClickListenerlistener1=null;
- OnClickListenerlistener2=null;
- OnClickListenerlistener3=null;
- OnClickListenerlistener4=null;
- OnClickListenerlistener5=null;
-
- Buttonbutton1;
- Buttonbutton2;
- Buttonbutton3;
- Buttonbutton4;
- Buttonbutton5;
-
- DatabaseHelpermOpenHelper;
-
- privatestaticfinalStringDATABASE_NAME="dbForTest.db";
- privatestaticfinalintDATABASE_VERSION=1;
- privatestaticfinalStringTABLE_NAME="diary";
- privatestaticfinalStringTITLE="title";
- privatestaticfinalStringBODY="body";
-
-
- privatestaticclassDatabaseHelperextendsSQLiteOpenHelper{
-
- DatabaseHelper(Contextcontext){
- super(context,DATABASE_NAME,null,DATABASE_VERSION);
- }
-
-
- @Override
- publicvoidonCreate(SQLiteDatabasedb){
-
- Stringsql="CREATETABLE"+TABLE_NAME+"("+TITLE
- +"textnotnull,"+BODY+"textnotnull"+");";
- Log.i("haiyang:createDB=",sql);
- db.execSQL(sql);
- }
-
- @Override
- publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
- }
- }
-
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- prepareListener();
- initLayout();
- mOpenHelper=newDatabaseHelper(this);
-
- }
-
- privatevoidinitLayout(){
- button1=(Button)findViewById(R.id.button1);
- button1.setOnClickListener(listener1);
-
- button2=(Button)findViewById(R.id.button2);
- button2.setOnClickListener(listener2);
-
- button3=(Button)findViewById(R.id.button3);
- button3.setOnClickListener(listener3);
-
- button4=(Button)findViewById(R.id.button4);
- button4.setOnClickListener(listener4);
-
- button5=(Button)findViewById(R.id.button5);
- button5.setOnClickListener(listener5);
-
- }
-
- privatevoidprepareListener(){
- listener1=newOnClickListener(){
- publicvoidonClick(Viewv){
- CreateTable();
- }
- };
- listener2=newOnClickListener(){
- publicvoidonClick(Viewv){
- dropTable();
- }
- };
- listener3=newOnClickListener(){
- publicvoidonClick(Viewv){
- insertItem();
- }
- };
- listener4=newOnClickListener(){
- publicvoidonClick(Viewv){
- deleteItem();
- }
- };
- listener5=newOnClickListener(){
- publicvoidonClick(Viewv){
- showItems();
- }
- };
- }
-
-
-
-
- privatevoidCreateTable(){
-
-
- SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
- Stringsql="CREATETABLE"+TABLE_NAME+"("+TITLE
- +"textnotnull,"+BODY+"textnotnull"+");";
- Log.i("haiyang:createDB=",sql);
-
- try{
- db.execSQL("DROPTABLEIFEXISTSdiary");
- db.execSQL(sql);
- setTitle("数据表成功重建");
- }catch(SQLExceptione){
- setTitle("数据表重建错误");
- }
- }
-
-
-
-
- privatevoiddropTable(){
-
-
- SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
- Stringsql="droptable"+TABLE_NAME;
- try{
- db.execSQL(sql);
- setTitle("数据表成功删除:"+sql);
- }catch(SQLExceptione){
- setTitle("数据表删除错误");
- }
- }
-
-
-
-
- privatevoidinsertItem(){
-
-
- SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
- Stringsql1="insertinto"+TABLE_NAME+"("+TITLE+","+BODY
- +")values('haiyang','android的发展真是迅速啊');";
- Stringsql2="insertinto"+TABLE_NAME+"("+TITLE+","+BODY
- +")values('icesky','android的发展真是迅速啊');";
- try{
-
-
- Log.i("haiyang:sql1=",sql1);
- Log.i("haiyang:sql2=",sql2);
- db.execSQL(sql1);
- db.execSQL(sql2);
- setTitle("插入两条数据成功");
- }catch(SQLExceptione){
- setTitle("插入两条数据失败");
- }
- }
-
-
-
-
- privatevoiddeleteItem(){
- try{
-
-
- SQLiteDatabasedb=mOpenHelper.getWritableDatabase();
-
-
-
- db.delete(TABLE_NAME,"title='haiyang'",null);
- setTitle("删除title为haiyang的一条记录");
- }catch(SQLExceptione){
-
- }
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- privatevoidshowItems(){
-
- SQLiteDatabasedb=mOpenHelper.getReadableDatabase();
- Stringcol[]={TITLE,BODY};
-
- Cursorcur=db.query(TABLE_NAME,col,null,null,null,null,null);
- Integernum=cur.getCount();
- setTitle(Integer.toString(num)+"条记录");
- }
- }
四.内容提供器(Content provider)方式
在Android的设计“哲学”里是鼓励开发者使用内部类的,这样不但使用方便,而且执行效率也高。
1.什么是ContentProvider
数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。难道两个程序之间就没有办法对于数据进行交换?解决这个问题主要靠ContentProvider。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。
下边列举一些较常见的接口,这些接口如下所示。
query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
2.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。
content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
第一部分是:"content://" 。
第二部分是要获得数据的一个字符串片段。
最后就是ID(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
Contacts.People.CONTENT_URI (联系人的URI)。
1)实现的功能
在这个例子里边,首先在系统的联系人应用当中插入一些联系人信息,然后把这些联系人的名字和电话再显示出来
2)实现方法
- packagecom.contentProvider;
-
- importandroid.app.ListActivity;
- importandroid.database.Cursor;
- importandroid.os.Bundle;
- importandroid.provider.Contacts.Phones;
- importandroid.widget.ListAdapter;
- importandroid.widget.SimpleCursorAdapter;
-
- publicclassContentProviderDemoextendsListActivity{
-
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
-
-
-
-
-
-
-
- Cursorc=getContentResolver().query(Phones.CONTENT_URI,null,null,null,null);
- startManagingCursor(c);
- ListAdapteradapter=newSimpleCursorAdapter(
- this,
- android.R.layout.simple_list_item_2,
- c,
- newString[]{Phones.NAME,Phones.NUMBER},
- newint[]{android.R.id.text1,android.R.id.text2});
- setListAdapter(adapter);
- }
-
- }
五. 网络存储方式
1.例子介绍
通过邮政编码查询该地区的天气预报,以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务,具体信息请参考其WSDL文档,网址为:
http://www.webservicex.net/WeatherForecast.asmx?WSDL。
输入:美国某个城市的邮政编码。
输出:该邮政编码对应城市的天气预报。
2.实现步骤如下
(1)如果需要访问外部网络,则需要在AndroidManifest.xml文件中加入如下代码申请权限许可:
<!-- Permissions -->
<uses-permission Android:name="Android.permission.INTERNET" />
(2)以HTTP POST的方式发送(注意:SERVER_URL并不是指WSDL的URL,而是服务本身的URL)。实现的代码如下所示:
private static final String SERVER_URL = "http://www.webservicex.net/WeatherForecast. asmx/GetWeatherByZipCode"; //定义需要获取的内容来源地址
HttpPost request = new HttpPost(SERVER_URL); //根据内容来源地址创建一个Http请求
// 添加一个变量
List <NameValuePair> params = new ArrayList <NameValuePair>();
// 设置一个华盛顿区号
params.add(new BasicNameValuePair("ZipCode", "200120")); //添加必须的参数
request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //设置参数的编码
try {
HttpResponse httpResponse = new DefaultHttpClient().execute(request); //发送请求并获取反馈
// 解析返回的内容
if(httpResponse.getStatusLine().getStatusCode() != 404)
{
String result = EntityUtils.toString(httpResponse.getEntity());
Log.d(LOG_TAG, result);
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
}
代码解释:
如上代码使用Http从webservicex获取ZipCode为“200120”(美国WASHINGTON D.C)的内容,其返回的内容如下:
<WeatherForecasts xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http: //www.w3.org/2001/XMLSchema-instance" xmlns="http://www.webservicex.net">
<Latitude>38.97571</Latitude>
<Longitude>77.02825</Longitude>
<AllocationFactor>0.024849</AllocationFactor>
<FipsCode>11</FipsCode>
<PlaceName>WASHINGTON</PlaceName>
<StateCode>DC</StateCode>
<Details>
<WeatherData>
<Day>Saturday, April 25, 2009</Day>
<WeatherImage>http://forecast.weather.gov/images/wtf/sct.jpg</WeatherImage>
<MaxTemperatureF>88</MaxTemperatureF>
<MinTemperatureF>57</MinTemperatureF>
<MaxTemperatureC>31</MaxTemperatureC>
<MinTemperatureC>14</MinTemperatureC>
</WeatherData>
<WeatherData>
<Day>Sunday, April 26, 2009</Day>
<WeatherImage>http://forecast.weather.gov/images/wtf/few.jpg</WeatherImage>
<MaxTemperatureF>89</MaxTemperatureF>
<MinTemperatureF>60</MinTemperatureF>
<MaxTemperatureC>32</MaxTemperatureC>
<MinTemperatureC>16</MinTemperatureC>
</WeatherData>
…
</Details>
</WeatherForecasts>