GVKun编程网logo

Android 数据存储之ContentProvider 使用和应用场景(android的contentprovider)

9

对于Android数据存储之ContentProvider使用和应用场景感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android的contentprovider,并且为您提供关于And

对于Android 数据存储之ContentProvider 使用和应用场景感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android的contentprovider,并且为您提供关于Android ContentProvider、android ContentProvider 使用实例、Android ContentProvider 基本原理和使用详解、Android ContentProvider 详解的宝贵知识。

本文目录一览:

Android 数据存储之ContentProvider 使用和应用场景(android的contentprovider)

Android 数据存储之ContentProvider 使用和应用场景(android的contentprovider)

备注:

  ContentProvider1进程一代码:https://github.com/zengyuan/ContentProvider1Mode

  ContentProvider1进程二代码:https://github.com/zengyuan/ContentProvider2Mode

  Android Service 启动方式,生命周期和应用场景详解: https://www.cnblogs.com/finn21/p/11652482.html

  iOS APP打包上传审核和 客服咨询流程(审核被拒,账号问题等):https://www.cnblogs.com/finn21/p/11084272.html

注意,最主要是provider的配置,以及URl的provider地址要相同;

一,ContentProvider 主要用于不同的应用程序之间实现数据共享功能!

        主要分为6个:

  1.使用sqlite技术,创建好数据库和数据表;
  2.新建类继承ContentProvider,重写6个抽象方法(通过这六个方法对数据库进行操作);
  3.在Manifest中注册provider;
  4.创建UriMatcher,定义Uri规则,添加注册数据库链表的名称;
  5.ContentResolver对ContentProvider中共享的数据进行增删改查操作;
  6.获取数据并修改等(进程一进程二增删改查方式一样);

 

二,进程一代码伺候:

    第一步创建数据库(sql语句直接执行的):

package com.saiyi.contentproviderdemo1;

import android.content.Context;
import android.database.sqlite.sqliteDatabase;
import android.database.sqlite.sqliteOpenHelper;
/**
 * <pre>
 *     author : Finn
 *     e-mail : 892603597@qq.com
 *     time   : 2019/10/14
 *     desc   : https://www.cnblogs.com/finn21/
 * </pre>
 */
public class DBHelper extends sqliteOpenHelper {

    // 数据库名
    private static final String DATABASE_NAME = "Finn.db";

    // 链表名称
    public static final String USER_TABLE_NAME = "user";
    public static final String JOB_TABLE_NAME = "job";

    private static final int DATABASE_VERSION = 1;
    //数据库版本号

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

    @Override
    public void onCreate(sqliteDatabase db) {

        // 创建两个表格:用户表 和职业表
        db.execsql("CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");

        db.execsql("CREATE TABLE IF NOT EXISTS " + JOB_TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " job TEXT)");

    }

    @Override
    public void onUpgrade(sqliteDatabase db, int oldVersion, int newVersion) {

    }

    @Override
    public void onopen(sqliteDatabase db) {
        super.onopen(db);
    }

    @Override
    public synchronized void close() {
        super.close();
    }
}

    第一步创建数据库(通过自行封装之后的数据库):

    

package com.saiyi.contentproviderdemo1;

import android.content.Context;
import android.database.sqlite.sqliteDatabase;
import android.database.sqlite.sqliteOpenHelper;

/**
 * <pre>
 *     author : Finn
 *     e-mail : 892603597@qq.com
 *     time   : 2019/10/14
 *     desc   : https://www.cnblogs.com/finn21/
 * </pre>
 */
public class MyDataBaseHelper extends sqliteOpenHelper {
    private static final String DATABASE_NAME = "KfbFinnSS.db";
    private static final int DATABASE_VERSION = 1;


    public static final String USER_ID = "userid_id";// 自动增长表id

    /***.用户名(存储车型af)**/
    public static final String TABLE_USER_AF = "table_user_af";
    public static final String USER_USER_AF = "car_user_name_af";//用户名
    public static final String USER_CAR_AZ_AF = "car_user_az_af";//字符分段
    public static final String USER_CAR_NAME_AF = "car_kfb_name_af";// 车名称
    public static final String USER_CAR_TYPE_AF = "car_user_cartype_af";// 车型号
    public static final String USER_CAR_DATE_AF = "car_kfb_date_af";// 年份
    public static final String USER_CAR_LENGM_AF = "car_kfb_lengmei_af";// 冷媒量


    public MyDataBaseHelper(Context context) {
        super(context,DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(sqliteDatabase db) {

/** AF * */
        db.execsql("CREATE TABLE " + TABLE_USER_AF + " (" + USER_ID + " INTEGER PRIMARY KEY," + USER_USER_AF + " INTEGER NOT NUll,"
                + USER_CAR_AZ_AF + " TEXT,"  + USER_CAR_NAME_AF + " TEXT," + USER_CAR_TYPE_AF
                + " TEXT," + USER_CAR_DATE_AF + " TEXT NOT NUll,"  + USER_CAR_LENGM_AF + " INTEGER default 0);");
    }


    @Override
    public void onUpgrade(sqliteDatabase db, int oldVersion, int newVersion) {
        //数据库升级启动该方法

    }

    @Override
    public synchronized void close() {
        super.close();
    }

    @Override
    public void onopen(sqliteDatabase db) {
        super.onopen(db);
    }
}

    

package com.saiyi.contentproviderdemo1;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.sqliteDatabase;

import java.util.ArrayList;
import java.util.List;


/**
 * <pre>
 *     author : Finn
 *     e-mail : 892603597@qq.com
 *     time   : 2019/10/14
 *     desc   : https://www.cnblogs.com/finn21/
 * </pre>
 */
public class MyDataBase {

    private static final String TAG = MyDataBase.class.getSimpleName();
    private static MyDataBaseHelper databaseHelper;
    private static MyDataBase mydisplacementBase;
    private static String USER_NAME = "13006602877";
    private static String TABLE_USER_NAME = "KFB";


    private MyDataBase(Context context) {
        databaseHelper = new MyDataBaseHelper(context);
    }

    public static MyDataBase getInstance(Context context) {
        if (mydisplacementBase == null) {
            mydisplacementBase = new MyDataBase(context.getApplicationContext());
        }
        return mydisplacementBase;
    }

    /**
     * 关闭当前的db
     *
     * @param db
     */
    synchronized void closesqlDB(sqliteDatabase db) {
        sqliteDatabase.releaseMemory();
        if (db != null && db.isopen()) {
            db.close();
            db = null;
        }
    }

    /**
     * 通知本地数据库有变化
     */
    public interface InotifyDBhasChange {
        int CODE_ERR_REPET = 0;
        void onChange(Object... obj);
        void err(int... code);
    }

    /**
     * --------------------------------------添加车名AF---------------------------------------
     */
    public synchronized void InsertCaraF(CarInfo info) {
        sqliteDatabase writeDB = databaseHelper.getWritableDatabase();
        Cursor cursor = writeDB.query(MyDataBaseHelper.TABLE_USER_AF,
                new String[]{MyDataBaseHelper.USER_ID},
                MyDataBaseHelper.USER_USER_AF + " = ? ",
                new String[]{TABLE_USER_NAME}, null, null, null);

        cursor.close();

        ContentValues cv = new ContentValues();
        cv.put(MyDataBaseHelper.USER_USER_AF, TABLE_USER_NAME);
        cv.put(MyDataBaseHelper.USER_CAR_AZ_AF, info.getAz());
        cv.put(MyDataBaseHelper.USER_CAR_NAME_AF, info.getCarName());
        cv.put(MyDataBaseHelper.USER_CAR_TYPE_AF, info.getCarType());
        cv.put(MyDataBaseHelper.USER_CAR_DATE_AF, info.getCarDate());
        cv.put(MyDataBaseHelper.USER_CAR_LENGM_AF, info.getCarLengM());
        try {
            writeDB.insert(MyDataBaseHelper.TABLE_USER_AF, null, cv);//插入数据
        } catch (Exception e) {
            return;
        } finally {
            closesqlDB(writeDB);
        }
    }

    /**
     * 查询所有用户
     */
    public synchronized List<CarInfo> QueryListAF() {
        List<CarInfo> list = new ArrayList<>();
        sqliteDatabase readDB = databaseHelper.getReadableDatabase();
        Cursor cursor = readDB.query(MyDataBaseHelper.TABLE_USER_AF,
                null, MyDataBaseHelper.USER_USER_AF + " = ? ",
                new String[]{TABLE_USER_NAME}, null,
                null, MyDataBaseHelper.USER_ID, null);
        try {
//            int user = cursor.getColumnIndex(MyDataBaseHelper.USER_USER_AF);
            int pswd = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_AZ_AF);
            int icon = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_NAME_AF);
            int nickname = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_TYPE_AF);
            int vertion = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_DATE_AF);
            int veron = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_LENGM_AF);
            while (cursor.movetoNext()) {
                CarInfo info = new CarInfo(
                        cursor.getString(pswd),
                        cursor.getString(icon),
                        cursor.getString(nickname),
                        cursor.getString(vertion),
                        cursor.getString(veron)
                );
                list.add(info);
            }
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            closesqlDB(readDB);
        }
        return list;
    }
    public Cursor cursor;
    public sqliteDatabase readDB;
    public synchronized Cursor QueryCursor() {
        readDB = databaseHelper.getReadableDatabase();
        cursor = readDB.query(MyDataBaseHelper.TABLE_USER_AF,
                null, MyDataBaseHelper.USER_USER_AF + " = ? ",
                new String[]{TABLE_USER_NAME}, null,
                null, MyDataBaseHelper.USER_ID, null);
        return cursor;
    }

    public synchronized void CloseCursor() {
        if (cursor != null) {
            cursor.close();
        }
        closesqlDB(readDB);
    }
    /**
     * 查询所有用户
     */
    public synchronized Cursor QueryListAFA() {
        sqliteDatabase readDB = databaseHelper.getReadableDatabase();
        Cursor cursor = readDB.query(MyDataBaseHelper.TABLE_USER_AF,
                null, MyDataBaseHelper.USER_USER_AF + " = ? ",
                new String[]{TABLE_USER_NAME}, null,
                null, MyDataBaseHelper.USER_ID, null);
        try {
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            closesqlDB(readDB);
        }
        return cursor;
    }
    /**
     * 查询AF特定名称的车类型
     */
    public synchronized List<CarInfo> QueryListAFCarName(String Carname) {
        List<CarInfo> list = new ArrayList<>();
        sqliteDatabase readDB = databaseHelper.getReadableDatabase();
        Cursor cursor = readDB.query(MyDataBaseHelper.TABLE_USER_AF,
                null, MyDataBaseHelper.USER_USER_AF + " = ? and " + MyDataBaseHelper.USER_CAR_NAME_AF + " = ? ",
                new String[]{TABLE_USER_NAME, Carname}, null,
                null, MyDataBaseHelper.USER_ID, null);
        try {
//            int user = cursor.getColumnIndex(MyDataBaseHelper.USER_USER_AF);
            int pswd = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_AZ_AF);
            int icon = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_NAME_AF);
            int nickname = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_TYPE_AF);
            int vertion = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_DATE_AF);
            int veron = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_LENGM_AF);
            while (cursor.movetoNext()) {
                CarInfo info = new CarInfo(
                        cursor.getString(pswd),
                        cursor.getString(icon),
                        cursor.getString(nickname),
                        cursor.getString(vertion),
                        cursor.getString(veron)
                );
                list.add(info);
            }
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            closesqlDB(readDB);
        }
        return list;
    }

    /**
     * 查询AF特定名称和车类型 的生产年份
     */
    public synchronized List<CarInfo> QueryListAFCarNameYeas(String Carname,String CarType) {
        List<CarInfo> list = new ArrayList<>();
        sqliteDatabase readDB = databaseHelper.getReadableDatabase();
        Cursor cursor = readDB.query(MyDataBaseHelper.TABLE_USER_AF,
                null, MyDataBaseHelper.USER_USER_AF + " = ? and "
                        + MyDataBaseHelper.USER_CAR_NAME_AF + " = ? and "
                        + MyDataBaseHelper.USER_CAR_TYPE_AF + " = ? ",
                new String[]{TABLE_USER_NAME, Carname,CarType}, null,
                null, MyDataBaseHelper.USER_ID, null);
        try {
//            int user = cursor.getColumnIndex(MyDataBaseHelper.USER_USER_AF);
            int pswd = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_AZ_AF);
            int icon = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_NAME_AF);
            int nickname = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_TYPE_AF);
            int vertion = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_DATE_AF);
            int veron = cursor.getColumnIndex(MyDataBaseHelper.USER_CAR_LENGM_AF);
            while (cursor.movetoNext()) {
                CarInfo info = new CarInfo(
                        cursor.getString(pswd),
                        cursor.getString(icon),
                        cursor.getString(nickname),
                        cursor.getString(vertion),
                        cursor.getString(veron)
                );
                list.add(info);
            }
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            closesqlDB(readDB);
        }
        return list;
    }

    /**
     * 删除列表数据
     */
    public synchronized void DeletInfoAF(InotifyDBhasChange dbChange) {
        sqliteDatabase writeDB = databaseHelper.getWritableDatabase();
        try {
            writeDB.delete(
                    MyDataBaseHelper.TABLE_USER_AF,
                    MyDataBaseHelper.USER_USER_AF + " = ? "
                    , new String[]{TABLE_USER_NAME});
        } catch (Exception e) {
            dbChange.err();
            return;
        } finally {
            closesqlDB(writeDB);
        }
        dbChange.onChange();
    }
}

  第二步:2.新建类继承ContentProvider,重写6个抽象方法

  

package com.saiyi.contentproviderdemo1;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.sqliteDatabase;
import android.net.Uri;
import android.util.Log;

/**
 * <pre>
 *     author : Finn
 *     e-mail : 892603597@qq.com
 *     time   : 2019/10/14
 *     desc   : https://www.cnblogs.com/finn21/
 * </pre>
 */
public class BaseContentProvider extends ContentProvider{

    private Context mContext;
    DBHelper mDbHelper = null;
    sqliteDatabase db = null;
    public static final String AUTOHORITY = "Finn.zy.myprovider";
    // 设置ContentProvider的唯一标识

    public static final int User_Code = 1;
    public static final int Job_Code = 2;
    public static final int AF_Code = 32;
    // UriMatcher类使用:在ContentProvider 中注册URI
    private static final UriMatcher mMatcher;

    public static DataBaseMonitor mListener = null;

    public static void setDataBaseMonitorListener(DataBaseMonitor listener) {
        mListener = listener;
    }

    static{
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        // 初始化
        mMatcher.addURI(AUTOHORITY,DBHelper.USER_TABLE_NAME, User_Code); // 若URI资源路径 = "content://" + AUTOHORITY +"/user"
        mMatcher.addURI(AUTOHORITY, DBHelper.JOB_TABLE_NAME, Job_Code);// 若URI资源路径 = "content://" + AUTOHORITY +"/job"
        mMatcher.addURI(AUTOHORITY, MyDataBaseHelper.TABLE_USER_AF, AF_Code);// 若URI资源路径 = "content://" + AUTOHORITY +"/job"
        // 固定前缀 "content://" + AUTOHORITY,数据库链表的名称,则返回注册码User_Code
        //固定前缀 "content://" + AUTOHORITY ,数据库链表的名称,则返回注册码Job_Code
    }

    // 以下是ContentProvider的6个方法
    @Override
    public boolean onCreate() {
        //该方法在ContentProvider创建后就会被调用,Android开机后,
        // ContentProvider在其它应用第一次访问它时才会被创建,同时,要讲返回值修改成true。
        mContext = getContext();

        // 在ContentProvider创建时对数据库进行初始化
        // 运行在主线程,故不能做耗时操作,此处仅作展示
        mDbHelper = new DBHelper(getContext());
        db = mDbHelper.getWritableDatabase();

        // 初始化两个表的数据(先清空两个表,再各加入一个记录)
        db.execsql("delete from user");
        db.execsql("insert into user values(1,'Carson');");
        db.execsql("insert into user values(2,'Kobe');");

        MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo("宝马","奔驰","路虎","宾利","保时捷"));
        MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo("宝马1","奔驰2","路虎3","宾利4","保时捷5"));

        return true;
    }
    public static final int ITEMS = 1;
    public static final int ITEMS_ID = 2;
//    如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。
//    例如:要得到所有person记录的Uri为content://com.cfox.contentprovid.PersonProvider/person
//    那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"

    //    如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
//    例如:得到id为10的person记录,Uri为content://com.cfox.contentprovid.PersonProvider/person/10
//    那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //该方法用于供外部应用往ContentProvider添加数据。

        // 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
        // 该方法在最下面
        String table = getTableName(uri);
        Log.e("sdfsdf",table+"====="+MyDataBaseHelper.TABLE_USER_AF);
        if(table != null && !MyDataBaseHelper.TABLE_USER_AF.equals(table)){
            // 向该表添加数据
            db.insert(table, null, values);
            // 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
            mContext.getContentResolver().notifyChange(uri, null);
            return uri;
        }else{
            FinnInsert(uri,values);
            return uri;
        }
//        // 通过ContentUris类从URL中获取ID
//        long personid = ContentUris.parseId(uri);
//        System.out.println(personid);

    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //该方法用于供外部应用从ContentProvider删除数据。
        String tableName = getTableName(uri);
        if(!MyDataBaseHelper.TABLE_USER_AF.equals(tableName)){
            //使用原生函数的数据库
            return 0;
        }else{
            //使用封装之后的数据库
            return 0;
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        return 0;
    }
    /**
     * 查询数据
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        //该方法用于供外部应用从ContentProvider中获取数据。
        // 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
        // 该方法在最下面
        String tableName = getTableName(uri);
        FinnQuery(tableName);

//        // 通过ContentUris类从URL中获取ID
//        long personid = ContentUris.parseId(uri);
//        System.out.println(personid);
        // 查询数据+
        if(!MyDataBaseHelper.TABLE_USER_AF.equals(tableName)){
            //使用原生函数的数据库
            return db.query(tableName,projection,selection,selectionArgs,null,null,sortOrder,null);
        }else{
            //使用封装之后的数据库
            return  MyDataBase.getInstance(mContext).QueryCursor();
        }
    }



    /**
     * 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
     */
    public static  final  String getTableName(Uri uri){
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case User_Code:
                tableName = DBHelper.USER_TABLE_NAME;
                break;
            case Job_Code:
                tableName = DBHelper.JOB_TABLE_NAME;
                break;
            case AF_Code:
                tableName = MyDataBaseHelper.TABLE_USER_AF;
                break;
        }
        return tableName;
    }
    public void FinnInsert(Uri uri, ContentValues values) {
        //自行封装方法添加数据库
        String table = getTableName(uri);
        switch (table){//根据表名 插入数据
            case MyDataBaseHelper.TABLE_USER_AF://通过KEY获取value
                MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo(
                        values.get("car_user_az_af").toString(), values.get("car_kfb_name_af").toString(),
                        values.get("car_user_cartype_af").toString(), values.get("car_kfb_date_af").toString(),
                        values.get("car_kfb_lengmei_af").toString()
                ));
//                MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo(
//                        values.get(MyDataBaseHelper.USER_CAR_AZ_AF).toString(), values.get(MyDataBaseHelper.USER_CAR_NAME_AF).toString(),
//                        values.get(MyDataBaseHelper.USER_CAR_TYPE_AF).toString(), values.get(MyDataBaseHelper.USER_CAR_DATE_AF).toString(),
//                        values.get(MyDataBaseHelper.USER_CAR_LENGM_AF).toString()
//                ));
                break;
            default:
                break;
        }
    }

    public void FinnQuery(String tablename) {
        //自行封装方法添加数据库
        switch (tablename){//根据表名 插入数据
            case MyDataBaseHelper.TABLE_USER_AF:
                mListener.OnRelist(MyDataBase.getInstance(mContext).QueryListAF());
                break;
            default:
                break;
        }
    }

}

 

 

 第三步:在Manifest中注册provider;

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.saiyi.contentproviderdemo1">


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--authorities地址和 BaseContentProvider中的AUTOHORITY要保持一致-->
        <provider android:name=".BaseContentProvider"
            android:authorities="Finn.zy.myprovider"
            android:enabled="true"
            android:exported="true"
            />
    </application>

</manifest>

 

package com.saiyi.contentproviderdemo1;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.sqliteDatabase;
import android.net.Uri;
import android.util.Log;

/**
* <pre>
* author : Finn
* e-mail : 892603597@qq.com
* time : 2019/10/14
* desc : https://www.cnblogs.com/finn21/
* </pre>
*/
public class BaseContentProvider extends ContentProvider{

private Context mContext;
DBHelper mDbHelper = null;
sqliteDatabase db = null;
public static final String AUTOHORITY = "Finn.zy.myprovider";
// 设置ContentProvider的唯一标识

public static final int User_Code = 1;
public static final int Job_Code = 2;
public static final int AF_Code = 32;
// UriMatcher类使用:在ContentProvider 中注册URI
private static final UriMatcher mMatcher;

public static DataBaseMonitor mListener = null;

public static void setDataBaseMonitorListener(DataBaseMonitor listener) {
mListener = listener;
}

static{
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 初始化
mMatcher.addURI(AUTOHORITY,DBHelper.USER_TABLE_NAME, User_Code); // 若URI资源路径 = "content://" + AUTOHORITY +"/user"
mMatcher.addURI(AUTOHORITY, DBHelper.JOB_TABLE_NAME, Job_Code);// 若URI资源路径 = "content://" + AUTOHORITY +"/job"
mMatcher.addURI(AUTOHORITY, MyDataBaseHelper.TABLE_USER_AF, AF_Code);// 若URI资源路径 = "content://" + AUTOHORITY +"/job"
// 固定前缀 "content://" + AUTOHORITY,数据库链表的名称,则返回注册码User_Code
//固定前缀 "content://" + AUTOHORITY ,数据库链表的名称,则返回注册码Job_Code
}

// 以下是ContentProvider的6个方法
@Override
public boolean onCreate() {
//该方法在ContentProvider创建后就会被调用,Android开机后,
// ContentProvider在其它应用第一次访问它时才会被创建,同时,要讲返回值修改成true。
mContext = getContext();

// 在ContentProvider创建时对数据库进行初始化
// 运行在主线程,故不能做耗时操作,此处仅作展示
mDbHelper = new DBHelper(getContext());
db = mDbHelper.getWritableDatabase();

// 初始化两个表的数据(先清空两个表,再各加入一个记录)
db.execsql("delete from user");
db.execsql("insert into user values(1,'Carson');");
db.execsql("insert into user values(2,'Kobe');");

MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo("宝马","奔驰","路虎","宾利","保时捷"));
MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo("宝马1","奔驰2","路虎3","宾利4","保时捷5"));

return true;
}
public static final int ITEMS = 1;
public static final int ITEMS_ID = 2;
// 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。
// 例如:要得到所有person记录的Uri为content://com.cfox.contentprovid.PersonProvider/person
// 那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"

// 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
// 例如:得到id为10的person记录,Uri为content://com.cfox.contentprovid.PersonProvider/person/10
// 那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"
@Override
public String getType(Uri uri) {
return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
//该方法用于供外部应用往ContentProvider添加数据。

// 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
// 该方法在最下面
String table = getTableName(uri);
Log.e("sdfsdf",table+"====="+MyDataBaseHelper.TABLE_USER_AF);
if(table != null && !MyDataBaseHelper.TABLE_USER_AF.equals(table)){
// 向该表添加数据
db.insert(table, null, values);
// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}else{
FinnInsert(uri,values);
return uri;
}
// // 通过ContentUris类从URL中获取ID
// long personid = ContentUris.parseId(uri);
// System.out.println(personid);

}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//该方法用于供外部应用从ContentProvider删除数据。
String tableName = getTableName(uri);
if(!MyDataBaseHelper.TABLE_USER_AF.equals(tableName)){
//使用原生函数的数据库
return 0;
}else{
//使用封装之后的数据库
return 0;
}
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

return 0;
}
/**
* 查询数据
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//该方法用于供外部应用从ContentProvider中获取数据。
// 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
// 该方法在最下面
String tableName = getTableName(uri);
FinnQuery(tableName);

// // 通过ContentUris类从URL中获取ID
// long personid = ContentUris.parseId(uri);
// System.out.println(personid);
// 查询数据+
if(!MyDataBaseHelper.TABLE_USER_AF.equals(tableName)){
//使用原生函数的数据库
return db.query(tableName,projection,selection,selectionArgs,null,null,sortOrder,null);
}else{
//使用封装之后的数据库
return MyDataBase.getInstance(mContext).QueryCursor();
}
}



/**
* 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
*/
public static final String getTableName(Uri uri){
String tableName = null;
switch (mMatcher.match(uri)) {
case User_Code:
tableName = DBHelper.USER_TABLE_NAME;
break;
case Job_Code:
tableName = DBHelper.JOB_TABLE_NAME;
break;
case AF_Code:
tableName = MyDataBaseHelper.TABLE_USER_AF;
break;
}
return tableName;
}
public void FinnInsert(Uri uri, ContentValues values) {
//自行封装方法添加数据库
String table = getTableName(uri);
switch (table){//根据表名 插入数据
case MyDataBaseHelper.TABLE_USER_AF://通过KEY获取value
MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo(
values.get("car_user_az_af").toString(), values.get("car_kfb_name_af").toString(),
values.get("car_user_cartype_af").toString(), values.get("car_kfb_date_af").toString(),
values.get("car_kfb_lengmei_af").toString()
));
// MyDataBase.getInstance(mContext).InsertCaraF(new CarInfo(
// values.get(MyDataBaseHelper.USER_CAR_AZ_AF).toString(), values.get(MyDataBaseHelper.USER_CAR_NAME_AF).toString(),
// values.get(MyDataBaseHelper.USER_CAR_TYPE_AF).toString(), values.get(MyDataBaseHelper.USER_CAR_DATE_AF).toString(),
// values.get(MyDataBaseHelper.USER_CAR_LENGM_AF).toString()
// ));
break;
default:
break;
}
}

public void FinnQuery(String tablename) {
//自行封装方法添加数据库
switch (tablename){//根据表名 插入数据
case MyDataBaseHelper.TABLE_USER_AF:
mListener.OnRelist(MyDataBase.getInstance(mContext).QueryListAF());
break;
default:
break;
}
}

}



Android ContentProvider

Android ContentProvider

1. 适用场景

1) ContentProvider 为存储和读取数据提供了统一的接口

2) 使用 ContentProvider,应用程序可以实现数据共享

3) android 内置的许多数据都是使用 ContentProvider 形式,供开发者调用的 (如视频,音频,图片,通讯录等)

2. 相关概念介绍

1)ContentProvider 简介

       当应用继承 ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享 数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用 sharedpreferences 共享 数据,需要使用 sharedpreferences API 读写数据。而使用 ContentProvider 共享数据的好处是统一了数据访问方式。

2)Uri 类简介

      Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

      在 Content Provider 中使用的查询字符串有别于标准的 SQL 查询。很多诸如 select, add, delete, modify 等操作我们都使用一种特殊的 URI 来进行,这种 URI 由 3 个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的 ID。以下是一些示例 URI:

     content://media/internal/images  这个 URI 将返回设备上存储的所有图片

     content://contacts/people/  这个 URI 将返回设备上的所有联系人信息

     content://contacts/people/45 这个 URI 返回单个结果(联系人信息中 ID 为 45 的联系人记录)

  尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android 提供一系列的帮助类(在 android.provider 包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面 content://contacts/people/45 这个 URI 就可以写成如下形式:

  Uri person = ContentUris.withAppendedId(People.CONTENT_URI,  45);

然后执行数据查询:

Cursor cur = managedQuery(person, null, null, null);

这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package  com.wissen.testApp;
public  class  ContentProviderDemo  extends  Activity {
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
        displayRecords();
     }
 
     private  void  displayRecords() {
         //该数组中包含了所有要返回的字段
      String columns[] =  new  String[] { People.NAME, People.NUMBER };
        Uri mContacts = People.CONTENT_URI;
        Cursor cur = managedQuery(
           mContacts,
           columns,   // 要返回的数据字段
           null ,           // WHERE子句
           null ,          // WHERE 子句的参数
           null          // Order-by子句
      );
        if  (cur.moveToFirst()) {
            String name =  null ;
            String phoneNo =  null ;
            do  {
               // 获取字段的值
             name = cur.getString(cur.getColumnIndex(People.NAME));
              phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
              Toast.makeText( this , name + ” ” + phoneNo, Toast.LENGTH_LONG).show();
           while  (cur.moveToNext());
        }
     }
}

上例示范了一个如何依次读取联系人信息表中的指定数据列 name 和 number。

修改记录:

我们可以使用 ContentResolver.update () 方法来修改数据,我们来写一个修改数据的方法:

?

1
2
3
4
5
6
private  void  updateRecord( int  recNo, String name) {
     Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
     ContentValues values =  new  ContentValues();
     values.put(People.NAME, name);
     getContentResolver().update(uri, values,  null null );
}

现在你可以调用上面的方法来更新指定记录:

updateRecord (10,”XYZ”);   // 更改第 10 条记录的 name 字段值为 “XYZ”

添加记录:

要增加记录,我们可以调用 ContentResolver.insert () 方法,该方法接受一个要增加的记录的目标 URI,以及一个包含了新记录值的 Map 对象,调用后的返回值是新记录的 URI,包含记录号。

上面的例子中我们都是基于联系人信息簿这个标准的 Content Provider,现在我们继续来创建一个 insertRecord () 方法以对联系人信息簿中进行数据的添加:

?

1
2
3
4
5
6
7
8
9
10
11
private  void  insertRecords(String name, String phoneNo) {
     ContentValues values =  new  ContentValues();
     values.put(People.NAME, name);
     Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
     Log.d(”ANDROID”, uri.toString());
     Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
     values.clear();
     values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
     values.put(People.NUMBER, phoneNo);
     getContentResolver().insert(numberUri, values);
}

这样我们就可以调用 insertRecords (name, phoneNo) 的方式来向联系人信息簿中添加联系人姓名和电话号码。

删除记录:

Content Provider 中的 getContextResolver.delete () 方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:

?

1
2
3
4
private  void  deleteRecords() {
     Uri uri = People.CONTENT_URI;
     getContentResolver().delete(uri,  null null );
}

你也可以指定 WHERE 条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除 name 为‘XYZ XYZ’的记录。

3. 创建 ContentProvider

要创建我们自己的 Content Provider 的话,我们需要遵循以下几步:

a. 创建一个继承了 ContentProvider 父类的类

b. 定义一个名为 CONTENT_URI,并且是 public static final 的 Uri 类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:

public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

c. 定义你要返回给客户端的数据列名。如果你正在使用 Android 数据库,必须为其定义一个叫_id 的列,它用来表示每条记录的唯一性。

d. 创建你的数据存储系统。大多数 Content Provider 使用 Android 文件系统或 SQLite 数据库来保持数据,但是你也可以以任何你想要的方式来存储。

e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的 URI 字符串,通过它来读取对应的文件数据。处理这种数据类型的 Content Provider 需要实现一个名为_data 的字段,_data 字段列出了该文件在 Android 文件系统上的精确路径。这个字段不仅是供客户端使用,而 且也可以供 ContentResolver 使用。客户端可以调用 ContentResolver.openOutputStream () 方法来处理该 URI 指向的文件资源;如果是 ContentResolver 本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

f. 声明 public static String 型的变量,用于指定要从游标处返回的数据列。

g. 查询返回一个 Cursor 类型的对象。所有执行写操作的方法如 insert (), update () 以及 delete () 都将被监听。我们可以通过使用 ContentResover ().notifyChange () 方法来通知监听器关于数据更新的信息。

h. 在 AndroidMenifest.xml 中使用 <provider> 标签来设置 Content Provider。

i. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的 MIME 类型,以供 ContentProvider.geType (url) 来返回。MIME 类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:

  vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的 MIME 类型)

  比如,一个请求列车信息的 URI 如 content://com.example.transportationprovider/trains/122 可能就会返回 typevnd.android.cursor.item/vnd.example.rail 这样一个 MIME 类型。

  vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的 MIME 类型)

  比如,一个请求所有列车信息的 URI 如 content://com.example.transportationprovider/trains 可能就会返回 vnd.android.cursor.dir/vnd.example.rail 这样一个 MIME 类型。

下列代码将创建一个 Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite 数据库存储这些数据):

?

1
2
3
4
5
6
7
8
9
10
public  class  MyUsers {
     public  static  final  String AUTHORITY  = “com.wissen.MyContentProvider”;
 
     // BaseColumn类中已经包含了 _id字段
    public  static  final  class  User  implements  BaseColumns {
         public  static  final  Uri CONTENT_URI  = Uri.parse(”content: //com.wissen.MyContentProvider”);
         // 表数据列
         public  static  final  String  USER_NAME  = “USER_NAME”;
     }
}

上面的类中定义了 Content Provider 的 CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的 Content Provider 类:

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public  class  MyContentProvider  extends  ContentProvider {
     private  SQLiteDatabase     sqlDB;
     private  DatabaseHelper    dbHelper;
     private  static  final  String  DATABASE_NAME = “Users.db”;
     private  static  final  int   DATABASE_VERSION=  1 ;
     private  static  final  String TABLE_NAME= “User”;
     private  static  final  String TAG = “MyContentProvider”;
 
     private  static  class  DatabaseHelper  extends  SQLiteOpenHelper {
         DatabaseHelper(Context context) {
             super (context, DATABASE_NAME,  null , DATABASE_VERSION);
         }
 
         @Override
         public  void  onCreate(SQLiteDatabase db) {
             //创建用于存储数据的表
         db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”);
         }
 
         @Override
         public  void  onUpgrade(SQLiteDatabase db,  int  oldVersion,  int  newVersion) {
             db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME);
             onCreate(db);
         }
     }
 
     @Override
     public  int  delete(Uri uri, String s, String[] as) {
         return  0 ;
     }
 
     @Override
     public  String getType(Uri uri) {
         return  null ;
     }
 
     @Override
     public  Uri insert(Uri uri, ContentValues contentvalues) {
         sqlDB = dbHelper.getWritableDatabase();
         long  rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues);
         if  (rowId >  0 ) {
             Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
             getContext().getContentResolver().notifyChange(rowUri,  null );
             return  rowUri;
         }
         throw  new  SQLException(”Failed to insert row into ” + uri);
     }
 
     @Override
     public  boolean  onCreate() {
         dbHelper =  new  DatabaseHelper(getContext());
         return  (dbHelper ==  null ) ?  false  true ;
     }
 
     @Override
     public  Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         SQLiteQueryBuilder qb =  new  SQLiteQueryBuilder();
         SQLiteDatabase db = dbHelper.getReadableDatabase();
         qb.setTables(TABLE_NAME);
         Cursor c = qb.query(db, projection, selection,  null null null , sortOrder);
         c.setNotificationUri(getContext().getContentResolver(), uri);
         return  c;
     }
 
     @Override
     public  int  update(Uri uri, ContentValues contentvalues, String s, String[] as) {
         return  0 ;
     }
}

一个名为 MyContentProvider 的 Content Provider 创建完成了,它用于从 Sqlite 数据库中添加和读取记录。

Content Provider 的入口需要在 AndroidManifest.xml 中配置:

?

1
<provider android:name=”MyContentProvider” android:authorities=”com.wissen.MyContentProvider” />

之后,让我们来使用这个定义好的 Content Provider:

1)为应用程序添加 ContentProvider 的访问权限。

2)通过 getContentResolver () 方法得到 ContentResolver 对象。

3)调用 ContentResolver 类的 query () 方法查询数据,该方法会返回一个 Cursor 对象。

4)对得到的 Cursor 对象进行分析,得到需要的数据。

5)调用 Cursor 类的 close () 方法将 Cursor 对象关闭。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public  class  MyContentDemo  extends  Activity {
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         insertRecord(”MyUser”);
         displayRecords();
     }
    
     private  void  insertRecord(String userName) {
         ContentValues values =  new  ContentValues();
         values.put(MyUsers.User.USER_NAME, userName);
         getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
     }
 
     private  void  displayRecords() {
         String columns[] =  new  String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
         Uri myUri = MyUsers.User.CONTENT_URI;
         Cursor cur = managedQuery(myUri, columns, null null null  );
         if  (cur.moveToFirst()) {
             String id =  null ;
             String userName =  null ;
             do  {
                 id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
                 userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
                 Toast.makeText( this , id + ” ” + userName, Toast.LENGTH_LONG).show();
            while  (cur.moveToNext());
        }
     }
}


android ContentProvider 使用实例

android ContentProvider 使用实例

创建 PeopleProvider.java:

package com.example.ch9;

import com.example.db.DbHelper;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;

public class PeopleProvider extends ContentProvider {
	
	private static final int ITEMS = 1;
	private static final int ITEM_ID = 2;
	public static final String DbName = "Db_People";
	public static final String TableName = "tb_people";
	DbHelper dbhelper ;
	SQLiteDatabase db;
	public static final String CONTENT_ITEMS_TYPE = "vnd.android.cursor.items/com.example.ch94.Db_People";
	public static final String CONTENT_ITEMID_TYPE = "vnd.android.cursor.itemid/com.example.ch94.Db_People";
	public static final Uri CONTENT_URI = Uri.parse("content://com.example.ch94.Db_People/tb_people");
	
	private static final UriMatcher sMatcher;
	static{
		sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sMatcher.addURI("com.example.ch94.Db_People", TableName, ITEMS);
		sMatcher.addURI("com.example.ch94.Db_People", TableName+"/#", ITEM_ID);
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		db = dbhelper.getWritableDatabase();
		int count = 0;
		switch(sMatcher.match(uri)){
		case ITEMS:
			count = db.delete("tb_people", selection, selectionArgs);
			break;
		case ITEM_ID:
			String id = uri.getPathSegments().get(1);
			count = db.delete("tb_people", "_ID="+id+(!TextUtils.isEmpty("_ID=?")?"AND("+selection+'')'':""), selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI"+uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	@Override
	public String getType(Uri uri) {
		switch(sMatcher.match(uri)){
		case ITEMS:
			return CONTENT_ITEMS_TYPE;
		case ITEM_ID:
			return CONTENT_ITEMID_TYPE;
		default:
			throw new IllegalArgumentException("Unknown URI"+uri);
			
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		db = dbhelper.getWritableDatabase();
		long rowId ;
		if(sMatcher.match(uri)!=ITEMS){
			throw new IllegalArgumentException("Unknown URI"+uri);
		}
		rowId = db.insert("tb_people", "_ID", values);
		if(rowId>0){
			Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
			getContext().getContentResolver().notifyChange(noteUri, null);
			return noteUri;
		}
		throw new IllegalArgumentException("Unknown URI"+uri);
	}

	@Override
	public boolean onCreate() {
		dbhelper = new DbHelper(this.getContext(),"Db_People",null,1);
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {
		db = dbhelper.getReadableDatabase();
		Cursor c;
		switch(sMatcher.match(uri)){
		case ITEMS:
			c = db.query("tb_people", projection, selection, selectionArgs, null, null, null);
			break;
		case ITEM_ID:
			String id = uri.getPathSegments().get(1);
			c = db.query("tb_people", projection, "_ID="+id+(!TextUtils.isEmpty(selection)?"AND("+selection+'')'':""), selectionArgs, null, null, sortOrder);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI"+uri);
		}
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		db = dbhelper.getWritableDatabase();
		int count = 0;
		switch(sMatcher.match(uri)){
		case ITEMS:
			count = db.update("tb_people", values, selection, selectionArgs);
			break;
		case ITEM_ID:
			String id = uri.getPathSegments().get(1);
			count = db.update("tb_people", values, "_ID="+id+(!TextUtils.isEmpty(selection)?"AND("+selection+'')'':""), selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI"+uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

}

创建主活动类 SqlMainActivity.java:

package com.example.ch94;

import com.example.baseexample.R;
import com.example.db.DbHelper;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

public class SqlMainActivity extends Activity {
	private ListView list_people;
	private ContentResolver contentResolver;
	private Uri CONTENT_URI = Uri.parse("content://com.example.ch94.Db_People/tb_people");
//	private DbHelper dbhelper;
//	private SQLiteDatabase db;
	
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.ch9_sqlmain);
		
		contentResolver = this.getContentResolver();
		
		list_people = (ListView)findViewById(R.id.list_people);
		
//		dbhelper = new DbHelper(this, "Db_People", null, 1);
//		db = dbhelper.getReadableDatabase();
		Cursor c = contentResolver.query(CONTENT_URI, new String[]{"_id","name","phone","mobile","email"}, null, null, null);
		SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.ch9_peoplelist, c, new String[]{"_id","name","phone","mobile","email"}, new int[]{R.id.id,R.id.name,R.id.phone,R.id.mobile,R.id.email});
		
		this.list_people.setAdapter(adapter);
		this.registerForContextMenu(list_people);
	}
	
	public boolean onCreateOptionsMenu(Menu menu){
		menu.add(Menu.NONE,Menu.FIRST+1,1,"添加").setIcon(android.R.drawable.ic_menu_add);
		menu.add(Menu.NONE,Menu.FIRST+1,2,"退出").setIcon(android.R.drawable.ic_menu_delete);
		return true;
	}
	
	public boolean onOptionsItemSelected(MenuItem item){
		switch(item.getItemId()){
		case Menu.FIRST+1:
			Intent intent = new Intent();
			intent.setClass(SqlMainActivity.this, AddPeopleActivity.class);
			startActivity(intent);
			break;
		case Menu.FIRST+2:finish();
			break;
		}
		return super.onOptionsItemSelected(item);
	}
	
	public void onCreateContextMenu(ContextMenu menu,View v,ContextMenuInfo menuInfo){
		menu.setHeaderIcon(R.drawable.ic_launcher);
		menu.add(0,3,0,"修改");
		menu.add(0, 4, 0, "删除");
	}
	
	public boolean onContextItemSelected(MenuItem item){
		AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)item.getMenuInfo();
		switch(item.getItemId()){
		case 3:
			String name=((TextView)menuInfo.targetView.findViewById(R.id.name)).getText().toString();
			String phone=((TextView)menuInfo.targetView.findViewById(R.id.phone)).getText().toString();
			String mobile=((TextView)menuInfo.targetView.findViewById(R.id.mobile)).getText().toString();
			String email=((TextView)menuInfo.targetView.findViewById(R.id.email)).getText().toString();
			Intent intent = new Intent();
			intent.setClass(SqlMainActivity.this, AddPeopleActivity.class);
			Bundle bundle = new Bundle();
			bundle.putLong("id", menuInfo.id);
			bundle.putString("name", name);
			bundle.putString("phone", phone);
			bundle.putString("mobile", mobile);
			bundle.putString("email", email);
			intent.putExtras(bundle);
			startActivity(intent);
			break;
		case 4:
			contentResolver.delete(CONTENT_URI, "_ID=?", new String[]{menuInfo.id+""});
//			dbhelper = new DbHelper(this,"Db_People",null,1);
//			db = dbhelper.getWritableDatabase();
//			db.delete("tb_people", "_id=?", new String[]{menuInfo.id+""});
			break;
		}
		
		return true;
	}
	
}

创建添加数据的活动类 AddPeopleActivity.java:

package com.example.ch94;

import com.example.baseexample.R;
import com.example.db.DbHelper;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AddPeopleActivity extends Activity {
	private EditText edt_name;
	private EditText edt_phone;
	private EditText edt_mobile;
	private EditText edt_email;
	private Button bt_save;
	private ContentResolver contentResolver;
	private Uri CONTENT_URI = Uri.parse("content://com.example.ch94.Db_People/tb_people");
	String name,phone,mobile,email;
//	DbHelper dbhelper;
//	SQLiteDatabase db;
	Bundle bundle;
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.ch9_addpeople);
		contentResolver = this.getContentResolver();
		
		edt_name=(EditText)findViewById(R.id.edt_name);
		edt_phone=(EditText)findViewById(R.id.edt_phone);
		edt_mobile=(EditText)findViewById(R.id.edt_mobile);
		edt_email=(EditText)findViewById(R.id.edt_email);
		bt_save = (Button)findViewById(R.id.bt_save);
		
		bundle = this.getIntent().getExtras();
		if(bundle!=null){
			edt_name.setText(bundle.getString("name"));
			edt_phone.setText(bundle.getString("phone"));
			edt_mobile.setText(bundle.getString("mobile"));
			edt_email.setText(bundle.getString("email"));
		}
		
		bt_save.setOnClickListener(new Button.OnClickListener(){

			@Override
			public void onClick(View v) {
				name = edt_name.getText().toString();
				phone = edt_phone.getText().toString();
				mobile = edt_mobile.getText().toString();
				email = edt_email.getText().toString();
				
				ContentValues value = new ContentValues();
				value.put("name", name);
				value.put("phone", phone);
				value.put("mobile", mobile);
				value.put("email", email);
				
//				DbHelper dbhelper = new DbHelper(AddPeopleActivity.this,"Db_People",null,1);
//				SQLiteDatabase db = dbhelper.getWritableDatabase();
				long status;
				if(bundle!=null){
					status = contentResolver.update(CONTENT_URI, value, "_ID=?", new String[]{bundle.getLong("id")+""});
				}else{
					Uri uri2 = contentResolver.insert(CONTENT_URI, value);
					if(uri2!=null){
						status =1;
					}else{
						status = -1;
					}
//					status = db.insert("tb_people", null, value);
				}
				if(status!=-1){
					Toast.makeText(AddPeopleActivity.this, "保存成功", Toast.LENGTH_LONG).show();
				}else{
					Toast.makeText(AddPeopleActivity.this, "保存失败", Toast.LENGTH_LONG).show();
				}
			}
			
		});
	}
}

最后,在 AndroidManifest.xml 增加:

<provider android:name="com.example.ch9.PeopleProvider" android:authorities="com.example.ch94.Db_People"/>




Android ContentProvider 基本原理和使用详解

Android ContentProvider 基本原理和使用详解

ContentProvider(内容提供者)是 Android 的四大组件之一,管理 Android 以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。

Android 的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、sqlite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider。而且 Android 为常见的一些数据提供了默认的 ContentProvider(包括音频、视频、图片和通讯录等)。

 
要实现与其他的 ContentProvider 通信首先要查找到对应的 ContentProvider 进行匹配。Android 中 ContenProvider 借助 ContentResolver 通过 Uri 与其他的 ContentProvider 进行匹配通信。 

URI(Uniform Resource Identifier)

其它应用可以通过 ContentResolver 来访问 ContentProvider 提供的数据,而 ContentResolver 通过 uri 来定位自己要访问的数据,所以我们要先了解 uri。URI(Universal Resource Identifier)统一资源定位符,如果您使用过安卓的隐式启动就会发现,在隐式启动的过程中我们也是通过 uri 来定位我们需要打开的 Activity 并且可以在 uri 中传递参数。

URI 为系统中的每一个资源赋予一个名字,比方说通话记录。每一个 ContentProvider 都拥有一个公共的 URI,用于表示 ContentProvider 所提供的数据。URI 的格式如下:

// 规则
[scheme:][host:port][path][?query]
 示例
content:com.wang.provider.myprovider/tablename/id:
  1. 标准前缀(scheme)——content://,用来说明一个Content Provider控制这些数据;
  2. URI 的标识 (host:port)—— com.wang.provider.myprovider,用于唯一标识这个 ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方应用程序,为了保证 URI 标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities属性中说明,一般是定义该 ContentProvider 的包.类的名称;

  3. 路径(path)——tablename,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;

  4. 记录ID(query)——id,如果URI中包含表示需要获取的记录的 ID,则返回该id对应的数据,如果没有ID,就表示返回全部;

对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:

  • 操作tablename表中id为11的记录,构建路径:/tablename/11;

  • 操作tablename表中id为11的记录的name字段:tablename/11/name;

  • 操作tablename表中的所有记录:/tablename;

  • 操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;

  • 若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:

Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");

再来看一个例子:

http:www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack

uri 的各个部分在安卓中都是可以通过代码获取的,下面我们就以上面这个 uri 为例来说下获取各个部分的方法:

  • getScheme():获取 Uri 中的 scheme 字符串部分,在这里是 http

  • getHost():获取 Authority 中的 Host 字符串,即 www.baidu.com

  • getPost():获取 Authority 中的 Port 字符串,即 8080

  • getPath():获取 Uri 中 path 部分,即 wenku/jiatiao.html

  • getQuery():获取 Uri 中的 query 部分,即 id=15&name=du

MIME

MIME 是指定某个扩展名的文件用一种应用程序来打开,就像你用浏览器查看 PDF 格式的文件,浏览器会选择合适的应用来打开一样。Android 中的工作方式跟 HTTP 类似,ContentProvider 会根据 URI 来返回 MIME 类型,ContentProvider 会返回一个包含两部分的字符串。MIME 类型一般包含两部分,如:

text/html
text/css
text/xml
application/pdf

分为类型和子类型,Android 遵循类似的约定来定义MIME类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。

  • 集合记录(dir):
vnd.android.cursor.dir/自定义 
  • 单条记录(item):
vnd.android.cursor.item/自定义 

vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。

在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。

UriMatcher

Uri 代表要操作的数据,在开发过程中对数据进行获取时需要解析 Uri,Android 提供了两个用于操作 Uri 的工具类,分别为 UriMatcher 和 ContentUris 。掌握它们的基本概念和使用方法,对一个 Android 开发者来说是一项必要的技能。

UriMatcher 类用于匹配 Uri,它的使用步骤如下:

  • 将需要匹配的Uri路径进行注册,代码如下:
常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
如果match()方法匹配“content:com.wang.provider.myprovider/tablename”路径,返回匹配码为1 sMatcher.addURI("content://com.wang.provider.myprovider"," tablename ",1);
如果match()方法匹配content:com.wang.provider.myprovider/tablename/11路径,返回匹配码为2 sMatcher.addURI("com.wang.provider.myprovider","tablename/#",2);

此处采用 addURI 注册了两个需要用到的 URI;注意,添加第二个 URI 时,路径后面的 id 采用了通配符形式 “#”,表示只要前面三个部分都匹配上了就 OK。

  • 注册完需要匹配的 Uri 后,可以使用 sMatcher.match(Uri) 方法对输入的 Uri 进行匹配,如果匹配就返回对应的匹配码,匹配码为调用 addURI() 方法时传入的第三个参数。 

switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
    case 1:
        match 1,todo something
        break;
    case 2
        match 2,1)">defaultmatch nothing,1)">;
}

ContentUris

ContentUris 类用于操作 Uri 路径后面的 ID 部分,它有两个比较实用的方法:withAppendedId(Uri uri,long id) 和 parseId(Uri uri)。

  • withAppendedId(Uri uri,long id) 用于为路径加上 ID 部分:

Uri uri = Uri.parse("content://cn.scu.myprovider/user")

生成后的Uri为:content:cn.scu.myprovider/user/7
Uri resultUri = ContentUris.withAppendedId(uri,7); 
  • parseId(Uri uri) 则从路径中获取 ID 部分:

Uri uri = Uri.parse("content://cn.scu.myprovider/user/7"获取的结果为:7
long personid = ContentUris.parseId(uri);

ContentProvider 主要方法

ContentProvider 是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:
  • public boolean onCreate()在创建 ContentProvider 时使用

  • public Cursor query()用于查询指定 uri 的数据返回一个 Cursor

  • public Uri insert():用于向指定uri的 ContentProvider 中添加数据

  • public int delete()用于删除指定 uri 的数据

  • public int update()用户更新指定 uri 的数据

  • public String getType()用于返回指定的 Uri 中的数据 MIME 类型

数据访问的方法 insert,delete 和 update 可能被多个线程同时调用,此时必须是线程安全的。

如果操作的数据属于集合类型,那么 MIME 类型字符串应该以 vnd.android.cursor.dir/ 开头,

  • 要得到所有 tablename 记录: Uri 为 content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为:vnd.android.cursor.dir/table。

如果要操作的数据属于非集合类型数据,那么 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头,

  • 要得到 id 为 10 的 tablename 记录,Uri 为 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 类型字符串为:vnd.android.cursor.item/tablename 。

方法使用示例

使用 ContentResolver 对 ContentProvider 中的数据进行操作的代码如下:

ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename"// 添加一条记录
ContentValues values =  ContentValues();
values.put("name","wang1");
values.put("age",28);
resolver.insert(uri,values); 
获取tablename表中所有记录 Cursor cursor = resolver.query(uri,null,"tablename data"); while(cursor.movetoNext()){ Log.i("ContentTest","tablename_id="+ cursor.getInt(0)+ ",name="+ cursor.getString(1)); }
把id为1的记录的name字段值更改新为zhang1 ContentValues updateValues = ContentValues(); updateValues.put("name","zhang1"); Uri updateIdUri = ContentUris.withAppendedId(uri,2); resolver.update(updateIdUri,updateValues,null删除id为2的记录,即字段age Uri deleteIdUri = ContentUris.withAppendedId(uri,1)">); resolver.delete(deleteIdUri,1)">null);

监听数据变化

如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri,null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:

public class MyProvider extends ContentProvider {
   public Uri insert(Uri uri,ContentValues values) {
      db.insert("tablename","tablenameid",values);
      getContext().getContentResolver().notifyChange(uri,1)">);
   }
}

而访问者必须使用 ContentObserver 对数据(数据采用 uri 描述)进行监听,当监听到数据变化通知时,系统就会调用 ContentObserver 的 onChange() 方法:

getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),1)">true,1)">new PersonObserver( Handler()));
class PersonObserver  ContentObserver{
    PersonObserver(Handler handler) {
      super(handler);
   }
   void onChange(boolean selfChange) {
      to do something
   }
}
实例说明

数据源是 sqlite,用 ContentResolver 操作 ContentProvider。

这里写图片描述

Constant.java(储存一些常量)

class Constant {  
      
    static final String TABLE_NAME = "user";  
      
    final String COLUMN_ID = "_id";  
    final String COLUMN_NAME = "name";  
       
       
    final String AUTOHORITY = "cn.scu.myprovider"final int ITEM = 1int ITEM_ID = 2;  
       
    final String CONTENT_TYPE = "vnd.android.cursor.dir/user"final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/user"final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/user");  
}  

DBHelper.java (操作数据库)

class DBHelper  sqliteOpenHelper {  
  
    private final String DATABASE_NAME = "finch.db";    
    int DATABASE_VERSION = 1;    
  
     DBHelper(Context context) {  
        super(context,DATABASE_NAME,DATABASE_VERSION);  
    }  
  
    @Override  
    void onCreate(sqliteDatabase db)  throws sqlException {  
        创建表格  
        db.execsql("CREATE TABLE IF NOT EXISTS "+ Constant.TABLE_NAME + "("+ Constant.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Constant.COLUMN_NAME +" VARCHAR NOT NULL);");  
    }  
  
    @Override  
    void onUpgrade(sqliteDatabase db,1)">int oldVersion,1)">int newVersion)  这里知识简单删除并创建表格 
        如果需要保留原来的数据,需要先备份再删除
        db.execsql("DROP TABLE IF EXISTS "+ Constant.TABLE_NAME+";");  
        onCreate(db);  
    }  
}  

MyProvider.java (自定义的 ContentProvider ) 

 ContentProvider {    
    
    DBHelper mDbHelper = ;    
    sqliteDatabase db = ;    
    
    final UriMatcher mMatcher;    
    static{    
        mMatcher =  UriMatcher(UriMatcher.NO_MATCH); 
     // 注册 uri mMatcher.addURI(Constant.AUTOHORITY,Constant.TABLE_NAME,Constant.ITEM); mMatcher.addURI(Constant.AUTOHORITY,Constant.TABLE_NAME
+"/#" String getType(Uri uri) {
     // 根据匹配规则返回对应的类型 switch (mMatcher.match(uri)) { case Constant.ITEM: return Constant.CONTENT_TYPE; Constant.ITEM_ID: Constant.CONTENT_ITEM_TYPE; : throw new IllegalArgumentException("UnkNown URI"+uri); } } @Override Todo Auto-generated method stub long rowId; if(mMatcher.match(uri)!=Constant.ITEM){ uri); } rowId = db.insert(Constant.TABLE_NAME,values); if(rowId>0){ Uri noteUri=ContentUris.withAppendedId(Constant.CONTENT_URI,rowId); getContext().getContentResolver().notifyChange(noteUri,1)">); noteUri; } new sqlException("Failed to insert row into " + uri); } @Override onCreate() { Todo Auto-generated method stub mDbHelper = DBHelper(getContext()); db = mDbHelper.getReadableDatabase(); return true; } @Override Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) { Todo Auto-generated method stub Cursor c = ; Constant.ITEM: c = db.query(Constant.TABLE_NAME,projection,selection,selectionArgs,sortOrder); Constant.ITEM_ID: c = db.query(Constant.TABLE_NAME,Constant.COLUMN_ID + "="+uri.getLastPathSegment(),1)">uri); } c.setNotificationUri(getContext().getContentResolver(),uri); c; } @Override int update(Uri uri,ContentValues values,String[] selectionArgs) { return 0; } @Override delete(Uri uri,String[] selectionArgs) { Todo Auto-generated method stub ; } }

MainActivity.java(ContentResolver操作)

class MainActivity  Activity {
    private ContentResolver mContentResolver = ; 
    private Cursor cursor = ;  
         @Override
        protected void onCreate(Bundle savedInstanceState) {
             Todo Auto-generated method stub
            .onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
               TextView tv = (TextView) findViewById(R.id.tv);
                
                mContentResolver = getContentResolver();  
                tv.setText("添加初始数据 ");
                for (int i = 0; i < 10; i++) {  
                    ContentValues values =  ContentValues();  
                    values.put(Constant.COLUMN_NAME,"fanrunqi"+i);  
                    mContentResolver.insert(Constant.CONTENT_URI,values);  
                } 
                
                tv.setText("查询数据 ");
                cursor = mContentResolver.query(Constant.CONTENT_URI,1)">new String[]{Constant.COLUMN_ID,Constant.COLUMN_NAME},1)">);  
                if (cursor.movetoFirst()) {
                    String s = cursor.getString(cursor.getColumnIndex(Constant.COLUMN_NAME));
                    tv.setText("第一个数据: "+s);
                }
        }
         
}  

最后在manifest申明 :

<provider android:name="MyProvider" android:authorities="cn.scu.myprovider" />
总结:
  • 如何通过 ContentProvider 查询数据?   通过 ContentResolver 进行uri匹配

  • 如何实现自己的ContentProvider? 继承 ContentProvider,实现对应的方法。在 manifest 中声明

额外补充:隐式 Intent 中 <data> 标签

该部分内容与 ContentProvider 没关系,只是这里讲到了 URI,就顺便此处在插入另外一个知识点:Intent 中 <data> 标签。看不懂的可以直接略过,看下一步分的内容,此处内容与 activity 相关。

Data 的匹配规则:如果过滤规则 intent-filter 中定义了 data,那么 Intent 中必须也要携带可匹配的 data

data 的语法如下所示:

<data android:scheme=“string”
      android:hostandroid:portandroid:pathandroid:pathPatternandroid:pathPrefixandroid:mimeType=“string”>

data 由两部分组成:mimeType 和 URI。mimeType 可以为空,URI 一定不会为空,因为有默认值。mimeType 指媒体类型,比如 image/jpeg,video/* 等,可表示图片,视频等不同的媒体格式

示例1 

data 的匹配:

intent-filter>
     action android:name="com.action.demo1"></actioncategory ="android.intent.category.DEFAULT" />
     data
         ="x7"
         android:host="www.360.com"
            />
 </> 

清单文件 intent-filter 定义的 data 中,只有 URI,没有 mimeType 类型,匹配如下

intent.setData(Uri.parse("x7://www.360.com"))

示例2

>
       />
       ="image/*" >

清单文件 intent-filter 定义的 data 中,没有定义 URI,只有 mimeType 类型,但是 URI 却有默认值,URI 中的 scheme 默认为 content 或者 file,host 一定不能为空,随便给个字符串abc 都可以,匹配如下

intent.setDataAndType(Uri.parse("content://abc"),"image/png");

注意:
content://abc 换成 file://abc 在 7.0 以上的版本(即把 targetSdkVersion 指定成 24 及之上并且在 API>=24 的设备上运行)会出现 FileUriExposedException 异常,google 提供了FileProvider 解决这个异常,使用它可以生成 content://Uri 来替代 file://Uri.

示例3

>
      />
        data
               ="image/*"
               android:scheme
               android:host="www.360.com"
         data
             ="video/*"
             android:scheme
             android:host/>
>

清单文件 intent-filter 定义的 data 中,URI 和 mimeType 都有,匹配如下

intent.setDataAndType(Uri.parse("x7://www.360.com"),"image/png" 或者
intent.setDataAndType(Uri.parse("x7://www.360.com"),"video/mpeg");

 

Inent 中携带的 data 标签对应的数据,在某一组 intent-filter 中可以找到,即匹配成功。data 标签数据在 intent-filter 中也可以有多组

隐示启动,防止匹配失败的可以提前检测:

判断的方法如下:

PackageManager mPackageManager = getPackageManager();
返回匹配成功中最佳匹配的一个act信息,intent 需要按照前面的把 action,data 等都设置好
ResolveInfo info = mPackageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY);
返回所有匹配成功的act信息,是一个集合
 List<ResolveInfo> infoList = mPackageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);

只要上述 2 个方法的返回值不为 null,那么 startActivity 一定可以成功

 

参考文章:

ContentProvider 数据访问详解

ContentProvider全解析和使用

总结

以上是小编为你收集整理的Android ContentProvider 基本原理和使用详解全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

Android ContentProvider 详解

Android ContentProvider 详解

1. 适用场景

1) ContentProvider 为存储和读取数据提供了统一的接口

2) 使用 ContentProvider,应用程序可以实现数据共享

3) android 内置的许多数据都是使用 ContentProvider 形式,供开发者调用的 (如视频,音频,图片,通讯录等)

2. 相关概念介绍

1)ContentProvider 简介

       当应用继承 ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用 sharedpreferences 共享数据,需要使用 sharedpreferences API 读写数据。而使用 ContentProvider 共享数据的好处是统一了数据访问方式。

2)Uri 类简介

      Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

      在 Content Provider 中使用的查询字符串有别于标准的 SQL 查询。很多诸如 select, add, delete, modify 等操作我们都使用一种特殊的 URI 来进行,这种 URI 由 3 个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的 ID。以下是一些示例 URI:

     content://media/internal/images  这个 URI 将返回设备上存储的所有图片

     content://contacts/people/  这个 URI 将返回设备上的所有联系人信息

     content://contacts/people/45 这个 URI 返回单个结果(联系人信息中 ID 为 45 的联系人记录)

  尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android 提供一系列的帮助类(在 android.provider 包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面 content://contacts/people/45 这个 URI 就可以写成如下形式:

  Uri person = ContentUris.withAppendedId(People.CONTENT_URI,  45);

然后执行数据查询:

Cursor cur = managedQuery(person, null, null, null);

这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:

package com.wissen.testApp;
public class ContentProviderDemo extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       displayRecords();
    }

    private void displayRecords() {
        //该数组中包含了所有要返回的字段
     String columns[] = new String[] { People.NAME, People.NUMBER };
       Uri mContacts = People.CONTENT_URI;
       Cursor cur = managedQuery(
          mContacts,
          columns,  // 要返回的数据字段
         null,          // WHERE子句
         null,         // WHERE 子句的参数
         null         // Order-by子句
     );
       if (cur.moveToFirst()) {
           String name = null;
           String phoneNo = null;
           do {
              // 获取字段的值
            name = cur.getString(cur.getColumnIndex(People.NAME));
             phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
             Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();
          } while (cur.moveToNext());
       }
    }
}

上例示范了一个如何依次读取联系人信息表中的指定数据列 name 和 number。

修改记录:

我们可以使用 ContentResolver.update () 方法来修改数据,我们来写一个修改数据的方法:

private void updateRecord(int recNo, String name) {
    Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
    ContentValues values = new ContentValues();
    values.put(People.NAME, name);
    getContentResolver().update(uri, values, nullnull);
}

现在你可以调用上面的方法来更新指定记录:

updateRecord (10,”XYZ”);   // 更改第 10 条记录的 name 字段值为 “XYZ”

添加记录:

要增加记录,我们可以调用 ContentResolver.insert () 方法,该方法接受一个要增加的记录的目标 URI,以及一个包含了新记录值的 Map 对象,调用后的返回值是新记录的 URI,包含记录号。

上面的例子中我们都是基于联系人信息簿这个标准的 Content Provider,现在我们继续来创建一个 insertRecord () 方法以对联系人信息簿中进行数据的添加:

private void insertRecords(String name, String phoneNo) {
    ContentValues values = new ContentValues();
    values.put(People.NAME, name);
    Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
    Log.d(”ANDROID”, uri.toString());
    Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
    values.clear();
    values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
    values.put(People.NUMBER, phoneNo);
    getContentResolver().insert(numberUri, values);
}

这样我们就可以调用 insertRecords (name, phoneNo) 的方式来向联系人信息簿中添加联系人姓名和电话号码。

删除记录:

Content Provider 中的 getContextResolver.delete () 方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:

private void deleteRecords() {
    Uri uri = People.CONTENT_URI;
    getContentResolver().delete(uri, nullnull);
}

你也可以指定 WHERE 条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除 name 为‘XYZ XYZ’的记录。

3. 创建 ContentProvider

要创建我们自己的 Content Provider 的话,我们需要遵循以下几步:

a. 创建一个继承了 ContentProvider 父类的类

b. 定义一个名为 CONTENT_URI,并且是 public static final 的 Uri 类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:

public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

c. 定义你要返回给客户端的数据列名。如果你正在使用 Android 数据库,必须为其定义一个叫_id 的列,它用来表示每条记录的唯一性。

d. 创建你的数据存储系统。大多数 Content Provider 使用 Android 文件系统或 SQLite 数据库来保持数据,但是你也可以以任何你想要的方式来存储。

e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的 URI 字符串,通过它来读取对应的文件数据。处理这种数据类型的 Content Provider 需要实现一个名为_data 的字段,_data 字段列出了该文件在 Android 文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供 ContentResolver 使用。客户端可以调用 ContentResolver.openOutputStream () 方法来处理该 URI 指向的文件资源;如果是 ContentResolver 本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

f. 声明 public static String 型的变量,用于指定要从游标处返回的数据列。

g. 查询返回一个 Cursor 类型的对象。所有执行写操作的方法如 insert (), update () 以及 delete () 都将被监听。我们可以通过使用 ContentResover ().notifyChange () 方法来通知监听器关于数据更新的信息。

h. 在 AndroidMenifest.xml 中使用 <provider> 标签来设置 Content Provider。

i. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的 MIME 类型,以供 ContentProvider.geType (url) 来返回。MIME 类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:

  vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的 MIME 类型)

  比如,一个请求列车信息的 URI 如 content://com.example.transportationprovider/trains/122 可能就会返回 typevnd.android.cursor.item/vnd.example.rail 这样一个 MIME 类型。

  vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的 MIME 类型)

  比如,一个请求所有列车信息的 URI 如 content://com.example.transportationprovider/trains 可能就会返回 vnd.android.cursor.dir/vnd.example.rail 这样一个 MIME 类型。

下列代码将创建一个 Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite 数据库存储这些数据):

public class MyUsers {
    public static final String AUTHORITY  = “com.wissen.MyContentProvider”;

    // BaseColumn类中已经包含了 _id字段
   public static final class User implements BaseColumns {
        public static final Uri CONTENT_URI  = Uri.parse(”content://com.wissen.MyContentProvider”);
        // 表数据列
        public static final String  USER_NAME  = “USER_NAME”;
    }
}

上面的类中定义了 Content Provider 的 CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的 Content Provider 类:

public class MyContentProvider extends ContentProvider {
    private SQLiteDatabase     sqlDB;
    private DatabaseHelper    dbHelper;
    private static final String  DATABASE_NAME = “Users.db”;
    private static final int  DATABASE_VERSION= 1;
    private static final String TABLE_NAME= “User”;
    private static final String TAG = “MyContentProvider”;

    private static class DatabaseHelper extends SQLiteOpenHelper {
        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            //创建用于存储数据的表
        db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME);
            onCreate(db);
        }
    }

    @Override
    public int delete(Uri uri, String s, String[] as) {
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentvalues) {
        sqlDB = dbHelper.getWritableDatabase();
        long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues);
        if (rowId > 0) {
            Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
            getContext().getContentResolver().notifyChange(rowUri, null);
            return rowUri;
        }
        throw new SQLException(”Failed to insert row into ” + uri);
    }

    @Override
    public boolean onCreate() {
        dbHelper = new DatabaseHelper(getContext());
        return (dbHelper == null) ? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        qb.setTables(TABLE_NAME);
        Cursor c = qb.query(db, projection, selection, nullnullnull, sortOrder);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
        return 0;
    }
}

一个名为 MyContentProvider 的 Content Provider 创建完成了,它用于从 Sqlite 数据库中添加和读取记录。

Content Provider 的入口需要在 AndroidManifest.xml 中配置:

<provider android:name=”MyContentProvider” android:authorities=”com.wissen.MyContentProvider” />

之后,让我们来使用这个定义好的 Content Provider:

1)为应用程序添加 ContentProvider 的访问权限。

2)通过 getContentResolver () 方法得到 ContentResolver 对象。

3)调用 ContentResolver 类的 query () 方法查询数据,该方法会返回一个 Cursor 对象。

4)对得到的 Cursor 对象进行分析,得到需要的数据。

5)调用 Cursor 类的 close () 方法将 Cursor 对象关闭。

public class MyContentDemo extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        insertRecord(”MyUser”);
        displayRecords();
    }
   
    private void insertRecord(String userName) {
        ContentValues values = new ContentValues();
        values.put(MyUsers.User.USER_NAME, userName);
        getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
    }

    private void displayRecords() {
        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
        Uri myUri = MyUsers.User.CONTENT_URI;
        Cursor cur = managedQuery(myUri, columns,nullnullnull );
        if (cur.moveToFirst()) {
            String id = null;
            String userName = null;
            do {
                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
                Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show();
           } while (cur.moveToNext());
       }
    }
}

我们今天的关于Android 数据存储之ContentProvider 使用和应用场景android的contentprovider的分享已经告一段落,感谢您的关注,如果您想了解更多关于Android ContentProvider、android ContentProvider 使用实例、Android ContentProvider 基本原理和使用详解、Android ContentProvider 详解的相关信息,请在本站查询。

本文标签: