运行时权限
危险权限表格

运行时申请权限(以打电话为例)
1.先借助ContextCompat.checkSelfPermission()方法判断用户是否进行了授权。
checkSelfPermission()接收两个参数,第一个是Context,第二个是具体的权限名
2.使用方法的返回值和PackageManager.PERMISSION_GRANTED作比较,不等表示未授权,相等表示已授权
3.把打电话的逻辑封装在**call()方法当中如果授权直接执行该方法,如果未授权,利用ActivityCompat.requestPermissions()**方法向用户申请授权,**requestPermissions()**接收三个参数,第一个是Activity实例,第二个是String数组,把权限名放入其中,第三个是请求码,只要是唯一值就行。
4.调用requestPermissions()方法后最终会回调到onRequestPermissionResult()方法,而授权结果会封装在grantResults参数中,判断最后的授权结果
完整的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button makeCall=(Button) findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { 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(){ try{ Intent intent=new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (SecurityException e){ e.printStackTrace(); } } @SuppressLint("MissingSuperCall") @Override public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){ switch(requestCode){ case 1: if(grantResults.length>0 && grantResults[0]== PERMISSION_GRANTED){ call(); }else{ Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } } }
|
访问其他程序的数据
ContentResolver的基本用法
通过getContentResolver()方法获取ContentResolver实例ContentResolver提供了一系列方法,用于对数据进行CRUD和SQLite相似但参数上略有一些区别。
不同于数据库的是表名参数被Uri参数代替,主要由两部分组成authority和path,authority一般以程序包名进行命名,path则对同一程序不同表进行区分,标准格式为:content://com.example.app.provider/table1,还需要将其解析才可以作为参数传入,
Uri uri=Uri.parse("content://com.example.app.provider/table1")
Query( )
1 2 3 4 5 6 7 8
| Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder); if(cursor!=null){ while(cursor.moveToNext()){ String column1=cursor.getString(cursor.getColumnIndex("column1")); int column2=cursor.getInt(cursor.getColumnIndex("column2")); } cursor.close(); }
|
Insert( )
1 2 3 4
| ContentValues values=new ContentValues(); values.put("column1","text"); values.put("column2",1); getContentResolver().insert(uri,values);
|
update( )
1 2 3
| ContentValues values=new ContentValues(); values.put("column1",""); getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","1"});
|
delete( )
1
| getContentResolver().delete(uri,"column2=?",new String[]{"1"});
|
读取系统联系人
完整实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public class MainActivity extends AppCompatActivity { ArrayAdapter<String>adapter; List<String>contactsList=new ArrayList<>();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView contactsView=(ListView) findViewById(R.id.contacts_view); adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactsList); contactsView.setAdapter(adapter); if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1); } 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()){ @SuppressLint("Range") String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); @SuppressLint("Range") String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); contactsList.add(displayName+"\n"+number); } adapter.notifyDataSetChanged(); } }catch(Exception e){ e.printStackTrace(); }finally{ if(cursor!=null){ cursor.close(); } } }
@SuppressLint("MissingSuperCall") @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode==1){ if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){ readContacts(); } else{ Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } } } }
|
重点看readContacts()方法 里面的uri与前面讲的不太一样,这是为什么呢?这是因为ContactsContract.CommonDataKinds.Phone类已经帮我们做好了封装,提供另一个CONTENT_URI常量,而这个常量就是Uri.parse解析出来的结果。
创建内容提供器
1.利用Android Studio创建一个Content Provider,已经在在AndroidManifest.xml中自动注册。
onCraete():返回true表示创建成功,返回false表示创建失败。
query():返回值为Cursor类型
insert():返回一个表示这条新记录的URI
update():更新已有数据
delete():被删除的行数将作为返回值返回
getType():根据传入的URI来返回相应的MIME类型。
使用通配符表示两种格式的URI:
***:*表示匹配任意长度的任意字符。例子content://com.example.app.provider/
**#:**表示匹配任意长度的数字。例子content://com.example.app.provider/table1/#
2.可以借助UriMatcher这个类实现匹配内容URI的功能,利用UriMatcher中的addURI()方法,接受三个参数,第一个是authority第二个是path第三个是自定义代码,这样当调用match()方法时就能匹配相应的表数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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_DIR); uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR); uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR); } @Override public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) { if(uriMatcher.match(uri)==TABLE1_DIR){
} else if (uriMatcher.match(uri)==TABLE1_ITEM) {
} else if (uriMatcher.match(uri)==TABLE2_DIR) {
} else if (uriMatcher.match(uri) == TABLE2_ITEM) {
} return null; }
|
3.getType()方法一个URI内容对应的MIME字符由三部分组成:
<必须以vnd开头>
<如果内容以路径结尾,则后接android.cursor.dir/,如果以id结尾则后接android.cursor.item/.>
<最后接上vnd..>
URI为“content://com.example.app.provider/table1”对应的MIME类型为“vnd.android.cursor.dir/vnd.com.example.app.provider.table1”
URI为“content://com.example.app.provider/table1/1”对应的MIME类型为“vnd.android.cursor.item/vnd.com.example.app.provider.table1”
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Override public String getType(@NonNull Uri uri) { if(uriMatcher.match(uri)==TABLE1_DIR){ return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"; } else if (uriMatcher.match(uri)==TABLE1_ITEM) { return "vnd.android.cursor.item/vnd.com.example.app.provider.table1"; } else if (uriMatcher.match(uri)==TABLE2_DIR) { return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2"; } else if (uriMatcher.match(uri) == TABLE2_ITEM) { return "vnd.android.cursor.item/vnd.com.example.app.provider.table2"; } return null; }
|