此书pdf格式版本在文章最下面会给出),写的很详细,这边我就大致的介绍一下哦:
首先android的数据存储分为:
1,SharedPreferences
2,文件存储
3,sqlite数据库
4,内容提供器(Content provider)
5,网络(通过访问webservice服务获取网络数据)
1,SharedPreferences
SharedPreferences是最简单一种存储方式,主要是用来存储一些简单的配置信息,比如APP的主题类型,APP的一些简单的个性化定制,你可以把它理解为cookie,不过跟cookie不一样的是cookie的时间限制(时间一到会被浏览器清除),但是SharedPreferences一旦存储是永久保存在手机端的,除非用户通过此APP修改或者删除里面的值。
SharedPreferences格式的文件其实是xml格式的文件,我写了一个很简单的代码学习了一下这个功能,代码为:SharedPreferences.demo
输入名字和密码,点击“保存至SharedPrefenrences”后,我们输入的两个值就被保存了。当你退出后再次进入这个程序会发现自动读取出了保存在SharedPrefenrences中的数据,那么名字和密码的值到底是保存在哪个目录下面的哪个文件里面呢?看了下面的代码你就明白了:
Button save_btn=(Button) findViewById(R.id.btn_shared);
listener1=new OnClickListener(){
public void onClick(View v){
//获得SharedPreferences对象
SharedPreferences info=getSharedPreferences("share_info",0);
info.edit()
.putString("name",edit_name.getText().toString())
.putString("password",edit_password.getText().toString())
.commit();
}
};
save_btn.setOnClickListener(listener1);
按钮监听的事件代码中,首先 SharedPreferences info=getSharedPreferences("share_info",0);来获取名字叫做“share_info”的SharedPreferences文件对象(如果刚开始share_info这个文件找不到,会自动创建这么一个文件),最后通过putString()方法往这个文件存入值。那么这个叫做“share_info”的文件时存在于哪里呢?通过Eclipse工具下面的DDMS中的File Explorer来看一下吧,首先我们会看到在data目录,点击打开这个data目录后会发现还有一个data目录,再打开这个data目录会看到好多子目录,其实当一个应用程序加入到虚拟机中去的时候,android系统都会自动在data/data/这个目录下面建一个目录,目录的名字就是你刚开始创建android程序的时候的包名,例如在这个程序中我命名的包名叫做:com.demo.data,如下图所示,在data/data/目录下面就有一个子目录:
如此就可以知道,com.demo.data目录下面的shared_prefs目录下面是用来存放SharedPreferences类型文件的,看可以看到我们代码中创建的share_info.xml在这个目录下面了,点击如图所示的按钮就可以导出share_info.xml这个文件了:
当然读取SharedPreferences类型的文件也很简单,看下面的代码:
/获得SharedPreferences对象
SharedPreferences info=getSharedPreferences("share_info",0);
edit_password.setText(info.getString("password", ""));
edit_name.setText(info.getString("name", ""));
既然android系统已经把SharedPreferences类型的文件存放的目录给指定了,那么我们只要按照名字去读取出来,通过getString()方法读出里面的键值对就可以了,其实的事情就交给android去做吧!
2,文件存储
文件存储这一部分本人暂时没有研究,因为比较简单,所以打算下次用到的时候在看一下。
3,sqlite数据库
sqlite数据库是非常轻量级的数据库,大米不知道今后android开发会不会用到mysql或者mssql(如果用到那就太不可思议了!),但是目前来讲sqlite是最适合android的应用。
网上找了个记事本的例子,自己重新写了一遍代码,并且对代码重新分层了一遍,使代码看起来更新容易懂,代码:记事本代码(riji.demo)
例子很简单,主要操作图示见下面:
点击menu后出现:
,可以删除日记,也可以点击”添加一篇新日记“按钮,跳转到
页面进行新增操作(当然在日记列表中点击某一篇日记也会跳转到这个页面下面进行更新操作)。
项目结构如图:
其中com.demo.dbHelper包中我存放的是底层的辅助类,com.demo.dbAdapter包中存放的是业务逻辑类,com.demo.riji包中存放的是窗体(Activity)执行类.
我们来研究一下比较重要的一些代码:
首先是DatabaseHelper.java文件这个类:
public class DatabaseHelper extends SQLiteOpenHelper {
//数据库名
private static final String DATABASE_NAME="db_riji";
private static final int DATABASE_VERSION=1;
//创建日记表
private static final String DATABASE_CREATE = "create table tb_wenzhang (_id integer primary key autoincrement, "
+ "title text not null, body text not null, created text not null);";
//重载构造器
public DatabaseHelper(Context context){
super(context,DATABASE_NAME,null,DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建表
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//先删除表 然后创建表
db.execSQL("DROP TABLE IF EXISTS diary");
onCreate(db);
}
}
这个类继承了SQLiteOpenHelper类,只有继承了SQLiteOpenHelper父类才能对sqlite数据库进行操作。另外,DatabaseHelper 类重写了onCreate和onUpgrade方法。
onCreate:会在数据库第一次生成的时候被调用,比如这个例子中RijiAdapter类中dbHelper=new DatabaseHelper(mCtx);生成DatabaseHelper对象时如果没有找到db_riji这个数据库的话,那么就会调用onCreate方法创建这个数据库。
onUpgrade:当数据库需要升级的时候,android就会去调用这个方法,比如生成DatabaseHelper对象的时候我们给它加上第三个参数:DatabaseHelper helper=new DatabaseHelper(SqlliteTestActivity.this,"db_riji",2);android就被通知到sqlite数据库要被升级了,所以我们可以在onUpgrade方法中添加一些修改数据库的逻辑。
当我们dbHelper=new DatabaseHelper(mCtx);生成了继承于SQLiteOpenHelper的子类dbHelper对象后,接着通过dbSqlite=dbHelper.getWritableDatabase();获得sqlite数据库对象,接下来就可以就可以操作sqlite对象来对sqlite数据库进行操作了,这个例子中就用到了“增删改查”所有操作:
//插入日记
public long insertRiji(String title,String body){
ContentValues values=new ContentValues();
values.put(KEY_TITLE,title);
values.put(KEY_BODY,body);
//当前时间
String created = getCreateTime();
values.put(KEY_CREATED, created);
return dbSqlite.insert(DATABASE_TABLE, null, values);
}
//删除日记
public boolean deleteRiji(long id){
return dbSqlite.delete(DATABASE_TABLE, KEY_ROWID+"="+id, null)>0;
}
//得到所有的日记记录
public Cursor getAllNote(){
return dbSqlite.query(DATABASE_TABLE,
new String[]{KEY_ROWID,KEY_TITLE,KEY_BODY,KEY_CREATED},
null, null, null, null, null);
}
//得到某一篇日记
public Cursor getOneRiji(long id) throws SQLException{
Cursor list=dbSqlite.query(DATABASE_TABLE,
new String[]{KEY_ROWID,KEY_TITLE,KEY_BODY,KEY_CREATED}, KEY_ROWID+"="+id,
null, null, null, null);
if(list!=null){
list.moveToFirst();
}
return list;
}
//修改某一篇日记
public boolean updateRiji(long id,String title,String body){
ContentValues values=new ContentValues();
values.put(KEY_TITLE, title);
values.put(KEY_BODY, body);
values.put(KEY_CREATED, getCreateTime());
return dbSqlite.update(DATABASE_TABLE, values, KEY_ROWID+"="+id, null)>0;
}
可以看到通过dbSqlite.insert,dbSqlite.update,dbSqlite.query,dbSqlite.delete等方法就可以实现对数据库的操作,具体这些方法(insert,update,delete,query)的参数还有SQLiteOpenHelper类的其他方法可以查看Sdk Api了解。
4,ContentProvider
ContentProvider为两个程序进行数据交流提供了可能,也就是说一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。这边我也提供了一个用contentprovider实现的日记本程序,代码:ContentProvider实现的记事本
这个项目代码太多,最为核心的大米认为就这么几行,也就是说只要把这几行搞懂了ContentProvider也就弄懂七七八八了。
首先我们来看这一段代码:
private void insertDiary() {
String title = mTitleText.getText().toString();
String body = mBodyText.getText().toString();
ContentValues values = new ContentValues();
values.put(Diary.DiaryColumns.CREATED, DiaryContentProvider
.getFormateCreatedDate());
values.put(Diary.DiaryColumns.TITLE, title);
values.put(Diary.DiaryColumns.BODY, body);
//根据第一个参数Diary.DiaryColumns.CONTENT_URI 去匹配AndroidManifest.xml下面的配置
getContentResolver().insert(Diary.DiaryColumns.CONTENT_URI1, values);
}
这个方法是ActivityDiaryEditor中的,作用是通过ContentProvider插入数据,其中最关键的代码:getContentResolver().insert(Diary.DiaryColumns.CONTENT_URI1, values);可以看到在Diary类中我们定义了两个授权:
public static final String AUTHORITY = "com.ex09_2_contentprovider.diarycontentprovider";
public static final String AUTHORITY1 = "com.ex09_2_contentprovider.diarycontentprovider1";
并且在两个URI中实现了这两个授权:
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/diaries");
public static final Uri CONTENT_URI1 = Uri.parse("content://" + AUTHORITY1 + "/diaries");
那授权对应的是DiaryContentProvider还是DiaryContentProvider1呢?很简单,我们在AndroidManifest.xml中进行配置了:
<provider android:name="Diary1ContentProvider"
android:authorities="com.ex09_2_contentprovider.diarycontentprovider1" />
<provider android:name="DiaryContentProvider"
android:authorities="com.ex09_2_contentprovider.diarycontentprovider" />
DiaryContentProvider和DiaryContentProvider1这两个类都继承了ContentProvider接口,也就是这两个类可以当做接口为其他应用程序调用,如调用这行代码:
getContentResolver().insert(Diary.DiaryColumns.CONTENT_URI1, values);
通过Diary.DiaryColumns.CONTENT_URI1这个参数然后去AndroidManifest.xml获取到对应的ContentProvider接口,然后调用insert()、update()等方法,这些方法在DiaryContentProvider或者DiaryContentProvider1中都已经进行重载了。
所以至此,如果有其他的应用程序想查看这个应用的日记,就可用通过getContentResolver().query(“com.ex09_2_contentprovider.diarycontentprovider”, “参数值”);查询到了。
5,调用webservice获取远程数据
这边大米用VS写了一个.net的webservice:webserviceDemo
然后android应用为:webcerviceTest
这个例子是在是很简单,大米就不介绍了,贴出核心的代码:
public class WebservicetestActivity extends Activity {
/** Called when the activity is first created. */
private Button btn_send;
private EditText edt_name1;
private EditText edt_name2;
//WebService服务返回的结果信息
public String resultInfo=null;
//名空间
private String NameSpace="http://tempuri.org/";
//webservice服务地址
private String url="http://10.0.2.2:16216/HelloWorldService.asmx";
//要调用的方法
private String MethodName="SayHello";
//soapAction
private String soapAction = NameSpace + MethodName;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn_send=(Button) findViewById(R.id.btn_send);
edt_name1=(EditText) findViewById(R.id.edt_name1);
edt_name2=(EditText) findViewById(R.id.edt_name2);
btn_send.setOnClickListener(new OnClickListener(){
public void onClick(View v){
//创建HttpTransportSE对象
HttpTransportSE se=new HttpTransportSE (url);
se.debug=true;
//创建Soap对象
SoapObject soapObject=new SoapObject(NameSpace,MethodName);
soapObject.addProperty("name1",edt_name1.getText().toString());
soapObject.addProperty("name2",edt_name2.getText().toString());
//创建Soap请求对象
SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut=soapObject;
envelope.dotNet=true;
envelope.setOutputSoapObject(soapObject);
try{
se.call(soapAction, envelope);
//返回的数据
//第一种方式
SoapObject so = (SoapObject) envelope.bodyIn;
resultInfo = so.getProperty("SayHelloResult").toString();
Log.e("xml", resultInfo);
showDialog(1);
//第二种方式
// SoapObject so = (SoapObject) envelope.getResponse();
// resultInfo = so.toString();
// Log.e("xml", resultInfo);
// showDialog(1);
//
}
catch(IOException e){
e.printStackTrace();
}
catch(XmlPullParserException e){
e.printStackTrace();
}
}
});
}
//初始化 就执行一次
protected Dialog onCreateDialog(int id){
AlertDialog.Builder builder = new AlertDialog.Builder(WebservicetestActivity.this);
builder.setIcon(R.drawable.ic_launcher);
builder.setTitle(resultInfo);
builder.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
return ;
}
});
return builder.create();
}
//每次showDialog的时候都会执行
protected void onPrepareDialog(int i ,Dialog dialog){
dialog.setTitle(resultInfo);
}
}
任何应用都是以数据为前提的,数据操作对android应用来讲是非常重要的,但是大米的文笔功底真的很差,抱歉,如果感兴趣的朋友可以看大米提供的这些demo代码,虽然都是入门级别的代码,但是我编写了一遍后还是感觉受益匪浅的。