【Android】内容提供器
一、什么是内容提供器
定义:Android 四大组件之一,用于管理应用数据的跨应用共享。
核心功能:
提供统一的接口访问数据(SQLite、文件、网络等)
控制数据的安全访问权限
支持跨进程数据交互
主要用于在不同的应用程序之间实现数据共享的功能,允许程序访问另一个程序的数据并保证被访问数据的安全性。
内容提供器可选择对哪一部分数据进行共享。
二、运行时权限
危险权限需要在运行时处理,普通权限需要在AndroidManifest.xml文件中添加权限声明
2.1在程序运行时申请权限
以CALL_PHONE权限为例
1.修改AndroidManifest.xml文件,声明权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.CALL_PHONE"/>
2.修改主活动,先判断用户是否已经授权,借用ContextCompat.checkSelfPermission方法,接收两个参数,Context和具体权限名,使用返回值和PackageManager.PERMISSION_GRANTED比较,相等表示已授权,直接执行逻辑。
没有授权,调用 ActivityCompat.requestPermissions向用户申请授权,接收三个参数,Activity实例,含有要申请权限名的String数组和请求码,之后系统自动回调onRequestPermissionResult方法,授权结果封装在grantResults中,只需要判断授权结果
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button=(Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);} else {call();}}});}private void call(){Intent intent=new Intent(Intent.ACTION_CALL);intent.setData(Uri.parse("tel:10086"));startActivity(intent);}public void onRequestPermissionResult(int requestCode,String[] permissions,int[] grantResults){switch (requestCode){case 1:if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){call();}else{Toast.makeText(this,"permission",Toast.LENGTH_SHORT).show();}break;default:}}
用户之后在手机设置中中可以对程序危险权限进行关闭
三、访问其他程序中的数据
3.1ContentResolver的基本用法
想要访问内容提供器中共享的数据,要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。
ContentResolver中的CRUD操作接收Uri参数,这个参数被称为内容URI,内容URI为内容提供器中的数据建立了唯一标识符,主要由authority和path两部分组成。
authority:对不同应用程序进行区分,采用程序包名进行命名
path:对同一应用程序中的不同表区分,一般添加在authority后
得到内容URI后通过Uri.parse得到Uri对象后才能作为参数传入。
Uri uri=Uri.parse("content://com.example.app.provider/tabel");
查询数据
Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
- uri:指定查询某个应用程序下的某一张表
- projection:指定查询的列名
- selection:指定where的约束条件
- selectionArgs:为where中的占位符提供具体的值
- sortOrder:指定查询结果的排序方式
查询完成后返回Cursor对象,可以将数据从Cursor中逐个读取出来
if(cursor!=null){while (cursor.moveToNext()){String column1=cursor.getString(cursor.getColumnIndex("column1"));int column2=cursor.getInt(cursor.getColumnIndex("column2"));}cursor.close();}
添加数据
ContentValues values=new ContentValues();values.put("column1","text");values.put("column2",1);getContentResolver().insert(uri,values);
将待添加的数据组装到ContentValues中,然后调用ContentResolver的insert方法,传入参数uri和values
更新数据
ContentValues values=new ContentValues();values.put("column1","");getContentResolver().update(uri,values,"column1 = ? and column2 = ?",new String[]{"text","1"});
上述代码使用了selection和selectionArgs参数对想要更新的数据进行约束
删除数据
getContentResolver().delete(uri,"column2 = ?",new String[]{"1"});
调用ContentResolver的delete方法删除数据
四、创建内容提供器
1.新建一个类继承ContentProvider,需要全部重写6个抽象方法
public class MyProvider extends ContentProvider {public boolean onCreate(){return false;}public Cursor query(Uri uri, String[] projection,String selection,String[] selectionArgs,String sortOrder){return null;}public Uri insert(Uri uri, ContentValues values){return null;}public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){return 0;}public int delete(Uri uri,String selection,String[] selectionArgs){return 0;}public String getType(Uri uri){return null;}
}
onCreate:初始化内容提供器时调用,完成对数据库的创建和升级
query:从内容提供器中查询数据,查询结果存放在Cursor对象中返回
insert:向内容提供器中添加一条数据,返回一个用于表示这条新记录的URI
update:更新已有数据,返回受影响的行数
delete:删除数据,返回被删除的行数
getType:返回相应的MIME类型
URI以路径结尾表示访问该表中的所有数据,以id结尾表示访问该表中有相应id的数据
可以使用通配符的方式分别匹配这两种格式的内容URI
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字
2.借助UriMatcher类可以实现匹配内容的URI功能
UriMatcher中提供了addURI方法,接收三个参数,authority,path和自定义代码
调用UriMatcher的match方法时,传入一个Uri对象,返回的是自定义代码,通过代码判断调用方相要调用的数据
public class MyProvider extends ContentProvider {public static final int TABLE1_DIR=0;public static final int TABLE1_ITEM=1;public static final int TABLE2_DIR=2;public static final int TABLE2_ITEM=3;private static UriMatcher uriMatcher;static {uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);}public boolean onCreate(){return false;}public Cursor query(Uri uri, String[] projection,String selection,String[] selectionArgs,String sortOrder){switch (uriMatcher.match(uri)){case TABLE1_DIR:break;case TABLE1_ITEM:break;case TABLE2_DIR:break;case TABLE2_ITEM:break;default:break;}return null;}public Uri insert(Uri uri, ContentValues values){return null;}public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){return 0;}public int delete(Uri uri,String selection,String[] selectionArgs){return 0;}public String getType(Uri uri){return null;}
}
getType是所有内容提供器必须提供的一个方法,用于获取Uri对象的MIME类型,由三部分组成:
必须以vnd开头
如果以路径结尾,则后接android.cursor.dir/,如果以id结尾,后接android.cursor.item/
最后接vnd.<authority>.<path>
public String getType(Uri uri){switch (uriMatcher.match(uri)){case TABLE1_DIR:return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";case TABLE1_ITEM:return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";case TABLE2_DIR:return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";case TABLE2_ITEM:return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";default:break;}return null;}
内容提供器要在AndroidManifest.xml中注册才能使用
<providerandroid:name=".DatabaseProvider"android:authorities="com.example.databasetest.provider"android:enabled="true"android:exported="true"></provider>