Android6.0權(quán)限與RxPermissions關(guān)系

前言

Android6.0之后開(kāi)始對(duì)權(quán)限進(jìn)行控制。

如果你把targetSdkVersion設(shè)置大于等于23時(shí),需要運(yùn)行時(shí)獲取權(quán)限。
設(shè)置targetSdkVersion設(shè)置小于23,系統(tǒng)默認(rèn)開(kāi)啟所有權(quán)限。這個(gè)也是Android給出的一個(gè)的兼容方案。

鑒于現(xiàn)在市面上大部分手機(jī)都是4.x,5.x,6.x的手機(jī)占有量還是少的。

大部分的開(kāi)發(fā)者做法:將targetSdkVersion設(shè)置為22,默認(rèn)全部開(kāi)啟權(quán)限

我以targetSdkVersion設(shè)置為23舉例
代碼如下:

// REQUEST_CODE
private static final int REQUEST_CODE = 1;

...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
     // 判斷是否開(kāi)啟權(quán)限,沒(méi)有則去申請(qǐng)權(quán)限
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED){
        //申請(qǐng)WRITE_EXTERNAL_STORAGE權(quán)限
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_CODE);
    }

}

// 權(quán)限申請(qǐng)結(jié)果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == REQUEST_CODE){
        if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
            Log.d(TAG,">>>>>granted");
        }else{
            Log.d(TAG,">>>>>deny");
        }
    }
}

通過(guò)上面可以知道,在啟動(dòng)的時(shí)候需要去檢查權(quán)限,申請(qǐng)權(quán)限,處理返回結(jié)果
上面用的是v4包的兼容方式調(diào)用,為了兼容Android6.0以下的手機(jī)

不是所有權(quán)限都要首頁(yè)進(jìn)行申請(qǐng),有些可以到指定頁(yè)面在申請(qǐng),如拍照權(quán)限

關(guān)于使用到的API

1.ActivityCompat.checkSelfPermission(權(quán)限名)
檢查是否有權(quán)限

2.ActivityCompat.shouldShowRequestPermissionRationale(權(quán)限名) 
返回true表示在詢問(wèn)用戶權(quán)限的對(duì)話框 用戶未選擇 不要再詢問(wèn),反之則已選擇不要再詢問(wèn)。

3.ActivityCompat.requestPermissions
請(qǐng)求授權(quán)

4.onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
回調(diào)獲取授權(quán)結(jié)果,判斷是否授權(quán)

回到本文的重心,RxPermissions

RxPermission介紹

RxPermissions是基于Rxjava實(shí)現(xiàn)的Android 6.0中處理運(yùn)行時(shí)權(quán)限檢測(cè)的框架。

吐槽:
現(xiàn)在RxJava很火,有很多它的實(shí)現(xiàn)框架,RxAndroid RxBus RxPermissions Rxbinding RxLifeCircle等

說(shuō)白了,就是用RxJava的方式實(shí)現(xiàn)上述方法的使用,將方法用的更簡(jiǎn)潔。如果你覺(jué)得理解RxPermissions麻煩的話,我建議還是按照Android的API方式調(diào)用會(huì)更好

RxPermission的git地址:https://github.com/tbruyelle/RxPermissions

RxPermissions的使用姿勢(shì)

發(fā)現(xiàn)網(wǎng)上說(shuō)的RxPermissions的介紹都是關(guān)于老版本的介紹。

repositories {
jcenter() // If not already there
}
dependencies {
    compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0'
}

// Must be done during an initialization phase like onCreate
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(new Subscriber<Boolean>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Boolean granted) {
                 if (granted) {
                       // All requested permissions are granted
                    } else {
                       // At least one permission is denied
                    }
            }
        });

這個(gè)分支是fix46,大家可以去看下。

作為一個(gè)程序員,當(dāng)然要以最新的為準(zhǔn)了。
最新的使用姿勢(shì)

repositories {
jcenter() // If not already there
}

dependencies {
    compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4'
}

RxPermissions rxPermissions = new RxPermissions(this);


// Must be done during an initialization phase like onCreate
rxPermissions
.request(Manifest.permission.CAMERA)
.subscribe(new Subscriber<Boolean>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Boolean granted) {
                 if (granted) {
                       // All requested permissions are granted
                    } else {
                       // At least one permission is denied
                    }
            }
        });


// 多個(gè)權(quán)限申請(qǐng)
rxPermissions
.request(Manifest.permission.CAMERA,
         Manifest.permission.READ_PHONE_STATE)
.subscribe(new Subscriber<Boolean>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Boolean granted) {
                 if (granted) {
                       // All requested permissions are granted
                    } else {
                       // At least one permission is denied
                    }
            }
        });

具體可以看github介紹

新版本主要是將RxPermissions.getInstance(this) -> new RxPermissions(this),現(xiàn)在不再使用單例
至于原因:我猜應(yīng)該是單例持有當(dāng)前的Activity,容易造成內(nèi)存泄漏吧

姿勢(shì)講完了,看代碼吧。

.
├── AndroidManifest.xml
└── java
    └── com
        └── tbruyelle
            └── rxpermissions
                ├── Permission.java。--封裝了的權(quán)限的基類(lèi),包含權(quán)限名稱,是否授權(quán)等
                ├── RxPermissions.java --核心處理類(lèi),進(jìn)行rxjava轉(zhuǎn)換
                └── RxPermissionsFragment.java --實(shí)際權(quán)限請(qǐng)求的Fragment

因?yàn)榇a較少,我直接就貼代碼了。添加了注釋,大家就就這樣將就看吧

  • Permission.java
package com.tbruyelle.rxpermissions;

/**
 * 權(quán)限的基類(lèi)
 * 封裝了權(quán)限名稱 是否授權(quán) 受否顯示請(qǐng)求授權(quán)的對(duì)話框
 *
 * ActivityCompat.checkSelfPermission --> granted
 * ActivityCompat.shouldShowRequestPermissionRationale -> shouldShowRequestPermissionRationale
 */
public class Permission {
    public final String name;  // 權(quán)限名稱
    public final boolean granted; // 是否授權(quán)
    public final boolean shouldShowRequestPermissionRationale; // 受否顯示請(qǐng)求授權(quán)的對(duì)話框

    public Permission(String name, boolean granted) {
        this(name, granted, false);
    }

    public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) {
        this.name = name;
        this.granted = granted;
        this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale;
    }

    @Override
    @SuppressWarnings("SimplifiableIfStatement")
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        final Permission that = (Permission) o;

        if (granted != that.granted) return false;
        if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale)
            return false;
        return name.equals(that.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + (granted ? 1 : 0);
        result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Permission{" +
                "name='" + name + '\'' +
                ", granted=" + granted +
                ", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale +
                '}';
    }
}

  • RxPermissionsFragment.java
package com.tbruyelle.rxpermissions;

import android.annotation.TargetApi;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;

import java.util.HashMap;
import java.util.Map;

import rx.subjects.PublishSubject;

/**
 * RxPermissionsFragment用于權(quán)限的相關(guān)操作
 * 如調(diào)用requestPermission,onRequestPermissionsResult回調(diào)等
 *
 * 此Fragment直接調(diào)用Android6.0的fragment,兼容處理在RxPermission中實(shí)現(xiàn)
 */
public class RxPermissionsFragment extends Fragment {

    /** requestPermission時(shí)用的REQUEST_CODE**/
    private static final int PERMISSIONS_REQUEST_CODE = 42;

    // Contains all the current permission requests.
    // Once granted or denied, they are removed from it.
    //  PublishSubject 是最直接主要的Subject實(shí)現(xiàn),
    // 當(dāng)一個(gè)事件值被發(fā)送給PublishSubject時(shí),它會(huì)將這個(gè)事件值發(fā)送給訂閱它的每個(gè)訂閱者:
    private Map<String, PublishSubject<Permission>> mSubjects = new HashMap<>();
    /** 日志開(kāi)關(guān) **/
    private boolean mLogging;

    public RxPermissionsFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 在 Activity 重繪時(shí),我們的 Fragment 不會(huì)被重復(fù)繪制,也就是它會(huì)被“保留”。
        // 為了驗(yàn)證其作用,我們發(fā)現(xiàn)在設(shè)置為 true 狀態(tài)時(shí),旋轉(zhuǎn)屏幕,F(xiàn)ragment 依然是之前的 Fragment。
        setRetainInstance(true);
    }

    /**
     * 發(fā)起請(qǐng)求Permissions
     * @param permissions
     */
    @TargetApi(Build.VERSION_CODES.M)
    void requestPermissions(@NonNull String[] permissions) {
        requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
    }

    /**
     * 請(qǐng)求Permissions的回調(diào)
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode != PERMISSIONS_REQUEST_CODE) return;

        boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];

        for (int i = 0; i < permissions.length; i++) {
            shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);
        }

        onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);
    }

    void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
        for (int i = 0, size = permissions.length; i < size; i++) {
            log("onRequestPermissionsResult  " + permissions[i]);
            // Find the corresponding subject
            PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
            if (subject == null) {
                // No subject found
                Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
                return;
            }
            mSubjects.remove(permissions[i]);
            boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
            subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
            subject.onCompleted();
        }
    }

    /**
     * 是否授權(quán)
     * @param permission
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    boolean isGranted(String permission) {
        return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * 判斷是不是在包中申明
     * @param permission
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    boolean isRevoked(String permission) {
        return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
    }

    public void setLogging(boolean logging) {
        mLogging = logging;
    }

    public PublishSubject<Permission> getSubjectByPermission(@NonNull String permission) {
        return mSubjects.get(permission);
    }

    public boolean containsByPermission(@NonNull String permission) {
        return mSubjects.containsKey(permission);
    }

    public PublishSubject<Permission> setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject<Permission> subject) {
        return mSubjects.put(permission, subject);
    }

    void log(String message) {
        if (mLogging) {
            Log.d(RxPermissions.TAG, message);
        }
    }

}

  • RxPermissions.java
/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tbruyelle.rxpermissions;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.List;

import rx.Observable;
import rx.functions.Func1;
import rx.subjects.PublishSubject;

/**
 * RxPermissions處理類(lèi)
 */
public class RxPermissions {

    static final String TAG = "RxPermissions";

    /**
     * 為什么要使用RxPermissionsFragment???
     * 通過(guò)這個(gè)fragment進(jìn)行權(quán)限的操作
     */
    RxPermissionsFragment mRxPermissionsFragment;

    public RxPermissions(@NonNull Activity activity) {
        mRxPermissionsFragment = getRxPermissionsFragment(activity);
    }

    /**
     * 生成RxPermissionsFragment,用來(lái)獲取Fragment對(duì)象
     * @param activity
     * @return
     */
    private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {

        // 在當(dāng)前Activity添加一個(gè)Fragment,然后通過(guò)這個(gè)Fragment進(jìn)行權(quán)限處理

        RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
        boolean isNewInstance = rxPermissionsFragment == null;
        if (isNewInstance) {
            rxPermissionsFragment = new RxPermissionsFragment();
            FragmentManager fragmentManager = activity.getFragmentManager();
            fragmentManager
                    .beginTransaction()
                    .add(rxPermissionsFragment, TAG)
                    .commitAllowingStateLoss();
            fragmentManager.executePendingTransactions();
        }
        return rxPermissionsFragment;
    }

    private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {
        return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
    }

    /**
     * 設(shè)置打印日志
     * @param logging
     */
    public void setLogging(boolean logging) {
        mRxPermissionsFragment.setLogging(logging);
    }

    /**
     * Map emitted items from the source observable into {@code true} if permissions in parameters
     * are granted, or {@code false} if not.
     * <p>
     * If one or several permissions have never been requested, invoke the related framework method
     * to ask the user if he allows the permissions.
     */
    @SuppressWarnings("WeakerAccess")
    public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
        return new Observable.Transformer<Object, Boolean>() {
            @Override
            public Observable<Boolean> call(Observable<Object> o) {
                return request(o, permissions)
                        // Transform Observable<Permission> to Observable<Boolean>
                        .buffer(permissions.length)
                        .flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
                            @Override
                            public Observable<Boolean> call(List<Permission> permissions) {
                                if (permissions.isEmpty()) {
                                    // Occurs during orientation change, when the subject receives onComplete.
                                    // In that case we don't want to propagate that empty list to the
                                    // subscriber, only the onComplete.
                                    return Observable.empty();
                                }
                                // Return true if all permissions are granted.
                                for (Permission p : permissions) {
                                    if (!p.granted) {
                                        return Observable.just(false);
                                    }
                                }
                                return Observable.just(true);
                            }
                        });
            }
        };
    }

    /**
     * Map emitted items from the source observable into {@link Permission} objects for each
     * permission in parameters.
     * <p>
     * If one or several permissions have never been requested, invoke the related framework method
     * to ask the user if he allows the permissions.
     */
    @SuppressWarnings("WeakerAccess")
    public Observable.Transformer<Object, Permission> ensureEach(final String... permissions) {
        return new Observable.Transformer<Object, Permission>() {
            @Override
            public Observable<Permission> call(Observable<Object> o) {
                return request(o, permissions);
            }
        };
    }

    /**
     * Request permissions immediately, <b>must be invoked during initialization phase
     * of your application</b>.
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(null).compose(ensure(permissions));
    }

    /**
     * Request permissions immediately, <b>must be invoked during initialization phase
     * of your application</b>.
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Permission> requestEach(final String... permissions) {
        return Observable.just(null).compose(ensureEach(permissions));
    }

    private Observable<Permission> request(final Observable<?> trigger, final String... permissions) {
        if (permissions == null || permissions.length == 0) {
            throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");
        }
        return oneOf(trigger, pending(permissions))
                .flatMap(new Func1<Object, Observable<Permission>>() {
                    @Override
                    public Observable<Permission> call(Object o) {
                        return requestImplementation(permissions);
                    }
                });
    }

    private Observable<?> pending(final String... permissions) {
        for (String p : permissions) {
            if (!mRxPermissionsFragment.containsByPermission(p)) {
                return Observable.empty();
            }
        }
        return Observable.just(null);
    }

    private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
        if (trigger == null) {
            return Observable.just(null);
        }
        return Observable.merge(trigger, pending);
    }

    @TargetApi(Build.VERSION_CODES.M)
    private Observable<Permission> requestImplementation(final String... permissions) {
        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        List<String> unrequestedPermissions = new ArrayList<>();

        // In case of multiple permissions, we create an Observable for each of them.
        // At the end, the observables are combined to have a unique response.
        for (String permission : permissions) {
            mRxPermissionsFragment.log("Requesting permission " + permission);
            if (isGranted(permission)) {
                // Already granted, or not Android M
                // Return a granted Permission object.
                list.add(Observable.just(new Permission(permission, true, false)));
                continue;
            }

            if (isRevoked(permission)) {
                // Revoked by a policy, return a denied Permission object.
                list.add(Observable.just(new Permission(permission, false, false)));
                continue;
            }

            PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);
            // Create a new subject if not exists
            if (subject == null) {
                unrequestedPermissions.add(permission);
                subject = PublishSubject.create();
                mRxPermissionsFragment.setSubjectForPermission(permission, subject);
            }

            list.add(subject);
        }

        if (!unrequestedPermissions.isEmpty()) {
            String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
            requestPermissionsFromFragment(unrequestedPermissionsArray);
        }
        return Observable.concat(Observable.from(list));
    }

    /**
     * Invokes Activity.shouldShowRequestPermissionRationale and wraps
     * the returned value in an observable.
     * <p>
     * In case of multiple permissions, only emits true if
     * Activity.shouldShowRequestPermissionRationale returned true for
     * all revoked permissions.
     * <p>
     * You shouldn't call this method if all permissions have been granted.
     * <p>
     * For SDK < 23, the observable will always emit false.
     */
    @SuppressWarnings("WeakerAccess")
    public Observable<Boolean> shouldShowRequestPermissionRationale(final Activity activity, final String... permissions) {
        if (!isMarshmallow()) {
            return Observable.just(false);
        }
        return Observable.just(shouldShowRequestPermissionRationaleImplementation(activity, permissions));
    }

    @TargetApi(Build.VERSION_CODES.M)
    private boolean shouldShowRequestPermissionRationaleImplementation(final Activity activity, final String... permissions) {
        for (String p : permissions) {
            if (!isGranted(p) && !activity.shouldShowRequestPermissionRationale(p)) {
                return false;
            }
        }
        return true;
    }

    @TargetApi(Build.VERSION_CODES.M)
    void requestPermissionsFromFragment(String[] permissions) {
        mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
        mRxPermissionsFragment.requestPermissions(permissions);
    }

    /**
     *
     * 是否授權(quán)兼容處理
     * Android6.0以下,都為true
     * @param permission
     * @return
     */
    @SuppressWarnings("WeakerAccess")
    public boolean isGranted(String permission) {
        return !isMarshmallow() || mRxPermissionsFragment.isGranted(permission);
    }

    /**
     * 權(quán)限是否在包中
     * 兼容處理
     * Android6.0以下都為false
     * @param permission
     * @return
     */
    @SuppressWarnings("WeakerAccess")
    public boolean isRevoked(String permission) {
        return isMarshmallow() && mRxPermissionsFragment.isRevoked(permission);
    }

    /**
     * 是否為Android6.0以上的包
     * @return
     */
    boolean isMarshmallow() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    void onRequestPermissionsResult(String permissions[], int[] grantResults) {
        mRxPermissionsFragment.onRequestPermissionsResult(permissions, grantResults, new boolean[permissions.length]);
    }

}

就像剛開(kāi)始介紹的一樣,只是用Rxjava方式封裝了這個(gè)權(quán)限請(qǐng)求的過(guò)程,如果對(duì)Rxjava很熟悉,建議使用。

里面使用了很多RxJava的方法,這里不展開(kāi)講了。具體API看下方

ReactiveX/RxJava文檔中文版

不是很擅長(zhǎng)的話,推薦看下這篇權(quán)限請(qǐng)求的封裝 android 6.0 Permission權(quán)限兼容的封裝

相關(guān)資料

ReactiveX/RxJava文檔中文版

Android6.0權(quán)限適配的那些坑

Android M 新的運(yùn)行時(shí)權(quán)限開(kāi)發(fā)者需要知道的一切

安卓6.0權(quán)限適配 ----RxPermissions

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容