简介
- 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时保证数据的安全性
- 内容提供器可以选择只对一部分数据进行共享
运行时权限
Android权限机制详解
-
Android现在将所有的权限分成两类
-
普通权限
- 不会直接威胁到用户的安全和隐私的权限
- 系统会自动帮用户进行授权,不需要手动操作,比如申请网络权限
-
危险权限
可能触及到用户隐私或对设备安全性造成影响的权限(比如获取设备联系人信息,定位等)
这种权限,必须用户手动点击授权才行
-
一共有9组24个危险权限
权限组名 权限名 CALENDAR READ_CALENDAR WRITE_CALENDAR CAMERA CAMERA CONTACTS READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS LOCATION ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION MICROPHONE RECORD_AUDIO PHONE READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS SENSORS BODY_SENSORS SMS SNED_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS STORAGE READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE - 注意:用户一旦同意授权了,那么这个授权对应的权限组中所有的其他权限也会得到授权
-
程序运行时申请权限
-
例子:获取电话权限
-
首先,先在AndroidManifest.xml中写入权限
<uses-permission android:name="android.permission.CALL_PHONE">
-
然后修改ManiActivity中的代码
public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); Button makeCall = (Button) findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // checkSelfPermission()方法来判断用户是否给指定的权限授权了 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ // 如果没有授权,调用requestPermissions()方法向用户要授权 // 这个方法需要三个参数,第一个是活动的实例,第二个是一个数组,要申请的权限都在这个数组里,第三个是请求码,必须是个唯一值 ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},1); } else { call(); } } }); } private void call(){ try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); } catch (SecurityException e){ e.printStackTrace(); } } 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,"没有权限",Toast.LENGTH_SHORT).show(); } break; default: } } }
-
访问其他程序中的数据
ContentResolver的基本用法
对于每一个应用程序来说,需要借助ContentResolver类来访问内容提供器共享的数据
通过ContentResolver类的getContentResolver()方法获取该类的实例
-
ContentResolver类也有对应的方法对数据进行增删改查,这些方法接收一个Uri参数,这个参数叫做内容URI
-
内容URI给内容提供器中的数据建立了唯一标识符,主要由两部分组成:authority和path
- authority:用于对不同的应用程序做区分,一半是用程序包名的方式命名,比如com.xxx.yy.provider
- path:用于对同一应用程序中不同的表进行区分,通常添加在authority后面,比如com.xxx.yy.provider/table1
-
得到内容URI字符串之后,还需要解析成URI对象
Uri uri = Uri.parse("content://com.xxx.yy.provider/table1")
-
查询操作
Cursor cursor = getContentResolver().query( uri, projection, selection, selectionArgs, sortOrder ); if (cursor != null){ while (cursor.moveToNext()){ String col1 = cursor.getString(cursor.getColumnIndex("col1")); int col2 = cursor.getInt(cursor.getColumnIndex("col2")); } cursor.close(); }
- uri:指定查询某个应用中的某一个表
- project:指定查询的列名
- selection:指定where的约束条件
- selectionArgs:给where中的占位符提供具体的值
- sortOrder:指定查询结果的排序方式
- 查询结束之后返回的是一个Cursor对象,然后可以将数据从其中读取出来
-
添加数据
Uri uri = Uri.parse("content://com.xxx.yy.provider/table1") ContentValues values = new ContentValues(); values.put("name","Tom"); values.put("age",18); getContentReslover().insert(uri,values);
-
修改数据
Uri uri = Uri.parse("content://com.xxx.yy.provider/table1") ContentValues values = new ContentValues(); values.put("name","Tom"); getContentReslover().update(uri,values,"name=? and age=?",new Stirng[]{"Tom",15});
-
删除数据
Uri uri = Uri.parse("content://com.xxx.yy.provider/table1") getContentReslover().delete(uri,"age=?",new String[] {45});
-
自定义内容提供器
简单实现自定义内容提供器
可以通过新建一个类去继承ContentProvider来自定义内容提供器
-
ContentProvider类有6个抽象方法,新建的类需要将它们全部重写
import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; public class MyProvider extends ContentProvider { public static final int TABLEONE_DIR = 0; public static final int TABLEONE_ITEM = 1; public static final int TABLETWO_DIR = 2; public static final int TABLETWO_ITEM = 3; private static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.xxx.yy.provider", "tableOne", TABLEONE_DIR); uriMatcher.addURI("com.xxx.yy.provider", "tableOne/#", TABLEONE_ITEM); uriMatcher.addURI("com.xxx.yy.provider", "tableTwo", TABLETWO_DIR); uriMatcher.addURI("com.xxx.yy.provider", "tableTwo/#", TABLETWO_ITEM); } // 在这里完成对数据库的创建和升级的操作,返回true表示内容提供器初始化成功,false表示初始化失败 @Override public boolean onCreate() { return false; } /** * 从内容提供器中查询数据 * * @param uri:确定查询哪张表 * @param projection:确定查询哪些列 * @param selection,selectionArgs:用于约束查询哪些行 * @param sortOrder:对结果进行排序 * @return cursor:查询的结果放在Cursor对象中返回 */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case TABLEONE_DIR: // 查询表tableOne中所有的数据 break; case TABLEONE_ITEM: // 查询表tableOne中的一条数据 break; case TABLETWO_DIR: // 查询表tableTWO中所有的数据 break; case TABLETWO_ITEM: // 查询表tableTWO中的一条数据 break; default: break; } return null; } /** * 向内容提供器中添加数据 * * @param uri:确定要添加到哪张表 * @return 返回一个用于表示这条新纪录的URI */ @Override public Uri insert(Uri uri, ContentValues values) { return null; } /** * 更新已有的数据 * * @param uri:确定哪张表 * @param values:新的数据保存在values中 * @param selection,selectionArgs:用于约束更新哪些行 * @return 被修改的内容作为返回值返回 */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } /** * 更新已有的数据 * * @param uri:确定哪张表 * @param selection,selectionArgs:用于约束删除哪些行 * @return 被删除的内容作为返回值返回 */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } /* *根据传入的内容URI来返回对应的MIME类型 */ @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case TABLEONE_DIR: return "vnd.android.cursor.dir/vnd.com.xxx.yy.provider.tableOne"; case TABLEONE_ITEM: return "vnd.android.cursor.item/vnd.com.xxx.yy.provider.tableOne"; case TABLETWO_DIR: return "vnd.android.cursor.dir/vnd.com.xxx.yy.provider.tableTwo"; case TABLETWO_ITEM: return "vnd.android.cursor.item/vnd.com.xxx.yy.provider.tableTwo"; } return null; } }
-
MIME类型
-
多用途互联网邮件扩展类型,设定某种扩展名的文件用一种应用程序打开的方式类型
,当该扩展名文件被访问的时候,浏览器就会自动使用指定的应用程序打开它
-
-
getType()方法可以根据传入的内容URI来返回对应的MIME类型
- 内容URI对应的MIME字符串包含三部分
- 必须以vnd开头
- 如果内容URI以路径结尾,那么后面跟上android.curosr.dir/
- 如果内容URI以id结尾,那么后面跟上android.curosr.item/
- 最后接上authority和path
- 内容URI对应的MIME字符串包含三部分
-
-
URI写法示例
-
一个标准的URI
content://com.xxx.yy.provider/tableOne
这里表示用户希望调用com.xxx.yy这个应用中,表tableOne的数据
对应的MIME字符串:vnd.android.cursor.dir/vnd.com.xxx.yy.provider.tableOne
-
-
指定id
content://com.xxx.yy.provider/tableOne/1
- 这里表示用户希望调用com.xxx.yy这个应用中,表tableOne中,id为1的数据
- 对应的MIME字符串:vnd.android.cursor.item/vnd.com.xxx.yy.provider.tableOne
以路径结尾就表示访问表中所有的数据(比如标准的URI)
以id结尾表示访问该表中拥有相应id的数据