Android開發面試題總結(精華)

Android面試總結(持續更新修改)

1.Android 的四大組件是哪些,它們的作用?

①Activity是Android程序與用戶交互的窗口,是Android構造塊中最基本的一種,它需要為保持各界面的狀態,做很多持久化的事情,妥善管理生命周期以及一些跳轉邏輯

②service:後臺服務於 Activity,封裝一個完整的功能邏輯實現,接受上層指令,完成相關的事物

③Content Provider:是 Android 提供的第三方應用數據的訪問方案,可以派生 Content Provider 類,對外提供數據,可以像數據庫一樣進行選擇排序,屏蔽內部數據的存儲細節,向外提供統一的接口模型,大大簡化上層應用,對數據的整合提供瞭更方便的途徑

④BroadCastReceiver:接受一種或者多種Intent作觸發事件,接受相關消息,做一些簡單處理,轉換成一條Notification,統一瞭 Android 的事件廣播模型

2.Activity的生命周期,及每個周期的應用場景

比如說手機衛士每次進入某個界面的時候都要看到最新的數據,這個刷新列表的操作 就放在onStart()的方法裡面.這樣保證每次用戶看到的數據都是最新的.

多媒體播放, 播放來電話.

onStop() 視頻,

視頻聲音設置為0 , 記錄視頻播放的位置 mediaplayer.pause();

onStart() 根據保存的狀態恢復現場. mediaplayer.start()

3.Activity的四種啟動模式分別是什麼,以及他們的特點

意義在於 : 記錄這些activity開啟的先後順序,google引入任務棧(task stack)概念,幫助維護好的用戶體驗。

①standard 默認標準的啟動模式, 每次startActivity都是創建一個新的activity的實例。

適用於絕大大數情況

②singleTop 單一頂部,如果要開啟的activity在任務棧的頂部已經存在,就不會創建新的實例,而是調用 onNewIntent() 方法。應用場景: 瀏覽器書簽。 避免棧頂的activity被重復的創建,解決用戶體驗問題。

③singletask 單一任務棧 ,activity隻會在任務棧裡面存在一個實例。如果要激活的activity,在任務棧裡面已經存在,就不會創建新的activity,而是復用這個已經存在的activity,調用 onNewIntent() 方法,並且清空這個activity任務棧上面所有的activity 應用場景:瀏覽器activity, 整個任務棧隻有一個實例,節約內存和cpu的目的註意: activity還是運行在當前應用程序的任務棧裡面的。不會創建新的任務棧。

④singleInstance 單態 單例模式單一實例,整個手機操作系統裡面隻有一個實例存在。不同的應用去打開這個activity共享 公用的同一個activity。他會運行在自己單獨,獨立的任務棧裡面,並且任務棧裡面隻有他一個實例存在。

5.你後臺的Activity被系統 回收怎麼辦?

答:重寫onSaveInstanceState()方法,在此方法中保存需要保存的數據,該方法將會在activity被回收之前調用。通過重寫 onRestoreInstanceState()方法可以從中提取保存好的數據

6.Fragment生命周期是什麼?

onAttach() – onCreate() – onCreateView()- onActivitCreated – onStart() – onResume() – onPause()

onStop() – onDestoryView() – onDestory() – onDetach()

7.Activity和Fragment如何進行通信,Activity之間通信有幾種方式

①Activity向Fragment傳遞數據 :一 通過bundle設置參數Bundle bundle = new Bundle();bundle.putString(“name”,”加多寶”);sf.setArguments(bundle);二 通過調用接口設置接口 讓被接收數據方 實現該方法讓發送方調用該接口

②Fragment 向 A ctivity 傳遞數據 :一 通過 getActivity 強轉成 需要接收數據的Activity 調用其方法 二 通過接口 還可以利用 sp sqLite 等

③Activity之間傳遞數據:通過全局application類共享數據;通過Intent傳值;使用靜態變量傳遞數據;使用剪切板傳遞數據;

8.Service 的生命周期(兩種)?

①onCreate – onStartCommand(每次開啟都要執行,可以執行多次) – onDestory()——-隻要不執行方法onStop(),在內存中永遠駐留

②onCreate – onBind(隻能執行一次) – onUnbind() – onDestory()——-依賴於Activity,隨著Activity的銷毀而關閉

註意:同一個服務可以被開啟多次,但是關閉隻需要一次

9.如何開機就啟服務?

通過廣播接收者監聽開機廣播然後在onReceive(Context context, Intent intent)方法 裡開啟相應的服務

10.Service是否在mainThread中執行, service裡面是否能執行耗時的操作?

是 不能

11.Service和IntentService的區別?

Service主要用於後臺服務,當應用程序被掛到後臺的時候,為瞭保證應用某些組件仍然可以工作而引入瞭Service這個概念,那麼這裡面要強調的是Service不是獨立的進程,也不 是獨立的線程,它是依賴於應用程序的主線程的,在更多時候不建議在Service中編寫耗時的邏輯和操作,否則會引起ANR。IntentService是繼承Service的,那麼它包含瞭Service的全部特性,當然也包含service的生命周期,那麼與service不同的是,IntentService在執行onCreate操作的時候,內部開瞭一個線程,去執行耗時操作

12.廣播的兩種註冊方式?

①清單文件註冊

②代碼註冊

IntentFilter filter = new IntentFilter();

filter.addAction(Intent.ACTION_MEDIA_REMOVED);

filter.addAction(Intent.ACTION_MEDIA_EJECT);

filter.addAction(Intent.ACTION_MEDIA_MOUNTED);

filter.addDataScheme(“file”);

sdcardStateReceiver = new SdcardStateChanageReceiver();

registerReceiver(sdcardStateReceiver,filter);

13.IPC(Inter-Progress-Communication)哪些方式?簡述AIDL

Messenger Socket AIDL ContentProvider Intent

14.數據存儲幾種方式?分別是什麼?

Android 提供瞭 5 種方式存儲數據:

①使用 SharedPreferences 存儲數據:它是 Android 提供的用來存儲一些簡單配置信息的一種機制,采用瞭 XML 格式將數據存儲到設備中。隻能在同一個包內使用,不能在不同的包之間使用。

②文件存儲數據:文件存儲方式是一種較常用的方法,在 Android 中讀取/寫入文件的方法,與 Java 中實現 I/O 的程序是完全一樣的,提供瞭 openFileInput()和

openFileOutput()方法來讀取設備上的文件。

③SQLite 數據庫存儲數據:SQLite 是 Android 所帶的一個標準的數據庫,它支持SQL 語句,它是一個輕量級的嵌入式數據庫。

④使用 ContentProvider 存儲數據:主要用於應用程序之間進行數據交換,從而能夠讓其他的應用保存或讀取此 Content Provider 的各種數據類型

⑤網絡存儲數據:通過網絡上提供給我們的存儲空間來上傳(存儲)和下載(獲取)我們存儲在網絡空間中的數據信息。

15.listView的優化

①第一層: 復用converterView

if(converterView==null) {

converterView = View.inflate(R.layout.xxx); 10–>11

}

問題: 每次執行getView()都需要執行converterView.findViewById()得到子View

②第二層: 使用ViewHolder, 減少findViewById()的次數

③第三層: 對數據列表進行分頁加載顯示

1). 自己做: 通過Scroll監聽listView.setonScrollListener(scrollListener), 當到達底部時加載下一頁列表數據並顯示

2). 使用第方開源框架: Android-PullToRefresh或其它

④第四層優化: 圖片級緩存處理 參見圖片級緩存機制

⑤Item佈局,層級越少越好,使用hierarchyview工具查看優化。

⑥item中圖片時,異步加載

⑦快速滑動時,不加載圖片、

⑧item中圖片時,應對圖片進行適當壓縮

16.簡述三級緩存的實現?

例如: 如何根據url根據圖片顯示?

①根據url從一級緩存(Map

3.是否存在滾動條

當我們做類似上拉加載下一頁這樣的功能的時候,頁面初始的時候需要知道當前WebView是否存在縱向滾動條,如果有則不加載下一頁,如果沒有則加載下一頁直到其出現縱向滾動條。首先繼承WebView類,在子類添加下面的代碼:

public boolean existVerticalScrollbar () {

return computeVerticalScrollRange() > computeVerticalScrollExtent();

}

computeVerticalScrollRange得到的是可滑動的最大高度,computeVerticalScrollExtent得到的是滾動把手自身的高,當不存在滾動條時,兩者的值是相等的。當有滾動條時前者一定是大於後者的。

是否已滾動到頁面底部

同樣我們在做上拉加載下一頁這樣的功能時,也需要知道當前頁面滾動條所處的狀態,如果快到底部,則要發起網絡請求數據更新網頁。同樣繼承WebView類,在子類覆蓋onScrollChanged方法,具體如下:

@Override

protected void onScrollChanged(int newX, int newY, int oldX, int oldY) {

super.onScrollChanged(newX, newY, oldX, oldY);

if (newY != oldY) {

float contentHeight = getContentHeight() * getScale();

// 當前內容高度下從未觸發過, 瀏覽器存在滾動條且滑動到將抵底部位置

if (mCurrContentHeight != contentHeight && newY > 0 && contentHeight <= newY + getHeight() + mThreshold) {

// TODO Something…

mCurrContentHeight = contentHeight;

}

}

}

上面mCurrContentHeight用於記錄上次觸發時的網頁高度,用來防止在網頁總高度未發生變化而目標區域發生連續滾動時會多次觸發TODO,mThreshold是一個閾值,當頁面底部距離滾動條底部的高度差<=這個值時會觸發TODO。

遠程網頁需訪問本地資源

當我們在WebView中加載出從web服務器上拿取的內容時,是無法訪問本地資源的,如assets目錄下的圖片資源,因為這樣的行為屬於跨域行為(Cross-Domain),而WebView是禁止的。解決這個問題的方案是把html內容先下載到本地,然後使用loadDataWithBaseURL加載html。這樣就可以在html中使用 file:///android_asset/xxx.png 的鏈接來引用包裡面assets下的資源瞭。示例如下:

private void loadWithAccessLocal(final String htmlUrl) {

new Thread(new Runnable() {

public void run() {

try {

final String htmlStr = NetService.fetchHtml(htmlUrl);

if (htmlStr != null) {

TaskExecutor.runTaskOnUiThread(new Runnable() {

@Override

public void run() {

loadDataWithBaseURL(htmlUrl, htmlStr, “text/html”, “UTF-8”, “”);

}

});

return;

}

} catch (Exception e) {

Log.e(“Exception:” + e.getMessage());

}

TaskExecutor.runTaskOnUiThread(new Runnable() {

@Override

public void run() {

onPageLoadedError(-1, “fetch html failed”);

}

});

}

}).start();

}

上面幾點需要註意:

從網絡上下載html的過程應放在工作線程中

html下載成功後渲染出html的步驟應放在UI主線程,不然WebView會報錯

html下載失敗則可以使用我們前面講述的方法來顯示自定義錯誤界面

ViewPager裡非首屏WebView點擊事件不響應

如果你的多個WebView是放在ViewPager裡一個個加載出來的,那麼就會遇到這樣的問題。ViewPager首屏WebView的創建是在前臺,點擊時沒有問題;而其他非首屏的WebView是在後臺創建,滑動到它後點擊頁面會出現如下錯誤日志:

20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found

解決這個問題的辦法是繼承WebView類,在子類覆蓋onTouchEvent方法,填入如下代碼:

@Override

public boolean onTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());

}

return super.onTouchEvent(ev);

}

該方法的最先提出在WebView in ViewPager not receive user inputs。

WebView硬件加速導致頁面渲染閃爍

4.0以上的系統我們開啟硬件加速後,WebView渲染頁面更加快速,拖動也更加順滑。但有個副作用就是,當WebView視圖被整體遮住一塊,然後突然恢復時(比如使用SlideMenu將WebView從側邊滑出來時),這個過渡期會出現白塊同時界面閃爍。解決這個問題的方法是在過渡期前將WebView的硬件加速臨時關閉,過渡期後再開啟,代碼如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

}

避免addJavaScriptInterface帶來的安全問題

使用開源項目Safe Java-JS WebView Bridge可以很好替代addJavaScriptInterface方法,同時增加瞭異步回調等支持,並且不存在瞭安全風險。

@Override

public boolean onTouchEvent(MotionEvent ev) {

boolean ret = super.onTouchEvent(ev);

if (mPreventParentTouch) {

switch (ev.getAction()) {

case MotionEvent.ACTION_MOVE:

requestDisallowInterceptTouchEvent(true);

ret = true;

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

requestDisallowInterceptTouchEvent(false);

mPreventParentTouch = false;

break;

}

}

return ret;

}

public void preventParentTouchEvent () {

mPreventParentTouch = true;

}

代碼控制的關鍵在於mPreventParentTouch這個變量,mPreventParentTouch默認為false,當用戶touchdown頁面元素時通知該WebView將mPreventParentTouch設置為true。示意代碼如下:

document.getElementById("targetEle").addEventListener("touchstart", function(ev) { HostApp.preventParentTouchEvent(); // 通知WebView阻止祖先對其Touch事件的攔截 } ); document.getElementById("targetEle").addEventListener("touchmove", function(ev) { // todo something on this page } );

關於web頁面如何通知WebView(即調用Java方法)請參看第8條。

剛提到瞭上面是一種簡單的做法,並不能很好的解決手指滑動過快帶來的誤操作問題,即當用戶快速地滑動時,還是有一定機率會出現ViewPager攔截TouchMove事件而發生瞭Tab切換而非頁面元素做出瞭響應。要完美解決此問題,就要用到稍微復雜一點的方法(僅是整體消息傳遞流程復雜一點)。

首先假設在ViewPager之上還有一個父元素叫做ParentViewOnViewPager,當我們接收到頁面preventParentTouchEvent通知時就先於ViewPager而進行攔截。如下:

ParentViewOnViewPager.java

public class ParentViewOnViewPager extends FrameLayout {

private MineWebView mDispatchWebView;

public void preventParentTouchEvent (WebView view) {

mDispatchWebView = (MineWebView)view;

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_MOVE && mDispatchWebView != null) {

mDispatchWebView.ignoreTouchCancel(true);

return true;

}

return false;

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

if (mDispatchWebView != null){

switch (ev.getAction()) {

case MotionEvent.ACTION_MOVE:

mDispatchWebView.onTouchEvent(ev);

break;

default:

mDispatchWebView.ignoreTouchCancel(false);

mDispatchWebView.onTouchEvent(ev);

mDispatchWebView = null;

break;

}

return true;

}

return super.onTouchEvent(ev);

}

}

即當ParentViewOnViewPager接收到通知時,發起TouchEvent攔截,將攔截到的Touch事件轉嫁到裝載頁面的mDispatchWebView進行事件派發。這樣就直接跳過瞭ViewPager這一層。這裡需要註意的是當ParentViewOnViewPager發起攔截時,WebView會接收到一個TouchCancel事件,WebView應該忽略這個事件,以避免頁面接收到這個事件而打斷整個處理流程。如下代碼所示:

MineWebView.java

public class MineWebView extends WebView {

boolean mIgnoreTouchCancel;

public void ignoreTouchCancel (boolean val) {

mIgnoreTouchCancel = val;

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

return ev.getAction() == MotionEvent.ACTION_CANCEL && mIgnoreTouchCancel || super.onTouchEvent(ev);

}

}

另外針對這種解決方案,頁面端的JS腳本不用做任何變動。

42.如何優化佈局?

1、場景:些佈局會隱藏顯佈局會展示

把View的初始可見View.GONE但是在Inflate佈局的時候View仍然會被Inflate,也就是說仍然會創建對象,會被實例化,會被設置屬性。也就是說,會耗費內存等資源。

2、替換方式:

推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,使用非常簡單:

mViewStub = (ViewStub) this.findViewById(R.id.viewstub);

mViewStub.inflate();

它是一個占用資源非常小的控件,相當於一個“占位控件”。使用時可以為ViewStub指定一個佈局,在Inflate佈局的時候,隻有ViewStub會被初始化,然後當ViewStub被設置為可見的時或調用瞭ViewStub.inflate()的時候,ViewStub所指向的佈局就會被inflate實例化,此佈局文件直接將當前ViewStub替換掉,然後ViewStub的佈局屬性都會傳給它所指向的佈局。這樣,就可以使用ViewStub在運行時動態顯示佈局,節約內存資源。

目的: 正確把握住ViewStub的應用場景非常重要,因為使用ViewStub可以優化佈局,一般應用在當前佈局或控件在用戶使用較少情況下,這樣可以提高性能,節約內存,加快界面渲染

3、佈局重用可以通過這個標簽直接加載外部的xml到當前結構中,是復用UI資源的常用標簽

4、減少視圖層級

標簽在UI的結構優化中起著非常重要的作用,它可以刪減多餘的層級,優化UI。多用於替換FrameLayout(因為所有的Activity視圖的根結點都是FrameLayout,如果當前的佈局根結點是Framelayout,那麼可以用merge替代,減少多餘的層級)或者當一個佈局包含另一個時,標簽消除視圖層次結構中多餘的視圖組。例如你的主佈局文件是垂直佈局,又include引入瞭一個垂直佈局,這是如果include佈局使用的LinearLayout就沒意義瞭,使用的話反而減慢你的UI渲染。這時可以使用標簽進行優化。

You May Also Like