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();
    }
}

至此五种数据存储全部实现。当然,实际开发中可能比这更复杂,会嵌入到别的知识点中,但数据操作无疑是最为基本的一步,是整体项目开发的基础。

时间: 2024-10-12 10:24:36

Android系统的五种数据存储形式(二)的相关文章

Android系统的五种数据存储形式(一)

Android系统有五种数据存储形式,分别是文件存储.SP存储.数据库存储.contentprovider 内容提供者.网络存储.其中,前四个是本地存储.存储的类型包括简单文本.窗口状态存储.音频视频数据.XML注册文件的各种数据.各种存储形式的特点不尽相同,因此对于不同的数据类型有着固定的存储形式,本文为演示方便给出的案例基本相同,都是是采用账号登录来演示数据存储,保存账号和密码信息,下次登录时记住账号和密码.重在说明各种存储形式的原理. 文件存储: 以I/O流的形式把数据存入手机内存或SD卡

android五种数据存储方式

在Android中提供了如下五种数据存储方式: SharedPreferences 文件存储 SQLite数据库方式 内容提供器(ContentProvider) 网络存储 下面具体介绍一下这五种数据存储方式的用法. 1. SharedPreferences SharedPreferences是android平台上的一个轻量级存储类,主要保存一些常用的配置信息.可以保存long.int.String类型数据. SharedPreferences以键值对方式读取和存入,最终会以XML方式保存数据,

安卓五种数据存储的方式

本文介绍Android平台进行数据存储的五大方式,分别如下: 1 使用SharedPreferences存储数据     2 文件存储数据       3 SQLite数据库存储数据 4 使用ContentProvider存储数据 5 网络存储数据 下面详细讲解这五种方式的特点 第一种: 使用SharedPreferences存储数据     适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型.基本类型的值.比如应用程序的各种配置信息(如是否打开音效.是否使用震动效果.小游戏的玩家积分

Android中常用的五种数据存储方式

第一种: 使用SharedPreferences存储数据 适用范围: 保存少量的数据,且这些数据的格式非常简单:字符串型.基本类型的值.比如应用程序的各种配置信息(如是否打开音效.是否使用震动效果.小游戏的玩家积分等),解锁口 令密码等 核心原理: 保存基于XML文件存储的key-value键值对数据,通常用来存储一些简单的配置信息.通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data/<package name

Android中的5种数据存储方式

Ref:http://www.cnblogs.com/hanyonglu/archive/2012/03/01/2374894.html 1 SharedPreferences存储数据 2 文件存储数据 3 SQLite数据库存储数据 4 ContentProvider存储数据 5 网络存储数据 LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发时最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表.増删改查的操

Android 进程的五种生命周期学习

本节学习进程的生命周期: Android系统是尽可能的去保护每一个进程,但是最终需要为新的进程,或者很重要的进程释放以前的老进程.为了决定那个进程被保护,那个被杀死.Android系统根据当前进程中组件的状态,以及运行在进行中的组件决定保留那个,杀死那个进程.当然了系统资源短缺时,进程等级低的先杀死,以此类推. android系统中有五种进程等级: 1: 前台进程(前台进程有五种状态,只有其中一种满足就是前台进程,前台进程是很难被杀死的) a:拥有一个正在与用户交互的Activity(此时Act

Android面试准备 第二天 第五例 数据存储

參考:http://blog.csdn.net/lmj623565791/article/details/24015867 5.Activity用SharedPreferences保存数据,大小有木有限制? 个人理解:SharedPreferences是哪种存储数据的方式居然记不清楚了,个人印象中在第一行代码中,介绍了三种数据存储的方式.记得包括SharedPreferences,还有通过IO流把数据存储到手机本地文件下,仅仅记得SQLite数据库存储的方式.只是它们的使用方式已经忘干净了. 所

iOS 常用四种数据存储方式

iOS 常用四种数据存储方式 在iOS开发过程中,不管是做什么应用,都会碰到数据保存的问题.将数据保存到本地,能够让程序的运行更加流畅, ,使得用户体验更好.下面介绍?一下数据保存的方式: 1.NSKeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法.前?一个方法告诉系统怎么对对象进行编码,而后?一个方法则是告诉系统怎么对对象进行解码.例如对Possession对

Android应用之——不要将数据存储在Application类中

前言:最近在开发中发现了一个比较严重的问题,当我们将应用按home键放入后台运行,一段时间后,当我们再次打开应用的时候,十有八九会出现一个NullPointException的空指针异常,根据logcat的日志,就会定位到一个去全局性到变量去,这是什么原因呢?原来,是因为我们我们将很多数据放入了application中作为全局变量,导致了问题的产生,下面来说下为什么不能将数据放在application中. 一.application类的简介 Application和Activity,Servic