Android ContentProvider 的简单使用

来源:互联网 发布:湖南中原数据研究中心 编辑:程序博客网 时间:2024/05/22 14:08

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;

 <provider android:authorities="com.contentprovidersdemo.db.bank"                   android:name=".provide.AccountManage"                   android:exported="true"/>

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

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/.dhlock11-12 16:31:30.250 1612-1717/? E/Cataloger: need not sync path:/storage/emulated/0/Mob/comm/locks/.dhlock11-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/.dhlock11-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的清单文件中添加
 <!--authorities 授权许可,在resolve中的uri-->        <provider android:authorities="com.contentprovidersdemo.db.bank"                  android:name=".provide.AccountManage"                  android:exported="true"//报异常的时候添加                  />


表名的区分: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/account11-12 23:03:02.484 32743-32754/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account11-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/info11-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.实现增删改查方法