Android 6.0 运行时权限封装之路

来源:互联网 发布:湖南工业大学网络认证 编辑:程序博客网 时间:2024/05/16 22:03

前言

时间如梭,总在不经意间流逝。经常会想,毕业到现在我都干了些什么,该有什么样的追求?

运行时权限

运行时权限是 Android 6.0 (SDK 23)新特性,更好的保护了用户的隐私。

如果你build.gradle文件中声明targetSdkVersion23及以上

    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 如下图:

per

你也可以通过adb命令进行权限查看:

adb shell pm list permissions

危险权限查看:

adb shell pm list permissions -d -g

dangerous permissions是分组的,那么这么分组又会有什么影响呢?

如果你申请某个危险的权限,假设你的app已经被用户授权了同一组的某个危险权限比如READ_CONTACTS,那么WRITE_CONTACTSGET_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) {    }}

如上案例,运行时权限的处理非常方便,只需要两步:

  1. 第一步请求权限处理:
    private void requestPermission() {        if (!mayRequestPermission(needContactsPermissions)) {            return;        }        initLoader();//读取联系人    }    
  • needContactsPermissions 参数表示:申请的权限字符数组。

如果你需要申请定位,调摄像头或其它的危险权限,请 initLoader(); 替换成定位,开启摄像头的方法。

  1. 第二步请求权限的回调处理:
    @Override    public void requestPermissionResult(boolean allowPermission) {        if (allowPermission) {            initLoader();//读取联系人        }    }
  • allowPermission 参数表示:用户是否允许申请的权限,true 允许;false 拒绝。

如果你需要申请定位,调摄像头或其它的危险权限,请 initLoader(); 替换成定位,开启摄像头的方法。

看下运行效果:

per

点击【始终允许】:

per

快速,简单,便捷,只需要你复制 CheckPermissionsActivity 类到你的项目中,需要申请权限的 Activity 继承于它。

可能你会说为啥不使用:

per

插件生成权限处理呢?

真心觉得不好用。

用哪种方式,取决于自己。

最后祝愿大家,每天适当休息下,不要一直敲代码,起来动一动。感觉自己就是每天坐太久,身体不如以前好了。

若有什么疑问请留言。源码地址

Android 权限一栏

2 0