1. 程式人生 > >Android 開發過程中平時遇到的一些問題及總結

Android 開發過程中平時遇到的一些問題及總結

相信大家都有面試的經歷,相對比面試官的問的一些問題其實都是基礎的知識,但就是一些基礎的知識我們也不是很完美的回答出來,我們也知道現在的開發人員很多,一家公司一個崗位就會有很多的開發者投遞,在那麼多開發者中你如何讓面試官很深的認識你,給面試官一個很深的印象,能讓他在技術水平差不多的情況的下第一個想起的是你。
從這篇文章對整個面試中所問到的問題進行梳理,查缺補漏。

JAVA

String,StringBuffer,StringBuilder區別?
一般回答是這樣的:
String 字串常量
StringBuffer 字串變數(執行緒安全)
StringBuilder 字串變數(非執行緒安全)
面試的時候千萬不要這麼說,這樣說會讓面試官你還只停留在會用的層面,不知道原理,你應該這麼說:

String,StringBuffer,StringBuilder最終底層儲存與操作的都是char陣列,但是String裡面的char陣列是final的,而StringBuffer,StringBuilder不是,也就是說,String是不可變的,想要新的字串只能重新生成String;而StringBuffer和StringBuilder只需要修改底層的char陣列就行,相對來說,開銷要小很多,String的大多數方法都是重新new一個新String物件返回,頻繁重新生成容易生成很多垃圾。

為什麼StringBuffer 是執行緒安全的?
StringBuffer是執行緒安全的,StringBuilder是執行緒不安全的,因為StringBuffer的方法是加了synchronized鎖起來了的,而StringBuilder沒有

增刪比較多時用StringBuffer或StringBuilder(注意單執行緒與多執行緒)。

Serializable與Parcelable的區別?
1)Serializable介面是javaSE的,而Parcelable介面是Android特有的
2)Serializable介面實現簡單,Parcelable略顯複雜
3)Parcelable儲存到磁碟上,Parcelable儲存到記憶體中

HashMap的實現機制?
HashMap實際上是一個“連結串列雜湊”的資料結構,即陣列和連結串列的結合體。HashMap底層就是一個數組結構,陣列中的每一項又是一個連結串列。當新建一個HashMap的時候,就會初始化一個數組。

/**
* The table, resized as necessary. Length MUST Always be a power of two.
 */
transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    final int hash;
    ……
}

可以看出,Entry就是陣列中的元素,每個 Map.Entry 其實就是一個key-value對,它持有一個指向下一個元素的引用,這就構成了連結串列。

如何執行緒安全的使用HashMap?
無非就三種實現方式:
1)Hashtable
2)ConcurrentHashMap
3)Synchronized Map

//Hashtable
Map<String, String> hashtable = new Hashtable<>();

//synchronizedMap
Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());

//ConcurrentHashMap
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

HashTable原始碼中是使用synchronized來保證執行緒安全的

public synchronized V get(Object key) {
       // 省略實現
    }
public synchronized V put(K key, V value) {
    // 省略實現
    }

所以當一個執行緒訪問HashTable的同步方法時,其他執行緒如果也要訪問同步方法,會被阻塞住。舉個例子,當一個執行緒使用put方法時,另一個執行緒不但不可以使用put方法,連get方法都不可以,好霸道啊!!!so~~,效率很低,現在基本不會選擇它了。

ConcurrentHashMap(以下簡稱CHM)是JUC包中的一個類,Spring的原始碼中有很多使用CHM的地方。CHM的一些重要特性和什麼情況下應該使用CHM。需要注意的是,上面部落格是基於Java 7的,和8有區別,在8中CHM摒棄了Segment(鎖段)的概念,而是啟用了一種全新的方式實現,利用CAS演算法。

// synchronizedMap方法
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
       return new SynchronizedMap<>(m);
   }
// SynchronizedMap類
private static class SynchronizedMap<K,V>
       implements Map<K,V>, Serializable {
       private static final long serialVersionUID = 1978198479659022715L;

       private final Map<K,V> m;     // Backing Map
       final Object      mutex;        // Object on which to synchronize

       SynchronizedMap(Map<K,V> m) {
           this.m = Objects.requireNonNull(m);
           mutex = this;
       }

       SynchronizedMap(Map<K,V> m, Object mutex) {
           this.m = m;
           this.mutex = mutex;
       }

       public int size() {
           synchronized (mutex) {return m.size();}
       }
       public boolean isEmpty() {
           synchronized (mutex) {return m.isEmpty();}
       }
       public boolean containsKey(Object key) {
           synchronized (mutex) {return m.containsKey(key);}
       }
       public boolean containsValue(Object value) {
           synchronized (mutex) {return m.containsValue(value);}
       }
       public V get(Object key) {
           synchronized (mutex) {return m.get(key);}
       }

       public V put(K key, V value) {
           synchronized (mutex) {return m.put(key, value);}
       }
       public V remove(Object key) {
           synchronized (mutex) {return m.remove(key);}
       }
       // 省略其他方法
   }

synchronizedMap從原始碼中可以看出呼叫synchronizedMap()方法後會返回一個SynchronizedMap類的物件,而在SynchronizedMap類中使用了synchronized同步關鍵字來保證對Map的操作是執行緒安全的。

HasMap為什麼會執行緒不安全?
個人覺得HashMap在併發時可能出現的問題主要是兩方面,
首先如果多個執行緒同時使用put方法新增元素,而且假設正好存在兩個put的key發生了碰撞(hash值一樣),那麼根據HashMap的實現,這兩個key會新增到陣列的同一個位置,這樣最終就會發生其中一個執行緒的put的資料被覆蓋。

第二就是如果多個執行緒同時檢測到元素個數超過陣列大小*loadFactor,這樣就會發生多個執行緒同時對Node陣列進行擴容,都在重新計算元素位置以及複製資料,但是最終只有一個執行緒擴容後的陣列會賦給table,也就是說其他執行緒的都會丟失,並且各自執行緒put的資料也丟失。

HashMap出現死迴圈的原因?
因為多執行緒會導致HashMap的Entry節點形成環鏈,這樣當遍歷集合時Entry的next節點用於不為空,從而形成死迴圈

Activity:

當面試官問你什麼Activity,你是不是會覺得一陣懵* ?

在日常應用中,Android是使用者互動的介面,他提供了一個介面,讓使用者進行點選,各種滑動操作等。

Activity的四種狀態:
running / paused / stopped / killed
running:點選螢幕,螢幕做出響應,處於activity棧頂的狀態
paused:失去焦點不能進行操作,或者是一個透明的在棧頂的Activity,注意並不是被銷燬了啊,它的成員資訊和變數都還在,當然還有另外一種狀態,當記憶體緊張的時候會北迴收掉
stopped:當一個Activity被另外一個Activity完全覆蓋的時候,被覆蓋的那個處於stopped狀態,不可見,成員資訊和變數都還在,如果記憶體緊張也是會被回收的
killed:已經被系統回收

生命週期:
啟動:onCreate >> onStart >> onResume
點選HOME鍵回到主介面:onPause >> onStop
再次回到遠Activity:onResrart>>onStart >> onResume
退出當前Activity:onPause >> onStop >> onDestroy

程序優先順序:
前臺 / 可見 /服務 /後臺 / 空
前臺:正在進行互動的Activity或者與前臺activity繫結的services
可見:一個activity可見但並處於前臺,不能點選
服務:在後臺開啟的服務
後臺:當一個Activity被點選home鍵,退居後臺,沒有=被回收
空 :不屬於前面四種程序的任意一種,處於快取的目的而保留

啟動模式:
standard / singletop / singletask / singleinstance

如何配置Activity的啟動模式?
直接在AndroidManifest.xml配置的android:launchMode屬性為以上四種之一即可

standrd:標準模式,每次啟動都會重新建立一個activity例項加入到任務棧中,不會考慮是不是有此例項存在,不會複用,消耗記憶體資源

singletop :棧頂複用模式,只檢測任務棧棧頂,只有在棧頂的Activity不會被建立,就算是在第二位也是會被建立

singletask:棧內複用模式,也是一個單例模式,檢測整個任務棧,如果有並把在之上的例項全部移除掉,回掉onNewIntent方法

singleinstance:一個activity如果在整個系統中只存在一個例項,而且這個activity獨享整個任務棧

應用場景:
singleTop:適合接收通知啟動的內容顯示頁面。例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都開啟一個新聞內容頁面是很煩人,另外,singleTop啟動模式適合於一些不常用的Activity頁面,比如“找回密碼”、“設定介面”等。

singleTask:最典型的樣例就是應用中展示的主頁(Home頁),假設使用者在主頁跳轉到其他頁面,執行多次操作後想返回到主頁,假設不使用SingleTask模式,在點選返回的過程中會多次看到主頁,這明顯就是設計不合理了。

singleInstance:比如說,使用微信調起自己的客戶端某個頁面,不做任何處理的情況下,按下回退或者當前Activity.finish(),頁面不會停留在自己的客戶端而是返回到微信的客戶端頁面。但是如果這個頁面的啟動模式設定為singleTask,當按下返回鍵或者Activity。finish(),頁面都會停留在自己的客戶端(因為自己的Application回退棧不為空),這明顯不符合邏輯的。產品的要求是,回退必須回到微信客戶端,而且要保證不殺死自己的Application.因此,顯然其他的其他的啟動模式都不具備這個功能。

使用方法:
方法一:

android:launchMode="standard|singleInstance|single Task|singleTop

使用android:launchMode=”standard|singleInstance|single Task|singleTop”來控制Acivity任務棧。

方法二:

Intent Flags:
Intent intent=new Intent();
intent.setClass(MainActivity.this, MainActivity2.class);
intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

Flags有很多,比如:
Intent.FLAG_ACTIVITY_NEW_TASK 相當於singleTask
Intent. FLAG_ACTIVITY_CLEAR_TOP 相當於singleTop

scheme(sigeimo)跳轉協議:
android中的scheme是一種頁內跳轉協議,是一種非常好的實現機制,通過定義自己的scheme協議,可以非常方便的在app內部跳轉到各個介面;通過scheme協議,伺服器可以定製化告訴app跳轉到哪個頁面,可以通過通知欄訊息欄定製化跳轉頁面,也可以通過H5頁面中定義連線跳轉指定的activity頁面等

應用場景:
1 服務端下發一個url路徑,客戶端根據下方的路徑跳轉到指定介面
2 從H5跳轉到App內
3 App根據url挑戰到另外的一個App

兩個 Activity 之間跳轉時必然會執行的是哪幾個方法?
一般情況下比如說有兩個 activity,分別叫 A,B,當在 A 裡面啟用 B 元件的時候, A 會呼叫 onPause()方法,然後 B 呼叫 onCreate() ,onStart(), onResume()。
這個時候 B 覆蓋了窗體, A 會呼叫 onStop()方法. 如果 B 是個透明的,或者 是對話方塊的樣式, 就不會呼叫 A 的 onStop()方法。

橫豎屏切換時 Activity 的生命週期?
此時的生命週期跟清單檔案裡的配置有關係。
1.不設定 Activity 的 android:configChanges 時,切屏會重新呼叫各個生命週期預設首先銷燬當前 activity,然後重新載入。
2.設定 Activity android:configChanges=”orientation|keyboardHidden|screenSize”時,切 屏不會重新呼叫各個生命週期,只會執行 onConfigurationChanged 方法。
通常在遊戲開發, 螢幕的朝向都是寫死的。

如何將一個 Activity 設定成視窗的樣式?
只需要給我們的 Activity 配置如下屬性即可。
android:theme=”@android:style/Theme.Dialog”

Android 中的 Context, Activity,Appliction 有什麼區別?
相同:Activity 和 Application 都是 Context 的子類。
Context 從字面上理解就是上下文的意思,在實際應用中它也確實是起到了管理 上下文環境中各個引數和變數的總用,方便我們可以簡單的訪問到各種資源。

不同:維護的生命週期不同。Context 維護的是當前的 Activity 的生命週期, Application 維護的是整個專案的生命週期。使用 context 的時候,小心記憶體洩露,防止記憶體洩露,注意一下幾個方面:
1)不要讓生命週期長的物件引用 activity context,即保證引用 activity 的對 象要與 activity 本身生命週期是一樣的。
2 )對於生命週期長的物件,可以使用 application,context。
3 )避免非靜態的內部類,儘量使用靜態類,避免生命週期問題,注意內部類 對外部物件引用導致的生命週期變化。

如何獲取當前螢幕Activity的物件?
使用ActivityLifecycleCallbacks
應用場景:可以利用ActivityLifecycleCallbacks 做一些資料埋點,統計之類的應用,對其統一做處理。這樣對減少Activity的程式碼入侵。儘量簡化和模組化的注入生命週期方法。

ActivityLifecycleCallbacks 是什麼?
見名知意,Activity生命週期回撥,Application通過此介面提供了一套回撥方法,用於讓開發者對Activity的生命週期事件進行集中處理。
但是這個要求API 14+ (Android 4.0+)以上使用,幸好我們這個最低支援,滿足需求。

ActivityLifecycleCallbacks 怎麼使用?
重寫Application的onCreate()方法,或在Application的無參構造方法內,呼叫Application.registerActivityLifecycleCallbacks()方法,並實現ActivityLifecycleCallbacks介面

知道onNewIntent嗎?
如果IntentActivity處於任務棧的頂端,也就是說之前開啟過的Activity,現在處於onPause、onStop 狀態的話,其他應用再發送Intent的話,執行順序為:
onNewIntent,onRestart,onStart,onResume。

除了用Intent 去啟動一個Activity,還有其他方法嗎?
使用adb shell am 命令

1)ams啟動一個activity
adb shell am start com.example.fuchenxuan/.MainActivity
2)am傳送一個廣播,使用action
adb shell am broadcast -a magcomm.action.TOUCH_LETTER

Android Service與Activity之間通訊的幾種方式?
通過Binder物件
1)當Activity通過呼叫bindService(Intent service, ServiceConnection conn,int flags),得到一個Service的一個物件,通過這個物件我們可以直接訪問Service中的方法。
2)通過Broadcast Receiver(廣播)的形式
3)EventBus 是一個Android開源事件匯流排框架 後面我們會有專門的講解。

TaskAffinity 是什麼?
標識Activity任務棧名稱的屬性:TaskAffinity,預設為應用包名

如果新Activity是透明主題時,舊Activity會不會走onStop?
不會!

android完全退出應用程式的三種方式?

第一種方法:首先獲取當前程序的id,然後殺死該程序。 建議使用這種方式

android.os.Process.killProcess(android.os.Process.myPid())

第二種方法:終止當前正在執行的Java虛擬機器,導致程式終止

System.exit(0);

第三種方法:強制關閉與該包有關聯的一切執行

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);   
manager.restartPackage(getPackageName());

使用這種方式關閉應用程式需要加上許可權

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

介紹下Android應用程式啟動過程
1)Launcher通過Binder程序間通訊機制通知ActivityManagerService,它要啟動一個Activity;

2)ActivityManagerService通過Binder程序間通訊機制通知Launcher進入Paused狀態;

3)Launcher通過Binder程序間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程序,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;

4)ActivityThread通過Binder程序間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;

5)ActivityManagerService通過Binder程序間通訊機制通知ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。
如果想深入研究的可以檢視羅昇陽大神的部落格https://blog.csdn.net/luoshengyang/article/details/6689748

Fragment:

三個小問題看你能完美解答幾個
1 Fragment為什麼被稱為第五大元件?
2 Fragment的生命週期 ?
3 Fragment之間的通訊 ?

1 Fragment為什麼被稱為第五大元件?
首先Fragment的使用頻率並不低於其他四大元件,他有自己的生命週期,同時可以動態靈活的載入到Activity中,所以說Fragment可以被稱為第五大元件

載入到Activity的兩種方式:
1)新增Fragment到Activity的佈局檔案當中,也叫靜態載入,name屬性哦
2)動態的在activity中新增fragment ,也叫動態載入
步驟:
FragmentManage用來管理所有要啟動的fragment,並用FragmentTransaction新增和替換相對應的fragment,並用容器資源來作為標誌位來設定fragment所要顯示到activity當中的位置,最後提交commit方法
(1)建立待新增的碎片例項
(2)獲取FragmentManager,在活動中可以直接通過呼叫 getSupportFragmentManager()方法得到。
(3)開啟一個事務,通過呼叫beginTransaction()方法開啟。
(4)向容器內新增或替換碎片,一般使用repalce()方法實現,需要傳入容器的id和待新增的碎片例項。
(5)提交事務,呼叫commit()方法來完成。

FragmentPageAdapter與FragmentStatePageAdapter的區別?
1 FragmentStatePageAdapter適合介面多,FragmentPageAdapter適合介面少
2 FragmentStatePageAdapter比FragmentPageAdapter更節省記憶體
原始碼分析:
FragmentPageAdapter適用於頁面較少的情況下,因為只對ui分離並沒有回收記憶體, 因為原始碼裡面destoryItem是detach方法()只是對fragment和activity的ui脫離開來,並不回收記憶體
FragmentStatePageAdapter用於介面較多的情況下,介面多也就意味著更加的消耗記憶體,FragmentStatePageAdapter在每次切換fragment的時候,他是回收記憶體的,因為原始碼裡面destoryItem的remove方法真正的釋放的記憶體

Fragement的生命週期:
onAttach >> onCreate >> onCreateView >> onActivityCreated >>onStart >> onResume >> onPause >> onStop >> onDestoryView >> onDestory >>onDetach
很顯然你要是像背書似的將上面的生命週期說一遍,你認為你跟其他的競爭者有什麼優勢?

你應該將Activity與Fragment的生命週期結合起來說:
首先,呼叫Activity的onCreate方法
當Fragment建立的時候會執行onAttach,是在Activity與fragment關聯之後呼叫的
在之後執行onCreate方法,也就是在fragment建立的時候呼叫,注意,此時的activity還沒有建立完畢
在呼叫fragment的onCreateView方法,系統首次繪製使用者介面的時候呼叫
呼叫fragment的onActivityCreated方法,也就是activity被渲染繪製成功後呼叫,
現在是要呼叫activity的onStart的方法,當activity可見之後呼叫,
fragment的onStart方法,表示fragment也可見了
接著呼叫activity的onResume的方法,表示當前activity可以與使用者互動了,當activity可見之後呼叫
fragment的onResume方法,表示當前fragment也可以與使用者進行互動操作了,以上步驟完成了從建立到互動的所有步驟

在之後,Fragment的onPause
Activity的onPause
Fragment的onStop
Activity的onStop
現在來到了Frament的onDestoryView方法,表示當前fragment不在被使用,介面銷燬,緊接著來到onDestory方法,表示fragment已經銷燬了,最後呼叫onDetach來解除與activity的聯絡
最後呼叫Activity的onDestory方法

Fragment通訊:
1)在Fragment中呼叫Activity的方法 geyActivity
2)在Activity中呼叫Fragment的方法 介面回撥(在Fragment建立介面,在Activity實現)
3)在Fragment呼叫Fragment的方法 getActivity.findFragmentById 或者廣播

Fragment的replace、add、remove?

add:將一個fragment例項新增到Activity的最上層,一般在使用add的時候會配合 hide,show一起使用,為了避免Fragment的重複建立節省資源。
remove:將fragment從fragment佇列中刪除
replace:替換fragment的例項,replace是FragmentManager的方法

commitAllowingStateLoss與commit的區別?
Activity被系統回收(介面已經不存在了),為了能在下次開啟的時候恢復原來的樣子,系統為我們儲存介面的所有狀態,這個時候再去修改介面理論上肯定是不被允許的,為了避免這種異常可以使用:

transaction.commitAllowingStateLoss();

來提交新增Fragment到Activity的事務,與commit()不同的是使用這種方法允許丟失介面的狀態和資訊。

ViewPager與Fragment結合使用時的懶載入問題?
所謂的 “懶載入” 就是資料只有在Fragment對於使用者可見的時才進行載入,我們需要判定Fragment在什麼時候是處於可見的狀態。一般我們通常是通過Fragment中的生命週期方法onResume來判斷Fragment是否可見,但是由於ViewPager預載入的特性,Fragment即便不可見也會執行onResume方法,可以通過setUserVisibleHint()方法來進行判斷:

什麼時候被呼叫?

當fragment被建立的時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為false。

當fragment可見時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為true。

當fragment由 可見 -> 不可見 時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為false

所以我們只需要當setUserVisibleHint(boolean isVisibleToUser)方法中的 isVisibleToUser 引數的值為true的時候我們才開始進行資料的載入就可以了。

但是有一點需要需要注意的是 setUserVisibleHint(boolean isVisibleToUser)方法在Fragment的生命週期方法onCreate 之前呼叫的,也就是說他並不在Fragment的生命週期中。既然是在 onCreate 方法之前被呼叫,這樣就存在許多不確定因素,如果Fragmnet的View還沒有完成初始化之前,就在setUserVisibleHint()方法中進行UI的操作,這樣顯然會導致空指標的出現。因此我們需要對Fragment建立的View進行快取,確保快取的View不為空的情況下我們才可以在setUserVisibleHint方法中進行UI操作。

Service

Service是什麼?
四大元件之一,可以在後臺處理一些耗時的邏輯,也可以執行某些長時間執行的任務,而且看不到介面,包括在程式退出的時候依然能在繼續執行

Service與Broadcastrecevier有一個共同點,都是執行在主執行緒當中,都不能進行長耗時操作

Service與Thread的區別?
Thread程式最小的執行單元,Thread可以進行非同步操作,相對獨立;而Service是Android的一種機制,如果是本地的Service,依賴與它所在的主執行緒之上,相比Thread沒有那麼獨立

為什麼要用Service而不是Thread呢?
Thread的執行是獨立於Activity的,也就是當一個Activity被finish之後,如果沒有主動停止Thread或者Thread中的run沒有執行完畢時那麼這個執行緒會一直執行下去。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

Service 是否在 main thread 中執行, service 裡面是否 能執行耗時的操作?
預設情況,如果沒有顯示的指 servic 所執行的程序, Service 和 activity 是運 行在當前 app 所在程序的 main thread(UI 主執行緒)裡面。
service 裡面不能執行耗時的操作(網路請求,拷貝資料庫,大檔案 )
特殊情況 ,可以在清單檔案配置 service 執行所在的程序 ,讓 service 在另 外的程序中執行

<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" >
</service>

Service 裡面可以彈吐司麼?
可以的。彈吐司有個條件就是得有一個 Context 上下文,而 Service 本身就是 Context 的子類,因此在 Service 裡面彈吐司是完全可以的。比如我們在 Service 中完成下載任務後可以彈一個吐司通知使用者

Service 的 onStartCommand 方法有幾種返回值?各代表什麼意思?

有四種返回值,不同值代表的意思如下:
START_STICKY:如果 service 程序被 kill 掉,保留 service 的狀態為開始狀態,但不保留遞送的 intent 物件。隨 後系統會嘗試重新建立 service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫 onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到 service,那麼引數 Intent 將為 null。

START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完 onStartCommand 後,服務被異常 kill 掉,系統不會自動重啟該服務。

START_REDELIVER_INTENT:重傳 Intent。使用這個返回值時,如果在執行完
onStartCommand 後,服務被異 常 kill 掉,系統會自動重啟該服務,並將 Intent 的值傳入。

START_STICKY_COMPATIBILITY:START_STICKY 的相容版本,但不保證服務被 kill 後一定能重啟。

Service 的 onRebind(Intent)方法在什麼情況下會執行?
如果在 onUnbind()方法返回 true 的情況下會執行,否則不執行。

Activity 呼叫 Service 中的方法都有哪些方式?
Binder:
通過 Binder 介面的形式實現,當 Activity 繫結 Service 成功的時候 Activity 會在 ServiceConnection 的類 的 onServiceConnected()回撥方法中獲取到 Service 的 onBind()方法 return 過來的 Binder 的子類,然後通過物件呼叫方法。
Aidl:
aidl 比較適合當客戶端和服務端不在同一個應用下的場景。

Messenger:
它引用了一個Handler物件,以便others能夠向它傳送訊息(使用mMessenger.send(Message msg)方法)。該類允許跨程序間基於Message的通訊(即兩個程序間可以通過Message進行通訊),在服務端使用Handler建立一個Messenger,客戶端持有這個Messenger就可以與服務端通訊了。一個Messeger不能同時雙向傳送,兩個就就能雙向傳送了

如何提高service的優先順序?
可以用 setForeground(true) 來設定 Service 的優先順序。

service 如何定時執行?
使用AlarmManager,根據AlarmManager的工作原理,alarmmanager會定時的發出一條廣播,然後在自己的專案裡面註冊這個廣播,重寫onReceive方法,在這個方法裡面啟動一個service,然後在service裡面進行網路的訪問操作,當獲取到新訊息的時候進行推送,同時再設定一個alarmmanager進行下一次的輪詢,當本次輪詢結束的時候可以stopself結束改service。這樣即使這一次的輪詢失敗了,也不會影響到下一次的輪詢。這樣就能保證推送任務不會中斷

在 service 的生命週期方法 onstartConmand()可不可以執行網路操作?如何在 service 中執行網路操作?
可以直接在 Service 中執行網路操作,在 onStartCommand()方法中可以執行網路操作

Service 和 Activity 在同一個執行緒嗎?
對於同一 app 來說預設情況下是在同一個執行緒中的,main Thread (UI Thread)。

什麼是 IntentService?有何優點?
會建立獨立的工作執行緒來處理所有的 Intent 請求;
會建立獨立的工作執行緒來處理 onHandleIntent()方法實現的程式碼,無需
處理多執行緒問題;
所有請求處理完成後,IntentService 會自動停止,無需呼叫 stopSelf()方法
停止 Service;
為 Service 的 onBind()提供預設實現,返回 null;
為 Service 的 onStartCommand 提供預設實現,將請求 Intent 新增到佇列
中;

Activity 怎麼和 Service 繫結,怎麼在 Activity 中啟動自 己對應的 Service?
Activity 通過 bindService(Intent service, ServiceConnection conn, int flags)跟 Service 進行繫結,當繫結成功的時候 Service 會將代理物件通過回撥 的形式傳給 conn,這樣我們就拿到了 Service 提供的服務代理物件。
在 Activity 中可以通過 startService 和 bindService 方法啟動 Service。一 般情況下如果想獲取 Service 的服務物件那麼肯定需要通過 bindService()方 法,比如音樂播放器,第三方支付等。如果僅僅只是為了開啟一個後臺任務那麼 可以使用 startService()方法。

IntentService 適用場景
IntentService 內建的是 HandlerThread 作為非同步執行緒,每一個交給 IntentService 的任務都將以佇列的方式逐個被執行到,一旦佇列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理
正在執行的 IntentService 的程式相比起純粹的後臺程式更不容易被系統殺死,該程式的優先順序是介於前臺程式與純後臺程式之間的

Broadcast Receiver

廣播的定義:
在Android中,Broadcast是一種廣泛運用在程式之間的傳輸資訊的機制,Android中我們要傳送的廣播內容中是一個Intent,這個Intent可以攜帶我們要傳輸的資料

廣播的使用場景:
1)在同一個App具有多個程序的不同元件之間的訊息傳遞
2)不同app之間的訊息通訊

廣播的種類:
1)普通廣播
2)系統廣播 (sendOrderedBroadcast)
3 ) 本地廣播(只在app內部傳播)

不同註冊方式廣播接收器回撥onReceive(context, intent)中context型別不一致?
manifest靜態註冊的ContextReceiver,回撥onReceive(context, intent)中的context是ReceiverRestrictedContext

程式碼動態註冊的ContextReceiver,回撥onReceive(context, intent)中的context是Activity Context;

內部實現機制?
1)自定義廣播接受者Broadcast Receiver,並複寫onRecvice方法
2)通過Binder機制像AMS進行註冊
3)廣播發送者通過Binder機制像AMS傳送廣播
4)AMS查詢符合相應條件(IntentFilter/Permission等)的Broadcast Receiver,將廣播發送到Broadcast Receiver相應的訊息佇列迴圈當中去(一般是Activity中)
5)訊息迴圈拿到此廣播,回撥Broadcast Receiver中的onReceive方法、

如何讓自己的廣播只讓指定的 app 接收?
通過自定義廣播許可權來保護自己發出的廣播。 在清單檔案裡receiver必須有這個許可權才能收到廣播。 首先,需要定義許可權: 然後,宣告許可權: 這時接收者就能收到傳送的廣播。

廣播的優先順序對無序廣播生效嗎?
生效的**

動態註冊的廣播優先順序誰高?
誰先註冊誰優先順序高。

如何判斷當前 BroadcastReceiver 接收到的是有序廣播還是無序廣播?
在 BroadcastReceiver 類中 onReceive()方法中,可以呼叫
boolean b = isOrderedBroadcast();判斷接收到的廣播是否為有序廣播。

粘性廣播有什麼作用?怎麼使用?

粘性廣播主要為了解決,在傳送完廣播之後,動態註冊的接收者,也能夠收到廣播。舉個例子首先發送一廣播,我的接收者是通過程式中的某個按鈕動態註冊的。如果不是粘性廣播,我註冊完接收者肯定無法收到廣播了。這是通過傳送粘性廣播就能夠在我動態註冊接收者後也能收到廣播。

網路

HttpConnection 與HttpURLConnection的區別?
在Android 2.2版本之前,HttpClient擁有較少的bug,因此使用它是最好的選擇
2.3之後使用HttpURLConnection,它的API簡單,體積較小,壓縮和快取機制可以有效地減少網路訪問的流量,在提升速度和省電方面也起到了較大的作用,利於維護與優化

TCP與UDP的區別?
1)基於連線與無連線
2)對系統資源的要求(TCP較多,UDP較少)
3)UDP程式結構較簡單一些
4)流模式與資料包模式
5)TCP保證資料的順序性以及正確性,UDP不能保證,可能存在丟包

Socket:
soket是套接字,我們可以先在服務端初始化ServerSocket,然後對指定的埠進行繫結與監聽,通過呼叫accept方法與getInputstream方法進行等待客戶端的連線與資料的接收。現在客戶端進行建立socket物件傳入ip和埠號,通過getOutputStream進行資料的輸入,並且制定結束字元,否則服務端會一直處於阻塞狀態。

socket.close() 與socket.shutdownOutput()的區別?
1)在客戶端或者服務端通過socket.shutdownOutput()都是單向關閉的,即關閉客戶端的輸出流並不會關閉服務端的輸出流,所以是一種單方向的關閉流;
2)通過socket.shutdownOutput()關閉輸出流,但socket仍然是連線狀態,連線並未關閉
3)如果直接關閉輸入或者輸出流,即:in.close()或者out.close(),會直接關閉socket

socket長連線?
在不關閉流的情況下,開啟迴圈執行緒進行定時傳送與服務端約定的心跳包資料

http1.0與http1.1的區別:
1)快取處理(快取處理策略不同1.1更豐富)
2)頻寬優化及網路連線的使用(1.1允許只請求某一部分的資料,不會浪費頻寬)
3)Host頭處理 (1.1請求訊息支援Host頭改進 )
4)長連線(1.1支援長連線 ,預設開啟KeepAlive ,避免建立浪費的資源)

http1.1與1.0的存在問題:

a. 1.0在傳輸資料時,每次都需要重新建立連線,無疑增加了大量的延遲時間
b. 1.1在傳輸資料的時候傳輸的都是明文,客戶端和服務端都無法驗證對方的身份(https)
c. 1.1在使用的時候 ,header裡攜帶的內容過大,在一定程度上增加了傳輸的成本
d. 雖然1.1支援了keep-alive來彌補多次建立連線產生的延遲,但是keep-alive使多了同樣會給服務端帶來大量的效能壓力

get與post 的區別?
主要區別:get獲取資源,post提供來更新伺服器上的資源

a、提交的資料(get資料一般放在url之後用?分隔,post提交資料放在http包的body中)
b、提交資料大小限制問題(get有限制,url有限制,post沒有限制)
c、取得變數值Requst.QueryStrig 與 Requst.Form的區別
d、安全問題(get使用者名稱密碼暴露在url上,不安全;post則不然)

cookie與session的區別??

a、存放位置不同(cookie儲存在客戶端,session儲存在服務端)
b、存取方式不同(cookie儲存的ASCII字串,session可以存取任何型別的字串)
c、安全性的不同(cookie存在客戶端可能會被修改資料)
d、有效期上的不同(cookie可以設定很長的時間,session依賴id)
e、對伺服器造成的壓力不同(併發很多的時候可以選擇cookie,伺服器壓力小)

Handler

什麼是Handler?
handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueue
作用:
1)可以讓對應的Message和Runnable在未來的某個時間點進行相應處理
2)讓自己想要處理的耗時操作放在子執行緒,讓更新ui的操作放在主執行緒

Handler傳送訊息的幾種方式?
1)post(Runnable)延遲訊息處理
2)sendMessage(message)

PS:post(Runnable)的Runnable中可以更新UI嗎?
可以的 ,因為經常會post一個Runnable,處理的程式碼直接寫在Runnable的run方法中,其實就是將這個Runnable傳送到Handler所線上程(一般是主執行緒)的訊息佇列中。sendMessage方法主執行緒處理方法一般則是寫在handleMessage中

Handler處理訊息有哪幾種方式?

直接看dispatchMessage()原始碼

//1. post()方法的最終處理方法
private static void handleCallback(Message message) {
    message.callback.run();
}
//2. sendMessage()方法的最終處理方法
public void handleMessage(Message msg) {
}

Handler、Message、Looper、MessageQueue

Message(訊息)
定義:Handler接收和處理的訊息物件(Bean物件)
作用:通訊時相關資訊的存放和傳遞

ThreadLocal
定義:ThreadLocal是執行緒內部的儲存類,通過它可以實現在每個執行緒中儲存自己的私有資料。即資料儲存以後,只能在指定的執行緒中獲取這個儲存的物件,而其它執行緒則不能獲取到當前執行緒儲存的這個物件。
作用:負責儲存和獲取本執行緒的Looper

MessageQueue(訊息佇列)
定義:採用單鏈表的資料結構來儲存訊息列表
作用:用來存放通過Handler發過來的Message,按照先進先出執行

Handler(處理者)
定義:Message的主要處理者
作用:負責傳送Message到訊息佇列&處理Looper分派過來的Message

Looper(迴圈器)
定義:扮演Message Queue和Handler之間橋樑的角色
作用:
訊息迴圈:迴圈取出Message Queue的Message
訊息派發:將取出的Message交付給相應的Handler

首先知道定義人,然後結合關係說清楚一些在加上HandlerThread基本上Handler這塊是沒什麼大問題了

Message、Handler、MessageQueue、Looper的之間的關係?
首先,是這個MessagQueue,MessageQueue是一個訊息佇列,它可以儲存Handler傳送過來的訊息,其內部提供了進隊和出隊的方法來管理這個訊息佇列,其出隊和進隊的原理是採用單鏈表的資料結構進行插入和刪除的,即enqueueMessage()方法和next()方法。這裡提到的Message,其實就是一個Bean物件,裡面的屬性用來記錄Message的各種資訊。
然後,是這個Looper,Looper是一個迴圈器,它可以迴圈的取出MessageQueue中的Message,其內部提供了Looper的初始化和迴圈出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper會關聯一個MessageQueue,而且將Looper存進一個ThreadLocal中,在loop()方法中,通過ThreadLocal取出Looper,使用MessageQueue的next()方法取出Message後,判斷Message是否為空,如果是則Looper阻塞,如果不是,則通過dispatchMessage()方法分發該Message到Handler中,而Handler執行handlerMessage()方法,由於handlerMessage()方法是個空方法,這也是為什麼需要在Handler中重寫handlerMessage()方法的原因。這裡要注意的是Looper只能在一個執行緒中只能存在一個。這裡提到的ThreadLocal,其實就是一個物件,用來在不同執行緒中存放對應執行緒的Looper。
最後,是這個Handler,Handler是Looper和MessageQueue的橋樑,Handler內部提供了傳送Message的一系列方法,最終會通過MessageQueue的enqueueMessage()方法將Message存進MessageQueue中。我們平時可以直接在主執行緒中使用Handler,那是因為在應用程式啟動時,在入口的main方法中已經預設為我們建立好了Looper。

Handler引起的記憶體洩漏以及解決辦法?

HandlerThread作用
當系統有多個耗時任務需要執行時,每個任務都會開啟一個新執行緒去執行耗時任務,這樣會導致系統多次建立和銷燬執行緒,從而影響效能。為了解決這一問題,Google提供了HandlerThread,HandlerThread是線上程中建立一個Looper迴圈器,讓Looper輪詢訊息佇列,當有耗時任務進入佇列時,則不需要開啟新執行緒,在原有的執行緒中執行耗時任務即可,否則執行緒阻塞。

HandlerThread是什麼?有哪些特點

1)HandlerThread本質上是一個執行緒類,它繼承了Thread
2)HandlerThread有自己內部Looper物件,可以進行Looper迴圈
3)通過獲取HandlerThread的looper物件傳遞給Handler物件,可以在handlerMessage方法中執行非同步任務
4)優點是不會阻塞,減少對效能的消耗,缺點是不能同時進行多工的處理,需要等待進行處理
5)與執行緒池注重併發不同,HandlerThread是一個序列佇列,HandlerThread背後只有一個執行緒

子執行緒為什麼不能開啟handler?
handler在呼叫sendMessage或者post(Runnable)的時候都需要一個MessageQueue 訊息佇列來儲存傳送的訊息,而預設子執行緒中是沒有開啟Looper輪循器的,而訊息佇列又是由Looper來進行管理的,所以是沒有辦法開啟的,
如果子執行緒想要開啟,需要初始化looper,並且呼叫loop.loopers開啟一個迴圈

如果簡歷中寫了AsyncTask,就看一下,沒寫的直接跳過上Rxjava

AsyncTask是什麼
AsyncTask是一種輕量級的非同步任務類,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並主執行緒中更新UI,通過AsyncTask可以更加方便執行後臺任務以及在主執行緒中訪問UI,但是AsyncTask並不適合進行特別耗時的後臺任務,對於特別耗時的任務來說,建議使用執行緒池。

AsyncTask使用方法
三個引數
Params:表示後臺任務執行時的引數型別,該引數會傳給AysncTask的doInBackground()方法
Progress:表示後臺任務的執行進度的引數型別,該引數會作為onProgressUpdate()方法的引數
Result:表示後臺任務的返回結果的引數型別,該引數會作為onPostExecute()方法的引數
五個方法
onPreExecute():非同步任務開啟之前回調,在主執行緒中執行
doInBackground():執行非同步任務,線上程池中執行
onProgressUpdate():當doInBackground中呼叫publishProgress時回撥,在主執行緒中執行
onPostExecute():在非同步任務執行之後回撥,在主執行緒中執行
onCancelled():在非同步任務被取消時回撥

AsyncTask引起的記憶體洩漏
原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放
解決:
AsyncTask內部持有外部Activity的弱引用
AsyncTask改為靜態內部類
Activity的onDestory()中呼叫AsyncTask.cancel()

結果丟失
螢幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。

AsyncTask並行or序列
AsyncTask在Android 2.3之前預設採用並行執行任務,AsyncTask在Android 2.3之後預設採用序列執行任務
如果需要在Android 2.3之後採用並行執行任務,可以呼叫AsyncTask的executeOnExecutor();

AsyncTask內部的執行緒池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor是SerialExecutor的一個例項,而且它是個靜態變數。也就是說,一個程序裡面所有AsyncTask物件都共享同一個SerialExecutor物件。

ListView+RecyclerView

既然RecyclerView在很多方面能取代ListView,Google為什麼沒把ListView劃上一條過時的橫線?
ListView採用的是RecyclerBin的回收機制在一些輕量級的List顯示時效率更高

ListView怎麼和ScrollView相容?
方法一:重寫ListView, 覆蓋onMeasure()方法
方法二:動態設定listview的高度,不需要重寫ListView
方法三:在xml檔案中,直接將Listview的高度寫死

listview怎麼優化?
1)、convertView複用,對convetView進行判空,當convertView不為空時重複使用,為空則初始化,從而減少了很多不必要的View的建立
2)定義一個ViewHolder,封裝Listview Item條目中所有的元件,將convetView的tag設定為ViewHolder,不為空時通過ViewHolder的屬性獲取對應元件即可
3)、當ListView載入資料量較大時可以採用分頁載入和圖片非同步載入

上拉載入和下拉重新整理怎麼實現?
實現OnScrollListener 介面重寫onScrollStateChanged 和onScroll方法,
使用onscroll方法實現”滑動“後處理檢查是否還有新的記錄,如果有,呼叫 addFooterView,新增記錄到adapter, adapter調notifyDataSetChanged 更新資料;如果沒有記錄了,把自定義的mFooterView去掉。使用onScrollStateChanged可以檢測是否滾到最後一行且停止滾動然後執行載入

listview失去焦點怎麼處理?
在listview子佈局裡面寫,可以解決焦點失去的問題
android:descendantFocusability=”blocksDescendants”

ListView圖片非同步載入實現思路?
1.先從記憶體快取中獲取圖片顯示(記憶體緩衝)
2.獲取不到的話從SD卡里獲取(SD卡緩衝,,從SD卡獲取圖片是放在子執行緒裡執行的,否則快速滑屏的話會不夠流暢)
3.都獲取不到的話從網路下載圖片並儲存到SD卡同時加入記憶體並顯示(視情況看是否要顯示)

你知道Listview裡有Button就點不動了你知道嗎?
原因是button強制獲取了item的焦點,只要設定button的focusable為false即可。

listview分頁載入的步驟?
通常實現分頁載入有兩種方式,一種是在ListView底部設定一個按鈕,使用者點選即載入。另一種是當用戶滑動到底部時自動載入。
當用戶滑動到底部時自動載入實現思路:
實現OnScrollListener 介面重寫onScrollStateChanged 和onScroll方法,使用onscroll方法實現”滑動“後處理檢查是否還有新的記錄,如果有,新增記錄到adapter, adapter呼叫 notifyDataSetChanged 更新資料;如果沒有記錄了,則不再載入資料。使用onScrollStateChanged可以檢測是否滾到最後一行且停止滾動然後執行載入.

ViewHolder內部類非得要宣告成static的呢?
因為非靜態成員類的例項會包含一個額外的指向外圍物件的引用,儲存這份引用要消耗時間和空間,並且導致外圍類例項符合垃圾回收時仍然被保留。如果沒有外圍例項的情況下,也需要分配例項,就不能使用非靜態成員類,因為非靜態成員類的例項必須要有一個外圍例項。

ScrollView巢狀ListView和GridView衝突的方法
重寫ListView的onMeasure方法,來自定義高度:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

BaseAdapter四個關鍵方法的含義?
1) getCount()
返回資料來源中資料的個數,如果該方法的返回值為0,那麼介面卡就不用生成佈局物件了,提高了程式效能
2)getItem(int position)
根據位置返回資料項
3)getItemId(int position)
返回資料項的位置
4)getView(int position, View convertView, ViewGroup parent)

RecyclerView滑動刪除原理實現?
兩種方法:
一種就是通過重寫RecyclerView的onTouchEvent()方法來檢測手勢的變化實現的,大致的流程如下:
1、根據手指觸控的座標點找到對應Item的ViewHolder,進而得到相應的Item佈局View。
2、手指繼續移動,在條件滿足的情況下,通過scrollBy()使Item佈局View內容跟隨手指一起移動,當然要注意邊界檢測。
3、手指擡起時,根據Item佈局View內容移動的距離以及手指的滑動速度,判斷是否顯示刪除按鈕,進而通過startScroll()使Item佈局View自動滑動到目標位置。
4、點選刪除按鈕則刪除對應Item,點選其它區域則隱藏刪除按鈕。

另外一種:
實現原理
主要是藉助 ItemTouchHelper.Callback 類來實現,我們要關注的方法為
* getMovementFlags( )
* onMove()
* onSwiped()
* onSelectedChanged()
* clearView()
* isLongPressDragEnabled()

首先自定義一個MyCallback類繼承 ItemTouchHelper.Callback ,定義兩個int變數dragFlags 與 swipeFlags並實現下方法。這個方法主要是為了獲取我們當前的事件是拖動還是滑動

其他:

Android View重新整理機制?
在Android的佈局體系中,父View負責重新整理、佈局顯示子View;而當子View需要重新整理時,則是通知父View來完成

RelativeLayout和LinearLayout效能比較?
1.RelativeLayout會讓子View呼叫2次onMeasure,LinearLayout 在有weight時,也會呼叫子View2次onMeasure
2.RelativeLayout的子View如果高度和RelativeLayout不同,則會引發效率問題,當子View很複雜時,這個問題會更加嚴重。如果可以,儘量使用padding代替margin。
3.在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。

View和ViewGroup什麼區別?
Android的UI介面都是由View和ViewGroup及其派生類組合而成的。其中,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,其本身也是從View派生出來的

自定義View優化策略
為了加速你的view,對於頻繁呼叫的方法,需要儘量減少不必要的程式碼。先從onDraw開始,需要特別注意不應該在這裡做記憶體分配的事情,因為它會導致GC,從而導致卡頓。在初始化或者動畫間隙期間做分配記憶體的動作。不要在動畫正在執行的時候做記憶體分配的事情。
你還需要儘可能的減少onDraw被呼叫的次數,大多數時候導致onDraw都是因為呼叫了invalidate().因此請儘量減少呼叫invaildate()的次數。如果可能的話,儘量呼叫含有4個引數的invalidate()方法而不是沒有引數的invalidate()。沒有引數的invalidate會強制重繪整個view。
另外一個非常耗時的操作是請求layout。任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有衝突的值,它會需要重新計算好幾次。另外需要儘量保持View的層級是扁平化的,這樣對提高效率很有幫助。
如果你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操作。與內建的view不同,自定義的view可以使得程式僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展示瞭如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據他自身的layout法則,直接設定它們的大小

View樹的繪製流程?
measure>layout>draw

View樹的繪製流程就像是一個遞迴過程,在onMeasure方法中,它的view會對它的所有子元素進行測量,測量過程就從它的父的ViewGroup傳遞到子的view,經過子元素的遞迴,測量好了所有的子元素的長度之後,進行一個遞迴,反覆之後就完成了整個父元素ViewGroup的測量,而layout也是相類似樹的遞迴過程

onMeasure:
ViewGroup.LayoutParams:用來指定檢視高度個寬度的引數。
MeasureSpec:測量規格,32位的int值,前兩位是測量模式,後者表示在這種模式下的尺寸的大小

開始於我們的父控制元件ViewGroup,它會不斷的遍歷子控制元件的measure方法,然後根據ViewGroup的MeasureSpec和子View的LayoutParams來決定我們的子檢視的MeasureSpec測量規格,通過這個測量規格,MeasureSpec進一步獲取到子View的寬高,然後一層一層的向下傳遞,不斷的儲存父控制元件的測量寬高,整個Measure的測量流程就是一個樹型的遞迴流程

layout:
layout其實也是一個樹形的結構,所以當它進行資料擺放的時候,他會以此的從ViewGroup呼叫它的子控制元件,所以也是依次進行的資料擺放,這就是layout與measure的相同的地方

onDraw:
經過測量和擺放之後進行繪製,需要注意兩個容易混淆的方法:
invalidate:invalidate()是用來重新整理View的,必須是在UI執行緒中進行工作。比如在修改某個view的顯示時,呼叫invalidate()才能看到重新繪製的介面。invalidate()的呼叫是把之前的舊的view從主UI執行緒佇列中pop掉。

requestlayout:當佈局發生變化的時候,方向變化或者尺寸變化,某些情況下重新測量view的大小啊,呼叫完改方法後就會觸發它的measure onlayout過程,但是不會呼叫onDraw方法。

音視訊

SurfaceView是什麼 ?
它繼承自類View,因此它本質上是一個View。但與普通View不同的是,它有自己的Surface。有自己的Surface,在WMS中有對應的WindowState,在SurfaceFlinger中有Layer。我們知道,一般的Activity包含的多個View會組成View hierachy的樹形結構,只有最頂層的DecorView,也就是根結點檢視,才是對WMS可見的。這個DecorView在WMS中有一個對應的WindowState。相應地,在SF中對應的Layer。而SurfaceView自帶一個Surface,這個Surface在WMS中有自己對應的WindowState,在SF中也會有自己的Layer。雖然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它與宿主視窗是分離的。這樣的好處是對這個Surface的渲染可以放到單獨執行緒去做,渲染時可以有自己的GL context。這對於一些