Android ContentProvider 的简单使用

Content Provider内容提供者:android中进程间通信的标准接口。即让当前进程中数据库中数据,可以让其它应用访问到。


作用 : 程序中的数据库中某一张表想要给其他程序访问时,为了安全的访问,就可以用到内容提供者;


举个栗子:A应用相当于一家银行,B应用相当于你自己,你要向银行借钱时,那么就要和银行建立联系,银行的客户经理就相当于provide负责将数据暴露出来,自己就相当于resolve,负责和银行的客户经理接洽,provide与resolve怎么建立通信呢?我们可以找不同的银行借钱,每家银行都有自己的网点,我们根据provide的地址建立连接,如果客户经理同意你的请求,那么你们的通道打通了,客户经理从银行拿到钱,然后通过通道将钱借给你;provide接收客户端resolve的数据请求-->获得数据库的数据-->返回数据;

奉上简单的代码;

创建一个工程:
在这个工程中创建数据库:
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      //  createBankDB(getBaseContext());
    }

    private void createBankDB(Context baseContext) {
        BankDbHelper helper = new BankDbHelper(baseContext);
        helper.getWritableDatabase();
    }
}
数据库帮助类
public class BankDbHelper extends SQLiteOpenHelper {
    public BankDbHelper(Context context) {
        super(context, "bank.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
//创建account表和info表;
 db.execSQL("create table account(id integer primary key autoincrement, name varchar(20),money folat)");
db.execSQL("create table info(id ,phone)");
 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
 创建对外提供数据的provide,继承ContentProvider实现增删改查方法;在每个方法中打印日志查看是否连接成功; 
  
public class AccountManage extends ContentProvider {
    private static final String TAG = "AccountManage";
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Log.e(TAG, "query: 查询调用" );
        return null;
    }

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

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.e(TAG, "insert: 增加方法调用");
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.e(TAG, "delete: 删除方法调用");
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.e(TAG, "update: 修改方法调用" );
        return 0;
    }
在清单文件中注册provide, 
authorities相当于一个provide的地址,不同的程序有可能提供不同的
provide;

 

创建新的工程,用来访问数据;界面上提供增删改查四个按钮,对应银行的增删改查;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.insert)
    Button mInsert;
    @BindView(R.id.delete)
    Button mDelete;
    @BindView(R.id.updata)
    Button mUpdata;
    @BindView(R.id.query)
    Button mQuery;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       ButterKnife.bind(this);
    }

    @OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.insert:
               
                break;
            case R.id.delete:
                break;
            case R.id.updata:
                break;
            case R.id.query:
                break;
        }
    }
}
增:

利用上下文获取到resolve对象:

  ContentResolver resolver =getContentResolver();
通过resolve调用内容提供者的增方法
 resolver.insert(uri,values);
insert方法中需要两个参数,一个uri,一个ContentValues对象,
uri:统一资源标识符;
  ContentValues values = new ContentValues();
//指定内容提供者的地址,就是注册时的
authorities来区分要连接的那个provide,一定要注意格式:"connect://+authorities"
 Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank");
 
  

 
  
  @OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.insert:
                ContentResolver resolver =getContentResolver();
                ContentValues values = new ContentValues();
                Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank");
               resolver.insert(uri,values);
               
                Toast.makeText(this,"insert点击",Toast.LENGTH_LONG).show();
                break;
            case R.id.delete:
                break;
            case R.id.updata:
                break;
            case R.id.query:
                break;
        }
将两个程序运行起来,点击insert按钮,可以看到输出日志:
11-12 16:31:30.200 32433-32444/? E/AccountManage: insert: 增加方法调用
11-12 16:31:30.250 1612-1717/? E/Cataloger: syncFile:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:30.250 1612-1717/? E/Cataloger: need not sync path:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:36.300 32433-32443/? E/AccountManage: insert: 增加方法调用
11-12 16:31:40.260 1612-1717/? E/Cataloger: syncFile:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:40.260 1612-1717/? E/Cataloger: need not sync path:/storage/emulated/0/Mob/comm/locks/.dhlock

 如果报

    java.lang.SecurityException: Permission Denial

异常,那就在provide的清单文件中添加
 
        


表名的区分:UriMatcher

resolver发起请求,也是通过uri来区分要操作的哪张表;就像访问网站时我们可以通过/来指定访问的目录,操作表也一样

Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
将表名添加到uri的authorities后面以/分开;通过log打印看看:
  public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.e(TAG, "insert: uri = " +uri.toString());
        return null;
    }
点击按钮输出:
E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
谷歌提供一个用来匹配URI的工具: UriMather
可以参考http://blog.csdn.net/feng88724/article/details/6331396这篇文章了解 UriMather;

在provide中创建uri匹配器对象并且以静态代码块的方式预设匹配规则

  //创建匹配器;参数默认是一个-1的常量匹配不上的时候返回-1
    static               UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    //预设匹配规则,将要操作的表都可以添加,定义不同的code\
    static {
        uriMatcher.addURI(authoritity,path,code);
    }
第一个参数就是provide中的authorities, path 就是要操作的表名,code是自己预设的一个整数常量;
添加匹配规则后,我们只要拿着这个匹配器插入一个uri对象,它会根据前面的authorities和path转换出一个code整形数,通过比较code就知道是操作的那个表;

  //创建匹配器;
    static               UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int ACCOUNT           = 1;
    private static final int INFO           = 2;

    //预设匹配规则\
    static {
        uriMatcher.addURI("com.contentprovidersdemo.db.bank","account",ACCOUNT);
        uriMatcher.addURI("com.contentprovidersdemo.db.bank","info",INFO);
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.e(TAG, "insert: uri = " +uri.toString());
     //通过uriMatcher.match(uri)返回的值来判断是哪张表,
 
  
 
  

 if (uriMatcher.match(uri) == INFO){ Log.d(TAG, "操作的是info表"); }else if (uriMatcher.match(uri) == ACCOUNT){ Log.d(TAG, "insert: 操作的是 account 表"); }else { Log.d(TAG, "insert: 没有匹配到"); } return null; } 
  
 日志输出: 
  
11-12 22:05:34.978 21992-22002/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
11-12 23:03:02.484 32743-32754/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
11-12 23:03:02.484 32743-32754/? D/AccountManage: insert: 操作的是 account 表
11-12 23:03:50.844 1377-1389/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/info
11-12 23:03:50.844 1377-1389/? D/AccountManage: 操作的是info表
通过这种方式就可以区分是那张表了!
现在建立连接了,也可以操作指定的数据表了,那么我们开始写数据库的增删改查;在provide中:

private BankDbHelper mHelper;

    @Override
    public boolean onCreate() {
        //在创建provide的时候创建数据库帮助类对象;
        mHelper = new BankDbHelper(getContext());
        return false;
    }
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.e(TAG, "insert: uri = " +uri.toString());
        SQLiteDatabase db = mHelper.getWritableDatabase();
        if (uriMatcher.match(uri) == INFO){
            //插入info表 ,插入是数据有resolve传过来
            db.insert("info",null,values);
            Log.d(TAG, "插入的是info表");
        }else if (uriMatcher.match(uri) == ACCOUNT){
            db.insert("account",null,values);
            Log.d(TAG, "insert: 插入的是 account 表");
        }else if (uriMatcher.match(uri)== UriMatcher.NO_MATCH){
        //匹配不到表名的时候抛出一个异常;
           throw new IllegalArgumentException("未知表名,请检查表名");
        }
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        SQLiteDatabase db = mHelper.getWritableDatabase();
        if (uriMatcher.match(uri)==INFO){
            //要删除的条件和数据都是resolve传递过来的
            db.delete("info",selection,selectionArgs);
            Log.d(TAG, "delete: 删除是info表的内容");
        }else if (uriMatcher.match(uri) == ACCOUNT){
            db.delete("account",selection,selectionArgs);
            Log.d(TAG, "delete: 删除是account表的内容");
        }else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
            throw new IllegalArgumentException("未知的表名");
        }
        Log.e(TAG, "delete: 删除方法调用");
        return 0;
    }
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.e(TAG, "update: 修改方法调用" );
        SQLiteDatabase db = mHelper.getWritableDatabase();
        if (uriMatcher.match(uri)==INFO){
            //要修改的条件和数据都是resolve传递过来的
         db.update("info",values,selection,selectionArgs);
            Log.d(TAG, "update: 修改是info表的内容");
        }else if (uriMatcher.match(uri) == ACCOUNT){
            db.update("account",values,selection,selectionArgs);
            Log.d(TAG, "update: 修改是account表的内容");
        }else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
            throw new IllegalArgumentException("未知的表名");
        }

        return 0;
    }
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Log.e(TAG, "query: 查询调用" );
        SQLiteDatabase db = mHelper.getReadableDatabase();
        if (uriMatcher.match(uri)==INFO){

            Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
//注意,这里查询的cursor对象是不能关闭的,要关也得由resolve来关闭,
            return cursor;

        }else if (uriMatcher.match(uri) == ACCOUNT){
            Cursor cursor = db.query("account", projection, selection, selectionArgs, null, null, sortOrder);
            return cursor;
        }else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
            throw new IllegalArgumentException("未知的表名");
        }
        return null;
    }

resolve端:

 @OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.insert: {
                ContentResolver resolver = getContentResolver();
                ContentValues values = new ContentValues();
                values.put("name", "和珅");
                values.put("money", 20000000);
                Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
                resolver.insert(uri, values);
                Toast.makeText(this, "insert点击", Toast.LENGTH_LONG).show();
                break;
            }
            case R.id.delete:{
                ContentResolver resolver =getContentResolver();


                Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
                resolver.delete(uri,"name=?",new String[]{"和珅"});
                Toast.makeText(this, "删除成功", Toast.LENGTH_LONG).show();
            }
                break;
            case R.id.updata:{
                Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
                ContentResolver resolver =getContentResolver();
                ContentValues values = new ContentValues();
                values.put("money",999999);
                resolver.update(uri,values,"name=?",new String[]{"和珅"});
                Toast.makeText(this, "修改成功", Toast.LENGTH_LONG).show();
            }
                break;
            case R.id.query:{
                Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
                ContentResolver resolver =getContentResolver();
            /**
             * resolver.query(uri,
             * projection,查询的列  ,null为所有列
             * selection,     查询条件, null 为查询所有
             * selectionArgs,  查询条件 的值
             * sortOrde);   排序  null 为默认排序
             */
                Cursor cursor = resolver.query(uri, null, "name=?", new String[]{"和珅"}, null);
                if (cursor.moveToFirst()){
                    float money = cursor.getFloat(cursor.getColumnIndex("money"));
                    Toast.makeText(this, "和珅的资产 = "+money, Toast.LENGTH_SHORT).show();
                }else
                {
                    Toast.makeText(this, "查无此人", Toast.LENGTH_SHORT).show();
                }
                cursor.close();

            }
                break;
        }

使用步骤总结:

provide :

1.创建ContentProvide 子类;

2.添加UriMatcher 匹配规则;

3.实现子类的曾删改查方法

Resolver :

1.上下文获取resolver对象

2.通过Uri区分访问的provide和具体的表

3.实现增删改查方法
















 
  
 
 

你可能感兴趣的:(Android ContentProvider 的简单使用)