基本使用
Android6.0之后,增加了動態權限配置,目的在于用戶可以自由的選擇自己是否給予app權限,就算沒有給予某個權限,也不影響其他功能的使用,不至于令用戶無法安裝
接下來先看一下基本的使用,代碼如下:
// 檢測是否授予了CALL_PHONE這個權限
if (ContextCompat.checkSelfPermission(MainActivity@ this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 還未授予,則申請權限
ActivityCompat.requestPermissions(MainActivity@ this, arrayOf(Manifest.permission.CALL_PHONE), 0x111)
} else {
// 已經授予,則進行相關操作
call()
}
// 該方法是申請權限后的回調方法
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
// 請求碼對應
0x111 -> {
// 判斷申請權限是否成功
if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 成功申請,執行相關操作
call()
} else {
// 申請失敗,提示用戶
Toast.makeText(this, "您拒絕了該權限", Toast.LENGTH_SHORT).show()
}
}
else -> {
}
}
}
// 權限申請成功后的相關操作
private fun call() {
try {
val intent = Intent(Intent.ACTION_CALL)
intent.setData(Uri.parse("tel:10086"))
startActivity(intent)
} catch (e: SecurityException) {
e.printStackTrace()
}
}
以上是申請權限的基本操作,操作的方式其實類似于startActivityForResult()這種啟動Activity的方法,也是會有具體的回調方法,但是如果每次都需要這樣寫,未免太過于繁瑣,所以一般情況都需要對其進行封裝,而RxPermissions這個庫就對其進行了很好的封裝,下面就使用RxPermissions進行實現
使用之前需要在項目中添加相關依賴
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
下面是詳細實現代碼
// 定義一個RxPermission對象
private lateinit var mRxPermission:RxPermissions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 創建Permission對象
mRxPermission = RxPermissions(this)
// 申請權限
mRxPermission.request(Manifest.permission.CALL_PHONE).subscribe(object :Action1<Boolean>{
override fun call(t: Boolean?) {
// 回調之后的操作
if (t!!){
call()
}else{
Toast.makeText(this@MainActivity, "您拒絕了該權限", Toast.LENGTH_SHORT).show() }
}
})
相比一開始的代碼,利用RxPermissions寫的簡潔了很多,也無需重寫onRequestPermissionsResult()方法,這就是使用了該庫的好處,節省了我們的代碼量,也使代碼變得更加清晰有條理,那么它是怎么做到的呢,下面就對其源碼進行解讀
1、其實,RxPermission采用的方式是利用了一個隱形的Fragment來請求權限,然后在回調中用RxJava進行數據的組裝和轉化,最后變成了布爾類型的數據回調回來,下面是具體的分析
// 創建RxPermissions的對象
mRxPermission = RxPermissions(this)
// 定義RxPermissions的對象
RxPermissionsFragment mRxPermissionsFragment;
// RxPermissions的構造方法
public RxPermissions(@NonNull Activity activity) {
// 獲取Fragment的實例
mRxPermissionsFragment = getRxPermissionsFragment(activity);
}
// 獲取Fragment的方法
private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
// 查詢是否已經存在了該Fragment,這樣是為了讓該Fragment只有一個實例
RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
boolean isNewInstance = rxPermissionsFragment == null;
// 如果還沒有存在,則創建Fragment,并添加到Activity中
if (isNewInstance) {
rxPermissionsFragment = new RxPermissionsFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(rxPermissionsFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return rxPermissionsFragment;
}
// 利用tag去找是否已經有該Fragment的實例
private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {
return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
通過以上的代碼可以知道,在創建RxPermissions的對象中,其實就是獲取Fragment的實例而已,既然這樣,我們就需要到這個Fragment的實現中,看它在被創建的時候做了什么事情
// Fragment的構造方法
public RxPermissionsFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 禁止橫豎屏切換時的Fragment的重建
setRetainInstance(true);
}
可以看到,在Fragment被創建時,并沒有重寫onCreateView()方法,來進行布局文件的加載,只是重寫了onCreate()方法,然后禁止了橫豎屏Fragment的重建,這就代表說,這個Fragment是一個沒有布局的隱形Fragment,不會在屏幕上展示出來,但是這個Fragment卻是關鍵,權限的申請與申請結果的回調都是在Fragment中完成的,這樣,我們才不需要為申請結果重寫回調方法
2、接下來是具體的請求
mRxPermission.request(Manifest.permission.CALL_PHONE).subscribe(object :Action1<Boolean>{
override fun call(t: Boolean?) {
if (t!!){
call()
}else{
Toast.makeText(this@MainActivity, "您拒絕了該權限", Toast.LENGTH_SHORT).show()
}
}
})
上面的代碼主要是調用RxPermissions對象中的request()方法,然后該方法會返回一個Observable,下面看具體的request()方法代碼
@SuppressWarnings({"WeakerAccess", "unused"})
public Observable<Boolean> request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}
首先,利用just(null)方法創建Observable對象
接下來會調用compose()方法。compose()的參數就為Observable.Transformer對象,該對象的作用是將一個類型的Observable對象轉換成另外一個類型的Observable對象,同時也可以對自身對象的一些重復操作進行封裝,避免重復編寫代碼
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)
// 將Observable<Permission>轉換成Observable<Boolean>
.buffer(permissions.length)
.flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(List<Permission> permissions) {
if (permissions.isEmpty()) {
// 如果申請的權限列表為空,則返回一個只回調onComplete()方法的Observable對象
return Observable.empty();
}
// 循環權限列表,判斷權限是否申請成功
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
}
};
}
上面代碼的作用是傳入一個Observable<Object>對象,然后轉換成一個Observable<Boolean>對象,而這個Observable<Object>對象就是前面Observable.just(null)所創建的對象,同時ensure()方法傳入了permissions權限申請列表,在轉換的初步,就是需要根據permissions權限列表去申請權限,具體操作在request()方法中,下面看看request()的實現
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");
}
// 首先,合并了兩個Observable對象,然后通過flatMap將Object對象轉換成Observable<Permission>對象
return oneOf(trigger, pending(permissions))
.flatMap(new Func1<Object, Observable<Permission>>() {
@Override
public Observable<Permission> call(Object o) {
return requestImplementation(permissions);
}
});
}
通過上面代碼可以看出,首先會判斷申請權限列表是否為空,如果為空就會拋出異常,然后通過oneOf方法和pending()方法來創建合并Observable對象,下面看一下這兩個方法
private Observable<?> pending(final String... permissions) {
// 循環遍歷,查詢該權限是否已經在申請過了
for (String p : permissions) {
if (!mRxPermissionsFragment.containsByPermission(p)) {
// 如果列表中有一個權限未在Fragment的HashMap集合中保存
// 則返回Observeble.empty(),返回的這個Observable對象
// 只會調用onComplete()方法,所以并不會進入flatMap等操作// 符號中
return Observable.empty();
}
}
return Observable.just(null);
}
private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
// 判斷Observable對象是否為null,在這里其實是不太可能會為null的
if (trigger == null) {
return Observable.just(null);
}
//返回合并的Observable對象
return Observable.merge(trigger, pending);
}
從以上的兩個方法的代碼可以看出,pending()主要是判斷權限申請列表中是否全部都在Fragment中的mSubjects集合中,如果有一個不在集合中,則返回Observable.empty()方法,如果已經全部在集合中,則返回Observable.just(null),然后在oneOf()方法中根據trigger是否為null來判斷是返回Observable.just(null)還是Observable.merge(trigger, pending)。這兩個方法在我看來最終并沒有起到實際的作用,因為具體的請求權限是在requestImplementation(permissions)方法中實現的,而通過以上兩個方法得到的Observable對象最終在flatMap操作轉換時并不會用到它們的對象,而是直接根據權限申請列表“permissions”作為參數,直接調用requestImplementation()方法,進行權限的實際請求,所以接下來主要看看requestImplementation()方法的具體實現
private Observable<Permission> requestImplementation(final String... permissions) {
// 創建集合保存已經申請的權限
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
// 創建集合保存未申請的權限
List<String> unrequestedPermissions = new ArrayList<>();
// 循環權限申請列表
for (String permission : permissions) {
mRxPermissionsFragment.log("Requesting permission " + permission);
// 如果權限已經申請過了,則直接保存到集合中
if (isGranted(permission)) {
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;
}
// 先去RxPermissionsFragment中查詢是否已經存在了該權限
PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);
// Create a new subject if not exists
// 如果還未存在,則創建一個PublishSubject對象
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);
}
// 利用list集合創建Observable對象,并且用concat進行鏈接,返回一個Observable<Permission>對象
return Observable.concat(Observable.from(list));
}
上面的就是進行權限申請的具體代碼,主要的操作就是定義一個集合保存保存一次申請的所有權限,無論這個權限是否已經申請,還是被撤銷,還是未申請,最終都會保存到list這個集合中,這樣,我們在后續的操作中,才可以進行轉換,同時,定義一個集合,用于保存未申請的權限,然后在循環結束之后進行未申請權限的申請。接下來看看requestPermissionsFromFragment()放到,它最終調用的還是mRxPermissionsFragment.requestPermissions(permissions)方法,就是RxPermissionsFragment中的requestPermissions方法了,所以我們進到該方法看看
void requestPermissions(@NonNull String[] permissions) {
requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
}
可以看到,該方法并沒有進行什么特別操作,就是申請權限,那么既然申請了權限了,是否申請成功的處理應該在回調中,所以我們看看回調的處理
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 如果請求碼不符合,則直接返回
if (requestCode != PERMISSIONS_REQUEST_CODE) return;
// 以權限列表的長度為容器的size創建一個boolean數組
boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
// 循環遍歷,看權限是否被永久拒絕了
for (int i = 0; i < permissions.length; i++) {
// 這里會調用Fragment中的shouldShowRequestPermissionRationale()方法,然后這個方法如果是申請成功會返回true,如果被點擊了不在提醒,并且拒絕權限時,則會返回false
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
// 查詢mSubjects集合中是否存在代表該permission的PublishSubject對象
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;
}
// 將集合中的permission的PublishSubject對象進行移除
mSubjects.remove(permissions[i]);
// 判斷是否申請成功
boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
// 返回相應的對象
subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
subject.onCompleted();
}
}
上面的代碼是回調處理權限申請的全部過程,主要用到了兩個方法,第一個方法主要用于創建一個boolean數組,用于保存判斷權限申請的狀態,如果權限被申請,并且用戶在對話框中點擊了“不在提醒”選項,這個時候shouldShowRequestPermissionRationale()這個方法還是會返回false,代表說這個權限被拒絕,我們可以根據這個在給用戶做一些提示性的工作。然后就到了重載的方法了,在重載的方法中,首先判斷在mSubjects集合中是否已經存在了該permission的PublishSubject對象,如果沒有,則直接返回,代表出錯,但是這里的對象在RxPermissions#requestImplementation()中已經通過mRxPermissionsFragment.setSubjectForPermission(permission, subject);語句進行賦值了,所以一定會是存在的。接著mSubjects集合將該permission的PublishSubjects對象移除,這是為什么呢,這個要看RxPermissions#requestImplementation()中的實現,在該方法中會PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);這個語句出現,這個語句代表說,如果權限未申請過,也未被撤銷,那么就直接在RxPermissionsFragment的mSubjects集合中查找是否存在該permission的PublishSubject對象,如果有,就直接通過list.add(subject);這個語句保存到集合中了,那么這樣被拒絕的權限,下次將不會被重新申請,所以需要移除,之后就是發射數據,發送時間結束標識了
分析完了RxPermissionsFragment中的權限申請的操作,我們就需要回到Rxpermissions#request()方法中,在該方法的flatMap的回調方法中返回了Observable<Permission>對象,因此這個方法的任務完成,接著再往回看,在ensure()方法中,我們可以看到在該方法中調用了request()方法之后,接著調用了buffer(permissions.length)方法,其實就是Observable<Permission>對象調用了buffer()方法,那么這個buffer()的作用是什么呢,它的作為是為了將一個序列的Observable<Permission>對象轉換成Observable<List<Permission>>對象,為什么要這樣轉換,就需要看requestImplementation()方法中的返回值,該方法返回值為Observable.concat(Observable.from(list));這代表說這是一個Observable<Permission>對象序列,所以需要通過buffer()方法進行轉換,接著就使用flatMap操作符轉換成Observable<Boolean>對象,看看代碼
@Override
public Observable<Boolean> call(List<Permission> permissions) {
// 如果集合為空,代表說沒有權限申請,直接返回Observable.empty()
if (permissions.isEmpty()) {
return Observable.empty();
}
// 循環遍歷集合
for (Permission p : permissions) {
// 如果權限列表中有一個權限申請失敗,則直接返回申請失敗
if (!p.granted) {
return Observable.just(false);
}
}
// 全部申請成功,則返回申請成功
return Observable.just(true);
}
通過以上代碼可以看到,最終會遍歷Permissions集合,這個集合其實就是一開始我們調用request(final String... permissions)這個方法之后的權限,經過處理返回的Observable<permission>序列對象,這里是庫提供的一個默認實現,它會遍歷集合,如果有一個權限申請失敗,都當作是申請權限失敗了,但是我們也可以自己來決定返回后的Observable<Permission>對象要怎么處理,那就是調用requestEach()方法,然后最終得到的就是一個未經過轉換成Observable<Boolean>處理的Observable<Permission>對象,我們可以自己根據實際需要進行必要的處理,而放棄掉默認提供的實現,不過一般情況下,使用默認的實現已經足夠
總結
如今大部分的Android手機設備的系統都在6.0以上,動態權限就變成了我們日常開發中必須要做的工作,那么為了避免編寫重復代碼,就需要使用到第三方庫來簡化我們的操作,但是我們在使用第三方庫的時候還是需要懂得其實現的原理,這樣使用起來會更加的方便,它好的思想也可以促進我們編碼水平的提供,最后就是,如果有特別的需求,我們也可以在其庫的基礎上進行特別的定制,來適應自身的需求,這也是看其源碼的目的和意義