AOP編程_Android優雅權限框架(1)概念基礎

2021-02-20 享學課堂online

前言

上一個大的系列文章叫 "手把手講解", 歷時10個月,出產博文二十餘篇,講解細緻,幾乎每一篇都提供了詳實的原理講解,提供了可運行 githubDemo,並且針對Demo中的關鍵地地方進行了重點拆解。相信每一位詳細閱讀文章的同行都會有所收穫。但是,講解雖詳細,但是缺乏對於技術的深度的挖掘。

從今天開始開闢新的專題: 移動架構師專業技能深入淺出,以一步步成為架構師為目標,詳述一項架構師技能的最直接使用價值橫向周邊知識以及縱深專業技術.

最直接使用價值: 網上最怕看到一種文章,全文開篇高大上,讓人覺得遙不可及,通篇看下來卻沒有展示技術如何落地,落地之後是何種效果。文章寫出來,就要以最容易讓人接受的方式帶讀者進入作者的世界,而不是裝作一副高高在上的樣子俯視眾生。所以,文章開篇,一定是最直接的展示技術的落地效果。提供可運行Demo可以讓讀者親自嘗試。

橫向周邊知識: 一項核心技術,必然不是獨立存在,技術是一個體系,但是一篇文章能夠詳述的技術有限,必然是以一項技術為中心,其他技術作為輔助。核心技術需要詳述,但是周邊技術,也需要交代,參天大樹拔地而起也少不得土壤作為依附。用簡明的語言交代周邊知識,並提供這些知識正確的研究方向。也是一個負責任的博文作者不可忽視的一步。

縱深專業技術: 做技術,最忌諱的就是淺嘗輒止。稍微深入一點就退出去,一來不利於理解底層實現,長此以往永遠只是一個技術小白,成不了大師;二來不利於長久記憶, 記憶力再強的人時間長了,技術細節必然會記憶模糊。但是如果深入內核,理解了原理,在技術的大方向上絕對不會偏差。作為要成為架構師的男人,即使記不了那麼多細節,但是對於大方向的把握絕對不能錯。所以,技術縱深很有必要。

正文大綱

1、Demo地址

2、本文所涉技術盤點

3、關於Android權限的梗

4、初級/中級/高級android開發的權限請求寫法

5、AOP優雅權限框架詳解

     gradle配置

     Java代碼

6、AOP思想以及常用AOP框架

7、AspectJ AOP框架的深入原理研究

正文1.Demo地址

Demo地址:https://github.com/18598925736/GracefulPermissionFramework/tree/dev_aspectJ

2. 本文所涉技術盤點

以下適合有一定Android開發年限的開發閱讀。並且對以下技術點至少有個基本了解,才能理解本文demo代碼

java代碼中大量使用@符號作為註解標誌,註解用途多種多樣,但是基本都是做標記,用於源碼期,編譯期,或者運行期的特別處理。註解有自己的特定語法以及API。

Gradle是androidStudio中的項目構建框架,用於將android源碼工程整合編譯打包成apk。其中可以自定義gradle插件,也可以引用他人發布的gradle插件來給項目構建過程中加入自己想要的邏輯。

java反射,某些不方便直接使用的類或者方法,可以通過反射的方式使用。反射通常用在框架設計,hook技術中。

本文的重點是優雅地寫出權限申請的代碼,要讀懂本文自然不能對權限一無所知。

面向切面編程是代碼解耦的重要手段之一。更多信息且看下文。

3. 關於Android權限的梗

權限問題,自android問世以來就是一個梗,最早做android的那一批人,當時可以隨便獲取用戶信息,包括聯繫人,包括簡訊內容,包括通話記錄,可以說Android被人詬病的安全問題,源自於此。代碼層面,開發者只需要對照android官網權限說明,在manifest文件中聲明所需的權限,即可在代碼種訪問所需的數據。各類權限十分繁多,超過上百種權限,適用於各種不同場景,記不下來,一般也不用記。

需要的時候到官網https://developer.android.com/reference/android/Manifest.permission.html查找即可.

下面總結幾點Android發展歷史種,權限體系的重大變革。

Android 1.0 - Android 5.0/5.1 App開發者只需要在清單文件中聲明權限,安裝的時候就會自動授予。

Android 6.0 起 谷歌把所有的權限分為2類,普通權限,即 依然是只需要在清單文件中聲明即可。另一類,是危險權限,涉及到用戶隱私的權限,除了在清單文件種聲明之外,還需要在 App啟動之時動態申請,並且,谷歌還提供了 權限組權限的概念差別,把某一些功能類似的權限放在一個組別,當你去申請其中一個權限的時候,其實也是在默認申請該組的其他權限。雖然這種做法可以讓你少寫一個權限,但是谷歌依然建議把所需的權限寫完整,因為保不齊哪天谷歌就變更了權限組,到時候代碼出兼容問題,沒必要,而且把所需權限寫完整也是編程好習慣。

下圖是所有的危險權限以及權限組。

   1. STORAGE 權限組的兩個權限,READEXTERNALSTORAGE / WRITEETERNALSTORAGE 無需動態申請(但是依然要在清單文件中聲明), 因為Q系統啟用了沙盒機制,app訪問自己app所屬目錄無需任何權限,而如果是要訪問app所屬目錄之外的地方,就需要申請 READEXTERNALSTORAGE / WRITEETERNALSTORAGE這兩個權限。

   2. 如果設備在後臺運行時,需要使用 位置信息,需要動態申請權限,Q 引入了 ACCESSBACKGROUNDLOCATION 這個新權限,目的是限制後臺進程獲取悄悄的獲取用戶位置信息。如果此權限運行在Q以下(不含)的系統時,就會默認授予,但是Q及以上,則必須申請。

   3. 其他一些改動,詳見官網,https://developer.android.google.cn/about/versions/10/privacy/changes?hl=zh-tw.

4. 初級/中級/高級android開發的權限請求寫法

權限的梗其實就那麼一些,比較簡單。上面這些梗,我們需要 特別關注的只有一個,那就是6.0以後的動態權限申請。它的處理方式為:

主要流程轉化成代碼展示出來:

AndroidManifest.xml

<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/>

<uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"/>

Java 代碼

/**

* 申請權限

*/

protectedvoid requestPermission(String[] permissions, int requestCode) {

// 檢查已經有了這些權限

if(PermissionUtil.hasSelfPermissions(this, permissions)) {

Log.e(TAG, "Activity,requestPermission: 所有權限都已經有了,無需申請");

} else{

// 開始請求權限

ActivityCompat.requestPermissions(this, permissions, requestCode);

}

}

/**

* 處理回調

*

* @param requestCode

* @param permissions

* @param grantResults

*/

@Override

publicvoid onRequestPermissionsResult(int requestCode, @NonNullString[] permissions, @NonNullint[] grantResults) {

if(PermissionUtil.verifyPermissions(grantResults)) {//檢查是否都賦予了權限

granted(requestCode);

} else{

// 顯示提示

if(PermissionUtil.shouldShowRequestPermissionRationale(this, permissions)) {

//shouldShowRequestPermissionRationale 這個方法就是檢查,是不是權限被永久拒絕了。。。如果用就拒絕,這裡就返回false,只是第一次拒絕,那就返回true

// 取消權限

denied(requestCode);

} else{

// 權限被拒絕

deniedForever(requestCode);

}

}

}

上面申請權限 ActivityCompat.requestPermissions 和處理回調 onRequestPermissionsResult是開發者需要手動編碼的地方。

同樣是上面的邏輯, 初級/中級/高級開發者的處理方式截然不同。

一個完整的商業項目,勢必會涉及到非常多的 Activity, Fragment,以及 普通Java類等等 ,諸多地方需要使用到特定的權限,如果我們 ctrl+H全文搜索一下 onRequestPermissionsResult,發現如下場景:

同樣一份回調方法,居然在項目中出現了25次之多. 而且是權限申請這種和業務並不直接搭邊的代碼 還嵌入到業務代碼內部。OK,這裡就不多說了。

(PS: 其實這個就是我自己公司的代碼,我不知道為什麼會這樣....也許是公司人員更替太多,後人都懶得改架構)

PS:參考 https://github.com/18598925736/GracefulPermissionFramework/tree/dev

中級開發,作為有一定工作經驗的程式設計師,知道如何優化代碼,減少維護成本,那麼他很可能會發現,需要用到權限申請的地方,基本上是以Activity和Fragment,只要解決了這裡的代碼冗餘,他會這麼寫

publicabstractclassBaseActivityextendsAppCompatActivityimplementsIPermissionCallback{

protectedstaticfinalString TAG = "BaseActivity";

/**

* 申請權限

*/

protectedvoid requestPermission(String[] permissions, int requestCode) {

// 檢查已經有了這些權限

if(PermissionUtil.hasSelfPermissions(this, permissions)) {

Log.e(TAG, "Activity,requestPermission: 所有權限都已經有了,無需申請");

} else{

// 開始請求權限

ActivityCompat.requestPermissions(this, permissions, requestCode);

}

}

/**

* 請求回饋

*

* @param requestCode

* @param permissions

* @param grantResults

*/

@Override

publicvoid onRequestPermissionsResult(int requestCode, @NonNullString[] permissions, @NonNullint[] grantResults) {

if(PermissionUtil.verifyPermissions(grantResults)) {//檢查是否都賦予了權限

granted(requestCode);

} else{

// 顯示提示

if(PermissionUtil.shouldShowRequestPermissionRationale(this, permissions)) {

//shouldShowRequestPermissionRationale 這個方法就是檢查,是不是權限被永久拒絕了。。。如果用就拒絕,這裡就返回false,只是第一次拒絕,那就返回true

// 取消權限

denied(requestCode);

} else{

// 權限被拒絕

deniedForever(requestCode);

}

}

}

}

publicabstractclassBaseFragmentextendsFragmentimplementsIPermissionCallback{

protectedstaticfinalString TAG = "BaseFragment";

/**

* 申請權限

*/

protectedvoid requestPermission(String[] permissions, int requestCode) {

// 是否已經有了這些權限

if(PermissionUtil.hasSelfPermissions(getActivity(), permissions)) {

Log.e(TAG, "Activity,requestPermission: 所有權限都已經有了,無需申請");

} else{

// 開始請求權限

ActivityCompat.requestPermissions(getActivity(), permissions, requestCode);

}

}

/**

* 請求回饋

*

* @param requestCode

* @param permissions

* @param grantResults

*/

@Override

publicvoid onRequestPermissionsResult(int requestCode, @NonNullString[] permissions, @NonNullint[] grantResults) {

if(PermissionUtil.verifyPermissions(grantResults)) {//檢查是否都賦予了權限

granted(requestCode);

} else{

// 顯示提示

if(PermissionUtil.shouldShowRequestPermissionRationale(getActivity(), permissions)) {

//shouldShowRequestPermissionRationale 這個方法就是檢查,是不是權限被永久拒絕了。。。如果用就拒絕,這裡就返回false,只是第一次拒絕,那就返回true

// 取消權限

denied(requestCode);

} else{

// 權限被拒絕

deniedForever(requestCode);

}

}

}

}

然後使用同樣一個 IPermissionCallback接口來處理申請權限的可能結果(用戶同意,用戶拒絕,用戶永久拒絕)

/**

* 權限申請結果接口

*/

publicinterfaceIPermissionCallback{

/**

* 授予權限

*/

void granted(int requestCode);

/**

* 這次拒絕,但是並沒有勾選"以後不再提示"

*/

void denied(int requestCode);

/**

* 勾選"以後不再提示",並且拒絕

*/

void deniedForever(int requestCode);

}

但是, 我們需要權限申請的地方只有Activity和Fragment麼?

,還可能有:

Service : 比如啟動一個Service播放本地音樂,可能需要本地存儲權限,如果此時才來申請,那麼service應該如何申請權限?經過實驗,我發現Service沒有辦法去申請權限,因為 ActivityCompat.requestPermissions()方法的第一個參數是 Activity, 而在一個Service中,無法直接去獲得一個Activity對象。

普通Java類: 一個普通的Java工具類,他的作用是從手機內部存儲中讀寫文件,那麼他需要本地存儲權限, 它該如何申請?獲取你可以想出一點偏方來解決這個問題,但是如果停留在中級開發的層次,永遠無法給出一個優雅的解決方案。

詳細的解析下一章節再寫,先來看代碼效果:

Activity:

Fragment:

普通Java類:

**Service**:

觀察以上三張圖中代碼的相同點:

都利用了3個自定義註解: @PermissionNeed , @PermissionDenied, @PermissionCancel

@PermissionNeed 修飾修飾的是 用戶授予權限之後的 java方法

@PermissionDenied 註解修飾的是 用戶拒絕權限之後的 java方法

@PermissionCancel 註解修飾的是 用戶永久拒絕之後的 java方法

3個註解,在Activity,Fragment,Service 以及 普通Java類的使用方式完全相同,也可以說,高級開發/架構師的處理方式,把 Activity,Fragment,Service以及普通Java類 的差異化消除了,達成了 代碼調用的通用性, 從根本上解決了 動態權限申請在 業務代碼中的冗餘問題。

使用這種做法,再也不用擔心自己的業務代碼會和 權限相關的代碼發生交叉,讓業務代碼更加清晰。

下一篇開始講解Demo.

萬水千山總是情,點個在看行不行

相關焦點

  • 安卓架構師必備之Android AOP面向切面編程詳解,超實用!
    而且類似的還有網絡判斷,權限管理,Log日誌的統一管理這樣的問題。那麼,我們也沒有更優雅的方式來解決這一類的問題呢,答案是有的,煩請各位接著往下看。這就是今天為大家帶來的Android AOP(面向切面編程)詳解。接下來,我先為大家帶來AOP的一些基礎概念,再來講解具體的實現方式。什麼是AOPAOP是Aspect Oriented Programming的縮寫,即『面向切面編程』。
  • 詳解Spring框架的AOP機制
    AOP是Spring框架面向切面的編程思想,AOP採用一種稱為「橫切」的技術,將涉及多業務流程的通用功能抽取並單獨封裝,形成獨立的切面,在合適的時機將這些切面橫向切入到業務流程指定的位置中。本篇結合實際案例詳細講述AOP的原理及實現過程。通過本篇的學習,可以達成如下目標。
  • Spring框架IOC和AOP簡介
    1、什麼是框架通常是指為了實現某個業界標準或完成特定基本任務的軟體組件規範,也指為了實現某個軟體組件規範時,提供規範所要求之基本功能的軟體產品2、Spring是什麼Spring是一個開源框架,為了解決企業應用開發的複雜性二而創建的,但現在已經不止應用於企業應用
  • 如何理解 Spring AOP 以及使用 AspectJ?
    作者 | 阿文責編 | 屠敏在 Spring 中 AOP 是一個非常非常重要的概念,那麼什麼是AOP呢?AOP 即面向切面編程,也可以叫做面向方向編程,AOP不是一個新東西,它是OOP,即面向對象編程的一種補充,在當前已經成為一種成熟的編程方式。
  • 最新Android框架排行榜,上百項資源匯總!
    :layout_width="96dp"    android:layout_height="96dp"    android:src="@drawable/profile"    app:civ_border_width="2dp"    app:civ_border_color="#FF000000"/>一句話介紹:一款讓log日誌優雅顯示的框架上榜理由:logger
  • Java 第一大框架:Spring 的 IoC 跟 AOP 雛形如何實現?
    讓我們的Java開發更加簡潔、現代化、響應式編程、高性能高產、微服務。簡而言之Spring是Java目前「第一大框架」,Spring框架是由於軟體開發的複雜性而創建的。Spring使用的是基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限於伺服器端的開發。從簡單性、可測試性和鬆耦合性角度而言,絕大部分Java應用都可以從Spring中受益。
  • 一次Android權限刪除經歷
    劉望舒作者: 魔焰之https://juejin.im/post/5cb53e93e51d456e55623b071.
  • 最新Android框架排行榜,上百項資源匯總不容錯過
    >35.logger一句話介紹:一款讓log日誌優雅顯示的框架上榜理由:logger作為調試框架,並未給出很強大的能力,它最大的亮點是優雅的輸出log信息,並且支持多種格式:線程、Json、Xml、List、Map等,如果你整日沉迷於汪洋大海般的log信息不能自拔,logger就是你的指路明燈!
  • Android 必須知道2019年流行的框架庫及開發語言,看這一篇就夠了!
    Glide,是google員工在Picasso基礎上進行優化,總體比Picasso更優秀,在Google很多項目在用。Fresco,FaceBook的明星項目,也是去年最火的項目之一,匿名共享緩存等機制保證低端機表現極佳,但是原始碼基於C/C++。Universal-Image-Loader,早期廣泛被用的一個可重複使用的儀器為異步圖像加載、緩存、顯示。
  • 聊聊AOP
    「AOP是編程中常用的一種方式,以Spring為例我們經常使用spring-aop組件和aspectJ的註解或者配置文件來完成對接口或類中的 某些方法的執行的攔截
  • Spring AOP是什麼?你都拿它做什麼?
    我們知道Java是一個面向對象(OOP)的語言,但它有一些弊端,比如當我們需要為多個不具有繼承關係的對象引入一個公共行為,例如日誌、權限驗證、事務等功能時,只能在在每個對象裡引用公共行為。這樣做不便於維護,而且有大量重複代碼。AOP的出現彌補了OOP的這點不足。
  • spring AOP是什麼?你都拿它做什麼?
    回到正題,為什麼會有面向切面編程(AOP)?我們知道java是一個面向對象(OOP)的語言,但它有一些弊端,比如當我們需要為多個不具有繼承關係的對象引入一個公共行為,例如日誌,權限驗證,事務等功能時,只能在在每個對象裡引用公共行為,這樣做不便於維護,而且有大量重複代碼。AOP的出現彌補了OOP的這點不足。
  • 歷時幾個月,終於錄完Android binder,如釋重負
    了然後就是android文件系統了解它的啟動過程,並構造出來。已發布C++課程c++類的引入(試看)c++基礎知識_訪問控制c++基礎知識_程序結構c++基礎知識_重載_指針_引用c++基礎知識_構造函數c++基礎知識_靜態成員_友員c++基礎知識_運算符重載_成員函數c++基礎知識_運算符重載_類外函數
  • android手機app開發程式語言是什麼,自學難嗎?愛好者告訴你答案
    大家好,首先,小編我也是一名編程愛好者,有C語言編程基礎,和一些數據結構算法等基礎,隨著手機應用的崛起,也加入到了android編程的行列中來。做為一名android編程愛好者,水平初級,走過彎路,所以本文目的就是讓大家學習起來不走彎路。
  • Javaweb開發學習路線及Java三大框架分享
    熟悉jdk,jvm,eclipse,安裝於配置jdk2:熟悉並掌握java的基礎語法,類,抽象類,接口,內部類等概念3: java核心編程,如輸入輸出流,多線程,集合,XML,正則表達式等4:java圖形化編程,如awt,swing5:java
  • Android權限機制與適配經驗
    一、概要Android M已經發布一段時間了,市面上很多應用都已經適配Android M。舉個例子,讀sd卡和寫sd卡,這兩個權限通常都是成對聲明和使用的,因此,它們被分為一組,而且,只要我們獲取了這個權限組裡面的任意一個權限,就可以獲取整個權限組的權限。Google對於危險權限的定義和分組見下圖。權限相關API說明首先,在動態權限申請的流程中,開發者主要關注流程和API如下:1、檢查權限是否授予。
  • [JAVA] 85天 精通JAVAEE+Android 黑馬程式設計師JavaEE+Android培訓課程60G
    JavaScript基礎JavaScript編程,JavaScript語法、運算符、流程控制、函數、數組、對象、 JavaScript的內部對象,JavaScript中專用於操作對象的語句。第二階段:JavaWEB+JavaMail開發技術+網上銀行交易系統+網上在線支付JavaEE技術+項目案例JavaWEB開發核心基礎:XML的概念與基本作用、XML的基本語法、XML的約束模式、DTD、XMLSchema、名稱空間。
  • 手機上的安卓作業系統是什麼語言編程?免費開源?碼農給你解釋
    因此,面對龐大的用戶市場,很多軟體開發者,把目光投向了安卓應用開發上來,也有很多在校生或者準備投入這一行業的新生代,都在學習或者準備學習Android編程。今天,便給大家分享一下這方面的基礎知識。1,Android系統框架層次。Android系統內核是基於Linux系統開發而來的,權限高度自由,原始碼完全開放,是一種拓展性非常強的作業系統,免費開源,可定製,這也是迅速風靡全球的原因吧!主要使用對象是行動裝置,由Google和開放手機聯盟領導及開發。其內核程式語言是c/c++。
  • Android應用開發實戰:GPS與加速度傳感器
    在前一篇文章《編程實現谷歌Android攝像頭拍照》中,我們介紹了在增強現實技術(AR)引擎中Android SDK的兩種基本構件即攝像頭的用法。在本文中,我們將繼續介紹另外兩種基礎構件,即GPS與加速度傳感器。我們首先介紹所需的工具,然後講解如何請求位置更新,並說明加速度傳感器的工作機制。
  • 詳解Android 6.0運行時權限
    從Android 6.0開始,不再是安裝應用時用戶確定獲得全部的權限.而是在使用軟體過程中需要該權限時,彈出對話框讓用戶選擇權限.不僅如此,用戶選擇權限後還可以關閉。也就是說:用戶第一次點擊一個需要權限的地方,該方法返回false(因為用戶沒拒絕~),當用戶拒絕掉該權限,下次點擊此權限處,該方法會返回true。可在裡面進行對該權限的說明,然後彈出權限讓用戶選擇,並且對話框有don't ask again選項: