1. 程式人生 > >[乾貨]總結的真好,Android重點知識點總結,不看你會後悔!

[乾貨]總結的真好,Android重點知識點總結,不看你會後悔!

Activity的生命週期和啟動模式相關

1、當前Activity的onPause方法執行結束後才會執行下一個Activity的onCreate方法,所以在onPause方法中不適合做耗時較長的工作,這會影響到頁面之間的跳轉效率;

2、如果新的Activity採用了透明主題,那麼當前Activity的onStop方法不會被呼叫;

3、onSavedInstanceStateonRestoreInstanceState只會在Activity被異常終止的情況下被呼叫,正常情況下系統不會回撥這兩個方法,並且onRestoreInstanceState一旦被呼叫,其引數bundle必定為非空,不需要在方法內做空值判斷;

4、View和Activity一樣,每個View都有onSavedInstanceState和onRestoreInstanceState這兩個方法,用於儲存和恢復view的狀態;

5、如果一個程序中沒有四大元件在執行,那麼這個程序將很快被系統殺死,因此,一些後臺工作不適合脫離四大元件而獨自執行在後臺中,這樣程序就很容易被系統殺死,比較好的方法是將後臺工作放入Service中從而保證程序有一定的優先順序,這樣就不會輕易地被系統殺死;

6、避免螢幕旋轉時Activity重啟,可以在AndroidManifest.xml中對應Activity標籤宣告時加上“android:configChanges="orientation|screenSize"”即可;

7、Android有四種啟動模式:standard、singleTop、singleTask和singleInstance。

  • standard:標準模式,這也是系統的預設模式。每次啟動一個Activity都會重新建立一個新的例項,不管這個例項是否已經存在;

  • singleTop:棧頂複用模式。在這種模式下,如果新Activity已經位於任務棧的棧頂,那麼此Activity不會被重新建立,同時它的onNewIntent方法會被回撥,通過此方法的引數我們可以取出當前的請求資訊。需要注意的是,這個Activity的onCreate、onStart不會被系統呼叫,因為它並沒有發生改變。如果新的Activity的例項已經存在但不是位於棧頂,那麼新的Activity仍然會重新建立;

  • singleTask:棧內複用模式。這是一種單例項模式,在這種情況下,只要Activity在一個棧中存在,那麼多次啟動此Activity都不會重新建立例項,和singleTop一樣,系統也會回撥其onNewIntent;

  • singleInstance:單例項模式,這是一種加強的singleTask模式,它除了具有singleTask模式的所有特性外,還加強一點,那就是具有此種模式的Activity只能單獨地位於一個任務棧中。

8、Activity的Flags:

  • FLAG_ACTIVITY_NEW_TASK:這個標記位的作用是為Activity指定"singleTask"啟動模式,其效果和在XML中指定該啟動模式相同;

  • FLAG_ACTIVITY_SINGLE_TOP:這個標記位的作用是為Activity指定"singleTop"啟動模式,其效果和在XML中指定該啟動模式相同;

  • FLAG_ACTIVITY_CLEAR_TOP:具有此標記位的Activity,當它啟動時,在同一個任務棧中所有位於它上面的Activity都要出棧;

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有這個標記的Activity不會出現在歷史Activity的列表中,當某些情況下我們不希望使用者通過歷史列表回到我們的Activity的時候這個標記比較有用。它等同於在XML中指定Activity的屬性"android:excludeFromRecents="true""。

IPC機制相關

1、給四大元件(Activity、Service、Receiver、ContentProvider)在AndroidManifest.xml中指定"android:process"屬性可以在應用內實現多程序,如果程序名以":"開頭,說明該程序屬於私有程序,其他應用的元件不可以和它跑在同一個程序中,如果經常名不以":"開頭,則屬於全域性程序,其它應用通過ShareUID方式可以和它跑在同一個程序中。

2、所有執行在不同程序中的四大元件,只要它們之間需要通過記憶體來共享資料,都會共享失敗。使用多程序會造成如下幾個方面的影響:

  • 靜態成員和單例模式完全失效;

  • 執行緒同步機制完全失效;

  • SharedPreferences的可靠性下降;

  • Application會多次建立。

3、兩個序列化類Parcelable和Serializable的區別:Serializable是Java中的序列化介面,其使用起來簡單但是開銷很大,序列化和反序列化過程需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更適合於用在Android平臺上,它的缺點就是使用起來稍微麻煩點,但是它的效率很高。Parceable主要用在記憶體序列化上,通過Parcelable將物件序列化到儲存裝置中或者將物件序列化後通過網路傳輸也都是可以的,但是這個過程會稍顯複雜,此種情況建議使用Serializable。

4、在AIDL檔案中,並不是所有的資料型別都是可以使用的,AIDL只支援如下六種資料型別:

  • 基本資料型別(int、long、char、boolean、double等);

  • String和CharSequence;

  • List:只支援ArrayList,裡面每個元素都必須能夠被AIDL支援;

  • Map:只支援HashMap,裡面的每個元素都必需被AIDL支援,包括key和value;

  • Parcelable:所有事項了Parcelable介面的物件;

  • AIDL:所有的AIDL介面本身也可以在AIDL檔案中使用。

其中自定義的Parcelable物件和AIDL物件必須要顯式地import進來,不管它們時候和當前的AIDL檔案位於同一個包內。

5、如果AIDL檔案中用到了自定義的Parcelable物件,那麼必須新建一個和它同名的AIDL檔案,並在其中宣告它為Parcelable型別。

6、Android中有Intent、檔案共享、SharedPreferences、Binder通訊、ContentProvider、網路通訊等多種IPC通訊機制,各種通訊機制的特點如下:

  • Intent:Activity、Receiver、Service這三個元件都支援Intent通訊,Intent適合於傳遞可序列化的資料;

  • 檔案共享:檔案共享方式適合在對資料同步要求不高的程序之間進行通訊,並且要妥善處理併發讀/寫的問題。

  • SharedPreferences:SharedPreferences也屬於檔案的一種(以鍵值對方式儲存資料的一個xml檔案),但是由於系統對它的讀/寫有一定的快取策略,即在記憶體中會有一份SharedPreferences檔案的快取,因此在多程序模式下,系統對它的讀/寫就變得不可靠,當面對高併發的讀/寫訪問,SharedPreferences有很大機率會丟失資料,因此,不建議在程序間通訊中使用SharedPreferences(MODE_MULTI_PROCESS)。

  • Binder通訊:適用於程序間傳遞資料;

  • ContentProvider:使用於應用間共享資料,ContentProvider主要以表格的形式來組織資料,並且可以包含多個表,對於每個表格來說,它們都具有行和列的層次性,這點和資料庫很類似;雖然ContentProvider的底層資料看起來很像一個SQLite資料庫,但是ContentProvider對底層的資料儲存方式沒有任何要求,我們既可以使用SQLite資料庫,也可以使用普通的檔案,甚至可以採用記憶體中的一個物件來進行資料的儲存。

  • 網路通訊:網路資料交換。

7、不能在主執行緒中訪問網路,因為這會導致我們的程式無法在Android 4.0及以上的設別中執行,會丟擲如下異常:android.os.NetworkOnMainThreadException。

8、AIDL的建立流程:首先建立一個Service和一個AIDL介面,接著建立一個類繼承自AIDL介面中的Stub類並實現Stub中的抽象方法,在Service的onBind方法中返回這個類的物件,然後客戶端就可以繫結服務端的Service,建立連線後就可以訪問遠端服務端的方法了。

View的事件體系相關

1、View的座標都是相對於View的父容器來說的,因此它是一種相對座標;

2、在Android中,x軸和y軸的正方向分別為右和下,不僅僅是Android,大部分顯示系統都是按照這個標準來定義座標系的;

3、MotionEvent提供了兩組方法:getX/getY和getRawX/getRawY,getX/getY返回的是相對於當前View左上角的x和y座標,而getRawX/getRawY返回的是相對於手機螢幕左上角的x和y座標;

4、TouchSlop是系統所能識別出的被認為是滑動的最小距離,如果兩次滑動之間的距離小於這個常量,那麼系統就不認為你是在進行滑動操作。這是一個常量,和裝置有關,在不同裝置上這個值有可能是不同的,可以通過如下方式獲取這個常量:ViewConfiguration.get(getContext()).getScaledTouchSlop()。但我們在處理滑動時,可利用這個常量來對觸控操作做去抖處理;

5、VelocityTracker用於追蹤手指在滑動過程中的速度,包括水平和豎直方向的速度;

6、GesturceDetector用於輔助檢測使用者的單擊、滑動、長按、雙擊等行為;

7、Scroller用於實現View的彈性滑動,Scroller本身無法讓View彈性滑動,它需要和View的computeScroll方法配合使用才能共同完成這個功能;

8、可以通過如下三種方式來實現View的滑動:

  • 第一種:通過View本身提供的scrollTo/scrollBy方法來實現滑動;特點是操作簡單,適合對View內容的滑動;

  • 第二種:通過動畫給View施加平移效果來實現滑動;特點是操作簡單,適用於沒有互動的View和實現複雜的動畫效果;

  • 第三種:通過改變View的LayoutParams使得View重新佈局從而實現滑動;操作稍微複雜,適用於有互動的View。

9、點選事件的分發過程由三個很重要的方法來共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent:

  • dispatchTouchEvent:用來進行事件的分發,如果事件能夠傳遞給當前View,那麼此方法一定會被呼叫,返回結果受當前View的onTouchEvent和View的dispatchTouchEvent方法的影響,表示是否當消耗當前事件;

  • onInterceptTouchEvent:用來判斷是否攔截某個事件,如果當前View攔截了某個事件,那麼在同一個事件序列當中,此方法不會被再次呼叫,返回結果表示是否攔截當前事件;

  • onTouchEvent:在dispatchTouchEvent方法中呼叫,用來處理點選事件,返回結果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,當前View無法再次接收到事件。

View的工作原理相關

1、View的繪製流程是從ViewRoot的performTraversals方法開始的,它經過measure、layout和draw三個過程才能最終將一個View繪製出來,其中measure用來測量View的寬和高,layout用來確定View在父容器中的放置位置,而draw則負責將View繪製在螢幕上。

2、DecorView作為頂級View,一般情況下它內部包含一個豎直方向的LinearLayout,在這個LinearLayout裡面有上下兩個部分(具體情況和Android版本及主體有關),上面的是標題欄,下面的是內容欄。在Activity中通過setContentView所設定的佈局檔案其實就是被加到內容欄之中的,而內容欄的id是content,在程式碼中可以通過ViewGroup content = findViewById(R.android.id.content)來得到content對應的layout;

3、MeasureSpec將SpecMode和SpecSize打包成一個int值來避免過多的物件記憶體分配,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指測量模式,而SpecSize是指在某種測量模式下的規格大小。MeasureSpec在很大程度上決定了一個View的尺寸規格,之所以說是很大程度上是因為這個過程還受父容器的影響,因為父容器影響View的MeasureSpec的建立過程,SpecMode有三類:

  • UNSPECIFIED:父容器不對View有任何限制,要多大給多大,這種情況一般用於系統內部,表示一種測量狀態;

  • EXACTLY:父容器已經檢測出View所需要的精確大小,這個時候View的最終大小就是SpecSize所指定的值。它對應於LayoutParams中的match_parent和具體的數值這兩種模式;

  • AT_MOST:父容器指定了一個可用大小即SpecSize,View的大小不能大於這個值,具體是什麼值要看不同View的具體實現。它對應於LayoutParams中的wrap_content。

4、對於普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定,只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以快速地確定出子元素的MeasureSpec了,有了MeasureSpec就可以進一步確定出子元素測量後的大小。針對不同的父容器和View本身不同的LayoutParams,View就可以有多重MeasureSpec。當View採用固定寬/高的時候,不管父容器的MeasureSpec是什麼,View的MeasureSpec都是精確模式並且其大小遵循LayoutParams中的大小。當View的寬/高是match_parent時,如果父容器的模式是精確模式,那麼View也是精確模式並且其大小是父容器的剩餘空間;如果父容器是最大模式,那麼View也是最大模式並且其大小不會超過父容器的剩餘空間。當View的寬/高是wrap_content時,不管父容器的模式是精確還是最大化,View的模式總是最大化並且大小不能超過父容器的剩餘空間。

5、直接繼承View的自定義控制元件需要重寫onMeasure方法並設定wrap_content時的自身大小,否則在佈局中使用wrap_content時就相當於使用match_parent。

6、在Activity的onCreate、onStart、onResume方法中均無法正確得到某個View的寬/高資訊,這是因為View的measure過程和Activity的生命週期方法不是同步執行的,因此無法保證Activity執行了onCreate、onStart、onResume時某個View就已經測量完畢了,如果View還沒有測量完畢,那麼獲得的寬/高就是0。

可以通過如下四個方法來解決獲取View寬/高為0的問題:

  • 在Activity/View的onWindowFocusChanged方法(View已經初始化完畢了,寬/高已經準備好了)中獲取View的寬高;

  • 在view.post(runnable)方法(將runnable投遞到訊息佇列的尾部,等待Looper呼叫此runnable的時候,View也已經初始化好了)中獲取View的寬高;

  • 使用ViewTreeObserver;

  • 手動呼叫View的measure方法;

7、在View的預設實現中,View的測量寬/高和最終寬/高是相等的,只不過測量寬/高形成於View的measure過程,而最終寬/高形成於View的layout過程,即兩者的賦值時機不同,測量寬/高的賦值時機稍微早一些。多數情況下可以認為View的測量寬/高就等於最終的寬/高,但對於在View的layout中改變了View的left、top、right、bottom四個屬性時,得出的測量寬/高有可能和最終的寬/高不一致;還有就是View需要多次measure才確定自己測量寬/高的情況時,在前幾次的測量過程中,其得出的測量寬/高有可能和最終的寬/高不一致,但最終測量寬/高還是和最終寬/高相同。

8、View的draw流程如下:

  • 繪製背景(background.draw);

  • 繪製自己(onDraw);

  • 繪製children(dispatchDraw);

  • 繪製裝飾(onDrawScrollBars)。

9、View有一個特殊的方法setWillNotDraw,如果一個View不需要繪製任何內容,設定這個標記位true後,系統會進行優化。預設情況下,View沒有啟用這個優化標記位,但是ViewGroup會預設啟用這個優化標記位。這個標記位對實際開發的意義是:如果自定義控制元件繼承於ViewGroup並且本身不具備繪製功能時,就可以開啟這個標記位從而便於系統進行後續的優化。當明確知道一個ViewGroup需要通過onDraw來繪製內容時,需要顯示地關閉WILL_NOT_DRAW這個標記位。

持續更新中,未完待續!可關注本公眾號訂閱後續更新!!!

關於Java和Android大牛頻道

Java和Android大牛頻道是一個數萬人關注的探討Java和Android開發的公眾號,分享和原創最有價值的乾貨文章,讓你成為這方面的大牛

我們探討android和Java開發最前沿的技術:android效能優化 ,外掛化,跨平臺,動態化,加固和反破解等,也討論設計模式/軟體架構等。由群來自BAT的工程師組成的團隊

關注即送紅包,回覆:“百度” 、“阿里”、“騰訊” 有驚喜!!!關注後可用入微信群。群裡都是來自百度阿里騰訊的大牛。

歡迎關注我們,一起討論技術,掃描和長按下方的二維碼可快速關注我們。搜尋微信公眾號:JANiubility。


公眾號:JANiubility