ContentProvider基础操作

ContentProvider是Android四大组件之一,它为存储和获取数据提供统一的接口。即将应用程序的数据提供给外部程序使用的一种手段和方法。最大特点是可以实现不同的应用程序之间共享数据。

对于ContentProvider来说,无论数据的来源是什么,他都认为是一种表,它会把数据组织成表格。

两种使用情景:1.A应用访问B应用,2A应用访问系统应用(通讯录)

1A应用访问B应用:(主要是实现定义一个ContentProvider的操作)

(1)自定义一个类继承于ContentProvider,并且实现它的方法:注意操作步骤

想要建立文件的地方->右键->new->other->Content Provider->修改名称,填写唯一的uri->完成。

public class OutContentProvider extends ContentProvider {
    public OutContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

然后看一下manifest.xml:

  
        

        
            
                

                
            
        
    

当然也可以先自定义个类,然后继承ContentProvider,实现他的方法,然后配置mainfest.xml。

ok,下一步。

(2)由于这是一个简单的使用步骤,所以就简单的按照sqlite数据库来写,当然数据的来源还可以是其他的任何的一种,这里只是举个例子

由于ContentProvider的创建是在应用第一次打开的时候就创建的,所以为了方便吧数据也在ContentProvider中创建了

 SQLiteDatabase sqLiteDatabase;

  @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        //创建数据库
        Log.e("###", "onCreate");
        SQLiteOpenHelper helper = new SQLiteOpenHelper(getContext(), "outcp.db", null, 1) {
            @Override
            public void onCreate(SQLiteDatabase db) {
                //创建表格
                String sql = "create table demo_table(_id integer primary key autoincrement," +
                        "name varchar(20)," +
                        "age integer," +
                        "gender varchar(2))";
                db.execSQL(sql);
            }

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

            }
        };
        sqLiteDatabase = helper.getReadableDatabase();
        //返回true,这样才能会返回数据
        return true;
    }

注意返回值改成true。

(3)insert()方法:增加数据,在OutContentProvider中

  @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
//        throw new UnsupportedOperationException("Not yet implemented");
        Long itemId = 0l;
        try {
            itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
        } catch (Exception e) {
            Log.e("###", e.getLocalizedMessage());
        }
        Log.e("###", "调用了out中的insert方法");
        //表名,非空列,值,对于sqlite数据库,添加完成之后会返回一个Long的id
        //uri这个uri就是另外的程序调用contentprovider的时候传过来的uri,如果传回去那么跟调用程序的uri没有区别也没有意义
        // 所以用ContentUris把uri和itemid整合一下,成为一个新的Uri,返回回去。
        return ContentUris.withAppendedId(uri, itemId);
    }

注意:这里的 try {}catch((Exception e){} 可以不写,insertOrThrow可以改成insert。另外ContentUris是处理uri的一个类, ContentUris.withAppendedId(uri, itemId)是吧uri追加一个id作为返回值,为了方便调用者获取到添加成功的状态和id。

(4)insert()方法的调用:这里需要新建一个其他的项目,在调用项目的合适的位置写一个调用方法。这里写了一个textview的点击监听事件:

 Uri mUri = Uri.parse("content://com.stone.mycontentprovider.outcp");
ContentResolver resolver;
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     
        //获取ContentResolver对象
        resolver = getContentResolver();
    }

   public void insertTv(View view) {
        ContentValues values = new ContentValues();
        values.put("name", MyUtils.getChineseName());
        values.put("age", MyUtils.getNum(10, 60));
        values.put("gender", MyUtils.getGender());

        Uri reUri = resolver.insert(mUri, values);
        Long reId = ContentUris.parseId(reUri);
        Toast.makeText(MainActivity.this, reId + "", Toast.LENGTH_LONG).show();
    }

注意:这里的uri就是ContentProvider提供者的insert方法返回的我们添加了id的那个uri,同样的我们使用ContentUris这个类的 ContentUris.parseId(reUri);方法拿到添加成功的数据的id。

(5)query()查询方法:

  @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
//        throw new UnsupportedOperationException("Not yet implemented");
        Cursor cursor = sqLiteDatabase.query("demo_table", projection, selection, selectionArgs, null, null, sortOrder);
        return cursor;
    }

(6)调用查询方法:

SimpleCursorAdapter simpleCursorAdapter;
 ListView mListView;

 public void queryTv(View view) {
// 根据contentprovider中的查询方法:Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
        //rui,查询哪一列,查询条件,条件之,排序
        Cursor cursor = resolver.query(mUri, null, null, null, null);
        //这里的listview的adapter用到SimpleCursorAdapter
        simpleCursorAdapter = new SimpleCursorAdapter(this,
                R.layout.layout_adapter_cursor,
                cursor,
                new String[]{"_id", "name", "age", "gender"},
                new int[]{R.id.item_id, R.id.item_name, R.id.item_age, R.id.item_gender},
                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        mListView.setAdapter(simpleCursorAdapter);

    }

注意这里直接用的SimpleCursorAdapter其中的参数分别是:SimpleCursorAdapter(context,layout,cursor,键名的字符串数组,对应的控件的id,标志).关于SimpleCursorAdapter有一篇博客可以看一下

(7)delete():删除方法

  @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
//        throw new UnsupportedOperationException("Not yet implemented");
      int result = sqLiteDatabase.delete("demo_table", selection, selectionArgs);
      return result;
    }

(8)调用删除方法:

                    int delResult = resolver.delete(mUri, "_id=?", new String[]{itemId});
                        if (delResult > 0) {
                            Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_LONG).show();
                        } else {
                            Toast.makeText(MainActivity.this, "删除灰白", Toast.LENGTH_LONG).show();
                        }

(9)修改updata()方法

  @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
//        throw new UnsupportedOperationException("Not yet implemented");
        int upReslut = sqLiteDatabase.update("demo_table", values, selection, selectionArgs);
        return upReslut;
    }

(10)调用修改方法:

 ContentValues upValues = new ContentValues();
      upValues.put("name", MyUtils.getChineseName());
         upValues.put("age", MyUtils.getNum(10, 60));
        upValues.put("gender", MyUtils.getGender());
          int upResult=resolver.update(mUri,upValues,"_id=?",new String[]{itemId});
          if (upResult>0){
               Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_LONG).show();
               } else {
              Toast.makeText(MainActivity.this, "修改灰白", Toast.LENGTH_LONG).show();
           }

ok,以上记录的是设置ContentProvider的方法,和相应的方法的调用然后再看一下关于Uri的解析。

Uri解析有两种方法,一种是使用UriMatcher解析,一种是Uri自带的解析。

(一)UriMatcher解析:事先制定好匹配规则,当调用ContentProvide中的方法时,利用制定好的匹配规则匹配uri,根据不同的uri给出不同的处理:以删除为例在oncreat中制定规则:

UriMatcher matcher;
 @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        //闯将数据库
        Log.e("###", "onCreate");
        //匹配规则:
        //UriMatcher.NO_MATCH:无法匹配
        matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI("com.stone.mycontentprovider.outcp", "democp", 1000);
        matcher.addURI("com.stone.mycontentprovider.outcp", "democp/child", 1001);
        matcher.addURI("com.stone.mycontentprovider.outcp", "democp/#", 1002);//后面是数字
        matcher.addURI("com.stone.mycontentprovider.outcp", "democp/*", 1003);//任意字符
        //返回true,这样才能会返回数据
        return true;
    }

注意:其中的democp可以使任意的字符串,也可以没有。
删除方法:

  @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
//        throw new UnsupportedOperationException("Not yet implemented");
        int result = 0;
        int code = matcher.match(uri);
        switch (code) {
            case 1000:
                Log.e("###match","匹配到的路径是"+uri.toString());
                break;
            case 1001:
                Log.e("###match","匹配到的路径是"+uri.toString());
                break;
            case 1002:
                Log.d("###match","匹配到路径为"+uri.toString()+"任意数字的内容");
                break;
            case 1003:
                Log.i("###match","匹配到路径为"+uri.toString()+"任意字符的内容");
                break;
            default:
                Log.e("###match","执行删除数据库内容的方法");
                result = sqLiteDatabase.delete("demo_table", selection, selectionArgs);
                break;
        }
        return result;
    }

那么调用删除方法:

                 resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp"),null,null);
                   resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/child"),null,null);
                        resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/123"),null,null);
                        resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/090"),null,null);
                        resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/89ii"),null,null);
                        resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/ab90"),null,null);
                        resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/123/ab90"),null,null);

运行结果:

E/###match: 匹配到的路径是content://com.stone.mycontentprovider.outcp/democp
E/###match: 匹配到的路径是content://com.stone.mycontentprovider.outcp/democp/child
D/###match: 匹配到路径为content://com.stone.mycontentprovider.outcp/democp/123任意数字的内容
D/###match: 匹配到路径为content://com.stone.mycontentprovider.outcp/democp/090任意数字的内容
I/###match: 匹配到路径为content://com.stone.mycontentprovider.outcp/democp/89ii任意字符的内容
I/###match: 匹配到路径为content://com.stone.mycontentprovider.outcp/democp/ab90任意字符的内容
E/###match: 执行删除数据库内容的方法

(二)Uri自带的解析方法:以添加为例:

调用添加方法:

  Uri othUri=resolver.insert(Uri.parse("content://com.stone.mycontentprovider.outcp/anypath?name="+MyUtils.getChineseName()
        +"&age="+MyUtils.getNum(10, 60)
        +"&gender="+ MyUtils.getGender()),new ContentValues());

注意其中的anypath可以使任意的字符串,也可以不要。
MyContentProvider中的添加方法:

  @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
//        throw new UnsupportedOperationException("Not yet implemented");

        Long itemId = 0l;
        if (values.size()>0){
            try {
                itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
            } catch (Exception e) {
                Log.e("###", e.getLocalizedMessage());
            }
        }else {
            String authory=uri.getAuthority();
            Log.e("###authory",authory);
            String path=uri.getPath();
            Log.e("###path",path);
            String query=uri.getQuery();
            Log.e("###query",query);
            String name=uri.getQueryParameter("name");
            Log.e("###name",name);
            String age=uri.getQueryParameter("age");
            Log.e("###age",age);
            String gender=uri.getQueryParameter("gender");
            Log.e("###gender",gender);
            values.put("name", name);
            values.put("age",age);
            values.put("gender",gender);
            itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
        }

        Log.e("###", "调用了out中的insert方法");
        //表名,非空列,值,对于sqlite数据库,添加完成之后会返回一个Long的id
        //uri这个uri就是另外的程序调用contentprovider的时候传过来的uri,如果传回去那么跟调用程序的uri没有区别也没有意义
        // 所以用ContentUris把uri和itemid整合一下,成为一个新的Uri,返回回去。
        return ContentUris.withAppendedId(uri, itemId);
    }

运行结果:

2020-07-23 20:07:33.190 15996-16123/com.stone.mycontentprovider E/###: 调用了out中的insert方法
2020-07-23 20:07:33.246 15996-16123/com.stone.mycontentprovider E/###authory: com.stone.mycontentprovider.outcp
2020-07-23 20:07:33.246 15996-16123/com.stone.mycontentprovider E/###path: /anypath
2020-07-23 20:07:33.247 15996-16123/com.stone.mycontentprovider E/###query: name=咸桂&age=45&gender=男
2020-07-23 20:07:33.247 15996-16123/com.stone.mycontentprovider E/###name: 咸桂
2020-07-23 20:07:33.248 15996-16123/com.stone.mycontentprovider E/###age: 45
2020-07-23 20:07:33.248 15996-16123/com.stone.mycontentprovider E/###gender: 男
2020-07-23 20:07:33.256 15996-16123/com.stone.mycontentprovider E/###: 调用了out中的insert方法

两种添加方法都执行了,所以打印了两边“” 调用了out中的insert方法“”。

ok关于解析就简单记录这些东西。下面简单说一下调用系统ContentProvider的问题:

(一)读取短信息:基本分三个步骤:

(1)获取ContentResolver

 ContentResolver contentResolver = getContentResolver();

(2)查询短信息

 Uri smsUri = Uri.parse("content://sms/");
 Cursor smsCoursor = contentResolver.query(smsUri, null, null, null, null);

(3)遍历smsCoursor ,得到每一条的数据

//遍历所有的行
   while (smsCoursor.moveToNext()) {
          //参数是列索引,遍历所有的列
          for (int i = 0; i < smsCoursor.getColumnCount(); i++) {
             Log.e("###", smsCoursor.getString(i));//此时获取到的是每一条的所有内容
            String address=cursor.getString(cursor.getColumnIndex("address"));//地址
            String body=cursor.getString(cursor.getColumnIndex("body"));//短信内容
            String dateString=cursor.getString(cursor.getColumnIndex("date"));//日期
          }
      }

(4)注意点:

"content://sms/":短信箱
"content://sms/inbox":收件箱
"content://sms/sent":发件箱
"content://sms/draft":草稿箱

(5)权限不要忘了动态申请,manifest也要注册。

 

(6)还有我的红米手机获取不到短信息,查了资料说是没有权限,现在还没有解决,希望各位看官能给个指导意见和方法。华为的可以。

(二)读取联系人:通讯录中的姓名和其他内容是由不同的contentprovider提供的。也就是说姓名和其他内容是不同的表,姓名是主表,其他内容是从表,主表中的主键会在从表中作为外键使用

(1)权限:动态申请

 

(2)读取数据

 ContentResolver resolver;
 resolver = getContentResolver();
//先读取联系人信息
 Cursor cCursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        while (cCursor.moveToNext()) {
//            ContactsContract.Contacts.DISPLAY_NAME//姓名
//            ContactsContract.Contacts._ID//id
//根据读取到的联系人信息遍历Cursor获取每一个联系人的姓名和id
            String name = cCursor.getString(cCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            String mId = cCursor.getString(cCursor.getColumnIndex(ContactsContract.Contacts._ID));
            Log.e("###name", name + " ");
            Log.e("###mId", mId + " ");
//根据id获取对应的联系人的其他信息
            String selections = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?";//查询条件
            Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, selections, new String[]{mId}, null);
//变美丽其他信息
            while (phoneCursor.moveToNext()) {
//获取电话号码
                String phoneNum = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                name += "  " + phoneNum;
            }
//这个时候的log打印的就是姓名和联系电话
            Log.e("###name", name + " ");

        }

(三)添加联系人:上面我们知道了联系人姓名和电话是在两个表中,所以写入联系人也需要添加两次:

(1)权限:WRITE_CONTACTS,动态的你懂的

 

(2)写入联系人

 //1.往一个contentprovider中插入一条空数据,获取新生成的id
        ContentValues values = new ContentValues();
        Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long id = ContentUris.parseId(uri);//获取到刚刚生成的id
   //2.利用刚刚获取到的id分别组合姓名和电话号码往另外一个contentprovider中插入数据
        //插入姓名
        values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,MyUtils.getChineseName());//插入的姓名
        values.put(ContactsContract.Data.RAW_CONTACT_ID,id);//指定和姓名关联的id
        values.put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);//设置该行数据的类型
        resolver.insert(ContactsContract.Data.CONTENT_URI,values);//插入操作

        //插入电话
        values.clear();
        values.put(ContactsContract.Data.RAW_CONTACT_ID,id);//指定和电话关联的id
        values.put(ContactsContract.CommonDataKinds.Phone.NUMBER,"13788883333");//插入的电话
        values.put(ContactsContract.CommonDataKinds.Phone.TYPE,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);//插入的电话的类型
        values.put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);//设置该行数据的类型
        resolver.insert(ContactsContract.Data.CONTENT_URI,values);//插入操作

ok,到此关于ContentProvider基础操作已经差不多了,其中还有其他的遗漏或者不足的地方,希望看官指正。后续还会不断的改善。祝大家工作愉快。

你可能感兴趣的:(ContentProvider基础操作)