前言
时间如梭,总在不经意间流逝。经常会想,毕业到现在我都干了些什么,该有什么样的追求?
运行时权限
运行时权限是 Android 6.0 (SDK 23)新特性,更好的保护了用户的隐私。
如果你build.gradle
文件中声明targetSdkVersion
为23及以上
:
defaultConfig {
applicationId "xxxx"//you applicationId
minSdkVersion 14
targetSdkVersion 23//23及以上
versionCode 1
versionName "1.0"
multiDexEnabled true
}
用到的隐私权限就要进行申请;反之小于23
,默认授予AndroidManifest.xml
中的所有申请权限。
注意: 需要用到的权限必须在 AndroidManifest.xml 文件中申明。
权限的分类
Google
将权限分为两类,一类是Normal Permissions
,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、获取wifi连接状态、访问网络等;另一类是Dangerous Permission
,一般是涉及到用户隐私的,需要用户进行授权,比如获取位置信息、读取sdcard、访问通讯录等。最后会附上权限 Android 权限一栏。
危险权限Dangerous Permissions
如下图:
你也可以通过adb
命令进行权限查看:
adb shell pm list permissions
危险权限查看:
adb shell pm list permissions -d -g
dangerous permissions
是分组的,那么这么分组又会有什么影响呢?
如果你申请某个危险的权限,假设你的app
已经被用户授权了同一组的某个危险权限比如READ_CONTACTS
,那么WRITE_CONTACTS
和GET_ACCOUNTS
危险权限也同样被授权了,而不需要用户再次去申请。同样弹出的申请权限dialog
文本说明也是对整个权限组的说明,而不是单个权限。
权限处理流程
AndroidManifest.xml申明权限
在AndroidManifest.xml
文件下申明读的联系人权限:
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
检查SDK版本
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
如果targetSdkVersion
版本小于23,不需要进行权限的处理。
检查申明的权限是否已经被授权
if (checkSelfPermission(requestPermission) != PackageManager.PERMISSION_GRANTED) {
//未被授权
} else {
//已授权
}
如果未被授权,则需要申明授权处理。
申请授权
requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final int requestCode)
第一个参数是 activity
第二个参数是申请授权的权限组
第三个参数是请求码(如果不理解可以参考startActivityForResult()
方法)
权限回调处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSON_REQUESTCODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 申请权限已经授权
} else {
// 申请权限未授权
}
}
}
参数grantResults
申请权限返回的状态信息。
这里还有一个方法需要说明一下:
public boolean shouldShowRequestPermissionRationale(String permission)
当用户第一次勾选了【禁止后不再询问】勾选框,并点击【禁止】按钮,第二次申请该权限返回true
的方法,一般给于用户提示缺少改权限。\n\n请点击\”设置\”-\”权限\”-打开所需权限进行处理。
打开设置代码:
private void startAppSettings() {
Intent intent = new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
在实际开发中可能会几个权限同时需要授权处理,如定位需要位置权限,访问 sdcard 。若单个申请则会显得很麻烦。所以我这里单独的提取了一个抽象类用于权限的处理,并把返回结果延迟到它的子类进行处理。对的,这里用了模板模式。具体看以下代码:
public abstract class CheckPermissionsActivity extends AppCompatActivity {
首先申明CheckPermissionsActivity
为抽象类。
public abstract void requestPermissionResult(boolean allowPermission);
申明抽象方法,并把allowPermission
是否授予权限设置为参数。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSON_REQUESTCODE) {
if (verifyPermissions(grantResults)) {
requestPermissionResult(true);
} else {
requestPermissionResult(false);
showMissingPermissionDialog();
}
}
}
最后我们在回调函数中传递参数给requestPermissionResult
抽象方法。下面我们来看看一个具体的例子。文章的最后已经附上了源码。
获取通讯录案例
package com.github.permissondemo;
import android.Manifest;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
public class MainActivity extends CheckPermissionsActivity implements LoaderManager.LoaderCallbacks<Cursor> {
static final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME};
String mCurFilter;
static String TAG;
protected String[] needContactsPermissions = {
Manifest.permission.READ_CONTACTS
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TAG = getClass().getName();
requestPermission();
}
private void requestPermission() {
if (!mayRequestPermission(needContactsPermissions)) {
return;
}
initLoader();
}
@Override
public void requestPermissionResult(boolean allowPermission) {
if (allowPermission) {
initLoader();
}
}
private void initLoader() {
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI
, Uri.encode(mCurFilter));
} else {
baseUri = ContactsContract.Contacts.CONTENT_URI;
}
String select = "((" + ContactsContract.Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";
CursorLoader loader = new CursorLoader(MainActivity.this, baseUri,
PROJECTION, select, null, ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor == null) {
return;
}
while (cursor.moveToNext()) {
String[] names = cursor.getColumnNames();
for (String str : names) {
String contacts = cursor.getString(cursor.getColumnIndex(str));
Log.e(TAG, "contacts=" + contacts);
}
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
如上案例,运行时权限的处理非常方便,只需要两步:
- 第一步请求权限处理:
private void requestPermission() {
if (!mayRequestPermission(needContactsPermissions)) {
return;
}
initLoader();//读取联系人
}
- needContactsPermissions 参数表示:申请的权限字符数组。
如果你需要申请定位,调摄像头或其它的危险权限,请 initLoader();
替换成定位,开启摄像头的方法。
- 第二步请求权限的回调处理:
@Override
public void requestPermissionResult(boolean allowPermission) {
if (allowPermission) {
initLoader();//读取联系人
}
}
- allowPermission 参数表示:用户是否允许申请的权限,true 允许;false 拒绝。
如果你需要申请定位,调摄像头或其它的危险权限,请 initLoader();
替换成定位,开启摄像头的方法。
看下运行效果:
点击【始终允许】:
快速,简单,便捷,只需要你复制 CheckPermissionsActivity
类到你的项目中,需要申请权限的 Activity
继承于它。
可能你会说为啥不使用:
插件生成权限处理呢?
真心觉得不好用。
用哪种方式,取决于自己。
最后祝愿大家,每天适当休息下,不要一直敲代码,起来动一动。感觉自己就是每天坐太久,身体不如以前好了。
若有什么疑问请留言。源码地址