安卓元件化應用的6.0許可權適配優化方案
元件化應用
元件化應用的概念最近挺火的。隨著app版本的迭代,業務也會變的越來越複雜。元件化應用能將每個業務都單獨分成一個模組,作為一個元件(Module),業務模組彼此互不依賴,然後讓這些業務模組都依賴公共模組(也是Module)等,用路由的方式替代startactivity
進行模組間的跳轉和資料傳遞。這就是元件化應用的簡單概念。
因為module與module之間是程式碼隔離的,互不依賴,所以新增或移除module是很方便的,也方便了應用的多人並行開發。
Android6.0許可權
Android6.0已經出來快兩年了,除了繼續推進Material Design,相信最直觀的改變就是許可權申請方式了:許可權模式從一開始的全部列出授予,變成了現在的執行時動態申請。下圖列出了截止7月份最新的系統佔有率。雖然碎片化問題依舊嚴重,但6.0以上系統佔有率也已經接近一半了。所以說各位安卓開發者們,如果你的應用還沒適配6.0的話,可要抓緊了。
如何適配
簡單介紹了上面的兩個概念後,我們們切回正題。因為最近在做一個APP的元件化改造,原來的許可權適配方案是跳到一個空白activity做申請然後回撥申請結果的,但是現在module與module之間是互不依賴的,所以activity之間介面回撥的方式是行不通的。一時間不知道有什麼好的方法,也參考了github上幾個主流的許可權適配庫,但遺憾都沒有對元件化應用提供一個專門的解決方案。
因為這個專案採用的路由是阿里的ARouter。查閱文件後發現裡面有攔截器的功能,下面是簡單的示例程式碼。
// 比較經典的應用就是在跳轉過程中處理登陸事件,這樣就不需要在目標頁重複做登陸檢查
// 攔截器會在跳轉之間執行,多個攔截器會按優先順序順序依次執行
@Interceptor(priority = 8, name = "測試用攔截器")
public class TestInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
callback.onContinue(postcard); // 處理完成,交還控制權
// callback.onInterrupt(new RuntimeException("我覺得有點異常")); // 覺得有問題,中斷路由流程
// 以上兩種至少需要呼叫其中一種,否則不會繼續路由
}
@Override
public void init(Context context) {
// 攔截器的初始化,會在sdk初始化的時候呼叫該方法,僅會呼叫一次
}
}
那麼我們何不利用這個攔截器,在跳到一個需要頁面(比如相機頁面)之前 進行統一攔截,然後判斷許可權是否擁有,如果後則callback.onContinue(postcard)
,繼續跳轉,否則callback.onInterrupt(null)
,攔截跳轉並執行許可權申請。
同時通常一個應用通常有必要許可權,沒有必要許可權應用無法正常執行,如Manifest.permission.READ_PHONE_STATE
,還有非必要許可權,如Manifest.permission.ACCESS_FINE_LOCATION
。
那麼我們可以用priority
欄位建立優先順序最高的攔截器,檢測所有跳轉時是否有必要許可權,否則先申請必要許可權,然後繼續觸發下一個優先順序低的攔截器攔截檢測普通許可權。
實際實現
定義一個permissionActivity,讓所有需要跳轉許可權的頁面的activity繼承這個,因為如上文講的,我們實際許可權的申請和處理不是在真正需要許可權的頁面完成,而是在上一個頁面申請完成再跳到需要許可權的頁面的。如果頁面太多建議所有頁面都繼承這個activity。
public class PermissionActivity extends FragmentActivity {
private PermissionListener permissionListener;
public void setPermissionListener(PermissionListener permissionListener) {
this.permissionListener = permissionListener;
}
/**
* 許可權請求結果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (permissionListener != null ) {
permissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
建立攔許可權截器:
@Interceptor(priority = 1)
public class PermissionInterceptor implements IInterceptor {
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
final Activity activity = ActivityHelper.last();
final String[] PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
};
if (activity != null && !PermissionHelper.hasPermission(activity)) {
permissionRequest(postcard, callback, (PermissionActivity) activity, permissions);
} else {
callback.onContinue(postcard); // 已有許可權,無需申請,繼續跳轉
}
}
private void permissionRequest(final Postcard postcard, final InterceptorCallback interceptorCallback, final PermissionActivity activity, final String[] permissions) {
Runnable runnable = new Runnable() {
@Override
public void run() {
PermissionHelper.requestPermissions(activity, permissions, new PermissionListener() {
@Override
public void onsuccessed() {
callback.onContinue(postcard);// 許可權申請成功,繼續跳轉
}
@Override
public boolean onfail() {
callback.onInterrupt(null);// 許可權申請失敗,攔截跳轉
}
});
}
};
MainLooper.runOnUiThread(runnable );
}
@Override
public void init(Context context) {
}
}
實際實現中有幾個注意點:
1.攔截器init(Context context)
方法中的context
不是當前頁的activity,而是application,也就是說你在攔截器裡是無法直接拿到當前頁的上下文的。所以你需要額外維護一個activity棧,在每個acitivity的oncreate()
方法中入棧,在ondestory()
中出棧,然後在攔截器裡面取出棧頂的acitivity,也就是當前頁的acitivity。
2.void process(Postcard postcard, InterceptorCallback callback)
方法在子執行緒中執行,如果要執行許可權申請需要先切換回主執行緒。
public class MainLooper extends Handler {
private static MainLooper instance = new MainLooper(Looper.getMainLooper());
protected MainLooper(Looper looper) {
super(looper);
}
public static MainLooper getInstance() {
return instance;
}
public static void runOnUiThread(Runnable runnable) {
if(Looper.getMainLooper().equals(Looper.myLooper())) {
runnable.run();
} else {
instance.post(runnable);
}
}
}
總結
這樣實現的好處也是顯而易見的:所有的許可權申請操作都在攔截器裡完成,對原始碼無入侵:所有的許可權申請彈框都出現在需要許可權的頁面上一個activity。我們只要讓這個activity繼承permissionActivity並處理onRequestPermissionsResult
的回撥即可,其他譬如處理許可權申請,處理許可權申請成功,失敗操作都統一在攔截器裡完成即可。
相關文章
- Android6.0許可權的動態適配Android
- 「Android6.0許可權適配| 掘金技術徵文 」Android
- 這也許是Android一句話許可權適配的更優解決方案Android
- MAUI之安卓許可權UI安卓
- 安卓元件化化落地安卓元件化
- Linux 檔案許可權、系統優化Linux優化
- Vue 前端許可權控制的優化改進版Vue前端優化
- delphi安卓動態許可權申請安卓
- 給安卓 app 新增許可權的一種方法安卓APP
- 安卓6.0以上從相簿選擇圖片,圖片壓縮及動態許可權安卓
- Android應用優化方案Android優化
- 安卓應用效能除錯和優化經驗分享安卓除錯優化
- 類的許可權與應用
- Android 採用AOP方式封裝6.0許可權管理Android封裝
- Android6.0------許可權申請管理(單個許可權和多個許可權申請)Android
- 關於樹結構的查詢優化,及許可權樹的查詢優化優化
- 安卓應用優化:使用反射測試安卓裝置是否使用“動態桌布”安卓優化反射
- 許可權系統:許可權應用服務設計
- android 6.0許可權機制的簡單封裝(支援批量申請許可權)Android封裝
- 許可權系統:許可權應用服務設計Tu
- 安卓備份當前分割槽(需要root許可權)安卓
- Android6.0~9.0適配Android
- [譯] 為什麼你的應用需要對各種尺寸螢幕做適配優化?優化
- Android 6.0 在執行時請求許可權Android
- 原生Android之(6.0及以上)許可權申請Android
- android 許可權元件設計Android元件
- DRF內建許可權元件之自定義許可權管理類元件
- 前端單頁面應用的許可權管理前端
- 記一個 Android 14 適配引發的Android 儲存許可權問題Android
- 適配金融業的應用監控標準化演進之路
- PostgreSQL物件許可權如何在後設資料中獲取-許可權解讀、定製化匯出許可權SQL物件
- Android元件化開發實戰:封裝許可權管理請求框架Android元件化封裝框架
- 使用非同步元件優化Vue應用程式的效能非同步元件優化Vue
- django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解Django框架
- Android 6.0、7.0、8.0、9.0適配Android
- Android 6.0 執行時許可權管理最佳實踐Android
- android 6.0許可權申請機制(簡單案例)Android
- 許可權控制庫 Casbin 在 Slim 中的應用
- vuejs單頁應用的許可權管理實踐VueJS