編輯:Android資訊
以前一直想寫一篇總結 Android 開發經驗的文章,估計當時的我還達不到某種水平,所以思路跟不上,下筆又捉襟見肘。近日,思路較為明朗,於是重新操起鍵盤開始碼字一番。先聲明一下哈,本人不是大廠的程序猿。去年畢業前,就一直在當前創業小團隊從事自己熱愛的打碼事業至今。下面總結是建立在我當前的技術水平和認知上寫的,如有不同看法歡迎留下評論互相交流。
目前 Android 平台上絕大部分開發都是用著 Java ,而跟 Java 這樣一門面向對象的語言打交道,不免要觸碰到 抽象 和 封裝 的概念。我身邊接觸過的一些開發者,有一部分還對這些概念停留在寫一個抽象類、接口、或者一個方法(或抽象方法)。至於為什麼,我不大清楚是他們表達不出來,還是不理解。下面我也不高談闊論,直接舉例子來解釋我所理解的抽象。
//Activity 間使用 Intent 傳遞數據的兩種寫法 下面均是偽代碼形式,請忽略一些細節 //寫法一 //SrcActivity 傳遞數據給 DestActivity Intent intent = new Intent(this,DestActivity.class); intent.putExtra("param", "clock"); SrcActivity.startActivity(intent); //DestActivity 獲取 SrcActivity 傳遞過來的數據 String param = getIntent.getStringExtra("param"); //寫法二 //SrcActivity 傳遞數據給 DestActivity Intent intent = new Intent(this,DestActivity.class); intent.putExtra(DestActivity.EXTRA_PARAM, "clock"); SrcActivity.startActivity(intent); //DestActivity 獲取 SrcActivity 傳遞過來的數據 public final static String EXTRA_PARAM = "param"; String param = getIntent.getStringExtra(EXTRA_PARAM);
寫法一,存在的問題是,如果 SrcActivity 和 DestActivity 哪個把 “param” 打錯成 “para” 或者 “paran” ,傳遞的數據都無法成功接收到。而寫法二則不會出現此類問題,因為兩個 Activity 之間傳遞數據只需要知道 EXTRA_PARAM 變量即可,至於 EXTRA_PARAM 變量到底是 “param” 、 “para” 、”paran” 這一點並不需要關心,這就是一種對可能發生變化的地方進行抽象封裝的體現,它所帶來的好處就是降低手抖出錯的概率,同時方便我們進行修改。
基於抽象和封裝,Java 本身很多 API 在設計上就有這樣的體現,如 Collections 中的很多排序方法:
Collections中的排序API
這些方法都是基於 List 這個抽象的列表接口進行排序,至於這是一個用什麼樣的數據結構實現 List(ArrayList 還是 LinkedList),排序方法本身並不關心。看,是不是體現了 JDK 的設計人員的一種抽象編程的思維,因為 List 的具體實現可能有千萬種,如果每一類 List 都要寫一套排序方法,估計要哭瞎了。
小結:把容易出現變化的部分進行抽象,就是對變化的一種封裝。
一個項目的開發,我們不可能一切從0做起,如果真是這樣,那同樣要哭瞎。因此,善於借用已經做好的 “車輪” 非常重要,如:
一般情況下,我在選擇是否引入一些開源框架主要基於以下幾個因素:
針對不開源SDK的選擇,也主要基於以下幾點去考慮:
接入友盟的App
小結:選好 “車輪” ,事半功倍
為什麼要抽象依賴於第三方框架呢?這裡和第1點是互相照應的,就是降低我們對具體某個框架的依賴性,從而方便我們快速切換到不同的框架去。說到這裡,你可能覺得很抽象,那我直接舉一個加載圖片的例子好了。
假設你當前為項目引入一個加載圖片的框架 —— Android-Universal-Image-Loader,最簡單的做法就是加入相應的依賴包後,在任何需要加載圖片的地方寫上下面這樣的代碼段。
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance // Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view // which implements ImageAware interface) imageLoader.displayImage(imageUri, imageView); // Load image, decode it to Bitmap and return Bitmap to callback imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Do whatever you want with Bitmap } });
這種做法最簡單粗暴,但是帶來的問題也最嚴重的。如果我有幾十上百個地方都這麼寫,而在某一天,我聽說Facebook出了個神器 Fresco,想要換掉 Android-Universal-Image-Loader ,你就會發現你需要喪心病狂的去改動幾十上百個地方的代碼,不僅工作量大,而且還容易出錯。造成這樣的原因,就在於項目和加載圖片的框架之間形成了強耦合,而實際上,項目本身不應該知道我具體用了哪個加載圖片的框架。
正確的方式,應該是對框架做一個抽象的封裝,以應對未來發生的變化,我直接舉自己的開源項目 AndroidAlbum 中的一種封裝做法好了。
AndroidAlbum
大致代碼如下:
//1、聲明 ImageLoaderWrapper 接口,定義一些抽象的加載接口方法 public interface ImageLoaderWrapper { /** * 顯示 圖片 * * @param imageView 顯示圖片的ImageView * @param imageFile 圖片文件 * @param option 顯示參數設置 */ public void displayImage(ImageView imageView, File imageFile, DisplayOption option); /** * 顯示圖片 * * @param imageView 顯示圖片的ImageView * @param imageUrl 圖片資源的URL * @param option 顯示參數設置 */ public void displayImage(ImageView imageView, String imageUrl, DisplayOption option); /** * 圖片加載參數 */ public static class DisplayOption { /** * 加載中的資源id */ public int loadingResId; /** * 加載失敗的資源id */ public int loadErrorResId; } } // 2、將 UniversalAndroidImageLoader 封裝成繼承 ImageLoaderWrapper 接口的 UniversalAndroidImageLoader , //這裡代碼有點長,感興趣可以查看項目源碼中的實現 https://github.com/D-clock/AndroidAlbum // 3、做一個ImageLoaderFactory public class ImageLoaderFactory { private static ImageLoaderWrapper sInstance; private ImageLoaderFactory() { } /** * 獲取圖片加載器 * * @return */ public static ImageLoaderWrapper getLoader() { if (sInstance == null) { synchronized (ImageLoaderFactory.class) { if (sInstance == null) { sInstance = new UniversalAndroidImageLoader();//<link>https://github.com/nostra13/Android-Universal-Image-Loader</link> } } } return sInstance; } } //4、在所有需要加載圖片的地方作如下的調用 ImageLoaderWrapper loaderWrapper = ImageLoaderFactory.getLoader(); ImageLoaderWrapper.DisplayOption displayOption = new ImageLoaderWrapper.DisplayOption(); displayOption.loadingResId = R.mipmap.img_default; displayOption.loadErrorResId = R.mipmap.img_error; loaderWrapper.displayImage(imagview, url, displayOption);
這樣一來,切換框架所帶來的代價就會變得很小,這就是不直接依賴於框架所帶來的好處。當然,以上只是我比較簡單的封裝,你也可以進行更加細致的處理。
小結:預留變更,不強耦合於第三方框架
說實話,在沒接觸 MVP 的架構之前,一直都是使用 MVC 的模式進行開發。而隨著項目越來越大,Activity或者 Fragment裡面代碼越來越臃腫,看的時候想吐,改的時候想屎…這裡撇開其他各種各樣的架構不談,只對比MVC 和 MVP 。
你會發現,如果 View 層只包含了xml文件,那我們 Android 項目中對 View 層可做操作的程度並不大,頂多就是用include復用一下布局。而 Activity 等簡直就是一個奇葩,它雖然歸屬於 Controller 層,但實際上也干著 View 層的活(View 的初始化和相關操作都是在Activity中)。就是這種既是 View 又是 Controller 的結構,違背了單一責任原則,也使得 Activity 等出現了上述的臃腫問題。
相比 MVC,MVP在層次劃分上更加清晰了,不會出現一人身兼二職的情況(有些單元測試的童鞋,會發現單元測試用例更好寫了)。在此處你可以看到 View 和 Model 之間是互不知道對方存在的,這樣應對變更的好處更大,很多時候都是 View 層的變化,而 Model 層發生的變化會相對較少,遵循 MVP 的結構開發後,改起來代碼來也沒那麼蛋疼。
這裡也有地方需要注意,因為大量的交互操作集中在 Presenter 層中,所以需要把握好 Presenter 的粒度,一個 Activity 可以持有多個 View 和 Presenter,這樣也就可以避開一個碩大的 View 和 Presenter 的問題了。
推薦兩個不錯的 MVP 架構的項目給大家,還不明白的童鞋,可以自行體會一下其設計思想:
https://github.com/pedrovgs/EffectiveAndroidUI
https://github.com/antoniolg/androidmvp
小結:去加以實踐的理解 MVP 吧
把一些常用的工具類或業務流程代碼進行歸類整理,加入自己的代碼庫(還沒有自己個人代碼倉庫的童鞋可以考慮建一個了)。如加解密、拍照、裁剪圖片、獲取系統所有圖片的路徑、自定義的控件或動畫以及其其他他一些常用的工具類等。歸檔有助於提高你的開發效率,在遇到新項目的時候隨手即可引入使用。如果你想要更好的維護自己的代碼庫,不妨在不洩露公司機密的前提下,把這個私人代碼庫加上詳細文檔給開源出去。 這樣能夠吸引更多開發者來使用這些代碼,也可以獲得相應的bug反饋,以便於著手定位修復問題,增強這個倉庫代碼的穩定性。
小結:合理歸檔代碼,可以的話,加以開源維護
關於性能優化的問題,大體都還是關注那幾個方面:內存、CPU、耗電、卡頓、渲染、進程存活率等。對於這些地方的性能優化思路和分析方法,網絡上已經有很多答案了,此處不做贅述。我只想說以下幾點:
小結:合理優化,數據量化
Rxjava、React Native、Kotlin…開始興起後,身邊有很多開發者會跟風直上。學習新技術的精神是非常值得鼓勵的,但沒有經過一段時間實踐觀察,就擅自把新技術引入到商業項目中,則有失妥當。對於大公司的團隊來說,會有專門團隊或項目去研究這些新興技術,以確定是否在自己的產品線開發中引入。但作為小公司,是不是就意味著沒有實踐嘗試新技術的機會呢?並不是!個人有以下幾點建議:
小結:空談誤國,實干興邦
UML,馴服代碼和了解項目結構的利器,本人也在學習和體驗其好處的路途上。不管遇到大小項目,有了它,可以更好的理清一些脈絡結構。對付舊的龐大項目代碼,或者有志閱讀某些開源項目代碼的開發者,絕對是居家必備。
小結:工欲善其事,必先利其器
前面 2 提到,項目不可能從0開始,是需要引入很多第三方框架的。這裡並不與 2 互相違背,而是建議有想提高技術逼格的開發者,可以在空暇時間去編碼實現一個框架。如果你對網絡訪問、圖片加載方面很有研究見解,不妨把這些腦海裡的思想落實成具體的代碼。也許你會發現,你動手去實踐的時候,考慮的東西會多得多,自己最終得到的也會更多。(特別建議那些看過很多開源代碼,又至今未自己動手自撸一發的)
小結:不要停留在 api 調用的層面
有空又經濟能力承受得起的時候,不妨去參加一些自己感興趣的技術交流會。很多都有大牛上台演講,聽聽人家的解決方案,拓寬一下自己看問題的思路,也可以多參加一些含金量高的線上活動。我有挺多開發者朋友,就是參加活動的時候認識的,有時候遇到一些技術問題,還會互相探討交換一下解決思路。挺贊的!
小結:拓寬技術視野
這個可能沒什麼好說的,大家看了標題就懂了。它最大的好處在於:
小結:認真總結,不斷完善
程序猿不要老是對著電腦,趕緊找個對象提升一下幸福感。據說幸福感高的程序猿,編碼效率高,出bug幾率小…
總結:做個面向對象的程序員
大概就想到這些了,以後要是再有想寫的,另開新篇。絮絮叨叨寫了這麼多,最關鍵的還是自己要落實,千萬不要聽說過太多道理,卻依然過不好這一生哈!
Android 程序中實現Tab類型界面很常見,本人在做項目的時候也經常用到,所以想在這裡總結一下,實現tab類型界面的幾種方式,供大家參考。如有不對之處,歡迎大
本篇博客介紹如何使用Eclipse來創建一個Java Web程序,為後面講通過Android客戶端跟服務端進行交互打下基礎,關於服務端可以選用的程序很多,主流的搭
數據存儲在開發中是使用最頻繁的,在這裡主要介紹Android平台中實現數據存儲的5種方式,更加系統詳細的介紹了5種存儲的方法和異同。 第一種: 使用SharedP
LinearLayout是在線性方向顯示View元素的一個ViewGroup,可以是水平方向,也可以是垂直方向 你可以重復使用LinearLayout,如果你想使