未充分利用的 Android 庫的全面指南
已發表: 2022-03-11任何有經驗的開發人員都會告訴您,他們最好的代碼不是他們編寫的代碼。 這是他們從別人的工作中獲取的代碼。
是的,我們的開發人員是創新的問題解決者,但我們遇到的許多問題已經得到解決——並且補救措施打包到任何人都可以使用的庫中。 當免費的輪子無處不在時,為什麼要重新發明輪子?
安卓也不例外。 代碼重用的最終來源是 Android SDK 本身,它帶有出色的構造和服務,可以為您完成大量工作。
但是,在 SDK 不足的地方,Android 社區創建了一些頂級庫,可以為您節省大量的編碼工作,並用經過高度調整、審查和測試的實現取而代之。 我不是在談論顯而易見的庫——Android 支持庫、Android 設計支持庫、Gson。 我指的是你可能不知道的工具。 即使你這樣做了,你可能還沒有使用它們。
多年來,我一直在開發、指導和領導 Android 團隊,並且研究和使用了數十種外部工具和庫。 (我什至知道閱讀他們的實現代碼並與開發人員討論他們的內部結構。)許多人在幫助我完成工作方面非常有效,但事實是,大多數人都沒有。
這就是我將本指南放在一起的原因。 依靠我的經驗,以及其他移動開發人員的經驗,以確保您使用的是最好的庫。 我選了七個。 我懷疑它們很快也會成為你的最愛。
選擇正確的 Android 庫
在選擇庫時,我會尋找四個關鍵特性:
- 它為實際且重要的問題提供了一致且高質量的解決方案。
- 它使用盡可能簡單的 API。
- 它不會強制對我的整體架構進行任何更改。
- 它擁有龐大的用戶群,最好還有一個活躍的開發者社區。
前三個功能是交易破壞者。 如果它們不存在,我繼續前進或開始手動編碼。
我在下面介紹的庫通過了所有四個測試。 它們還解決了移動開發中一些最具挑戰性的方面。
- 兩個用於依賴注入、佈局到 Java 綁定、模擬對象的庫。
- 應用內發布/訂閱消息模型。
- 安全、高效、自恢復的 HTTP 通信層。
- 圖像處理:下載、緩存、調整大小和加載到 RAM。
- 實時視頻流。
- 內存洩漏檢測。
ButterKnife:終極依賴注入工具
這是 Android 的終極依賴注入庫。 簡單、健壯、超快速(無反射!),並且能夠消除您應用程序的大量樣板代碼。
不再需要通過調用findViewById()
來直接綁定每個視圖; 相反,有一個帶註釋的視圖讓您可以直接訪問代碼。ButterKnife 還消除了對樣板 UI 事件(例如onClick
、 onTouch
等)的需求,並將它們替換為自動注入的代碼。
但是足夠的閒聊,讓我們看看代碼。
視圖字段綁定:
class MyButterKnifeActivity extends Activity { @BindView(R.id.name) TextView name; @BindView(R.id.address) TextView address; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // MUST BE CALLED BEFORE ACCESSING UI FIELDS name.setText(“etc etc”); } }
資源綁定:
class ExampleActivity extends Activity { @BindString(R.string.username) String username; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.bg_color) int bgColor; @BindDimen(R.dimen.lower_padding) Float lowerPadding; // and no need for getResources().getString()/getDrawable/getColor() }
UI 事件綁定:
@OnClick(R.id.my_button) public void clickHandler(View view) { // onClick logic goes here }
AndroidAnnotations:將依賴注入提升到新的水平
在依賴注入方面,AndroidAnnotations 與 ButterKnife 緊隨其後,它使用了一種稍微不同的方法:自動生成的類,一旦你掌握了它們,就會非常簡單。 更有用的是它允許你“基於名稱”的依賴注入。 例如@ViewById ListView myUserList;
指示庫為該字段分配同名的layoutListView
。
AndroidAnnotations 也非常快,但它以與 ButterKnife 不同的方式實現了這一點。 AndroidAnnotations 不是運行時綁定依賴注入,而是創建所有受影響活動的構建時間副本並將其連接邏輯推送到其中,從而使您可以獲得與手動編碼邏輯相同的性能。
但 AndroidAnnotations 的注入功能比這更進一步。 您可以將狀態和佈局注入到活動中。
AndroidAnnotations 實現:
@NoTitle @Fullscreen @EActivity(R.layout.my_layout) public class MyActivity extends Activity { @ViewById ListView customerList; // auto-binded to R.id.customerList @App MyApplication app; // auto-binded to app object @AminationRes Animation fadeoutAnimation; @UiThread void updateUI() { // main thread action } }
最後一個註釋需要更多解釋:多線程 Android 應用程序的一個常見任務是從後台(或工作)線程切換到前向(或主或 UI)線程,這是唯一允許訪問 UI 組件的線程. 這項任務雖然並不復雜,但通常是必需的,並且涉及一些混亂的編碼:
new Handler(Looper.getMainLooper()).post(new Runnable() { logic goes here } ); // NO ANNOTATIONS
在 AndroidAnnotations 中,您需要做的就是使用 @UiThread 註釋您的函數,並且現在保證始終執行:
@UiThread void updateUI() {..} // WITH ANNOTATIONS
請注意,此註釋適用於標準 Android 組件類(活動、服務等)。 但是當我也想註釋自己的類時會發生什麼?
在這裡,AndroidAnnotations 提出了一個新概念,即EBean
。 您所要做的就是使用@EBean
標記您的類,然後您就可以開始了:
@EBean public class MyNonComponentClass { @SystemService NotificationManager notifManager; @Bean MyOtherClass dependency; @UiThread void updateUI() { // main thread work goes here } }
EventBus:輕鬆實現跨組件通信
EventBus 庫將困擾 Android 開發人員多年的問題變成了公園散步。 跨組件通信從未如此簡單——使用簡單的發布/訂閱模型在系統的任何兩個部分之間進行通信。
您的後台輪詢服務不再需要知道您的片段來為它們提供更改事件。
EventBus 的使用很簡單。
一種。 創建事件類。 在這裡使用 POJO 是最好的:
class NewUserEvent { String fullname; String address; String role; // add getters and setters }
灣。 在類中創建事件處理方法——您希望訂閱這些事件的任何類:
class MySubscriber { @Subscribe public void newUserHandler(NewUserEvent event) { // handle NewUserEvent } @Subscribe public void newUserHandler(AnotherEvent event) { // handle AnotherEvent } }
但是,嘿,任何一個經驗不足的 Android 開發人員都會停下來問:這些處理程序的線程模型是什麼? 如果處理程序涉及 UI 組件訪問,我可以強制處理程序離開主線程嗎? 好問題…
默認情況下,所有處理程序方法都在工作線程上運行,該工作線程取自由 EventBus 本身分配和維護的線程池。 如果您需要在主線程上運行處理程序方法,請按如下方式擴展您的訂閱註釋:
@Subscribe(threadMode = ThreadMode.MAIN) public void runOnMainThreadHandler(AnotherEvent event) { … }
警告:不要過度使用此功能! 永遠不要在主線程上執行長時間運行的操作,即使是快速操作,也要小心。 壓倒主線程是讓你的應用程序緩慢、跳躍並且對用戶來說基本上不那麼有趣的最可靠方法。
C。 管理訂閱者類的 EventBus 註冊生命週期——即它何時連接以及何時斷開與總線的連接? 活動的合理註冊流程是:
class MySubscriberActivity extends Activity { @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); // START RECEIVING EVENTS HERE } @Override public void onStop() { EventBus.getDefault().unregister(this); // NO MORE EVENTS super.onStop(); } }
當然,以上只是一個例子。 您可以在您選擇的任何地方執行(取消)註冊。
d。 最後,實際觸發一個事件:
EventBus.getDefault().post(new MyEvent(“I'm here”));
有關使用 EventBus 的更多信息:多播事件(默認行為)、使用粘性事件、傳遞線程、優先級等等。 但以上內容足以讓您開始使用這種簡單但功能強大的技術。
OkHttp:Android 的 HttpClient on Steroids
Android 的 HttpClient 應該是這樣寫的。 非常簡單,非常聰明。 OkHttp 庫在內部負責重試循環、有效負載自動壓縮、Http/2 支持、連接池和響應緩存,因此您可以避免不必要的網絡訪問。
OkHttp 的使用很簡單。
發帖:
OkHttpClient client = new OkHttpClient(); MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, json_str); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string();
Http 獲取:
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(urls[0]) .build(); Response responses = client.newCall(request).execute(); String jsonData = responses.body().string();
OkHttp 還支持異步聯網、請求重定向路由查詢、本地緩存查詢等實用功能。 隨意在需要的地方使用它們。 大多數開發人員使用 OkHttp 作為 Android 默認 HTTP 客戶端 HttpURLConnection 的更智能替代品。 事實上,整個項目都是作為 HttpURLConnection 的私有分支開始的。
我喜歡它的健壯性——它立即添加到您的網絡層。
畢加索:谷歌也使用它是有充分理由的!
Picasso 是管理圖像下載、緩存、調整大小和裁剪的最簡單、最可靠的方法。

這個說法:
Picasso.with(context).load(url).resize(50,50).centerCrop().into(imageView)
將為您執行此操作:
- 連接到遠程 URL。
- 下載圖像。
- 將其存儲在本地 LRU 緩存中,它也會為您管理。
- 在將原始圖像加載到內存之前調整其大小。
- 在 Picasso 管理的線程池上運行上述所有操作。
- 使用調整大小的圖像來填充您的 imageView。
- 在任何未來運行之前,請檢查本地緩存以確保確實需要網絡往返。
構建上述任務集需要很多小時的工作,即使對於高級開發人員也是如此。 這假設你記住了一切。 如果您忘記了調整大小部分怎麼辦?
好吧,在一般的 Android 設備上,一個應用程序的 RAM 不超過 50 到 60 兆字節,大多數 Android 設備的像素到字節因子是 4。這意味著嘗試從 SD 卡加載 13 兆像素的圖像將需要 52 兆字節的 RAM。 換句話說,您的應用程序會立即崩潰。
這只是畢加索實力的一個例子。 在優化/調試媒體密集型遺留項目時,我做的第一件事就是將所有圖像加載切換到 Picasso。 您會驚訝於這一簡單步驟對應用程序質量的影響。
該庫的強大功能的最有力證明之一:過去兩年 Google 自己的許多 Android 代碼示例都使用 Picasso 進行圖像加載。
ActiveAndroid:ORM 無性能開銷
ORM 是對象關係映射的縮寫,在 J2EE 時代很流行。 它允許您將 POJO 存儲在數據庫中並從數據庫中檢索它們,而無需將它們轉換為單獨的字段。
有幫助嗎? 非常重要,因為它允許您編寫大部分應用程序而無需編寫任何 SQL 語句。
它也非常有效。 在過去,ORM 平台大量依賴反射,並且因速度慢而臭名昭著。 現代平台,包括 ActiveAndroid,速度要快得多,並且對於大多數實際需求,不會因原始 SQL 編碼而受到性能開銷的影響。
用法:
一種。 通過擴展自定義應用程序類在應用程序對像中初始化:
public class MyApplication extends extends com.activeandroid.app.Application { … }
灣。 創建為模型類派生的 POJO,其中包含您計劃存儲在數據庫中的每條記錄的類。 每個這樣的 POJO 都可以駐留在自己的表中。 應該使用註釋來為每個存儲的成員指定數據庫字段的名稱:
@Table(name = "Categories") public class UserDetails extends Model { @Column(name = "Name") public String name; @Column(name = "Address") public String address; @Column(name = "Age") public int age; }
如果您希望為成員設置索引,請使用以下註釋:
@Column(name = "ID", index = true) public String userID;
C。 為了防止庫迭代所有最經典的啟動時間(這是默認行為),強烈建議您在以下清單部分中指定所有模型類:
<meta-data android:name="AA_MODELS" android:value=“com.myapp.MyModelA, com.myapp.MyModelB" />
注意:未出現在此列表中的模型類將不會被 ActiveAndroid 識別。
d。 寫入數據庫:
UserDetails usr = new UserDetails(); usr.save(); // RUNS ON A BACKGROUND THREAD
如果需要多次寫入,更有效的方法是在單個事務中對它們進行批處理:
ActiveAndroid.beginTransaction(); try { for (UserDetails u: userList) item.save(); ActiveAndroid.setTransactionSuccessful(); } finally { ActiveAndroid.endTransaction(); }
e. 從數據庫中讀取 POJO:
new Select() .from(UserDetails.class) .where("name = ?", usr.getName()) .orderBy("Age") .executeSingle();
在我作為服務器端開發人員的日子裡,ORM 是必備工具。 它進入 Android 領域的時間有點晚。 但是,最後,它是:數據庫編程盡可能簡單。 好好享受。
LibStreaming:無痛視頻流
由於未記錄的 API、跨 SDK 版本差異、反射使用等,實時視頻流曾經是一個主要難題。
幸運的是,libStreaming 改變了這一切,它封裝了大部分複雜的流式傳輸,並公開了一個簡單友好的 API,允許您在幾個小時內編寫一個基本的流式傳輸應用程序。
要將其用於 H.264 和 AAC,您需要執行以下操作:
一種。 在您的主要活動的 onCreate 方法中初始化一個會話對象。 會話對象表示到對等點的媒體流:
protected void onCreate(Bundle savedInstanceState) { mSession = SessionBuilder.getInstance() .setCallback(this) .setSurfaceView(mSurfaceView) .setPreviewOrientation(90) .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_NONE) .setAudioQuality(new AudioQuality(16000, 32000)) .setVideoEncoder(SessionBuilder.VIDEO_H264) .setVideoQuality(new VideoQuality(320,240,20,500000)) .build(); mSurfaceView.getHolder().addCallback(this); }
灣。 實際開始會話:
mSession.setDestination(destination_server_url); mSession.start();
C。 完成後暫停會話:
mSession.stop();
現在,請不要誤會。 實時流本質上是混亂的,libStreaming 並沒有消除這種複雜性。 但是,在大多數情況下,它確實可以很好地隱藏它。 在某些情況下,您需要處理複雜性,例如在選擇對等信號策略、選擇相機編碼(您通常希望使用 MediaCodec/surface-to-buffer)或處理分組時。
不過,您會發現 libStreaming 背後的好人付出了更多努力,將這些複雜性平滑地合併到一個簡單易用的 API 中。
LibStreaming 支持 Android 應用程序使用的大多數編碼器,包括 H.264、H.263、AAC 和 AMR。
我用這個庫獲得了很好的結果。 一些最受歡迎的流媒體應用程序將其用作其基礎架構的一部分。 如果您遇到需要,我相信它會讓您的媒體流體驗更加流暢。
LeakCanary:檢測一行代碼中的內存洩漏
讓我們從這個庫背後的動機開始:內存洩漏。 Android 應用程序很容易受到它們的影響,尤其是在您不小心編碼的情況下。 實際上,創建內存洩漏非常簡單。 您需要做的就是將活動引用存儲在其上下文之外。 事實上,即使在其活動的上下文之外存儲對單個視圖對象的引用也會產生洩漏。
為什麼? 因為視圖(實際上是所有視圖)在內部存儲對其包含活動的上下文引用。 只要保留對視圖的引用,垃圾收集器就無法回收其包含的活動以及其中的內容,包括可繪製對象、視圖層次結構和資源。
保持對洩漏活動的引用作為靜態參數並不總是顯而易見的。 每當您在活動中創建內部類或生成線程時,都會創建對該活動的引用,並且在該內部類或線程完成之前,該活動可能不會被回收。
洩漏對單個資源密集型活動的引用有時足以使您的應用程序因“內存不足”異常而崩潰。
你怎麼能防止他們? 當然,從嚴格的編碼實踐開始。 但並非所有人都是經驗豐富的 Android 開發人員,即使是經驗豐富的開發人員有時也會忘記規則。
強調內存洩漏的定期代碼審查可能會有所幫助,但它們需要時間。 此外,一些洩漏是真正偷偷摸摸的,僅通過代碼審查很難檢測到。
隨著時間的推移,使用 DDMS 的內存工具是了解您的應用程序是否洩漏的好方法。 你絕對應該使用它。 但是,它不會告訴您導致洩漏的原因。
來了leakCanary來救援。 它是目前最好的內存洩漏檢測器,它為您的所有活動提供自動洩漏檢測——就像在一兩行代碼中一樣。
要使用它,只需使用您的應用程序對象onCreate()
初始化 leakCanary :
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); // more initialisations } }
你完成了。 LeakCanary 將監視內存洩漏,並在檢測到內存洩漏時發送通知。
LeakCanary 通過在所有活動中自動注入一個名為ActivityRefWatcher
的對象並在調用onDestroy()
後監控它們的引用計數來實現這一魔力。 已破壞活動的引用計數 > 0 只能表示洩漏。
重要提示:洩漏檢測僅適用於調試模式應用程序。 永遠不要在發布模式 APK 中測試洩漏(好吧,不是用 LeakCanary)。
但是,如果我想測試系統的其他部分是否有洩漏怎麼辦? 在這裡,LeakCanary 提供了一個名為 refWatcher 的對象,它實際上是初始化調用的返回值:
refWatcher = LeakCanary.install(this);
它可以用來觀察即將被回收的值。 更準確地說,我認為很快就會恢復的價值觀。 為此,請致電:
refWatcher.watch(my_soon_to_be_reclaimed_obj);
如果這個對像在 watch 調用後的短時間內沒有被釋放,庫會通知你。
我在任何地方都找不到這個“短時間”的價值,但它可能並不那麼重要。 使用leakCanary,一切正常。 無價。
概括
經驗豐富的開發人員使用這些庫將他們的編碼和調試階段縮短了數天甚至數週,因此您沒有理由不能這樣做。
總而言之,這是我選擇的 Android 庫可以為您做的事情:
ButterKnife – 自動注入代碼將幫助您消除應用程序的大部分樣板代碼。 這是 Android 的終極代碼注入。 需要我多說?
AndroidAnnotations – 使用超快的自動生成類和基於名稱的代碼注入來節省時間,而不會影響手動編碼邏輯的性能。
EventBus – 解耦組件以獲得更健壯的代碼,跨組件通信從未如此簡單。
OkHttp – HttpURLConnection 的巧妙替代品,支持異步網絡、請求重定向路由查詢、本地緩存查詢等。
Picasso – 簡化的圖像處理,非常好,現在被 Google 使用。 在媒體繁重的項目和某些遺留項目中,它可以節省大量時間。
ActiveAndroid – ORM 變得簡單,沒有性能開銷。
LibStreaming – 主要流媒體應用程序使用的實時視頻流。
這些是唯一值得您花時間的 Android 庫嗎? 當然不是。 但我向你保證:在你的下一個項目中使用它們中的任何一個都會讓你成為一個更好的開發者。 如果您想查看它們的實際效果,請查看我的 GitHub。
如果您已經在使用其中的部分或全部,或者您正在使用替代庫,我敦促您在下面的評論中分享您的經驗。