安卓面試總結(jié)——基礎部分

基本組件

1.activity

running(棧頂 ---paused(可見,失去交互能力,沒有焦點-----stopped(不可見,完全覆蓋----killed(已回收

oncreate預加載---onstart可見,不可交互-----onresume可交互

android進程優(yōu)先級

前臺--可見--服務--后臺--空


注意:

1、不設置Activity的android:configChanges時,切屏會重新調(diào)用各個生命周期,切橫屏時會執(zhí)行一次,切豎屏時會執(zhí)行兩次

2、設置Activity的android:configChanges="orientation"時,切屏還是會重新調(diào)用各個生命周期,切橫、豎屏時只會執(zhí)行一次

3、設置Activity的android:configChanges="orientation|keyboardHidden|screensize"時,切屏不會重新調(diào)用各個生命周期,只會執(zhí)行onConfigurationChanged方法

橫豎屏

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

比方說我們點擊跳轉(zhuǎn)一個新Activity,這個時候Activity會入棧,同時它的生命周期也會從onCreate()到onResume()開始變換,這個過程是在ActivityStack里完成的,ActivityStack是運行在Server進程里的,這個時候Server進程就通過ApplicationThread的代理對象ApplicationThreadProxy向運行在app進程ApplicationThread發(fā)起操作請求。

ApplicationThread接收到操作請求后,因為它是運行在app進程里的其他線程里,所以ApplicationThread需要通過Handler向主線程ActivityThread發(fā)送操作消息。

主線程接收到ApplicationThread發(fā)出的消息后,調(diào)用主線程ActivityThread執(zhí)行響應的操作,并回調(diào)Activity相應的周期方法。

Activity:通過LoadedApk的makeApplication()方法創(chuàng)建。

Service:通過LoadedApk的makeApplication()方法創(chuàng)建。

2.service

不可做耗時操作,運行在主線程中

因為activity被銷毀后無法對thread進行控制,不能獲取之前子線程實例,所以需要用到service

啟動方式:

1.startservice(intent) stopservice

2.bindservice unbindservice

使用startService()方法啟用服務,調(diào)用者與服務之間沒有關連,即使調(diào)用者退出了,服 務仍然運行。

使用bindService()方法啟用服務,調(diào)用者與服務綁定在了一起,調(diào)用者一旦退出,服務也就終止。

如果打算采用startService()方法啟動服務,在服務未被創(chuàng)建時,系統(tǒng)會先調(diào)用服務的onCreate()方法,接著調(diào)用onStartCommand()方法。如果調(diào)用startService()方法前服務已經(jīng)被創(chuàng)建,多次調(diào)用startService()方法并不會導致多次創(chuàng)建服務,但會導致多次調(diào)用onStartCommand()方法。采用startService()方法啟動的服務,只能調(diào)用stopService()方法結(jié)束服務,服務結(jié)束時會調(diào)用onDestroy()方法。(由于會多次調(diào)用onStartCommand方法,所以可以通過intent來傳值)

如果打算采用bindService()方法啟動服務,在服務未被創(chuàng)建時,系統(tǒng)會先調(diào)用服務的onCreate()方法,接著調(diào)用onBind()方法。這個時候調(diào)用者和服務綁定在一起,調(diào)用者退出了,系統(tǒng)就會先調(diào)用服務的onUnbind()方法,接著調(diào)用onDestroy()方法。如果調(diào)用bindService()方法前服務已經(jīng)被綁定,多次調(diào)用bindService()方法并不會導致多次創(chuàng)建服務及綁定(也就是說onCreate()和onBind()方法并不會被多次調(diào)用,但是多次執(zhí)行bindService)。如果調(diào)用者希望與正在綁定的服務解除綁定,可以調(diào)用unbindService()方法,調(diào)用該方法也會導致系統(tǒng)調(diào)用服務的onUnbind()-->onDestroy()方法。(由于onBind只會調(diào)用一次,而onBind又會傳入intent參數(shù),所以intent的值不會改變,個人認為本實例不適合bindService)

讓service和activity交互,oncreate - > onbind ->onserviceconnected

a.服務端創(chuàng)建Ibinder接口實現(xiàn)實例,給客戶端調(diào)用

?b.用onBind()回調(diào)方法返回Binder實例

?c.客戶端用new serviceconnction,重寫onserviceConnectd回調(diào)方法接收IBinder,并用bindservice綁定服務

onServiceDisconnected 在服務意外終止時會調(diào)用

onUnbind會destroy服務

在Activity中創(chuàng)建一個thread跟在service中創(chuàng)建一個thread之間的區(qū)別?

在Activity中被創(chuàng)建:該Thread的就是為這個Activity服務的,完成這個特定的Activity交代的任務,主動通知該Activity一些消息和事件,Activity銷毀后,該Thread也沒有存活的意義了。

在Service中被創(chuàng)建:這是保證最長生命周期的Thread的唯一方式,只要整個Service不退出,Thread就可以一直在后臺執(zhí)行,一般在Service的onCreate()中創(chuàng)建,在onDestroy()中銷毀。所以,在Service中創(chuàng)建的Thread,適合長期執(zhí)行一些獨立于APP的后臺任務,比較常見的就是:在Service中保持與服務器端的長連接。

3.broadcast?

類似java觀察者模式

定義:發(fā)送攜帶數(shù)據(jù)的intent來廣播

?1.normal broadcast ?sendBroadcast

?2.有序廣播 包括系統(tǒng)廣播 sendOrderedBroadcast

?3.local broadcast app內(nèi)

靜態(tài)注冊 manifest 一直運行,app殺死也能收到

動態(tài)注冊,更靈活,跟隨activity生命周期,需要unregist

實現(xiàn)機制:

1.自定義broadcastReceiver,復寫onreceive

2.通過binder機制向AcitivityManagerService注冊

3.發(fā)送者通過binder機制向AcitivityManagerService發(fā)送廣播

4.AcitivityManagerService查找符合條件(intentfilter/permission)的receiver,將其發(fā)到相應receiver的消息循環(huán)隊列中

5.消息循環(huán)拿到此廣播,回調(diào)broadcastreceiver的onreceive方法

local broadcast

1.app內(nèi)傳播,不擔心數(shù)據(jù)泄漏

2.不可接受其他應用廣播

3.比系統(tǒng)廣播高效

高效的原因是因為內(nèi)部通過關聯(lián)主線程getlooper handler實現(xiàn),sendbroadcast其實通過handler的sendMessage實現(xiàn)

2.內(nèi)部協(xié)作主要靠兩個hashmap, mReceivers和mActions,還有mPendBroadCasts 接受的廣播對象,存儲廣播接收器

PackageManager是包管理服務,在安裝的時候,就會將靜態(tài)注冊的廣播保存下來。發(fā)送對應廣播的時候,AMS就會根據(jù)intent去查找PackagManager保存的靜態(tài)注冊receiver,若進程還沒起來,會先啟動進程,然后調(diào)用receiver的onReceive函數(shù)。

4.view

繪制原理http://www.itdecent.cn/p/6bdb3b50d788

由于onCreate會先于handleResumeActivity執(zhí)行,我們在onCreate中調(diào)用了setContentView,也只是生成DecorView并給這個DecorView的內(nèi)容設置了布局而已,此時還并沒有把這個DecorView添加到Window中,同樣,WMS中也還沒有這個Window,所以此時并不能做任何事情(繪制、接收點擊事件等),雖然調(diào)用了requestLayout和invalidate,并不會真正觸發(fā)布局和重繪(因為還沒有與ViewRootImpl進行綁定)

Activity與Window產(chǎn)生聯(lián)系,是在調(diào)用activity#attach方法中,生成了一個PhoneWindow,并把這個activity對象自身,設置給了Window的Callback回調(diào)(Activity實現(xiàn)了Window的Callback接口)

Window與WindowManagerService產(chǎn)生聯(lián)系,是在handleResumeActivity中,先執(zhí)行了onResume方法,通過調(diào)用WindowManagerImpl#addView方法將這個Activity對應的DecorView添加到了這個Window中,addView方法是一個IPC操作,將這個Window也添加到了WindowManagerService中;

ViewRootImpl與Window產(chǎn)生聯(lián)系,是在WindowManagerImpl#addView方法中,這個過程中會new一個ViewRootImpl與DecorView相對應,保存在WindowManagerGloable中;

總的來說,就是setContentView生成了DecorView及其視圖,在onResume之后才把這個視圖添加進了Window和WMS中,具備了交互能力;

布局查看工具view inspector 取代了hierachy viewer,但是沒有性能查看功能,hv仍然可以在sdk tools里找到

measure--layout--draw

measure

自上而下遍歷,每個ViewGroup會向它內(nèi)部的每個子View發(fā)送measure命令,然后由具體子View的onMeasure()來測量自己的尺寸。最后測量的結(jié)果調(diào)用setMeasureDimension()保存在View的mMeasuredWidth和mMeasuredHeight中

1.viewGroup.LayoutParams 視圖高寬

2.MeasureSpec 測量規(guī)格,傳遞父布局對子View尺寸測量的約束信息 32位int? ?3種模式1.不確定 2.exactly 3.atMost mode+大小?

?子view的layoutPrams結(jié)合父容器的mode生成子view的measureSpec

就是ScrollView內(nèi)部嵌套ListView,而該ListView數(shù)據(jù)條數(shù)是不確定的,所以需要設置為包裹內(nèi)容,然后就會發(fā)現(xiàn)ListView就會顯示第一行出來。需要繼承ListView,覆寫onMeasure方法。

scrollview的super.onInterceptTouchEvent就不會攔截listview的滑動

measure的過程

framelayout 和relativelayout會measure兩次

layout

子視圖的具體位置是相對父視圖而言,view.onlayout方法是抽象實現(xiàn),必須重新實現(xiàn),自上而下拜訪

draw?

invalidate()

請求重繪 View 樹,即 draw 過程,假如視圖發(fā)生大小沒有變化就不會調(diào)用layout()過程,并且只繪制那些調(diào)用了invalidate()方法的 View。

requestLayout()

當布局變化的時候,比如方向變化,尺寸的變化,會調(diào)用該方法,在自定義的視圖中,如果某些情況下希望重新測量尺寸大小,應該手動去調(diào)用該方法,它會觸發(fā)measure()和layout()過程,但不會進行 draw。

5.fragment

Fragment實現(xiàn)滑動可以借助ViewPager。ViewPager為了讓滑動的時候防止出現(xiàn)卡頓現(xiàn)象,它的內(nèi)部有一個緩存機制,默認情況下,ViewPager會提前創(chuàng)建好當前Fragment旁的兩個Fragment。但是如果加載的數(shù)據(jù)比較耗時或者占用內(nèi)存較大,就需要考慮是否實現(xiàn)懶加載來加載fragment。

加載方式:

1.xml?

2.動態(tài)加載 fragmentManager管理,fragmentTrancaction.add? ----commit() //add和replace的區(qū)別,replace替換最上方的F

頁面較少使用 FragmentpagerAdapter,只會detach fragment,? 較多使用FragmentStatePagerAdapter,會釋放不用的fragment,節(jié)約內(nèi)存

生命周期

onattach--onCreate--onCreateView---onViewCreated--(Activity onCreate)---onActivityCreated--(Acitivity onstart)--onStart--(Activity onResume)---onResume

onpause--(Activity onpause)---onstop---(Activity onstop)--onDestroyView--onDestroy--onDettach--(Activity onedestroy)

Fragment和acitivity互相調(diào)用

1.fragment調(diào)用getActivity

2.Activity調(diào)用F,接口回調(diào),F(xiàn)中定義接口,activity實現(xiàn)

???? 使用三方庫,Eventbus實現(xiàn),具體怎么實現(xiàn)百度Eventbus用法

???? 廣播

???? 使用觀察者模式

3.F調(diào)用F,找到另一個activity,然后調(diào)用另一個F的實例

8.事件分發(fā)機制

dispatchevent 決定是觸摸事件是由自己的ontouchevent處理還是分發(fā)給子view,并遞歸調(diào)用自身dispatchevent使之向下傳遞

oninterceptTouchEvent是ViewGroup的方法, 父控件下發(fā)事件給子控件處理,若true子控件需要攔截,那dispatchevent?來調(diào)用onTouchEvent,返回false繼續(xù)下發(fā),返回true表示已攔截

在Action_Down的情況下,事件會先傳遞到最頂層的ViewGroup,調(diào)用ViewGroup的dispatchTouchEvent(),

①如果ViewGroup的onInterceptTouchEvent()返回false不攔截該事件,則會分發(fā)給子View,調(diào)用子View的dispatchTouchEvent(),如果子View的dispatchTouchEvent()返回true,則調(diào)用View的onTouchEvent()消費事件。

②如果ViewGroup的onInterceptTouchEvent()返回true攔截該事件,則調(diào)用ViewGroup的onTouchEvent()消費事件,接下來的Move和Up事件將由該ViewGroup直接進行處理

當某個子View的dispatchTouchEvent()返回true時,會中止Down事件的分發(fā),同時在ViewGroup中記錄該子View。接下來的Move和Up事件將由該子View直接進行處理。

Activity---phoneWindow---decorview--viewGroup---子view

分發(fā)到最后也沒有消費哦,事件依次反轉(zhuǎn),最后回到activity,若activity也不處理,則放棄

10.webview?

1.APi16前有安全漏洞,沒有限制addJavascriptInterface ,遠程攻擊者可以執(zhí)行任何對象的方法

2.布局文件中使用,webview寫在其他容器中時,當destroy時,需要從容器中remove webview,然后webview.destroy,不然內(nèi)存泄漏

3.jsbridge 本地native和遠程js互相調(diào)用

4.頁面加載完成時調(diào)用webView.onPageFinished用webChrome.onProgressChanged

5.后臺耗電,webview沒正常銷毀,webview有時會開線程

6.webView硬件加速導致頁面渲染問題:webView設置硬件加速有可能會出現(xiàn)閃爍,加載白塊等現(xiàn)象,解決問題的方法就是關閉webView硬件加速。

解決方法

1、獨立進程,簡單暴力,不過可能涉及到進程間通信(和handler持有外部引用一樣

2、動態(tài)添加WebView,對傳入WebView中使用的Context使用弱引用,動態(tài)添加WebView意思是在布局創(chuàng)建一個ViewGroup用來放置WebView,activity創(chuàng)建時add進來,在Activity停止時remove掉。

開發(fā)中,webview使用完畢直接干掉進程

11.Binder

http://www.itdecent.cn/p/afa794939379

什么是binder

1.binder是一種跨進程的通信機制

2.對server來說,binder指binder本地對象,對client來說,binder是proxy對象

3.對傳輸過程而言,binder是可以跨進程的對象,用內(nèi)核轉(zhuǎn)換

為什么用binder,比socket性能好,更安全

在android系統(tǒng)當中,它是運行在內(nèi)核當中,它負責各個用戶進程,通過binder通訊的內(nèi)核來進行交互的一個模塊。

binder通信模型:

1.server端在serviceManager中注冊方法attachinterface

2.client端去sm中查詢此方法

3.sm給client一個proxy對象的方法(空方法

4.內(nèi)核在client調(diào)用此方法時會調(diào)用server端的方法

如果是自己寫的service是同一進程是Binder,也就是直接調(diào)用,這個在編譯后生成的IXXXX.java的asInterface中能看到

如果調(diào)用系統(tǒng)的service,顯然是跨進程的,是BinderProxy,這里也是在asInterface中轉(zhuǎn)換的。

AIDL?是對Binder的一種封裝

?全自動生成stub的靜態(tài)內(nèi)部抽象類,建立binder本地對象,需要自己實現(xiàn)方法

1.創(chuàng)建一個aidl文件,在android studio中直接右鍵->new->aidl->aidl file

2.創(chuàng)建一個Service,使用一個內(nèi)部類繼承Stub,實現(xiàn)其中的方法;重寫OnBind()方法,返回這個內(nèi)部類的實例。

3.將該aidl文件復制到要調(diào)用接口的項目中,其中包名也要和原項目相同。接著編譯該項目。

4.在新項目中連接遠程服務,重寫onServiceConnected方法,通過Stub的asInterface方法將IBinder對象轉(zhuǎn)換成相應的aidl類,最后就能通過這個aidl類做愛做的事了。

原理:

進程A與進程B進行通信,二者都含有一個相同的aidl文件。假設A要將消息發(fā)送給B,則A中的Proxy將消息發(fā)送到系統(tǒng)IBinder中,IBinder再將該消息發(fā)送到B中的Stub。即Proxy是發(fā)送方,Stub是接受方。

在Stub的構(gòu)造方法中有一個attachInterface方法,這是Binder類中的方法,它將當前的AIDL對象進行注冊到系統(tǒng)中,用于之后判斷是本地調(diào)用還是遠程調(diào)用

?asInterface方法,

???? asInterface方法用于在客戶端獲取aidl對象。該方法獲取IBinder對象后,將IBinder中的DESCRIPTOR與本地的DESCRIPTOR比較,若相同,則直接返回本地的AIDL對象;若不同,再通過Proxy創(chuàng)建一個AIDL對象。

onTransact方法,根據(jù)AIDL函數(shù)返回編號進行相應方法調(diào)用

12Listview

recyclebin機制,內(nèi)部類,只顯示 暴露的幾條item

優(yōu)化

getview用convertview參數(shù),緩存view

viewholder封裝view,避免多次findviewbyid,控件過多應i選哪個性能

getview少做耗時操作,不然滑動卡頓,可以加監(jiān)聽,滑動停止時再加載,減少半透明緩存

添加頭布局必須在setAdapter之前

13.launcher流程

Launcher也是個應用程序,不過是個特殊的應用。俗稱“桌面”。通過PackageManagerService查詢所有已安裝的應用程序,并保存相應的圖標、應用名稱、包名和第一個要啟動的類名等。

總體流程

點擊桌面應用圖標,Launcher進程將啟動Activity(MainActivity)的請求以Binder的方式發(fā)送給了AMS。

AMS接收到啟動請求后,交付ActivityStarter處理Intent和Flag等信息,然后再交給ActivityStackSupervisior/ActivityStack

處理Activity進棧相關流程。同時以Socket方式請求Zygote進程fork新進程。

Zygote接收到新進程創(chuàng)建請求后fork出新進程。

在新進程里創(chuàng)建ActivityThread對象,新創(chuàng)建的進程就是應用的主線程,在主線程里開啟Looper消息循環(huán),開始處理創(chuàng)建Activity。

ActivityThread利用ClassLoader去加載Activity、創(chuàng)建Activity實例,并回調(diào)Activity的onCreate()方法。這樣便完成了Activity的啟動。

14 activity啟動模式

singleTop的使用

使用singleTop模式的Activity在棧頂時只會在Task中存在一個實例

防止用戶多次觸發(fā)startActivity,可以將目標Activity設置為singleTop

singleTask的使用

當Task中存在Activity實例,不會創(chuàng)建Activity,而是銷毀Activity上面所有其他的Activity,以此來使將要跳轉(zhuǎn)的Activity至于棧頂顯示,如果不存在,則在棧頂創(chuàng)建一個Activity實例(所以假如有人問如何快速關閉100個Activity,只要給跳轉(zhuǎn)的Activity設置singleTask即可)

一般都是主頁和登陸頁。

singleTask的Activity如果設置了獨立的taskAffinity屬性值,啟動時就會在新的Task中,否則會在已有Task中

15contentporvider

public boolean onCreate()

在創(chuàng)建ContentProvider時使用

public Cursor query()

用于查詢指定uri的數(shù)據(jù)返回一個Cursor

public Uri insert()

用于向指定uri的ContentProvider中添加數(shù)據(jù)

public int delete()

用于刪除指定uri的數(shù)據(jù)

public int update()

用戶更新指定uri的數(shù)據(jù)

public String getType()

用于返回指定的Uri中的數(shù)據(jù)MIME類型

注冊ContentObserver來監(jiān)聽對應uri的數(shù)據(jù)變化,這步不是必須的,如果不需要監(jiān)聽數(shù)據(jù)變化也可以不注冊

在構(gòu)造函數(shù)中我們需要傳遞一個Handler,到此我們可以明白,ContentObserver在收到數(shù)據(jù)變化的通知后通過Handler機制來通知主線程更新UI

通過ContentProvider來操作數(shù)據(jù)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容