Android系統(tǒng)的五種數(shù)據(jù)存儲形式實例(二)
之前介紹了Android系統(tǒng)下三種數(shù)據(jù)存儲形式,http://www.fzitv.net/article/99468.htm。今天補充介紹另外兩種,分別是內(nèi)容提供者和網(wǎng)絡(luò)存儲。有些人可能認(rèn)為內(nèi)存提供者和網(wǎng)絡(luò)存儲更偏向于對數(shù)據(jù)的操作而不是數(shù)據(jù)的存儲,但這兩種方式確實與數(shù)據(jù)有關(guān),所以這里還是將這兩種形式簡要的說明一下。
Content Provider:
Content Provider,中文名是內(nèi)存提供者,Android四大組件之一,內(nèi)容提供者是應(yīng)用程序之間共享數(shù)據(jù)的接口,以數(shù)據(jù)庫形式存入手機內(nèi)存,可以共享自己的數(shù)據(jù)給其他應(yīng)用使用。之所以需要設(shè)計一個單獨的控件來操作數(shù)據(jù),是為了實現(xiàn)應(yīng)用程序之間的數(shù)據(jù)傳遞。通過查看DDMS中的目錄結(jié)構(gòu)可以看出,數(shù)據(jù)庫文件對于其他應(yīng)用來說是不可讀、不可寫,而日常生活中又需要獲取其他應(yīng)用的數(shù)據(jù),尤其是系統(tǒng)自帶軟件的數(shù)據(jù)。比如打開QQ或者微信時會提示是否同步聯(lián)系人,又比如備份短信的時候,這些都需要訪問和操作其他應(yīng)用的數(shù)據(jù)庫。因此谷歌工程師在底層軟件中集成了大量的方法利用內(nèi)存提供者的原理,類似于在數(shù)據(jù)庫中提供一個對外訪問的路徑,供其他應(yīng)用訪問。
為了更好的理解內(nèi)存提供者的工作原理,可以自定義一個內(nèi)容提示者來幫助理解。首先寫一個類繼承ContentProvider,實現(xiàn)該類中的方法,包括一些增刪改查和數(shù)據(jù)初始化的方法,可以在方法中實現(xiàn)對數(shù)據(jù)庫的增刪改查操作。數(shù)據(jù)庫本來是不對外開放的,所以為保護數(shù)據(jù),類中的方法原始返回數(shù)據(jù)均是空類型。為保證數(shù)據(jù)的安全性,可以創(chuàng)建一個UriMatcher對象,利用addURIf方法添加Uri的路徑規(guī)則,在每一次進行數(shù)據(jù)操作時先判斷傳入的路徑是否符合命名規(guī)則。使用內(nèi)存提供者需要在配置文件中添加provider標(biāo)簽,指定主機名。只有當(dāng)訪問者與內(nèi)容提供者的主機名一致時,才可以建立數(shù)據(jù)連接。在另一個應(yīng)用中實現(xiàn)對內(nèi)存提供者的訪問。具體操作是:創(chuàng)建內(nèi)容提供者解析器,定義要訪問的Uri的路徑。Uri路徑有著固定的格式:”content://主機名/匹配字符”。 利用內(nèi)容提供者解析器進行增刪改查,和要操作的數(shù)據(jù)庫之間建立聯(lián)系。以上內(nèi)容通常用來理解內(nèi)容提供者的工作原理,實際工作中很少用到自定義的內(nèi)容提示者。實際中用的比較多的是用內(nèi)容提供者操作系統(tǒng)聯(lián)系人、系統(tǒng)短信等系統(tǒng)應(yīng)用的數(shù)據(jù)庫。
內(nèi)容提供者操作系統(tǒng)應(yīng)用時相對簡單,需要用到的大部分程序已經(jīng)在底層實現(xiàn),要做的是調(diào)用各種方法和相關(guān)的參數(shù)。需要關(guān)注的參數(shù)有Uri路徑、數(shù)據(jù)庫的表單結(jié)構(gòu)。可以通過查看底層的代碼獲取相應(yīng)的參數(shù)。其中有些常用的參數(shù)可以記下來,方便調(diào)用。比如獲取全部短信的Uri路徑是: content://sms。與聯(lián)系人有關(guān)的數(shù)據(jù)庫表單有三個raw_contacts、data、mimetypes。操作raw_contacts 表的Uri是: content://com.android.contacts/raw_contacts,操作data 表的Uri是:content://com.android.contacts/data。本文以短信的備份、還原來演示利用內(nèi)容提供者訪問短信數(shù)據(jù)庫。
先看一下短信和手機聯(lián)系人有關(guān)的數(shù)據(jù)庫所在的路徑。短信在Android 模擬器下存放在的路徑是:/data/data/com.android.providers.telephony/databases/目錄,聯(lián)系人在Android 模擬器下存放在的路徑是:/data/data/com.android.providers.contacts/databases/目錄。對于短信數(shù)據(jù)庫我們關(guān)心的表數(shù)據(jù)有:address、type、body、date,分別表示發(fā)送者號碼、短信類型(收還是發(fā))、短信內(nèi)容、日期。對于聯(lián)系人數(shù)據(jù)庫的三張表一定要按照一定的順序依次查找才能得到相關(guān)的數(shù)據(jù),在這不做解釋。盡管開發(fā)的時候不需要了解短信和手機聯(lián)系人的數(shù)據(jù)庫路徑,但是要明白短信和手機聯(lián)系人的數(shù)據(jù)是存在數(shù)據(jù)庫中的,同時數(shù)據(jù)庫對外是不開放的。
與短信有關(guān)的數(shù)據(jù)庫的目錄結(jié)構(gòu):

本文給出的案例是短信的備份和還原,從而實現(xiàn)對系統(tǒng)應(yīng)用數(shù)據(jù)庫的操作。首先利用內(nèi)容提供者查詢到短信數(shù)據(jù)庫里的詳細(xì)參數(shù),將該數(shù)據(jù)以Xml文件的形式存入到指定的文件夾。利用xml解析得到數(shù)據(jù),將獲取的數(shù)據(jù)存在一個工具類中,這樣就能用ListView將數(shù)據(jù)顯示在界面上。具體實現(xiàn)如下。
package com.example.contentprovider;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* 短信的備份和還原
* 添加寫短信和讀短信的權(quán)限
* <uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
* @author Huang
*/
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private ListView lv;
private List<Person> mlist;
private Myadpter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
datafresh();
}
//數(shù)據(jù)刷新,一般用到ListView時最好記得刷新數(shù)據(jù)否則不顯示
public void datafresh(){
mlist = getList();
if(adapter == null){
adapter = new Myadpter();
}else {
adapter.notifyDataSetChanged();
}
}
//用ListView顯示短信內(nèi)容
public class Myadpter extends BaseAdapter{
public int getCount() {
// TODO Auto-generated method stub
return mlist.size();
}
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = new TextView(MainActivity.this);
tv.setText(mlist.get(position).toString());
return tv;
}
}
//短信備份
public void bankup(View view){
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
Cursor cursor = resolver.query(uri, new String[]{"address","body","type","date"}, null, null, null);
while(cursor.moveToNext()){
String address = cursor.getString(0);
String body = cursor.getString(1);
String type = cursor.getString(2);
String date = cursor.getString(3);
//序列化,把短信以Xml文件的形式存儲
XmlSerializer serializer = Xml.newSerializer();
File file = new File(getFilesDir(),"info.xml");
try {
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
serializer.startDocument("utf-8", true);
serializer.startTag(null, "person");
serializer.startTag(null, "address");
serializer.text(address);
serializer.endTag(null, "address");
serializer.startTag(null, "body");
serializer.text(body);
serializer.endTag(null, "body");
serializer.startTag(null, "type");
serializer.text(type);
serializer.endTag(null, "type");
serializer.startTag(null, "date");
serializer.text(date);
serializer.endTag(null, "date");
serializer.endTag(null, "person");
serializer.endDocument();
fos.close();
// Toast.makeText(this, "數(shù)據(jù)保存成功", 0).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void restore(View view){
datafresh();
lv.setAdapter(adapter);
}
//利用Xml解析得到短信內(nèi)容
private List<Person> getList() {
ContentResolver resolver = getContentResolver();
List<Person> list = new ArrayList();//創(chuàng)建一個Person類存儲標(biāo)簽內(nèi)容
Person p = new Person();
File file = new File(getFilesDir(),"info.xml");
XmlPullParser pullParser = Xml.newPullParser();
try {
FileInputStream fis = new FileInputStream(file);
pullParser.setInput(fis, "utf-8");
int mtype = pullParser.getEventType();
while(mtype != XmlPullParser.END_DOCUMENT){
String name = pullParser.getName();
switch (mtype){
case XmlPullParser.START_TAG:
if("address".equals(name)){
String address = pullParser.nextText();
p.setAddress(address);
}else if("body".equals(name)){
String body = pullParser.nextText();
p.setBody(body);
}else if("type".equals(name)){
String type = pullParser.nextText();
p.setType(type);
}else if("date".equals(name)){
String date = pullParser.nextText();
p.setDate(date);
}
break;
case XmlPullParser.END_TAG:
if("person".equals(name)){
list.add(p);
}
break;
}
mtype = pullParser.next();
}
Log.i(TAG, list.toString());
return list;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
效果演示,我的虛擬機中只存了一條短信:

網(wǎng)絡(luò)存儲:
網(wǎng)絡(luò)存儲是最容易理解的一種存儲方式了。其實說簡單點就是文件的上傳和下載。經(jīng)常聽到的云備份就是這種形式。優(yōu)勢也很明顯,即把數(shù)據(jù)存儲到服務(wù)器,不存儲在本地,使用的時候直接從網(wǎng)絡(luò)獲取,避免了手機端信息丟失以及其他的安全隱患。因此,對于這種形式就不作多的解釋,直接給出一個文件的上傳和下載的實例來演示網(wǎng)絡(luò)存儲。
代碼實現(xiàn):
package com.example.upload;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.http.Header;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
protected static final int SUCCESS = 1;
protected static final int ERORR = 2;
private EditText et_path;
private ImageView iv;
//訪問網(wǎng)絡(luò)操作耗時,需要在子線程中加一個代理
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SUCCESS:
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
break;
case ERORR:
Toast.makeText(MainActivity.this, "圖片獲取失敗", 0).show();
break;
}
super.handleMessage(msg);
}};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
iv = (ImageView) findViewById(R.id.iv);
}
//上傳程序
public void upload(View view){
// String path = et_path.getText().toString().trim();
// 這里為方便把路徑寫死,這種方式不太正規(guī),一般可以讀et_path來訪問
String path = "/mnt/sdcard/info.txt";
if(TextUtils.isEmpty(path)){
Toast.makeText(this, "路徑不能為空", 0).show();
return;
}
File file = new File(path);
AsyncHttpClient client = new AsyncHttpClient();
RequestParams param = new RequestParams();
try {
param.put("file", file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
client.post("http://192.168.1.114:8080/",param, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "提交成功", 0).show();
}
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "提交失敗", 0).show();
}
});
}
//下載程序
public void download(View view){
new Thread(){
public void run() {
try {
//這里為方便把路徑寫死,可以讀et_path來訪問,兩者結(jié)果一樣
URL url = new URL("http://192.168.1.114:8080/demo1.png");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if(code == 200){
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
Message msg = Message.obtain();
msg.what = SUCCESS;
msg.obj = bitmap;
handler.sendMessage(msg);
is.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Message msg = Message.obtain();
msg.what = ERORR;
handler.sendMessage(msg);
}
};
}.start();
}
}
至此五種數(shù)據(jù)存儲全部實現(xiàn)。當(dāng)然,實際開發(fā)中可能比這更復(fù)雜,會嵌入到別的知識點中,但數(shù)據(jù)操作無疑是最為基本的一步,是整體項目開發(fā)的基礎(chǔ)。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android開發(fā)筆記之: 數(shù)據(jù)存儲方式詳解
- 在android開發(fā)中進行數(shù)據(jù)存儲與訪問的多種方式介紹
- Android 數(shù)據(jù)存儲方式有哪幾種
- Android編程中的5種數(shù)據(jù)存儲方式
- Android使用文件進行數(shù)據(jù)存儲的方法
- Android數(shù)據(jù)存儲之SQLite使用
- 四種Android數(shù)據(jù)存儲方式
- android數(shù)據(jù)存儲之文件存儲方法
- 詳解Android數(shù)據(jù)存儲之Android 6.0運行時權(quán)限下文件存儲的思考
- Android系統(tǒng)的五種數(shù)據(jù)存儲形式實例(一)
相關(guān)文章
Android安裝應(yīng)用 INSTALL_FAILED_DEXOPT 問題及解決辦法
這篇文章主要介紹了Android安裝應(yīng)用 INSTALL_FAILED_DEXOPT 解決辦法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Android打開淘寶客戶端(手淘)效果及實現(xiàn)代碼
這篇文章主要介紹了Android打開淘寶客戶端(手淘)效果及實現(xiàn)代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-04-04
基于Android 監(jiān)聽ContentProvider 中數(shù)據(jù)變化的相關(guān)介紹
本篇文章小編為大家介紹,基于Android 監(jiān)聽ContentProvider 中數(shù)據(jù)變化的相關(guān)介紹。需要的朋友參考下2013-04-04

