什么是内容提供器?java
跨程序共享数据以内容提供器,这是个什么功能?看到这个名称的时候最能给咱们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的做用主要是用于在不一样的引用程序之间实现数据共享的功能,它提供了一完整的机制,容许一个程序访问另外一个程序中的数据,同时还能保证被访问的数据的安全性,在目前使用内容提供器是Android实现跨程序共享数据的标准方式。不一样于文件存储和SharePreferences存储中的两种全局可读性操做模式,内容提供器能够选择只对那一部分数据进行共享,从而保证咱们程序中的隐私数据不糊有泄漏的风险。android
不过在理解这个内容提供器以前,咱们须要理解Android的运行时权限,这个就不须要咱们解释什么是运行时权限了,由于咱们在以前其实已经使用过,好比“相机权限”,“照片权限”,“位置权限”等等!数据库
运行时权限数组
Android 将全部的权限大体的分为两类,一类是普通权限,另外一类是危险权限,咱们在下面将危险的权限整理了出来,供之后咱们参考使用:安全
举个小栗子app
下面是针对打电话咱们写的一个小Demo,其实逻辑很简单,说的直接点就是一句话“有权限直接打电话,没有权限就请求完了再打”。下面是点击事件咱们作的操做ide
// 利用checkSelfPermission这个函数检查有没有运行时权限 // 有权限就直接调用下面的call()方法,没有就请求权限 // checkSelfPermission(MainActivity.this, android.Manifest.permission.CALL_PHONE) 是否等于PackageManager.PERMISSION_GRANTED // PERMISSION_GRANTED 赞成权限 if (ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ // ActivityCompat的requestPermissions请求权限 // 第二个参数是一个String数组,咱们须要把申请的权限名称放到数组中便可 // 第三个参数是请求码,这个请求码咱们在下面权限回调的时候能够用来作判断,判断是那个权限再作相应的操做 ActivityCompat.requestPermissions(MainActivity.this,new String[]{ android.Manifest.permission.CALL_PHONE},1); }else { call(); }
接着就是咱们打电话的call()方法的操做,以及最后权限请求回来以后的回调方法:函数
// 防止有异常发生,写在这里 public void call(){ try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (SecurityException e){ e.printStackTrace(); } } //全部的权限回调都是在这里,咱们根据requestCode来判断是哪一个权限 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 1: if (grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ call(); }else { Toast.makeText(this,"打电话须要权限才能使用",Toast.LENGTH_LONG).show(); } } }
访问其余程序中的数据须要-ContentResolverthis
对于每个应用程序来讲,若是你想要访问内容提供器当中共享的数据,就必定要借助 ContentResolver 类,能够经过Context类当中的getContentResolver()方法来获取该类的实例, ContentResolver 类当中也提供了一系列的方法用于对数据进行CRUD的操做, insert() 添加 update() 更新 delete() 删除 query() 查询 对象
不一样于SQLiteDatabase,ContentResolver的CRUD的操做是不接收表名参数的,而是使用一个Uri参数表示。这个参数被称为内容URI,内容URI给内容提供器中的数据表创建了惟一的标识符,它主要是由两部分组成,一部分是 authority,它是用于对不一样的应用程序作区分,通常为了不冲突,都会采起程序包的方式来进行命名, 另外一部分是path,path则是相对于同一应用程序中的表走区分的,一般都是添加在authority的后面。固然只有这两部分仍是不够的,咱们须要在前面加上协议声明,所以标准的形式咱们举个例子:
content://com.example.app.provider/table1 当中content://是咱们头部的协议 com.example.app.provider是authority /table1就是path
上面咱们获得一个URI字符串以后,咱们还须要将它解析成Uri对象才能使用,解析的方法也很简单以下:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
有了这个Uri这个对象以后,咱们就能够利用它来查询数据了,代码以下:
Cursor cursor = getContentResolver().query(
uri, 指定查询某一个应用下面的某张表
projection, 指定查询的列名
selection, 指定where的约束条件
selectionArgs, 为where中的占位符提供具体的值
sortOrder); 指定查询结果的排序方式
查询完成以后返回的仍然是一个Cursor对象,这时候咱们就能够将数据从Cursor对象中逐个读取出来了,
读取的思路仍然是经过移动游标的位置来遍历Cursor的全部行没而后再取出每个行中国的数据没代码以下:
if(cursor != null){
while(cursor.moveNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
Int column1 = cursor.getInt(cursor.getColumnIndex("column1"));
}
}
掌握了比较复杂的查询以后,剩下的增长,删除,修改就比较简单了,咱们也就不在说了!
咱们读取一下联系人
咱们这里写一个小demo,来读取一下联系人的信息,而后把它展现在一个ListView当中,具体的代码以下:
public class ContactsActivity extends AppCompatActivity { //适配器和一个数组,用来存储联系人信息 ArrayAdapter<String> adapter; List<String> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_contacts); ListView contactsList = (ListView) findViewById(R.id.contactsListView); adapter = new ArrayAdapter<String>(ContactsActivity.this, android.R.layout.simple_list_item_1, list); contactsList.setAdapter(adapter); // 先检查有没有获取通信录的权限,要是没有就先请求权限 if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this,new String[]{android.Manifest.permission.READ_CONTACTS},2); }else { readContacts(); } } // 获取联系人 private void readContacts(){ Cursor cursor = null; try{ cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null); if (cursor != null){ while(cursor.moveToNext()){ // 联系人的名称 String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); // 联系人的手机号 String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); list.add(displayName+"\n"+number); } // 给适配器发消息说数据改变了,这时候会从新刷新一次数据 adapter.notifyDataSetChanged(); } }catch (Exception e){ e.printStackTrace(); }finally { if (cursor != null){ cursor.close(); } } } // 获取权限结果 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 2: if (grantResults.length>0&& grantResults[0] == PackageManager.PERMISSION_GRANTED){ readContacts(); }else { Toast.makeText(this,"访问通信录须要权限",Toast.LENGTH_SHORT).show(); } } } }
建立本身的内容提供器
前面也说过,要是想实现跨程序之间的共享数据的功能,官方推荐的就是使用内容提供器,咱们能够新建一个类去继承ContentProvider的方式来建立一个本身的内容提供器。ContentProvider里面的6个抽象方法咱们所有重写
onCreate()
初始化内容提供器的时候调用,一般会在这里完成数据库的建立和升级操做,返回true表示成功,返回false表示失败,注意的是只有当ContentResolver尝试访问咱们的数据库的时候内容提供器才会被初始化
query()
从内容提供器中查询数据,使用uri参数来肯定查询哪张表,具体的参数咱们就不在说了,前面咱们已经说过,查询的具体的结果就在cursor对象中存放返回
insert()
添加数据咱们也就再也不说了,成功以后会返回一个用于表示这条记录的URI
update()
注意的就一点,受影响的函数将做为返回值返回
delete()
这个和更新同样也是将受影响的行数做为返回值返回
getType()
根据传入的内容URI来返回相应的MIME类型
方法具体的内容咱们就不在多说了,能够本身点进类里面去看看。有一点须要注意的就是URI,有一点须要咱们注意:
* 表示匹配任意长度的任意字符
# 表示匹配任意长度的数字
因此,咱们把一个可以匹配任意表的内容URI能够写成:content://com.example.app.provider/*
咱们把一个可以匹配表中任意一行数据的内容URI能够写成:content://com.example.app.provider/table1/#
最后还有一个问题,就是内容URI的匹配问题,有个类UriMatcher类能够了解一下,咱们就不在多说了,想一想你匹配好了URI,当外面用户进行CRUD操做的时候具体的返回内容就是咱们本身自定义的了!以上就是咱们要说的关于内容控制器的内容!