脫掉RxPermission的嫁衣
Android 6.0許可權管理的接入方式有點累,這模式跟 ActivityResult
一樣的實現方式,請求和接收是分離的,沒有掛回調函式那麼方便,當頁面程式碼量大的時候閱讀性不好:
private void requestPermission(){ // Here, thisActivity is the current activity if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request. } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } // other 'case' lines to check for other // permissions this app might request } }
因為跟ActvitiyResult一個模式,請求返回結果是以陣列方式提供的, 想知道所有結果是否都grant得要遍歷陣列一一比較grantResults,想知道究竟哪個許可權被拒絕,也要遍歷permissions並判斷同樣index對應的grantResults結果。
其實,因為業務程式碼比較多,大家想擁有的請求許可權的API如下那麼簡單:
// 只關注全部授權通過 requestPermission(new OnGrantResult() { @Override public void onGrant() { // do something when grant } @Override public void onDenied() { // do something when denied } }, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE); // 關注個別許可權授權結果 requestEachPermission(new OnEachGrantResult() { @Override public void onNext(Permission permission) { // check someone permission and // its grant state and do something you want } }, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE);
說到封裝,如上API,可能很多人感覺不難,可能很多人都想到的辦法是:
requestPermissions(OnGrantResult, String... permissions)
看似完美,但是有一個最大的問題是程式碼嵌入性太強,BaseActivity和BaseFragment實在太臃腫,一有類似的功能增加就想動它們,可能有些朋友認為,BaseActivity或者BaseFragment可以搞多層繼承啊,這樣每一層只做對應的事情,比如把許可權相關的封裝在一個BasePermissionActivity裡,然後BaseActivity繼承BasePermissionActivity裡,可是時間長了你會發現BaseActivity的搞了很多層,一有新功能就加層級,說實話層級依賴不如扁平依賴。
前段時間在看RxJava原理的時候順便看到了衍生作品RxPermission,發現它非常奇妙,通過RxPermission請求許可權無需改變BaseActivity又能以掛回調方式接收請求結果,想看究竟內部是怎麼做到的。
看完之後的感慨是:好吧,RxJava的確很強大,不過RxPermission裡的Rx只是一個障眼法,本質原理是在需要請求許可權的Activity開啟一個無UI的Fragment或者在需要請求許可權的Fragment裡開啟一個無UI的Child Fragment,並在開啟的Fragment裡請求許可權,因為Fragment和它的宿主天生能進行通訊,所以在無UI的Fragment裡請求許可權並把授權結果告知它宿主即需要許可權的Activity或者Fragment。
知道這個原理後,如果不希望在自己專案中引入RxJava你就可以自己簡化RxPermission了,其實很簡單,保留一個用於許可權請求的fragment和許可權請求器之間的callback即可。
簡化後的程式碼少之又少:

struct.png
簡化後的使用如下:
public class MainActivity extends AppCompatActivity { private Permissions mPermissions; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // init permissions mPermissions = new Permissions(this); Button requestBtn = findViewById(R.id.requestBtn); requestBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPermissions.request(new OnGrantResult() { @Override public void onGrant() { // do something when grant } @Override public void onDenied() { // do something when denied } }, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE); } }); Button requestEachBtn = findViewById(R.id.requestEachBtn); requestEachBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPermissions.requestEach(new OnEachGrantResult() { @Override public void onNext(Permission permission) { // check someone permission and // its grant state and do something you want } }, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE); } }); } }
詳細實現可以參考 ofollow,noindex">這裡 .