Android-ContentProvider数据库操作

在实际的开发过程中,Android提供了5种方式存储数据:   

1.文件存储数据     2.使用 Sharedpreferences 存储数据     3.SQLite数据库存储数据     4.使用ContentProvider存储数据     5.网络存储数据

首先我们先简单了解下文件、SharedPreferred如何进行数据存储

1.文件存储操作

    文件存储一般存储在sdcard或者ROM,当文件存储在ROM上时,如果是存储在除自己私有空间外(data/data/包名/),在其他地方是需要system系统权限的(http://my.oschina.net/zhoulc/blog/119282)。
    文件IO流创建比较简单,就不详述了,在进行文件存储的时候注意两个路径方法就行。     存储到ROM:         Context mContext = XXXActivity.this;         mContext.getFilesDir().getAbsolutePath();         获取路径:/data/data/包名/files     存储到sdCard :         首先判断sdcard是否可用          Environment.getExternalStorageState()是否等于Environment.MEDIA_MOUNTED          Environment.getExternalStorageDirectory().getAbsolutePath()          对sdcard目录操作需要添加权限:          <!-- 添加对SDCARD的写权限 -->             < uses-permission   android:name          ="android.permission.WRITE_EXTERNAL_STORAGE" >          获取路径:/sdcard     拓展下 :支持u盘的移动设备,u盘一般都是挂载mnt/sda/sdal外设上面     顺带说下以前一直纠结的一个问题:         getApplicationContext() 生命周期是整个应用,应用摧毁它才摧毁         Activity.this的context属于activity ,activity 摧毁他就摧毁

2.通过Sharedpreferences进行数据存储

 

 

private final static String PERFS_NAME = "com.zlc.test";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //存放
        //第一个参数是存储时的名称,第二个参数则是文件的打开方式(Context.
MODE_PRIVATE)
        SharedPreferences userInfo = getSharedPreferences(PERFS_NAME,0);
        SharedPreferences.Editor editor = userInfo.edit();
        editor.putString("userName", "zhoulc");
        editor.putString("passWord", "ds");
        editor.commit();
        //读取
        System.out.println("##########################################");
        SharedPreferences readInfo = getSharedPreferences(PERFS_NAME, 0);
        String name = readInfo.getString("userName", "no_name");
        String psword = readInfo.getString("passWord", "000000");
        System.out.println("name = "+name+" password = "+psword);
    }

 

使用过程中注意三个问题: 
    1)getSharedPreferences有两个参数,第一个参数是存储时的名称, 第二个参数则是文件的打开方式(一般使用0或者Context.MODE_PRIVATE)     2)默认的模式为0或MODE_PRIVATE,如果访问其他应用中的Preference,前提条件是:该 preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。     3)千万不要偷懒写成         userInfo.edit().putString("userName", "zhoulc");         userInfo.edit().putString("passWord", "ds");         userInfo.edit().commit();         edit()方法每次都要产生一个新的SharedPreferences.Edit类的对象,所以commit 之后数据没有存储成功。

 

3.其他几个就不详细介绍了,说下今天的重点ContentProvider

    创建一个可对数据库进行操作的ContentProvider我们先要了解涉及到的知识点:     (1)Parcelable(实体类数据一般会用在进程之间通信)            http://my.oschina.net/zhoulc/blog/172163     (2)BaseColumns,这个类只是提供了两个字段,一个是"_id"一个是"_count",便于调用数据库时导致拼写错误,你也可以扩展它,或者自定义这么个,然后直接调用它的常量名,防止写sql语句时把列名拼错。

 

 

package android.provider;

public interface BaseColumns
{
    /**
     * The unique ID for a row.
     * <P>Type: INTEGER (long)</P>
     */
    public static final String _ID = "_id";

    /**
     * The count of rows in a directory.
     * <P>Type: INTEGER</P>
     */
    public static final String _COUNT = "_count";
}

 

(3)SQLiteOpenHelper,包装了数据库的创建、打开、更新的抽象类,通过实现和使用SQLiteOpenHandle可以隐去数据库打开的之前判断数据库是否需要创建或更新的逻辑。              主要实现三个方法             创建数据库:public DatabaseHelper(Context context)              创建表:public void onCreate(SQLiteDatabase db)             更新表:public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)      (4)UriMatcher用来匹配URI。             主要实现两个接口:              添加需要匹配的URI:MATCHER.addURI(String authority, String path, int code) 进行匹配返回定义的value;MATCHER.match(Uri uri)             例:                 MATCHER.addURI(Information.AUTHORITY, "informations", INFORMATIONS);                 MATCHER.addURI(Information.AUTHORITY, "informations/#", INFORMATION);                 # 号为通配符                 * 号为任意字符     (5)ContentUris一个工具类,主要是用来处理使用 "content" 约束的Uri对象,经常用到条件查询某个_ID的数据。             主要实现两个接口:             static long parseId(Uri contentUri)  将uri中的id解析出来,此方法返回的是一个long型的id。             static Uri withAppendedId(Uri contentUri, long id)在指定的uri后面添加一条id 为指定参数的记录。      (6)SQLiteDatabase,这个类是核心类,用于管理和操作SQLite数据库,几乎所有的数据库操作,最终都将由这个类完成。     (7)ContentProvider,提供数据库增删改查的接口。             主要实现接口:             public int delete(Uri uri, String selection, String[] selectionArgs)             public String getType(Uri uri)             public Uri insert(Uri uri, ContentValues values)             public boolean onCreate()             public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)             public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)     (8)ContentResolver接口和ContentProvider类似,两个区别一个是用户是通过ContentResolver调用ContentProvider提供的接口。     以上类都只是简单介绍,有兴趣的朋友可以去研究下源码和实现过程。这里主要讲使用过程和一些注意细节。
4.ContentProvider 在使用过程中需要注意两点
    (1)在AndroidManifest.xml里面需要注册这个ContentProvider                  <provider android:name=".MyProvider" android:authorities="com.zlc.provider.MyProvider" />     (2)当提供给其他进程调用(假如是其他应用),如果当前提供ContentProvider的应用在退出的时候把自己给kill掉了(这个时候ContentProvider也无法使用),则我们需要在AndroidManifest.xml里面再添加一条属性             android:process="com.zlc.provider.MyProvider"             为该组件指定一个不同的默认进程

 

5.下面贴一下具体实现代码

     (1)ContentProvider提供者TestContentProvider.apk             

实体类:Information.java 

package com.zlc.provider;

import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.BaseColumns;

public class Information implements Parcelable,BaseColumns{
    public final static String AUTHORITY = "com.zlc.provider.MyProvider";
    public final static Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/informations");
    //表字段
    public final static String INFO_ID = "info_id";
    public final static String INFO_NAME = "info_name";
    public final static String INFO_AGE = "info_age";
    
    private String info_id,info_name,info_age;

    public Information(Parcel source){
        info_id = source.readString();
        info_name = source.readString();
        info_age = source.readString();
    }
    public String getInfo_id() {
        return info_id;
    }

    public void setInfo_id(String info_id) {
        this.info_id = info_id;
    }

    public String getInfo_name() {
        return info_name;
    }

    public void setInfo_name(String info_name) {
        this.info_name = info_name;
    }

    public String getInfo_age() {
        return info_age;
    }

    public void setInfo_age(String info_age) {
        this.info_age = info_age;
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeString(info_id);
        dest.writeString(info_name);
        dest.writeString(info_age);
    }
    public final static Parcelable.Creator<Information> CREATOR = new Parcelable.Creator<Information>() {

        @Override
        public Information createFromParcel(Parcel source) {
            // TODO Auto-generated method stub
            return new Information(source);
        }

        @Override
        public Information[] newArray(int size) {
            // TODO Auto-generated method stub
            return new Information[size];
        }
    };
}

MyProvider.java :

package com.zlc.provider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

public class MyProvider extends ContentProvider {
    private SQLiteDatabase sqDb;
    private DatabaseHelper helper;
    // 数据库名
    private final static String DATABASE_NAME = "zhoulc.db";
    // 版本
    private static final int DATABASE_VERSION = 1;
    // 表名
    private static final String TABLE_NAME = "Information";
    // 创建表的sql语句
    private final static String CREATE_TABLE = "Create table " + TABLE_NAME
            + "( "+Information._ID+" integer primary key autoincrement," + Information.INFO_ID
            + " TEXT," + Information.INFO_NAME + " TEXT," + Information.INFO_AGE
            + " TEXT);";

    // Declaration Datababsehelper
    private static class DatabaseHelper extends SQLiteOpenHelper {

        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            // TODO Auto-generated method stub
            db.execSQL(CREATE_TABLE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
            db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME);
            onCreate(db);
        }

    }

    // UriMatcher add URI
    private static final UriMatcher MATCHER = new UriMatcher(
            UriMatcher.NO_MATCH);
    private final static int INFORMATIONS = 1;
    private final static int INFORMATION = 2;
    static {
        MATCHER.addURI(Information.AUTHORITY, "informations", INFORMATIONS);
        MATCHER.addURI(Information.AUTHORITY, "informations/#", INFORMATION);
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        sqDb = helper.getWritableDatabase();
        int count = 0;
        switch (MATCHER.match(uri)) {
        case INFORMATIONS:
            count = sqDb.delete(TABLE_NAME, selection, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
        return count;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        switch (MATCHER.match(uri)) {
        case INFORMATIONS:
            return "vnd.android.cursor.dir/Information";
        case INFORMATION:
            return "vnd.android.cursor.item/Information";
        default:
            throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        sqDb = helper.getWritableDatabase();
        Uri insertUri = null;
        long rowid = 0;
        switch (MATCHER.match(uri)) {
        case INFORMATIONS:
            rowid = sqDb.insert(TABLE_NAME, Information.INFO_ID, values);
            insertUri = ContentUris.withAppendedId(uri, rowid);
            Log.i("zhoulc", "insert record...values:" + values.toString());
            break;
        default:
            throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
        return insertUri;
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        helper = new DatabaseHelper(getContext());
        return helper == null ? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        sqDb = helper.getWritableDatabase();
        switch (MATCHER.match(uri)) {
        case INFORMATIONS:
            Cursor cursor = sqDb.query(TABLE_NAME, projection, selection,
                    selectionArgs, null, null, sortOrder);
            return cursor;
        case INFORMATION://条件查询,
            long id = ContentUris.parseId(uri);
            String where = Information._ID + "=" + id; 
             if (selection != null && !"".equals(selection))  
             {  
                 where = where + " and " + selection;  
             }  
             return sqDb.query(TABLE_NAME, projection, where, selectionArgs, null,  
                 null, sortOrder);  
        default:
            throw new IllegalArgumentException("unknow uri" + uri.toString());
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        sqDb = helper.getWritableDatabase();
        int count = 0;
        switch (MATCHER.match(uri)) {
        case INFORMATIONS:
            count = sqDb.update(TABLE_NAME, values, selection, selectionArgs);
            return count;
        default:
            throw new IllegalArgumentException("unknow uri" + uri.toString());
        }
        
    }

}

在同一个应用本身里面(进程)测试插入数据:MainActivity.java:

package com.zlc.provider;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Context mContext = MainActivity.this;
        Uri myUri = Information.CONTENT_URI;
        ContentValues values = new ContentValues();
        values.put(Information.INFO_NAME, "zhoulc");
        values.put(Information.INFO_AGE, "99");
        getContentResolver().insert(myUri, values);
        
    }
}


运行结果:              

其他应用里面测试(跨进程使用)

            TestActivity.java

 

package com.zlc.database;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;

public class TestActivity extends Activity {
    private ContentResolver resolver;
    public final static String AUTHORITY = "com.zlc.provider.MyProvider";
    private  final static Uri CONTENT_URIS = Uri.parse("content://"+AUTHORITY+"/informations");
    private  final static Uri CONTENT_URI =  Uri.parse("content://"+AUTHORITY+"/informations/1");
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        resolver = getContentResolver();
        //step 1 insert data
        ContentValues values = new ContentValues();
        values.put("info_name", "zhoulc");
        values.put("info_age", "24");
        insert(CONTENT_URIS,values);
        //查询
        Cursor cursor = query(CONTENT_URI,null,null,null,null);
        if(cursor != null){
            if(cursor.moveToFirst()){
                int index[] = new int[]{
                    cursor.getColumnIndex("info_id"),
                    cursor.getColumnIndex("info_name"),
                    cursor.getColumnIndex("info_age")
                };
                do {
                    System.out.println(cursor.getString(index[1]));
                    System.out.println(cursor.getString(index[2]));
                } while (cursor.moveToNext());
            }
        }
    }
//    该方法用于往ContentProvider添加数据。
    public Uri insert(Uri uri,ContentValues values){
        Uri dst = resolver.insert(uri, values);
        return dst;
    }
//    该方法用于从ContentProvider删除数据。
    public int delete(Uri uri,String where, String[] selectionArgs){
        int colums = resolver.delete(CONTENT_URI, where, selectionArgs);
        return colums;
    }
//    该方法用于更新ContentProvider中的数据。
    public int update(Uri uri,ContentValues values, String where, String[] selectionArgs){
        int colums = resolver.update(CONTENT_URI, values, where, selectionArgs);
        return colums;
    }
//    该方法用于从ContentProvider中获取数据。
    public Cursor query(Uri uri,String[] projection, String where, String[] selectionArgs, String sortOrder){
        Cursor cursor = resolver.query(CONTENT_URI, projection, where, selectionArgs, sortOrder);
        return cursor;
    }
        
}

 

6.运行过程中遇到的一些问题以及如何解决

(1)Open quote is expected for attribute "{1}" associated with an element type "
android:authorities".
  android:authorities="com.zlc.provider.MyProvider" /  

解决:不能使用中文的引号

(2)E/AndroidRuntime( 4509): FATAL EXCEPTION: main
E/AndroidRuntime( 4509): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.zlc.provider/com.zlc.provider.MainActivity}: java.lang.IllegalArgumentException: Unknown URL content://com.zlc.provider.MyProviderinformations
E/AndroidRuntime( 4509):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
E/AndroidRuntime( 4509):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
E/AndroidRuntime( 4509):        at android.app.ActivityThread.access$600(ActivityThread.java:141)
E/AndroidRuntime( 4509):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
E/AndroidRuntime( 4509):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 4509):        at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime( 4509):        at android.app.ActivityThread.main(ActivityThread.java:5041)
E/AndroidRuntime( 4509):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 4509):        at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime( 4509):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime( 4509):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
E/AndroidRuntime( 4509):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 4509): Caused by: java.lang.IllegalArgumentException: Unknown URL content://com.zlc.provider.MyProviderinformations
E/AndroidRuntime( 4509):        at android.content.ContentResolver.insert(ContentResolver.java:862)
E/AndroidRuntime( 4509):        at com.zlc.provider.MainActivity.onCreate(MainActivity.java:17)
E/AndroidRuntime( 4509):        at android.app.Activity.performCreate(Activity.java:5104)
E/AndroidRuntime( 4509):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
E/AndroidRuntime( 4509):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
E/AndroidRuntime( 4509):        ... 11 more
W/ActivityManager( 2138):   Force finishing activity com.zlc.provider/.MainActivity
D/dalvikvm( 2138): GC_FOR_ALLOC freed 596K, 13% free 7327K/8412K, paused 63ms, total 63ms

声明的时候掉了一个public final static Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/informations");

(3)E/AndroidRuntime( 5411): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.zlc.provider/com.zlc.provider.MainActivity}: android.database.sqlite.SQLiteException: near "tableInformation": syntax error (code 1): , while compiling: Create tableInformation(_id integer primary key autoincrement,info_idTEXT,info_nameTEXT,info_ageTEXT);
E/AndroidRuntime( 5411):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
E/AndroidRuntime( 5411):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
E/AndroidRuntime( 5411):        at android.app.ActivityThread.access$600(ActivityThread.java:141)
E/AndroidRuntime( 5411):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
E/AndroidRuntime( 5411):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 5411):        at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime( 5411):        at android.app.ActivityThread.main(ActivityThread.java:5041)
E/AndroidRuntime( 5411):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 5411):        at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime( 5411):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime( 5411):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
E/AndroidRuntime( 5411):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 5411): Caused by: android.database.sqlite.SQLiteException: near "tableInformation": syntax error (code 1): , while compiling: Create tableInformation(_id integer primary key autoincrement,info_idTEXT,info_nameTEXT,info_ageTEXT);
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:882)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:493)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1663)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1594)
E/AndroidRuntime( 5411):        at com.zlc.provider.MyProvider$DatabaseHelper.onCreate(MyProvider.java:39)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)
E/AndroidRuntime( 5411):        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
E/AndroidRuntime( 5411):        at com.zlc.provider.MyProvider.insert(MyProvider.java:92)
E/AndroidRuntime( 5411):        at android.content.ContentProvider$Transport.insert(ContentProvider.java:201)
E/AndroidRuntime( 5411):        at android.content.ContentResolver.insert(ContentResolver.java:866)
E/AndroidRuntime( 5411):        at com.zlc.provider.MainActivity.onCreate(MainActivity.java:17)
E/AndroidRuntime( 5411):        at android.app.Activity.performCreate(Activity.java:5104)
E/AndroidRuntime( 5411):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
E/AndroidRuntime( 5411):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
E/AndroidRuntime( 5411):        ... 11 more
W/ActivityManager( 2138):   Force finishing activity com.zlc.provider/.MainActivity

private final static String CREATE_TABLE = "Create table " + TABLE_NAME + "( _id integer primary key autoincrement," + Information.INFO_ID + " TEXT," + Information.INFO_NAME + " TEXT," + Information.INFO_AGE+ " TEXT);"; 注意空格

代码下载

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2014-02-21 23:31  MMLoveMeMM  阅读(943)  评论(0编辑  收藏  举报