1. 程式人生 > >Android 6.0 - 動態權限管理的解決方案(轉)

Android 6.0 - 動態權限管理的解決方案(轉)

long div span 添加 包含 detail 工具類 putextra ref

轉自:http://www.cnblogs.com/dubo-/p/6018262.html

Android 6.0 - 動態權限管理的解決方案

轉載請標註

Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用戶體驗, 同時也為程序員帶來新的負擔. 動態權限管理就是這樣, 一方面讓用戶更加容易的控制自己的隱私, 一方面需要重新適配應用權限. 時代總是不斷發展, 程序總是以人為本, 讓我們為應用添加動態權限管理吧! 這裏提供了一個非常不錯的解決方案, 提供源碼, 項目可以直接使用.


Permissions

Android系統包含默認的授權提示框, 但是我們仍需要設置自己的頁面. 原因是系統提供的授權框, 會有不再提示

的選項. 如果用戶選擇, 則無法觸發授權提示. 使用自定義的提示頁面, 可以給予用戶手動修改授權的指導.

本文示例的GitHub下載地址

在Api 23中, 權限需要動態獲取, 核心權限必須滿足. 標準流程:


流程圖

如果用戶點擊, 不再提示, 則系統授權彈窗將不會彈出. 流程變為:


流程圖

流程就這些, 讓我們看看代碼吧.


1. 權限

在AndroidManifest中, 添加兩個權限, 錄音修改音量.

    <!--危險權限-->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    <!--一般權限-->
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

危險權限必須要授權, 一般權限不需要.

檢測權限類

/**
 * 檢查權限的工具類
 * <p/>
 * Created by wangchenlong on 16/1/26.
 */
public class PermissionsChecker {
    private final Context mContext;

    public PermissionsChecker(Context context) {
        mContext = context.getApplicationContext();
    }

    // 判斷權限集合
    public boolean lacksPermissions(String... permissions) {
        for (String permission : permissions) {
            if (lacksPermission(permission)) {
                return true;
            }
        }
        return false;
    }

    // 判斷是否缺少權限
    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(mContext, permission) ==
                PackageManager.PERMISSION_DENIED;
    }
}

2. 首頁

假設首頁需要使用權限, 在頁面顯示前, 即onResume時, 檢測權限,
如果缺少, 則進入權限獲取頁面; 接收返回值, 拒絕權限時, 直接關閉.

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE = 0; // 請求碼

    // 所需的全部權限
    static final String[] PERMISSIONS = new String[]{
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.MODIFY_AUDIO_SETTINGS
    };

    @Bind(R.id.main_t_toolbar) Toolbar mTToolbar;

    private PermissionsChecker mPermissionsChecker; // 權限檢測器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setSupportActionBar(mTToolbar);

        mPermissionsChecker = new PermissionsChecker(this);
    }

    @Override protected void onResume() {
        super.onResume();

        // 缺少權限時, 進入權限配置頁面
        if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
            startPermissionsActivity();
        }
    }

    private void startPermissionsActivity() {
        PermissionsActivity.startActivityForResult(this, REQUEST_CODE, PERMISSIONS);
    }

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 拒絕時, 關閉頁面, 缺少主要權限, 無法運行
        if (requestCode == REQUEST_CODE && resultCode == PermissionsActivity.PERMISSIONS_DENIED) {
            finish();
        }
    }
}

核心權限必須滿足, 如攝像應用, 攝像頭權限就是必須的, 如果用戶不予授權, 則直接關閉.


3. 授權頁

授權頁, 首先使用系統默認的授權頁, 當用戶拒絕時, 指導用戶手動設置, 當用戶再次操作失敗後, 返回繼續提示. 用戶手動退出授權頁時, 給使用頁發送授權失敗的通知.

/**
 * 權限獲取頁面
 * <p/>
 * Created by wangchenlong on 16/1/26.
 */
public class PermissionsActivity extends AppCompatActivity {

    public static final int PERMISSIONS_GRANTED = 0; // 權限授權
    public static final int PERMISSIONS_DENIED = 1; // 權限拒絕

    private static final int PERMISSION_REQUEST_CODE = 0; // 系統權限管理頁面的參數
    private static final String EXTRA_PERMISSIONS =
            "me.chunyu.clwang.permission.extra_permission"; // 權限參數
    private static final String PACKAGE_URL_SCHEME = "package:"; // 方案

    private PermissionsChecker mChecker; // 權限檢測器
    private boolean isRequireCheck; // 是否需要系統權限檢測

    // 啟動當前權限頁面的公開接口
    public static void startActivityForResult(Activity activity, int requestCode, String... permissions) {
        Intent intent = new Intent(activity, PermissionsActivity.class);
        intent.putExtra(EXTRA_PERMISSIONS, permissions);
        ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
    }

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
            throw new RuntimeException("PermissionsActivity需要使用靜態startActivityForResult方法啟動!");
        }
        setContentView(R.layout.activity_permissions);

        mChecker = new PermissionsChecker(this);
        isRequireCheck = true;
    }

    @Override protected void onResume() {
        super.onResume();
        if (isRequireCheck) {
            String[] permissions = getPermissions();
            if (mChecker.lacksPermissions(permissions)) {
                requestPermissions(permissions); // 請求權限
            } else {
                allPermissionsGranted(); // 全部權限都已獲取
            }
        } else {
            isRequireCheck = true;
        }
    }

    // 返回傳遞的權限參數
    private String[] getPermissions() {
        return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
    }

    // 請求權限兼容低版本
    private void requestPermissions(String... permissions) {
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    }

    // 全部權限均已獲取
    private void allPermissionsGranted() {
        setResult(PERMISSIONS_GRANTED);
        finish();
    }

    /**
     * 用戶權限處理,
     * 如果全部獲取, 則直接過.
     * 如果權限缺失, 則提示Dialog.
     *
     * @param requestCode  請求碼
     * @param permissions  權限
     * @param grantResults 結果
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
            isRequireCheck = true;
            allPermissionsGranted();
        } else {
            isRequireCheck = false;
            showMissingPermissionDialog();
        }
    }

    // 含有全部的權限
    private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }

    // 顯示缺失權限提示
    private void showMissingPermissionDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(PermissionsActivity.this);
        builder.setTitle(R.string.help);
        builder.setMessage(R.string.string_help_text);

        // 拒絕, 退出應用
        builder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
            @Override public void onClick(DialogInterface dialog, int which) {
                setResult(PERMISSIONS_DENIED);
                finish();
            }
        });

        builder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
            @Override public void onClick(DialogInterface dialog, int which) {
                startAppSettings();
            }
        });

        builder.show();
    }

    // 啟動應用的設置
    private void startAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
        startActivity(intent);
    }
}

註意isRequireCheck參數的使用, 防止和系統提示框重疊.
系統授權提示: ActivityCompat.requestPermissions, ActivityCompat兼容低版本.

效果


自定義授權

關鍵部分就這些了, 動態權限授權雖然給程序員帶來了一些麻煩, 但是對用戶還是很有必要的, 我們也應該歡迎, 畢竟每個程序員都是半個產品經理.

危險權限列表

技術分享

Android 6.0 - 動態權限管理的解決方案(轉)