Android面試題——View篇
Android面試題View篇
Activity生命週期?
onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()
Activity的啟動過程(不要回答生命週期)
app啟動的過程有兩種情況,第一種是從桌面launcher上點選相應的應用圖示,第二種是在activity中通過呼叫startActivity來啟動一個新的activity。 我們建立一個新的專案,預設的根activity都是MainActivity,而所有的activity都是儲存在堆疊中的,我們啟動一個新的activity就會放在上一個activity上面,而我們從桌面點選應用圖示的時候,由於launcher本身也是一個應用,當我們點選圖示的時候,系統就會呼叫startActivitySately(),一般情況下,我們所啟動的activity的相關資訊都會儲存在intent中,比如action,category等等。我們在安裝這個應用的時候,系統也會啟動一個PackaManagerService的管理服務,這個管理服務會對AndroidManifest.xml檔案進行解析,從而得到應用程式中的相關資訊,比如service,activity,Broadcast等等,然後獲得相關元件的資訊。當我們點選應用圖示的時候,就會呼叫startActivitySately()方法,而這個方法內部則是呼叫startActivty(),而startActivity()方法最終還是會呼叫startActivityForResult()這個方法。而在startActivityForResult()這個方法。因為startActivityForResult()方法是有返回結果的,所以系統就直接給一個-1,就表示不需要結果返回了。而startActivityForResult()這個方法實際是通過Instrumentation類中的execStartActivity()方法來啟動activity,Instrumentation這個類主要作用就是監控程式和系統之間的互動。而在這個execStartActivity()方法中會獲取ActivityManagerService的代理物件,通過這個代理物件進行啟動activity。啟動會就會呼叫一個checkStartActivityResult()方法,如果說沒有在配置清單中配置有這個元件,就會在這個方法中丟擲異常了。當然最後是呼叫的是Application.scheduleLaunchActivity()進行啟動activity,而這個方法中通過獲取得到一個ActivityClientRecord物件,而這個ActivityClientRecord通過handler來進行訊息的傳送,系統內部會將每一個activity元件使用ActivityClientRecord物件來進行描述,而ActivityClientRecord物件中儲存有一個LoaderApk物件,通過這個物件呼叫handleLaunchActivity來啟動activity元件,而頁面的生命週期方法也就是在這個方法中進行呼叫。
Activity的啟動模式
-
standard:預設標準模式,每啟動一個都會建立一個例項,
-
singleTop:棧頂複用,如果在棧頂就呼叫onNewIntent複用,從onResume()開始
-
singleTask:棧內複用,本棧內只要用該型別Activity就會將其頂部的activity出棧
-
singleInstance:單例模式,除了3中特性,系統會單獨給該Activity建立一個棧,
Activity快取方法
-
1.配置改變導致Activity被殺死,橫屏變豎屏:在onStop之前會呼叫onSaveInstanceState()儲存資料在重建Activity之後,會在onStart()之後呼叫onRestoreInstanceState(),並把儲存下來的Bundle傳給onCreate()和它會預設重建Activity當前的檢視,我們可以在onCreate()中,回覆自己的資料。
-
2.記憶體不足殺掉Activity,優先順序分別是:前臺可見,可見非前臺,後臺。
Service的生命週期,兩種啟動方法,有什麼區別
- context.startService() ->onCreate()- >onStart()->Service running-->(如果呼叫context.stopService() )->onDestroy() ->Service shut down
-
1.如果Service還沒有執行,則呼叫onCreate()然後呼叫onStart();
-
2.如果Service已經執行,則只調用onStart(),所以一個Service的onStart方法可能會重複呼叫多次。
-
3.呼叫stopService的時候直接onDestroy,
-
4.如果是呼叫者自己直接退出而沒有呼叫stopService的話,Service會一直在後臺執行。該Service的呼叫者再啟動起來後可以通過stopService關閉Service。
- context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
-
1.onBind將返回給客戶端一個IBind介面例項,IBind允許客戶端回撥服務的方法,比如得到Service執行的狀態或其他操作。
-
2.這個時候會把呼叫者和Service繫結在一起,Context退出了,Service就會呼叫onUnbind->onDestroy相應退出。
-
3.所以呼叫bindService的生命週期為:onCreate --> onBind(只一次,不可多次繫結) --> onUnbind --> onDestory。
靜態的Broadcast 和動態的有什麼區別
-
1.動態的比靜態的安全
-
2.靜態在app啟動的時候就初始化了 動態使用程式碼初始化
-
3.靜態需要配置 動態不需要
-
4.生存期,靜態廣播的生存期可以比動態廣播的長很多
-
5.優先順序動態廣播的優先順序比靜態廣播高
-
6.靜態不受頁面生命週期的影響,即使退出了頁面,也可以收到廣播這種廣播一般用於想開機自啟動啊等等,由於這種註冊的方式的廣播是常駐型廣播,所以會佔用CPU的資源。
-
7.動態叫非常駐型廣播,收到生命週期的影響,退出頁面後,就不會收到廣播,我們通常運用在更新UI方面。這種註冊方式優先順序較高。最後需要解綁,否會會記憶體洩露。
-
8.廣播是分為有序廣播和無序廣播。
Android的佈局方式有哪些?
LinearLayout,RelativeLayout,TableLayout,FrameLayout,AbsoluteLayout,GridLayout
在建立fragment時如何傳遞初始化引數?
Fragment初始化一定要提供預設建構函式。不能用建構函式傳遞引數!不要寫帶引數的建構函式。在Fragment裡新增獲取Fragment的newInstance函式,以後獲取Fragment就使用這個函式,不要使用建構函式新建Fragment!使用setArgument和getArgument傳遞引數
裝置橫豎屏切換的時候,接下來會發生什麼?
-
1、不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次
-
2、設定Activity的android:configChanges=”orientation”時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次
-
3、設定Activity的android:configChanges=”orientation|keyboardHidden”時,切屏不會重新呼叫各個生命週期,只會執行onConfigurationChanged方法
Android啟動Service的兩種方式是什麼? 它們的適用情況是什麼?
-
如果後臺服務開始後基本可以獨立執行的話,可以用startService。音樂播放器就可以這樣用。它們會一直執行直到你呼叫 stopSelf或者stopService。你可以通過傳送Intent或者接收Intent來與正在執行的後臺服務通訊,但大部分時間,你只是啟動服務並讓它獨立執行。如果你需要與後臺服務通過一個持續的連線來比較頻繁地通訊,建議使用bind()。比如你需要定位服務不停地把更新後的地理位置傳給UI。Binder比Intent開發起來複雜一些,但如果真的需要,你也只能使用它。
-
startService:生命週期與呼叫者不同。啟動後若呼叫者未呼叫stopService而直接退出,Service仍會執行
-
bindService:生命週期與呼叫者繫結,呼叫者一旦退出,Service就會呼叫unBind->onDestroy
談談你對Android中Context的理解?
Context是一個抽象基類。在翻譯為上下文,也可以理解為環境,是提供一些程式的執行環境基礎資訊。Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各種承擔著不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert型別的Dialog),因此在這種場景下,我們只能使用Activity型別的Context,否則將會出錯。
getApplicationContext()和getApplication()方法得到的物件都是同一個application物件,只是物件的型別不一樣。 Context數量 = Activity數量 + Service數量 + 1 (1為Application)
理解Activity,View,Window三者關係
這個問題真的很不好回答。所以這裡先來個算是比較恰當的比喻來形容下它們的關係吧。Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示檢視)LayoutInflater像剪刀,Xml配置像窗花圖紙。 1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。 2:這個PhoneWindow有一個“ViewRoot”,這個“ViewRoot”是一個View或者說ViewGroup,是最初始的根檢視。 3:“ViewRoot”通過addView方法來一個個的新增View。比如TextView,Button等 4:這些View的事件監聽,是由WindowManagerService來接受訊息,並且回撥Activity函式。比如onClickListener,onKeyDown等。
Service的onCreate回撥在UI執行緒中嗎?
Service生命週期的各個回撥和其他的應用元件一樣,是跑在主執行緒中,會影響到你的UI操作或者阻塞主執行緒中的其他事情。
View的繪製流程
自定義控制元件: 1、組合控制元件。這種自定義控制元件不需要我們自己繪製,而是使用原生控制元件組合成的新控制元件。如標題欄。 2、繼承原有的控制元件。這種自定義控制元件在原生控制元件提供的方法外,可以自己新增一些方法。如製作圓角,圓形圖片。 3、完全自定義控制元件:這個View上所展現的內容全部都是我們自己繪製出來的。比如說製作水波紋進度條。
View的繪製流程:OnMeasure()——>OnLayout()——>OnDraw()
第一步:OnMeasure():測量檢視大小。從頂層父View到子View遞迴呼叫measure方法,measure方法又回撥OnMeasure。
第二步:OnLayout():確定View位置,進行頁面佈局。從頂層父View向子View的遞迴呼叫view.layout方法的過程,即父View根據上一步measure子View所得到的佈局大小和佈局引數,將子View放在合適的位置上。
第三步:OnDraw():繪製檢視。ViewRoot建立一個Canvas物件,然後呼叫OnDraw()。六個步驟:①、繪製檢視的背景;②、儲存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子檢視,如果沒有就不用; ⑤、還原圖層(Layer);⑥、繪製滾動條。
View,ViewGroup事件分發
-
Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。
-
ViewGroup和View組成了一個樹狀結構,根節點為Activity內部包含的一個ViwGroup。
-
觸控事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸控事件中,Down和Up都只有一個,Move有若干個,可以為0個。
-
當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。ViewGroup的遍歷可以看成是遞迴的。分發的目的是為了找到真正要處理本次完整觸控事件的View,這個View會在onTouchuEvent結果返回true。
-
當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是儲存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup儲存的會是真實處理事件的View所在的ViewGroup物件:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被儲存在ViewGroup1中,而ViewGroup1也會返回true,被儲存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。
-
當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是呼叫super.dispatchTouchEvent函式,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。
-
onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。
Android中touch事件的傳遞機制是怎樣的?
-
1.Touch事件傳遞的相關API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
-
2.Touch事件相關的類有View、ViewGroup、Activity
-
3.Touch事件會被封裝成MotionEvent物件,該物件封裝了手勢按下、移動、鬆開等動作
-
4.Touch事件通常從Activity#dispatchTouchEvent發出,只要沒有被消費,會一直往下傳遞,到最底層的View
-
5.如果Touch事件傳遞到的每個View都不消費事件,那麼Touch事件會反向向上傳遞,最終交由Activity#onTouchEvent處理
-
6.onInterceptTouchEvent為ViewGroup特有,可以攔截事件
-
7.Down事件到來時,如果一個View沒有消費該事件,那麼後續的MOVE/UP事件都不會再給它
Fragment與Fragment、Activity通訊的方式
-
直接在一個Fragment中呼叫另外一個Fragment中的方法
-
使用介面回撥
-
使用廣播
-
Fragment直接呼叫Activity中的public方法
在建立fragment時如何傳遞初始化引數?
Fragment初始化一定要提供預設建構函式。不能用建構函式傳遞引數!不要寫帶引數的建構函式。在Fragment裡新增獲取Fragment的newInstance函式,以後獲取Fragment就使用這個函式,不要使用建構函式新建Fragment!使用setArgument和getArgument傳遞引數
如何規避oom?
-
使用更加輕量的資料結構
-
避免在Android裡面使用Enum
-
減小Bitmap物件的記憶體佔用
-
使用更小的圖片
-
複用系統自帶的資源
-
注意在ListView/GridView等出現大量重複子元件的圖裡面對ConvertView的複用
-
Bitmap物件的複用
-
避免在onDraw方法裡面執行物件的建立
-
避免物件的記憶體洩露(重點)
-
考慮使用Application Context而不是Activity Context
-
注意WebView的洩漏(重點)
-
資原始檔需要選擇合適的資料夾進行存放
-
謹慎使用static物件(重點)
-
特別留意單例物件中不合理的持有
-
珍惜Services資源
-
謹慎使用“抽象”程式設計
-
謹慎使用依賴注入框架
-
.謹慎使用多程序
-
Handler的使用(重點)
-
強軟弱虛引用的應用(重點)
-
主執行緒操作UI,子執行緒操作資料(必填)
Android 中如何捕獲未捕獲的異常
自 定 義 一 個 Application , 比 如 叫 MyApplication 繼 承 Application 實 現 UncaughtExceptionHandler。 覆寫 UncaughtExceptionHandler 的 onCreate 和 uncaughtException 方法。
儲存Activity狀態
onSaveInstanceState(Bundle)會在activity轉入後臺狀態之前被呼叫,也就是onStop()方法之前,onPause方法之後被呼叫;
資料儲存有哪些方式?
1.sharedpreferences 2.file 3.Sqlite 4.ContentProvide 5.網路儲存
如何將一個Activity設定成視窗的樣式?
第一種方法,在styles.xml檔案中,可以新建如下的類似Dialog的style。
<style name="Theme.FloatActivity" parent="android:style/Theme.Dialog"> </style>。
第二種方法,在AndroidManifest.xml中在需要顯示為視窗的Activity中新增如下屬性: android:theme=“@style/Theme.FloatActivity”。 也可以直接新增對應需要展示為Dialog style的Activity的android:theme屬性為android:theme=“@android:style/Theme.Dialog”。
ScrollView是否可以和ListView混合使用?如何可以,說明混合使用的方式,如果不行,說明原因。
可以,計算整個ListView的高度,填充資料後重新設定ListView高度,重寫onMeasure和onInterceptTouchEvent方法
解決ScrollView巢狀ListView和GridView衝突的方法
重寫ListView的onMeasure方法,來自定義高度: 解決ScrollView巢狀ListView和GridView衝突的方法 重寫ListView的onMeasure方法,來自定義高度:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, expandSpec); }
通過Intent傳遞一些二進位制資料的方法有哪些? 使用Serializable介面實現序列化,這是Java常用的方法。 實現Parcelable介面,這裡Android的部分類比如Bitmap類就已經實現了,同時Parcelable在Android AIDL中交換資料也很常見的。
Serializable 和 Parcelable 的區別
在使用記憶體的時候,Parcelable 類比 Serializable 效能高,所以推薦使用 Parcelable 類。
-
Serializable 在序列化的時候會產生大量的臨時變數,從而引起頻繁的 GC。
-
Parcelable 不能使用在要將資料儲存在磁碟上的情況。儘管 Serializable 效率低點,但在這 種情況下,還是建議你用 Serializable 。
Bitmap的處理
-
1.當使用ImageView的時候,可能圖片的畫素大於ImageView,此時就可以通過BitmapFactory.Option來對圖片進行壓縮,inSampleSize表示縮小2^(inSampleSize-1)倍。
-
2.BitMap的快取:
-
1.同步載入只建立一個執行緒然後按照順序進行圖片載入
-
2.非同步載入使用執行緒池,讓存在的載入任務都處於不同執行緒
-
3.為了不開啟過多的非同步任務,只在列表靜止的時候開啟圖片載入
-
1.使用LruCache進行記憶體快取。
-
2.使用DiskLruCache進行硬碟快取。
-
3.實現一個ImageLoader的流程:同步非同步載入、圖片壓縮、記憶體硬碟快取、網路拉取
過度繪製、卡頓優化:
-
1.過度繪製:
-
1.移除Window預設的Background:getWidow.setBackgroundDrawable(null);
-
2.移除XML佈局檔案中非必需的Background
-
3.減少佈局巢狀(扁平化的一個體現,減少View數的深度,也就減少了View樹的遍歷時間,渲染的時候,前後期的工作,總是按View樹結點來)
-
4.在引入佈局檔案裡面,最外層可以用merge替代LinearLayout,RelativeLayout,這樣把子UI元素直接銜接在include位置
-
5.工具:HierarchyViewer 檢視檢視層級
-
2.卡頓優化:16ms資料更新
view繪製機制和載入過程,請詳細說整個流程
-
1.ViewRootImpl會呼叫performTraversals(),其內部會呼叫performMeasure()、performLayout、performDraw()。
-
2.performMeasure()會呼叫最外層的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),這之中會遍歷子View然後迴圈呼叫measureChild()這之中會用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起獲取本View的MeasureSpec,然後呼叫子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()預設返回measureSpec的測量數值,所以繼承View進行自定義的wrap_content需要重寫。
-
3.performLayout()會呼叫最外層的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()設定本View的四個頂點位置。在onLayout(抽象方法)中確定子View的位置,如LinearLayout會遍歷子View,迴圈呼叫setChildFrame()-->子View.layout()。
-
4.performDraw()會呼叫最外層ViewGroup的draw():其中會先後呼叫background.draw()(繪製背景)、onDraw()(繪製自己)、dispatchDraw()(繪製子View)、onDrawScrollBars()(繪製裝飾)。
-
5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(對應精確值和match_parent)、AT_MOST(對應warp_content))和30位SpecSize組成一個int,DecorView的MeasureSpec由視窗大小和其LayoutParams決定,其他View由父View的MeasureSpec和本View的LayoutParams決定。ViewGroup中有getChildMeasureSpec()來獲取子View的MeasureSpec。
-
6.三種方式獲取measure()後的寬高:
-
1.Activity#onWindowFocusChange()中呼叫獲取
-
2.view.post(Runnable)將獲取的程式碼投遞到訊息佇列的尾部。
-
3.ViewTreeObservable.
Bitmap影象模式有哪幾種,給出一張1080 * 1920的,ARGB 8888格式的佔用記憶體是多大
-
Bitmap.Config ARGB_4444:每個畫素佔四位,即A=4,R=4,G=4,B=4,那麼一個畫素點佔4+4+4+4=16位
-
Bitmap.Config ARGB_8888:每個畫素佔四位,即A=8,R=8,G=8,B=8,那麼一個畫素點佔8+8+8+8=32位
-
Bitmap.Config RGB_565:每個畫素佔四位,即R=5,G=6,B=5,沒有透明度,那麼一個畫素點佔5+6+5=16位
-
Bitmap.Config ALPHA_8:每個畫素佔四位,只有透明度,沒有顏色。
ARGB:指的是一種色彩模式,裡面A代表Alpha,R表示red,G表示green,B表示blue。 ARGB 8888一個畫素佔用4個位元組,一個位元組8位,1080 * 1920 * 4 * 8
圖片優化
-
對圖片本身進行操作。儘量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource來設定一張大圖,因為這些方法在完成decode後, 最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體.
-
圖片進行縮放的比例,SDK中建議其值是2的指數值,值越大會導致圖片不清晰。
-
不用的圖片記得呼叫圖片的recycle()方法
Android UI適配
字型使用sp,使用dp,多使用match_parent,wrap_content,weight 圖片資源,不同圖片的的解析度,放在相應的資料夾下可使用百分比代替。
Android中的幾種動畫
-
幀動畫:Drawable Animation,指通過指定每一幀的圖片和播放時間,有序的進行播放而形成動畫效果,比如想聽的律動條。
-
補間動畫:View Animation(Tween Animation),指通過指定View的初始狀態、變化時間、方式,通過一系列的演算法去進行圖形變換,從而形成動畫效果,主要有Alpha、Scale、Translate、Rotate四種效果。注意:只是在檢視層實現了動畫效果,並沒有真正改變View的屬性,比如滑動列表,改變標題欄的透明度。
-
屬性動畫:Property Animation,在Android3.0的時候才支援,通過不斷的改變View的屬性,不斷的重繪而形成動畫效果。相比於檢視動畫,View的屬性是真正改變了。比如view的旋轉,放大,縮小。
HybridApp WebView和JS互動
Android與JS通過WebView互相呼叫方法,實際上是: Android去呼叫JS的程式碼
-
通過WebView的loadUrl(),使用該方法比較簡潔,方便。但是效率比較低,獲取返回值比較困難。
-
通過WebView的evaluateJavascript(),該方法效率高,但是4.4以上的版本才支援,4.4以下版本不支援。所以建議兩者混合使用。 JS去呼叫Android的程式碼
-
通過WebView的addJavascriptInterface()進行物件對映 ,該方法使用簡單,僅將Android物件和JS物件對映即可,但是存在比較大的漏洞。
漏洞產生原因是:當JS拿到Android這個物件後,就可以呼叫這個Android物件中所有的方法,包括系統類(java.lang.Runtime 類),從而進行任意程式碼執行。 解決方式: (1)Google 在Android 4.2 版本中規定對被呼叫的函式以 @JavascriptInterface進行註解從而避免漏洞攻擊。 (2)在Android 4.2版本之前採用攔截prompt()進行漏洞修復。