Android需要精通的技能總結

轉載自知乎的回答,現下關於Android應該掌握的知識總結,內容精彩,情不自禁轉發。
原文地址:在2016年,Android 程序員應該如何選擇? - 回答作者:田元 http://zhihu.com/question/39274138/answer/89418429?utm_campaign=webshare&utm_source=weibo&utm_medium=zhihu

不知道你是不是指的純技術方面的準備,如果是的話,我就提供一些拙見,大部分算是一些開發知識死角或者tips吧,權當拋磚引玉了:)
下面的回答建立在JAVA基礎(看着《JAVA核心技術 vol1》目錄能梳理一遍JAVA常見知識點)和Android基礎都過關的情況下。
一、JAVA SE
1、JAVA標準容器
可能受一些網上流傳的各種demo的影響,大多數Android開發者最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList三劍客,但是要知道ArrayList還有兩個親戚,一個是近親CopyOnWriteArrayList,另外是遠房LinkedList、CurrentHashMap。CopyOnWriteArrayList的效率比ArrayList略有下降,空間利用率也下降了很多,但是CopyOnWriteArrayList是線程安全的,CopyOnWriteArrayList和ArrayList對尾的操作都爲O(1),但是其他位置的刪除,插入操作很增加很大的時間複雜度,涉及到一次內存搬移過程,不過random access效率很高;LinkedList的隨即插入和刪除性能很高。
tips:數組複製,請使用System.arrayCopy或Arrays.copyOf 實現,且在JAVA中後者基於前者實現。
2、JAVA併發
1、ThreadPoolExecutor,JAVA併發的核心線程池框架,不過它的構造函數非常複雜:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
所以有一個方便我們使用的工廠類,Executors,可以創建4種類型的ThreadPool:
固定線程數量的線程池:Executors.newFixedThreadPool(int size);
單線程異步隊列:Executors.newSingleThreadExecutor();
週期性調度:Executors.newSingleThreadScheduledExecutor();
多線程週期性調度:Executors.newScheduledThreadPool(1);
說到ThreadPoolExecutor的構造函數,它的最後一個參數BlockingQueue來自於包java.util.concurrent,ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue和這個包裏面其他的類也要過一遍。
workQueue一定要用有界隊列!設想一個極端情景,假設每個TaskWork都運行10s(更確切的說,IO等待10s),而ThreadPool在10s內接收到了1000個這樣的TaskWork,如果我們使用了無界隊列,隊列的大小必然會急速增長直至進程Crash,但是如果我們使用了有界隊列,假設隊列長度爲128,當TaskWork超過128,我們會有另外的線程幫忙處理,那系統的負載就可能降下來了。
上面說到了任務池的創建,那裏面的任務是什麼?從哪兒來呢?裏面的任務是FutureTask,構造函數的參數是一個Callable對象,而我們真正的任務就在Callable對象裏面的call方法,FuterTask執行實際任務後會在主線程調用done函數,最後通過ExecutorService的submit方法將FutureTask提交到任務池。發一段僞代碼:
public class SomeCallable implements Callable {

public SomeCallable() {
}

@Override
public String call() throws Exception {
    // some heavy work consting amounts of time
    return "result";
}

}
FutureTask futureTask = new FutureTask(new SomeCallable()) {
@Override
protected void done() {

      String result = get();
       //dosth to result
    }
};
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(futureTask);

2、一個線程sleep的小坑,我相信很多朋友都寫過這樣的代碼:
public void run() {
try{
Thread.sleep(1000);
} catch (InterruptedException ie){
ie.printStackTrace();
}
}
但是設想一下,如果我們的線程在執行sleep之前就被interrupt了呢,別以爲不可能,ThreadPoolExecutor框架就是通過對所有的Thread進行interrupt來取消所有線程,這是我們上述代碼就會拋出異常。所以良好的實踐應該是:
public void run() {

while (!Thread.currentThread().isInterrupted()) {  
    try{  
        Thread.sleep(1000);  
    } catch (InterruptedException ie){  
        ie.printStackTrace();  
    }  
}  

}
3、會用wait/notify來實現最簡單的生產者-消費者模式。生產者/消費者問題的多種Java實現方式
4、ThreadLocal變量的理解/定義。ThreadLocal在Android中的應用,最典型的應用就是在android的messengeQueue-Looper模型中,Handler如何找到當前線程的Looper呢?我們平常直接在UI線程中new Handler()就可以了,裏面就是mainLooper,但是Android怎麼確定的UIx線程中new Handler()裏面是mainLooper呢,答案就是通過將Looper作爲ThreadLocal變量。以及ThreadLocal基本的實現原理(在線程對象裏面有一個inner static class)。
5、JAVA的heap/stack的理解,可能會和多線程一起考察,blogspot.com 的頁面
6、ConcurrentHashMap的實現,blogspot.com 的頁面
7、 JAVA的Reflection,Android內部很多東西都是基於Reflection實現,比如我們經常用的屬性動畫,就是通過屬性名稱找到屬性的getter方法名,進而通過反射調用。不過對於日常開發的話,Reflection有兩個作用,一是在一些情形下(特別是對屬性的操作,包括更改、比較,比如我們可以定義一個通用的Collections.sort)可以提高代碼複用度,二是可以做一些比較hack的事情,比如使用一些internal class,內部的AIDL等,修改一些internal類的static value。
二、Android
1、預防內存泄漏!擅用WeakReference!
所有從類外部傳來的對象(特別對於Context,View,Fragmet,Activity對象),如果要將其放進類內部的容器對象或者靜態類中引用,請一直用WeakReference包裝!比如在TabLayout的源碼中,在TabLayoutOnPageChangeListener中,就爲TabLayout做了WeakReference wrap。

2、Android IPC,Binder的理解,理解Binder的引用和實體,知道所謂的客戶端和服務端分別代表什麼意思,懂得ServiceManger對每個Service註冊和根據服務名來getService的基本原理,這些沒多少坑,但是非常龐雜,建議閱讀《深入理解Android vol1》Chapter 6,深入理解Android (豆瓣)。
會寫AIDL,會用Messenger誇進程傳遞信息,具體的實踐:Android實戰技術:IPC方式簡介教程。
3、Activity的簡要繪製(創建)過程 ,Activity本質是爲了Window(PhoneWindow)服務,
onAttach:建立mwindow對象->setContentView:創建DecorView,在DecorView中根據Activity的風格來 創建Title(ActionBar),TitleContent(ActionBar下面的內容,內部資源id爲android:id=”@android:id/content”)->mWindow.addView(mDecorView),將創建好的DecorView添加進window,addview時創建一個RootView,也就是我們的R.layout.activity_main的母佈局,然後對子控件遞歸遍歷發送繪圖消息,子控件收到消息後 執行onMeasure->onLayout->onDraw,這時,我們就可以得到各控件的尺寸信息。
4、 在onCreate中獲得視圖的尺寸信息,注意到我們上面說,母控件通過遍歷向子控件發送dispatchView信息來使子控件繪圖,當然,我們也可以在(setContentView)這之後在子控件上post一個Runnable到控件的runnable隊列中去,在其中自然可以獲得正確的尺寸。
5、關於View的點點滴滴。
基礎:自定義單一View,換句話說,叫自定義UI,就是僅僅在OnDraw裏面做了文章,比如儀表盤,圓形頭像,自定義屬性
->中級:自定義View與其他系統Wdiget協同工作,比如繼承LinearLayout寫一個自定義的TabLayout來與ViewPager協同工作,處理事件(滑動、點擊、屏幕手勢)分發,處理滑動事件衝突,這個階段纔可以被稱之爲自定義Widget
->進階:突破Activity,直接向Window添加、刪除、更新View,理解WindowManager僅僅是個引用,真正的工作在WindowManagerService裏面完成。
6、圖片加載框架,熟悉並實踐過基本的緩存算法LruCache、DiskLruCache,對Bitmap重採樣以降低OOM的機率,熟讀一款中規中矩的圖片加載框架如Universal Image Loader的源碼,並能將各種策略總結。
7、SurfaceView的實踐,SurfaceView最常見的一個使用情境是在我們的界面之上繪製各種動畫,但是有一個問題,在佈局發生改變時,SurfaceView會出現部分屏幕變黑的情況,包括但不限於在ViewPager、DrawerActivity中使用SurfaceView,需要知道這個問題的解決方案。
7、熟練使用ContentProvider,並懂得大概原理(很慚愧,我對Provider沒做過深入理解)
8、Activity的幾種FLAG和LauchMode分別代表的意義,以及使用場景。
9、知道65535 dex-merge問題是怎麼回事
10、熟悉使用Android開發者選項功能,比如最常用的 佈局邊界顯示、過度繪製檢測、UI繪製速度檢測、Surface更新時閃爍、嚴格模式、CPU利用率展示、不保持Activity。
三、拓展(上面這些都真正透了,就沒有大的漏洞了)
1、事件流編程,EventBus、RxJava。
2、Kotlin語言。
3、DataBinding,Google的這個MVVM框架實現的很完全,很強大,包括像Angular中自定義directive/filter的類似feature。
4、知道Volley,okHttp他們的應用場景。
5、熟悉一些ORM,包括現在新出的非基於SqlLite的數據庫:Realm,Realm is a mobile database: a replacement for SQLite & Core Data
6、Android對Vector Icon的支持
7、Android L新特性,Palette、CoordinatorLayout自定義Behavior等

作者:田元
鏈接:https://www.zhihu.com/question/39274138/answer/89418429
來源:知乎
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章