Android 6.0 运行时权限封装之路

前言

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

运行时权限

运行时权限是 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 权限一览:

  • android.permission.EXPAND_STATUS_BAR允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序

  • android.permission.FACTORY_TEST作为一个工厂测试程序,运行在root用户

  • android.permission.FLASHLIGHT访问闪光灯,android开发网提示HTC Dream不包含闪光灯

  • android.permission.FORCE_BACK允许程序强行一个后退操作是否在顶层activities

  • android.permission.FOTA_UPDATE暂时不了解这是做什么使用的,android开发网分析可能是一个预留权限.

  • android.permission.GET_ACCOUNTS访问一个帐户列表在Accounts Service中

  • android.permission.GET_PACKAGE_SIZE允许一个程序获取任何package占用空间容量

  • android.permission.GET_TASKS允许一个程序获取信息有关当前或最近运行的任务,一个缩略的任务状态,是否活动等等

  • android.permission.HARDWARE_TEST允许访问硬件

  • android.permission.INJECT_EVENTS允许一个程序截获用户事件如按键、触摸、轨迹球等等到一个时间流,android开发网提醒算是hook技术吧

  • android.permission.INSTALL_PACKAGES允许一个程序安装packages

  • android.permission.INTERNAL_SYSTEM_WINDOW允许打开窗口使用系统用户界面

  • android.permission.ACCESS_CHECKIN_PROPERTIES允许读写访问“properties”表在checkin数据库中,改值可以修改上传。

  • android.permission.ACCESS_COARSE_LOCATION允许一个程序访问CellID或WiFi热点来获取粗略的位置

  • android.permission.ACCESS_FINE_LOCATION允许一个程序访问精良位置(如GPS)

  • android.permission.WRITE_CONTACTS允许程序写入但不读取用户联系人数据

  • android.permission.WRITE_GSERVICES允许程序修改Google服务地图

  • android.permission.WRITE_OWNER_DATA允许一个程序写入但不读取所有者数据

  • android.permission.WRITE_SETTINGS允许程序读取或写入系统设置

  • android.permission.WRITE_SMS允许程序写短信

  • android.permission.WRITE_SYNC_SETTINGS允许程序写入同步设置

  • android.permission.ACCESS_LOCATION_EXTRA_COMMANDS允许应用程序访问额外的位置提供命令

  • android.permission.ACCESS_MOCK_LOCATION允许程序创建模拟位置提供用于测试

  • android.permission.ACCESS_NETWORK_STATE允许程序访问有关GSM网络信息

  • android.permission.ACCESS_SURFACE_FLINGER允许程序使用SurfaceFlinger底层特性

  • android.permission.ACCESS_WIFI_STATE允许程序访问Wi-Fi网络状态信息

  • android.permission.ADD_SYSTEM_SERVICE允许程序发布系统级服务

  • android.permission.BATTERY_STATS允许程序更新手机电池统计信息

  • android.permission.BLUETOOTH允许程序连接到已配对的蓝牙设备

  • android.permission.BLUETOOTH_ADMIN允许程序发现和配对蓝牙设备

  • android.permission.BROADCAST_PACKAGE_REMOVED允许程序广播一个提示消息在一个应用程序包已经移除后

  • android.permission.BROADCAST_STICKY允许一个程序广播常用intents

  • android.permission.CALL_PHONE允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认

  • android.permission.DELETE_CACHE_FILES允许程序删除缓存文件

  • android.permission.DELETE_PACKAGES允许一个程序删除包

  • android.permission.DEVICE_POWER允许访问底层电源管理

  • android.permission.DIAGNOSTIC允许程序RW诊断资源

  • android.permission.DISABLE_KEYGUARD允许程序禁用键盘锁

  • android.permission.DUMP允许程序返回状态抓取信息从系统服务

  • android.permission.CALL_PRIVILEGED允许一个程序拨打任何号码,包含紧急号码无需通过拨号用户界面需要用户确认

  • android.permission.CAMERA请求访问使用照相设备

  • android.permission.CHANGE_COMPONENT_ENABLED_STATE允许一个程序是否改变一个组件或其他的启用或禁用

  • android.permission.CHANGE_CONFIGURATION允许一个程序修改当前设置,如本地化

  • android.permission.CHANGE_NETWORK_STATE允许程序改变网络连接状态

  • android.permission.CHANGE_WIFI_STATE允许程序改变Wi-Fi连接状态

  • android.permission.CLEAR_APP_CACHE允许一个程序清楚缓存从所有安装的程序在设备中

  • android.permission.CLEAR_APP_USER_DATA允许一个程序清除用户设置

  • android.permission.CONTROL_LOCATION_UPDATES允许启用禁止位置更新提示从无线模块

  • android.permission.REBOOT请求能够重新启动设备

  • android.permission.RECEIVE_BOOT_COMPLETED允许一个程序接收到ACTION_BOOT_COMPLETED广播在系统完成启动

  • android.permission.RECEIVE_MMS允许一个程序监控将收到MMS彩信,记录或处理

  • android.permission.RECEIVE_SMS允许程序监控一个将收到短信息,记录或处理

  • android.permission.RECEIVE_WAP_PUSH允许程序监控将收到WAP PUSH信息

  • android.permission.RECORD_AUDIO允许程序录制音频

  • android.permission.REORDER_TASKS允许程序改变Z轴排列任务

  • android.permission.RESTART_PACKAGES允许程序重新启动其他程序

  • android.permission.SEND_SMS允许程序发送SMS短信

  • android.permission.INTERNET允许程序打开网络套接字

  • android.permission.MANAGE_APP_TOKENS允许程序管理(创建、催后、 z-order默认向z轴推移)程序引用在窗口管理器中

  • android.permission.MASTER_CLEAR目前还没有明确的解释,android开发网分析可能是清除一切数据,类似硬格机

  • android.permission.MODIFY_AUDIO_SETTINGS允许程序修改全局音频设置

  • android.permission.MODIFY_PHONE_STATE允许修改话机状态,如电源,人机接口等

  • android.permission.MOUNT_UNMOUNT_FILESYSTEMS允许挂载和反挂载文件系统可移动存储

  • android.permission.PERSISTENT_ACTIVITY允许一个程序设置他的activities显示

  • android.permission.PROCESS_OUTGOING_CALLS允许程序监视、修改有关播出电话

  • android.permission.READ_CALENDAR允许程序读取用户日历数据

  • android.permission.READ_CONTACTS允许程序读取用户联系人数据

  • android.permission.READ_FRAME_BUFFER允许程序屏幕波或和更多常规的访问帧缓冲数据

  • android.permission.READ_INPUT_STATE允许程序返回当前按键状态

  • android.permission.READ_LOGS允许程序读取底层系统日志文件

  • android.permission.READ_OWNER_DATA允许程序读取所有者数据

  • android.permission.READ_SMS允许程序读取短信息

  • android.permission.READ_SYNC_SETTINGS允许程序读取同步设置

  • android.permission.READ_SYNC_STATS允许程序读取同步状态

  • android.permission.SET_ACTIVITY_WATCHER允许程序监控或控制activities已经启动全局系统中

  • android.permission.SET_ALWAYS_FINISH允许程序控制是否活动间接完成在处于后台时

  • android.permission.SET_ANIMATION_SCALE修改全局信息比例

  • android.permission.SET_DEBUG_APP配置一个程序用于调试

  • android.permission.SET_ORIENTATION允许底层访问设置屏幕方向和实际旋转

  • android.permission.SET_PREFERRED_APPLICATIONS允许一个程序修改列表参数

  • android.permission.SET_PROCESS_FOREGROUND允许程序当前运行程序强行到前台

  • android.permission.SET_PROCESS_LIMIT允许设置最大的运行进程数量

  • android.permission.SET_TIME_ZONE允许程序设置时间区域

  • android.permission.SET_WALLPAPER允许程序设置壁纸

  • android.permission.SET_WALLPAPER_HINTS允许程序设置壁纸hits

  • android.permission.SIGNAL_PERSISTENT_PROCESSES允许程序请求发送信号到所有显示的进程中

  • android.permission.STATUS_BAR允许程序打开、关闭或禁用状态栏及图标Allows an application toopen, close, or disable the status bar and its icons.

  • android.permission.SUBSCRIBED_FEEDS_READ允许一个程序访问订阅RSS Feed内容提供

  • android.permission.SUBSCRIBED_FEEDS_WRITE系统暂时保留改设置,android开发网认为未来版本会加入该功能。

  • android.permission.SYSTEM_ALERT_WINDOW允许一个程序打开窗口使用TYPE_SYSTEM_ALERT,显示在其他所有程序的顶层

  • android.permission.VIBRATE允许访问振动设备

  • android.permission.WAKE_LOCK允许使用PowerManager的 WakeLocks保持进程在休眠时从屏幕消失

  • android.permission.WRITE_APN_SETTINGS允许程序写入API设置

  • android.permission.WRITE_CALENDAR允许一个程序写入但不读取用户日历数据

其中的危险权限Dangerous Permissions

Android 6.0 运行时权限封装之路_第1张图片

你也可以通过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) {

    }
}

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

你可能感兴趣的:(android,Permission,运行时权限)