Android系统的五种数据存储形式(二)
之前介绍了Android系统下三种数据存储形式,今天补充介绍另外两种,分别是内容提供者和网络存储。有些人可能认为内存提供者和网络存储更偏向于对数据的操作而不是数据的存储,但这两种方式确实与数据有关,所以这里还是将这两种形式简要的说明一下。
Content Provider:
Content Provider,中文名是内存提供者,Android四大组件之一,内容提供者是应用程序之间共享数据的接口,以数据库形式存入手机内存,可以共享自己的数据给其他应用使用。之所以需要设计一个单独的控件来操作数据,是为了实现应用程序之间的数据传递。通过查看DDMS中的目录结构可以看出,数据库文件对于其他应用来说是不可读、不可写,而日常生活中又需要获取其他应用的数据,尤其是系统自带软件的数据。比如打开QQ或者微信时会提示是否同步联系人,又比如备份短信的时候,这些都需要访问和操作其他应用的数据库。因此谷歌工程师在底层软件中集成了大量的方法利用内存提供者的原理,类似于在数据库中提供一个对外访问的路径,供其他应用访问。
为了更好的理解内存提供者的工作原理,可以自定义一个内容提示者来帮助理解。首先写一个类继承ContentProvider,实现该类中的方法,包括一些增删改查和数据初始化的方法,可以在方法中实现对数据库的增删改查操作。数据库本来是不对外开放的,所以为保护数据,类中的方法原始返回数据均是空类型。为保证数据的安全性,可以创建一个UriMatcher对象,利用addURIf方法添加Uri的路径规则,在每一次进行数据操作时先判断传入的路径是否符合命名规则。使用内存提供者需要在配置文件中添加provider标签,指定主机名。只有当访问者与内容提供者的主机名一致时,才可以建立数据连接。在另一个应用中实现对内存提供者的访问。具体操作是:创建内容提供者解析器,定义要访问的Uri的路径。Uri路径有着固定的格式:”content://主机名/匹配字符”。 利用内容提供者解析器进行增删改查,和要操作的数据库之间建立联系。以上内容通常用来理解内容提供者的工作原理,实际工作中很少用到自定义的内容提示者。实际中用的比较多的是用内容提供者操作系统联系人、系统短信等系统应用的数据库。
内容提供者操作系统应用时相对简单,需要用到的大部分程序已经在底层实现,要做的是调用各种方法和相关的参数。需要关注的参数有Uri路径、数据库的表单结构。可以通过查看底层的代码获取相应的参数。其中有些常用的参数可以记下来,方便调用。比如获取全部短信的Uri路径是: content://sms。与联系人有关的数据库表单有三个raw_contacts、data、mimetypes。操作raw_contacts 表的Uri是: content://com.android.contacts/raw_contacts,操作data 表的Uri是: content://com.android.contacts/data。本文以短信的备份、还原来演示利用内容提供者访问短信数据库。
先看一下短信和手机联系人有关的数据库所在的路径。短信在Android 模拟器下存放在的路径是:/data/data/com.android.providers.telephony/databases/目录,联系人在Android 模拟器下存放在的路径是:/data/data/com.android.providers.contacts/databases/目录。对于短信数据库我们关心的表数据有:address、type、body、date,分别表示发送者号码、短信类型(收还是发)、短信内容、日期。对于联系人数据库的三张表一定要按照一定的顺序依次查找才能得到相关的数据,在这不做解释。尽管开发的时候不需要了解短信和手机联系人的数据库路径,但是要明白短信和手机联系人的数据是存在数据库中的,同时数据库对外是不开放的。
与短信有关的数据库的目录结构:

本文给出的案例是短信的备份和还原,从而实现对系统应用数据库的操作。首先利用内容提供者查询到短信数据库里的详细参数,将该数据以Xml文件的形式存入到指定的文件夹。利用xml解析得到数据,将获取的数据存在一个工具类中,这样就能用ListView将数据显示在界面上。具体实现如下。
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;
/**
* 短信的备份和还原
* 添加写短信和读短信的权限
* <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();
}
//数据刷新,一般用到ListView时最好记得刷新数据否则不显示
public void datafresh(){
mlist = getList();
if(adapter == null){
adapter = new Myadpter();
}else {
adapter.notifyDataSetChanged();
}
}
//用ListView显示短信内容
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, "数据保存成功", 0).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public void restore(View view){
datafresh();
lv.setAdapter(adapter);
}
//利用Xml解析得到短信内容
private List<Person> getList() {
ContentResolver resolver = getContentResolver();
List<Person> list = new ArrayList();//创建一个Person类存储标签内容
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;
}
}
}
效果演示,我的虚拟机中只存了一条短信:

网络存储:
网络存储是最容易理解的一种存储方式了。其实说简单点就是文件的上传和下载。经常听到的云备份就是这种形式。优势也很明显,即把数据存储到服务器,不存储在本地,使用的时候直接从网络获取,避免了手机端信息丢失以及其他的安全隐患。因此,对于这种形式就不作多的解释,直接给出一个文件的上传和下载的实例来演示网络存储。
代码实现:
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;
//访问网络操作耗时,需要在子线程中加一个代理
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();
// 这里为方便把路径写死,这种方式不太正规,一般可以读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来访问,两者结果一样
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();
}
}
至此五种数据存储全部实现。当然,实际开发中可能比这更复杂,会嵌入到别的知识点中,但数据操作无疑是最为基本的一步,是整体项目开发的基础。
Android系统的五种数据存储形式(二)的更多相关文章
- Android系统的五种数据存储形式(一)
Android系统有五种数据存储形式,分别是文件存储.SP存储.数据库存储.contentprovider 内容提供者.网络存储.其中,前四个是本地存储.存储的类型包括简单文本.窗口状态存储.音频视频 ...
- Android五种数据存储方式
android 五种数据存储 :SharePreferences.SQLite.Contert Provider.File.网络存储 Android系统提供了四种存储数据方式.分别为:SharePre ...
- Android中的5种数据存储方式
本文转自 http://hi.baidu.com/maguowei/blog/item/7aca46c25574a33ae5dd3ba4.htmlAndroid数据存储Android提供了5种方式存 ...
- Android中常用的五种数据存储方式
第一种: 使用SharedPreferences存储数据 适用范围: 保存少量的数据,且这些数据的格式非常简单:字符串型.基本类型的值.比如应用程序的各种配置信息(如是否打开音效.是否使用震动效果.小 ...
- [ Android 五种数据存储方式之四 ] —— ContentProvider存储数据
Android这个系统和其他的操作系统还不太一样,我们需要记住的是,数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据.那这个时候有读者就会提出问题,难道两个 ...
- [ Android 五种数据存储方式之三 ] —— SQLite存储数据
SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能.此外它还是开源的,任何人都可以使用它.许多开源项目((Mozilla, PHP, Python)都使用了 ...
- [ Android 五种数据存储方式之二 ] —— 文件存储数据
关于文件存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的. 文件可用来存放大量数据,如文本.图片.音 ...
- [ Android 五种数据存储方式之一 ] —— SharedPreferences存储数据
SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数. 主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceS ...
- Android编程中的5种数据存储方式
Android编程中的5种数据存储方式 作者:牛奶.不加糖 字体:[增加 减小] 类型:转载 时间:2015-12-03我要评论 这篇文章主要介绍了Android编程中的5种数据存储方式,结合实例形式 ...
随机推荐
- 前端学PHP之数组函数
× 目录 [1]键值操作 [2]记数[3]回调函数[4]组合[5]栈和队列[6]顺序 前面的话 PHP中的数组功能非常强大,数组处理函数有着强大.灵活.高效的特点.PHP5提供了近100个操作数组的系 ...
- .Net 转战 Android 4.4 日常笔记(9)--常用组件的使用方法[附源码]
经过两天的学习,把常用的组件都学习了一遍,并做成了App 学习可能真没有捷径,跟学习html有点类似,都是一个控件一个控件学习并使用,最后拼凑成一个系统 链接:http://pan.baidu.com ...
- DOM扩展-HTML5、专有扩展
HTML5 与类相关的扩充 1.getElementsByClassName()方法 改方法接受一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的NodeList.传入多个类型时, ...
- Android图片选择器
1.概述 应公司项目需求,要做一个图片选择器,网上搜索了一些源码,我在别人的基础上进行了修改,另外页面也进行了重整,我的是先加载图片文件夹列表,然后再进入选择图片. 参考博客地址 ...
- java多线程--多线程基础小结
什么是线程? 在同一个进程中可以执行多个任务,每一个任务可以看做一个线程. 线程是程序的执行单元,执行路径,使程序使用cpu的最基本单位 一个进程如果只有一条执行路径,那么就是单线程的 一个进程如果有 ...
- (十九)WebGIS中I查询的原理及设计(包含AGS、GeoServer、Supermap)
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 我们在使用arcmap时,经常会用到被称为I查询的工具.具体 ...
- JS代码实现的聊天框
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Android中Application的应用
当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息.通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Appli ...
- Apworks框架实战(二):开始使用
要使用Apworks进行应用程序开发,您需要安装Visual Studio 2012以上的版本(以下简称Visual Studio 2012+),从Apworks 2.5开始,需要Micrsoft.N ...
- nodejs学习笔记一——nodejs安装
a.nodejs安装 nodejs的安装没有什么说的默认安装即可.安装包官网下载即可:nodejs官网 本人用的是window的安装包node-v4.2.6-x64.msi 安装完成后打开命令行查看使 ...