Android面試常問基礎知識點(附詳細解答)

朝陽楊大爺發表於2019-03-28

1、四大元件是什麼

1)Activity:使用者可操作的視覺化介面,為使用者提供一個完成操作指令的視窗。一個Activity通常是一個單獨的螢幕,Activity通過Intent來進行通訊。Android中會維持一個Activity Stack,當一個新Activity建立時,它就會放到棧頂,這個Activity就處於執行狀態。

2)Service:服務,執行在手機後臺,適合執行不需和使用者互動且還需長期執行的任務。

3)ContentProvider:內容提供者,使一個應用程式的指定資料集提供給其他應用程式,其他應用可通過ContentResolver類從該內容提供者中獲取或存入資料。它提供了一種跨程式資料共享的方式,當資料被修改後,ContentResolver介面的notifyChange函式通知那些註冊監控特定URI的ContentObserver物件。 如果ContentProvider和呼叫者在同一程式中,ContentProvider的方法(query/insert/update/delete等)和呼叫者在同一執行緒中;如果ContentProvider和呼叫者不在同一程式,ContentProvider方法會執行在它自身程式的一個Binder執行緒中。

4)Broadcast Receiver: 廣播接收者,運用在應用程式間傳輸資訊,可以使用廣播接收器來讓應用對一個外部事件做出響應。

2、四大元件的生命週期和簡單用法

1)Activity:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory() onCreate():為Activity設定佈局,此時介面還不可見; onStart(): Activity可見但還不能與使用者互動,不能獲得焦點 onRestart(): 重新啟動Activity時被回撥 onResume(): Activity可見且可與使用者進行互動 onPause(): 當前Activity暫停,不可與使用者互動,但還可見。在新Activity啟動前被系統呼叫儲存現有的Activity中的持久資料、停止動畫等。 onStop(): 當Activity被新的Activity覆蓋不可見時被系統呼叫 onDestory(): 當Activity被系統銷燬殺掉或是由於記憶體不足時呼叫

2)Service a) onBind方式繫結的:onCreate->onBind->onUnBind->onDestory(不管呼叫bindService幾次,onCreate只會呼叫一次,onStart不會被呼叫,建立連線後,service會一直執行,直到呼叫unBindService或是之前呼叫的bindService的Context不存在了,系統會自動停止Service,對應的onDestory會被呼叫) b) startService啟動的:onCreate->onStartCommand->onDestory(start多次,onCreate只會被呼叫一次,onStart會呼叫多次,該service會在後臺執行,直至被呼叫stopService或是stopSelf) c) 又被啟動又被繫結的服務,不管如何呼叫onCreate()只被呼叫一次,startService呼叫多少次,onStart就會被呼叫多少次,而unbindService不會停止服務,必須呼叫stopService或是stopSelf來停止服務。必須unbindService和stopService(stopSelf)同時都呼叫了才會停止服務。

3)BroadcastReceiver a) 動態註冊:存活週期是在Context.registerReceiver和Context.unregisterReceiver之間,BroadcastReceiver每次收到廣播都是使用註冊傳入的物件處理的。 b) 靜態註冊:程式在的情況下,receiver會正常收到廣播,呼叫onReceive方法;生命週期只存活在onReceive函式中,此方法結束,BroadcastReceiver就銷燬了。onReceive()只有十幾秒存活時間,在onReceive()內操作超過10S,就會報ANR。 程式不存在的情況,廣播相應的程式會被拉活,Application.onCreate會被呼叫,再呼叫onReceive。

4)ContentProvider:應該和應用的生命週期一樣,它屬於系統應用,應用啟動時,它會跟著初始化,應用關閉或被殺,它會跟著結束。

3、Activity之間的通訊方式

1)通過Intent方式傳遞引數跳轉

2)通過廣播方式

3)通過介面回撥方式

4)藉助類的靜態變數或全域性變數

5)藉助SharedPreference或是外部儲存,如資料庫或本地檔案

4、Activity各種情況下的生命週期

  1. 兩個Activity(A->B)切換(B正常的Activity)的生命週期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)->oStop(A) 這時如果按回退鍵回退到A  onPause(B)->onRestart(A)->onStart(A)->onResume(A)->oStop(B) 如果在切換到B後呼叫了A.finish(),則會走到onDestory(A),這時點回退鍵會退出應用

  2. 兩個Activity(A->B)切換(B透明主題的Activity或是Dialog風格的Acivity)的生命週期:onPause(A)->onCreate(B)->onStart(B)->onResume(B) 這時如果回退到A  onPause(B)->onResume(A)->oStop(B)->onDestory(B)

  3. Activity(A)啟動後點選Home鍵再回到應用的生命週期:onPause(A)->oStop(A)->onRestart(A)->onStart(A)->onResume(A)

5、橫豎屏切換的時候,Activity 各種情況下的生命週期**

1)切換橫屏時:onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume

  1. 切換豎屏時:會列印兩次相同的log    onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume

  2. 如果在AndroidMainfest.xml中修改該Activity的屬性,新增android:configChanges="orientation"

  3. 如果AndroidMainfest.xml中該Activity中的android:configChanges="orientation|keyboardHidden",則只會列印 onConfigurationChanged->

6、Activity與Fragment之間生命週期比較

Fragment生命週期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach 切換到該Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume 按下Power鍵:onPause->onSaveInstanceState->onStop 點亮螢幕解鎖:onStart->onRestoreInstanceState->onResume 切換到其他Fragment: onPause->onStop->onDestoryView 切回到該Fragment: onCreateView->onActivityCreated->onStart->onResume 退出應用:onPause->onStop->onDestoryView->onDestory->onDetach

7、Activity上有Dialog的時候按Home鍵時的生命週期

AlertDialog並不會影響Activity的生命週期,按Home鍵後才會使Activity走onPause->onStop,AlertDialog只是一個元件,並不會使Activity進入後臺。

8、兩個Activity 之間跳轉時必然會執行的是哪幾個方法?

前一個Activity的onPause,後一個Activity的onResume

9、前臺切換到後臺,然後再回到前臺,Activity生命週期回撥方法。彈出Dialog,生命值週期回撥方法。

  1. 前臺切換到後臺,會執行onPause->onStop,再回到前臺,會執行onRestart->onStart->onResume

  2. 彈出Dialog,並不會影響Activity生命週期

10、Activity的四種啟動模式對比

1)standard:標準啟動模式(預設),每啟動一次Activity,都會建立一個例項,即使從ActivityA startActivity ActivityA,也會再次建立A的例項放於棧頂,當回退時,回到上一個ActivityA的例項。

  1. singleTop:棧頂複用模式,每次啟動Activity,如果待啟動的Activity位於棧頂,則不會重新建立Activity的例項,即不會走onCreate->onStart,會直接進入Activity的onPause->onNewIntent->onResume方法

  2. singleInstance: 單一例項模式,整個手機作業系統裡只有一個該Activity例項存在,沒有其他Actvity,後續請求均不會建立新的Activity。若task中存在例項,執行例項的onNewIntent()。應用場景:鬧鐘、瀏覽器、電話

  3. singleTask:棧內複用,啟動的Activity如果在指定的taskAffinity的task棧中存在相應的例項,則會把它上面的Activity都出棧,直到當前Activity例項位於棧頂,執行相應的onNewIntent()方法。如果指定的task不存在,建立指定的taskAffinity的task,taskAffinity的作用,進入指寫taskAffinity的task,如果指定的task存在,將task移到前臺,如果指定的task不存在,建立指定的taskAffinity的task. 應用場景:應用的主頁面

11、Activity狀態儲存於恢復

Activity被主動回收時,如按下Back鍵,系統不會儲存它的狀態,只有被動回收時,雖然這個Activity例項已被銷燬,但系統在新建一個Activity例項時,會帶上先前被回收Activity的資訊。在當前Activity被銷燬前呼叫onSaveInstanceState(onPause和onStop之間儲存),重新建立Activity後會在onCreate後呼叫onRestoreInstanceState(onStart和onResume之間被呼叫),它們的引數Bundle用來資料儲存和讀取的。 儲存View狀態有兩個前提:View的子類必須實現了onSaveInstanceState; 必須要設定Id,這個ID作為Bundle的Key

12、fragment各種情況下的生命週期

正常情況下的生命週期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach

1)Fragment在Activity中replace  onPause(舊)->onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onStop(舊)->onDestoryView(舊) 如果新增到backStack中,呼叫remove()方法fragment的方法會走到onDestoryView,但不會執行onDetach(),即fragment本身的例項是存在的,成員變數也存在,但是view被銷燬了。如果新替換的Fragment已在BackStack中,則不會執行onAttach->onCreate

13、Fragment狀態儲存onSaveInstanceState是哪個類的方法,在什麼情況下使用?**

在對應的FragmentActivity.onSaveInstanceState方法會呼叫FragmentController.saveAllState,其中會對mActive中各個Fragment的例項狀態和View狀態分別進行儲存.當Activity在做狀態儲存和恢復的時候, 在它其中的fragment自然也需要做狀態儲存和恢復.

14、Fragment.startActivityForResult是和FragmentActivity的startActivityForResult?

如果希望在Fragment的onActivityResult接收資料,就要呼叫Fragment.startActivityForResult, 而不是Fragment.getActivity().startActivityForResult。Fragment.startActivityForResult->FragmentActivitymHost.HostCallbacks.onStartActivityFromFragment->FragmentActivity.startActivityFromFragment。如果request=-1則直接呼叫FragmentActivity.startActivityForResult,它會重新計算requestCode,使其大於0xfffff。

15、如何實現Fragment的滑動?

ViewPager+FragmentPagerAdapter+List

16、fragment之間傳遞資料的方式?

1)在相應的fragment中編寫方法,在需要回撥的fragment裡獲取對應的Fragment例項,呼叫相應的方法; 2)採用介面回撥的方式進行資料傳遞; a) 在Fragment1中建立一個介面及介面對應的set方法; b) 在Fragment1中呼叫介面的方法;c)在Fragment2中實現該介面; 3)利用第三方開源框架EventBus

17、service和activity怎麼進行資料互動?

1)通過bindService啟動服務,可以在ServiceConnection的onServiceConnected中獲取到Service的例項,這樣就可以呼叫service的方法,如果service想呼叫activity的方法,可以在service中定義介面類及相應的set方法,在activity中實現相應的介面,這樣service就可以回撥介面言法;

2)通過廣播方式

18、說說ContentProvider、ContentResolver、ContentObserver 之間的關係

ContentProvider實現各個應用程式間資料共享,用來提供內容給別的應用操作。如聯絡人應用中就使用了ContentProvider,可以在自己應用中讀取和修改聯絡人資訊,不過需要獲取相應的許可權。它也只是一箇中介軟體,真正的資料來源是檔案或SQLite等。 ContentResolver內容解析者,用於獲取內容提供者提供的資料,通過ContentResolver.notifyChange(uri)發出訊息 ContentObserver內容監聽者,可以監聽資料的改變狀態,觀察特定Uri引起的資料庫變化,繼而做一些相應的處理,類似於資料庫中的觸發器,當ContentObserver所觀察的Uri發生變化時,便會觸發它。

19、請描述一下廣播BroadcastReceiver的理解

BroadcastReceiver是一種全域性監聽器,用來實現系統中不同元件之間的通訊。有時候也會用來作為傳輸少量而且傳送頻率低的資料,但是如果資料的傳送頻率比較高或者數量比較大就不建議用廣播接收者來接收了,因為這樣的效率很不好,因為BroadcastReceiver接收資料的開銷還是比較大的。

20、廣播的分類

1)普通廣播:完全非同步的,可以在同一時刻(邏輯上)被所有接收者接收到,訊息傳遞的效率比較高,並且無法中斷廣播的傳播。

2)有序廣播:傳送有序廣播後,廣播接收者將按預先宣告的優先順序依次接收Broadcast。優先順序高的優先接收到廣播,而在其onReceiver()執行過程中,廣播不會傳播到下一個接收者,此時當前的廣播接收者可以abortBroadcast()來終止廣播繼續向下傳播,也可以將intent中的資料進行修改設定,然後將其傳播到下一個廣播接收者。 sendOrderedBroadcast(intent, null);//傳送有序廣播

3)粘性廣播:sendStickyBroadcast()來傳送該型別的廣播資訊,這種的廣播的最大特點是,當粘性廣播傳送後,最後的一個粘性廣播會滯留在作業系統中。如果在粘性廣播傳送後的一段時間裡,如果有新的符合廣播的動態註冊的廣播接收者註冊,將會收到這個廣播訊息,雖然這個廣播是在廣播接收者註冊之前傳送的,另外一點,對於靜態註冊的廣播接收者來說,這個等同於普通廣播。

21、廣播使用的方式和場景

1)App全域性監聽:在AndroidManifest中靜態註冊的廣播接收器,一般我們在收到該訊息後,需要做一些相應的動作,而這些動作與當前App的元件,比如Activity或者Service的是否執行無關,比如我們在整合第三方Push SDK時,一般都會新增一個靜態註冊的BroadcastReceiver來監聽Push訊息,當有Push訊息過來時,會在後臺做一些網路請求或者傳送通知等等。

2)元件區域性監聽:這種主要是在Activity或者Service中使用registerReceiver()動態註冊的廣播接收器,因為當我們收到一些特定的訊息,比如網路連線發生變化時,我們可能需要在當前Activity頁面給使用者一些UI上的提示,或者將Service中的網路請求任務暫停。所以這種動態註冊的廣播接收器適合特定元件的特定訊息處理。

22、在manifest 和程式碼中如何註冊和使用BroadcastReceiver?

  1. mainfest中註冊:靜態註冊的廣播接收者就是一個常駐在系統中的全域性監聽器,也就是說如果你應用中配置了一個靜態的BroadcastReceiver,而且你安裝了應用而無論應用是否處於執行狀態,廣播接收者都是已經常駐在系統中了。
<receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
        <action android:name="com.smilexie.test.intent.mybroadcastreceiver"/>
    </intent-filter>
</receiver>
複製程式碼
  1. 動態註冊:動態註冊的廣播接收者只有執行了registerReceiver(receiver, filter)才會開始監聽廣播訊息,並對廣播訊息作為相應的處理。
IntentFilter fiter = new IntentFilter("com.smilexie.test.intent.mybroadcastreceiver");
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
registerReceiver(receiver, filter);
//撤銷廣播接受者的動態註冊
unregisterReceiver(receiver);
複製程式碼

23、本地廣播和全域性廣播有什麼差別?

1)LocalBroadcastReceiver僅在自己的應用內傳送接收廣播,也就是隻有自己的應用能收到,資料更加安全。廣播只在這個程式裡,而且效率更高。只能動態註冊,在傳送和註冊的時候採用LocalBroadcastManager的sendBroadcast方法和registerReceiver方法。 2)全域性廣播:傳送的廣播事件可被其他應用程式獲取,也能響應其他應用程式傳送的廣播事件(可以通過 exported–是否監聽其他應用程式傳送的廣播 在清單檔案中控制) 全域性廣播既可以動態註冊,也可以靜態註冊。

24、AlertDialog,popupWindow,Activity區別

(1)Popupwindow在顯示之前一定要設定寬高,Dialog無此限制。

(2)Popupwindow預設不會響應物理鍵盤的back,除非顯示設定了popup.setFocusable(true);而在點選back的時候,Dialog會消失。

(3)Popupwindow不會給頁面其他的部分新增蒙層,而Dialog會。

(4)Popupwindow沒有標題,Dialog預設有標題,可以通過dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消標題

(5)二者顯示的時候都要設定Gravity。如果不設定,Dialog預設是Gravity.CENTER。

(6)二者都有預設的背景,都可以通過setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉。

(7)Popupwindow彈出後,取得了使用者操作的響應處理許可權,使得其他UI控制元件不被觸發。而AlertDialog彈出後,點選背景,AlertDialog會消失。

25、Application 和 Activity 的 Context 物件的區別

1)Application Context是伴隨應用生命週期;不可以showDialog, startActivity, LayoutInflation 可以startService\BindService\sendBroadcast\registerBroadcast\load Resource values

2)Activity Context指生命週期只與當前Activity有關,而Activity Context這些操作都可以,即凡是跟UI相關的,都得用Activity做為Context來處理。 一個應用Context的數量=Activity數量+Service數量+1(Application數量)

26、Android屬性動畫特性

屬性動畫(Property Animation)是在 Android 3.0(API 11)後才提供的一種全新動畫模式 實現動畫效果在Android開發中非常常見,因此Android系統一開始就提供了兩種實現動畫的方式: 逐幀動畫(Frame Animation) 補間動畫( Tweened animation ) 逐幀動畫 & 補間動畫存在一定的缺點: a. 作用物件侷限:View 即補間動畫 只能夠作用在檢視View上,即只可以對一個Button、TextView、甚至是LinearLayout、或者其它繼承自View的元件進行動畫操作,但無法對非View的物件進行動畫操作 有些情況下的動畫效果只是檢視的某個屬性 & 物件而不是整個檢視; 如,現需要實現檢視的顏色動態變化,那麼就需要操作檢視的顏色屬性從而實現動畫效果,而不是針對整個檢視進行動畫操作

b. 沒有改變View的屬性,只是改變視覺效果 補間動畫只是改變了View的視覺效果,而不會真正去改變View的屬性。 如,將螢幕左上角的按鈕 通過補間動畫 移動到螢幕的右下角 點選當前按鈕位置(螢幕右下角)是沒有效果的,因為實際上按鈕還是停留在螢幕左上角,補間動畫只是將這個按鈕繪製到螢幕右下角,改變了視覺效果而已。

c. 動畫效果單一 補間動畫只能實現平移、旋轉、縮放 & 透明度這些簡單的動畫需求 一旦遇到相對複雜的動畫效果,即超出了上述4種動畫效果,那麼補間動畫則無法實現。 屬性動畫中最基礎的 ObjectAnimator ObjectAnimator 是屬性動畫中最重要的實行類,建立一個ObjectAnimator只需要通過他的靜態工廠方類直接返回一個ObjectAnimator物件。引數包括一個物件和物件的屬性名字,但這個屬性必須有get和set函式,內部會通過反射機制來呼叫set函式修改物件屬性值,也可以呼叫setInterpolator設定對應的差值器。 屬性動畫操作的是屬性,所以被操作的view可以響應事件。 屬性動畫可以對任何物件的屬性做動畫而不僅僅是View,甚至可以沒有物件。除了作用物件進行擴充套件外,屬性動畫的效果也加強了,不僅能實現View動畫的4中效果,還能實現其它多種效果,這些效果都是通過ValuAnimator或ObjectAnimator、AnimatorSet等來實現的。

區別和特性 1.  補間動畫:只產生了一個動畫效果,其真實的座標並沒有發生改變(只是改變了View的顯示效果而已,並不會真正的改變View的屬性)。View做在做動畫的時候,它並沒有真正的移動它的位置,而是根據動畫時間的插值,計算出一個Matrix,然後不停的invalidate,在onDraw中的Canvas上使用這個計算出來的Matrix去draw這個View的內容,並有onLayout中還是原來的位置,所以點選事件只能點選到原來的位置才能觸發 2. ObjectAnimator:一般直接用與View,要求作用的View提供該屬性(如View的scaleX屬性)的getter、setter方法(如setScaleX()方法),可以直接改變view的屬性所以View的位置也跟隨屬性的改變而改變,點選事件的觸發位置為動畫結束的位置。 3. ValueAnimator:屬性動畫的核心,這個我理解為數值動畫,ObjectAnimator也只不過是通過不斷改變的數值然後賦值給相應的屬性而已。通過設定初始和終點值,ValueAnimator 會通過相應的Interpolator  和duration 計算出平滑的數值變化,然後可以通過得到的Value進行任意操作

27、如何匯入外部資料庫?

我們平時見到的android資料庫操作一般都是在程式開始時建立一個空的資料庫,然後再進行相關操作。如果我們需要使用一個已有資料的資料庫怎麼辦呢? 我們都知道android系統下資料庫應該存放在 /data/data/com..(package name)/ 目錄下,所以我們需要做的是把已有的資料庫傳入那個目錄下。操作方法是用FileInputStream讀取原資料庫,再用FileOutputStream把讀取到的東西寫入到那個目錄。 操作方法:1. 把原資料庫包括在專案原始碼的 res/raw 目錄下,然後建立一個DBManager類 然後在程式的首個Activity中示例化一個DBManager物件,然後對其執行openDatabase方法就可以完成匯入了,可以把一些要對資料庫進行的操作寫在DBManager類裡,然後通過DBManager類的物件呼叫;也可以在完成匯入之後通過一個SQliteDatabase類的物件開啟資料庫,並執行操作。

28、LinearLayout、RelativeLayout、FrameLayout的特性及對比,並介紹使用場景。

LinearLayout LinearLayout按照垂直或者水平的順序依次排列子元素,每一個子元素都位於前一個元素之後。如果是垂直排列,那麼將是一個N行單列的結構,每一行只會有一個元素,而不論這個元素的寬度為多少;如果是水平排列,那麼將是一個單行N列的結構。如果搭建兩行兩列的結構,通常的方式是先垂直排列兩個元素,每一個元素裡再包含一個LinearLayout進行水平排列。

RelativeLayout RelativeLayout按照各子元素之間的位置關係完成佈局。在此佈局中的子元素裡與位置相關的屬性將生效。例如android:layout_below, android:layout_above等。子元素就通過這些屬性和各自的ID配合指定位置關係。

FrameLayout FrameLayout是五大布局中最簡單的一個佈局,在這個佈局中,整個介面被當成一塊空白備用區域,所有的子元素都不能被指定放置的位置,它們統統放於這塊區域的左上角,並且後面的子元素直接覆蓋在前面的子元素之上,將前面的子元素部分和全部遮擋。 總結 (1)RelativeLayout慢於LinearLayout是因為它會讓子View呼叫2次measure過程,而後者只需一次,但是有weight屬性存在時,後者同樣會進行兩次measure。

(2)RelativeLayout的子View如果高度和RelativeLayout不同,會引發效率問題,可以使用padding代替margin以優化此問題。

(3)在不響應層級深度的情況下,使用Linearlayout和FrameLayout而不是RelativeLayout。 結論中的第三條也解釋了文章前言中的問題:DecorView的層級深度已知且固定的,上面一個標題欄,下面一個內容欄,採用RelativeLayout並不會降低層級深度,因此這種情況下使用LinearLayout效率更高。 而為開發者預設新建RelativeLayout是希望開發者能採用儘量少的View層級,很多效果是需要多層LinearLayout的巢狀,這必然不如一層的RelativeLayout效能更好。因此我們應該儘量減少佈局巢狀,減少層級結構,使用比如viewStub,include等技巧

29、談談對介面與回撥的理解

介面回撥是指: 可以把使用實現了某一介面的類建立的物件的引用,賦給該介面宣告的介面變數,那麼該介面變數就可以呼叫被類實現的介面的方法。實際上,當介面變數呼叫被類實現的介面中的方法時,就是通知相應的物件呼叫介面的方法,這一過程稱為物件功能的介面回撥。 指導思想: 物件導向設計的封裝性,模組間要解耦,模組內要內聚 關鍵字:解耦 削弱模組間的協作性,也就是耦合性,使得模組之間高度獨立,模組間更多的從事著單向的呼叫工作。一個模組需要某種服務就去找另一個模組,這使得程式呈現出層次性,高層通過介面呼叫底層,底層提供服務。擯棄模組間你中有我我中有你的曖昧關係。 關鍵字:協作 但我們又需要模組間的協作,所以需要回撥(C++中是指標)。如果A物件關心B物件的狀態變化,那麼給B物件的狀態的變化註冊介面回撥函式,A實現介面,讓介面函式通知A,B物件狀態的改變,這樣在封裝了模組變化的同時實現了模組間的協作關係,另闢獨徑的給物件解耦。 關鍵字:變數 回撥函式跟普通函式只是在呼叫函式時略有區別。一般呼叫普通函式時,直接寫函式名即可。但是在呼叫所謂“回撥”函式時,是把它作為引數傳遞給另一函式。關鍵就在於“引數”這兩個字。為什麼要把某個東西引數化?就是它存在變化的可能性。既然可以把變數做成引數,那麼函式也同樣可以做成引數,只要它們有變化的可能。對於一成不變的東西,顯然直接嵌入便可。 1)定義介面: 定義一個介面、定義其中的抽象方法、抽象方法含有引數(被傳遞的資料);

2)編寫回撥方法: 在定義介面的類中,編寫使用者回撥的方法,要傳遞一個介面物件例項,讓別的類去實現。(相當於為介面成員變數賦值)

3)為抽象方法賦值: 獲取一個全域性的介面成員變數,在某個事件中使用介面成員變數呼叫介面中的方法,並且為抽象方法中的引數賦值。(這一步可以在回撥方法中實現) 需要一個介面的實現類:

1)讓當前Activity實現介面,變成介面的實現類;

2)寫一個類去實現介面,實現其中的抽象方法,然後在需要的地方建立一個介面實現類的子類物件

3)直接在當前位置使用匿名物件實現,建立一個介面例項。 回撥原理: 介面呼叫自己的抽象方法,相當於介面的實現類呼叫實現類中重寫的抽象方法; 介面中沒有建構函式:

1)介面中是沒有建構函式的,不能直接建立物件,只能由實現類建立物件;介面中的成員常量不需要進行初始化,所以不需要建構函式。

2)而抽象類是有構造方法的,為了給子類呼叫初始化抽象類中的成員變數。 介面的特點:

1)介面用關鍵字interface表示;類實現介面用implements表示。

2)介面不能例項化:那麼,介面如何例項化呢?按照多型的方式,由具體的子類例項化。其實這也是多型的一種,介面多型。

3)介面的子類:要麼是抽象類,要麼重寫介面中的所有抽象方法。 介面中的成員: 成員變數:只能是常量  預設修飾符 public static final(不能被修改) 構造方法:沒有,因為介面主要是擴充套件功能的,而沒有具體存在 成員方法:只能是抽象方法  預設修飾符 public abstract(可以省略) 類與類,類與介面以及介面與介面的關係: 類與類: 繼承關係,只能單繼承,但是可以多層繼承 類與介面:實現關係,可以多實現,還可以在繼承一個類的同時實現多個介面。 介面與介面:繼承關係,可以單繼承,也可以多繼承。

30、介紹下SurfView

1.什麼是SurfaceView?     Surface意為表層、表面,顧名思義SurfaceView就是指一個在表層的View物件。為什麼說是在表層呢,這是因為它有點特殊跟其他View不一樣,其他View是繪製在“表層”的上面,而它就是充當“表層”本身。SDK的文件 說到:SurfaceView就是在視窗上挖一個洞,它就是顯示在這個洞裡,其他的View是顯示在視窗上,所以View可以顯式在 SurfaceView之上,你也可以新增一些層在SurfaceView之上。 從API中可以看出SurfaceView屬於View的子類 它是專門為製作遊戲而產生的,它的功能非常強大,最重要的是它支援OpenGL ES庫,2D和3D的效果都可以實現。建立SurfaceView的時候需要實現SurfaceHolder.Callback介面,它可以用來監聽SurfaceView的狀態,比如:SurfaceView的改變 、SurfaceView的建立 、SurfaceView 銷燬等,我們可以在相應的方法中做一些比如初始化的操作或者清空的操作等等。 Android系統提供了View進行繪圖處理,我們通過自定義的View可以滿足大部分的繪圖需求,但是這有個問題就是我們通常自定義的View是用於主動更新情況的,使用者無法控制其繪製的速度,由於View是通過invalidate方法通知系統去呼叫view.onDraw方法進行重繪,而Android系統是通過發出VSYNC訊號來進行螢幕的重繪,重新整理的時間是16ms,如果在16ms內View完成不了執行的操作,使用者就會看著卡頓,比如當draw方法裡執行的邏輯過多,需要頻繁重新整理的介面上,例如遊戲介面,那麼就會不斷的阻塞主執行緒,從而導致畫面卡頓。而SurfaceView相當於是另一個繪圖執行緒,它是不會阻礙主執行緒,並且它在底層實現機制中實現了雙緩衝機制。 2.如何使用SurfaceView?         首先SurfaceView也是一個View,它也有自己的生命週期。因為它需要另外一個執行緒來執行繪製操作,所以我們可以在它生命週期的初始化階 段開闢一個新執行緒,然後開始執行繪製,當生命週期的結束階段我們插入結束繪製執行緒的操作。這些是由其內部一個SurfaceHolder物件完成的。   SurfaceView它的繪製原理是繪製前先鎖定畫布(獲取畫布),然後等都繪製結束以後在對畫布進行解鎖 ,最後在把畫布內容顯示到螢幕上。 通常情況下,使用以下步驟來建立一個SurfaceView的模板:

(1)建立SurfaceView 建立自定義的SurfaceView繼承自SurfaceView,並實現兩個介面:SurfaceHolder.Callback和Runnable.

(2)初始化SurfaceView 在自定義的MySurfaceView的構造方法中,需要對SurfaceView進行初始化,包括SurfaceHolder的初始化、畫筆的初始化等。在自定義的SurfaceView中,通常需要定義以下三個成員變數: private SurfaceHolder mHolder;

private Canvas mCanvas;//繪圖的畫布

private boolean mIsDrawing;//控制繪畫執行緒的標誌位

SurfaceHolder,顧名思義,它裡面儲存了一個對Surface物件的引用,而我們執行繪製方法本質上就是操控Surface。SurfaceHolder因為儲存了對Surface的引用,所以使用它來處理Surface的生命週期。(說到底 SurfaceView的生命週期其實就是Surface的生命週期)例如使用 SurfaceHolder來處理生命週期的初始化。

(3)使用SurfaceView 通過SurfaceHolder物件的lockCanvans()方法,我們可以獲取當前的Canvas繪圖物件。接下來的操作就和自定義View中的繪圖操作一樣了。需要注意的是這裡獲取到的Canvas物件還是繼續上次的Canvas物件,而不是一個新的物件。因此,之前的繪圖操作都會被保留,如果需要擦除,則可以在繪製前,通過drawColor()方法來進行清屏操作。 繪製的時候,在surfaceCreated()方法中開啟子執行緒進行繪製,而子執行緒使用一個while(mIsDrawing)的迴圈來不停的進行繪製,在繪製的邏輯中通過lockCanvas()方法獲取Canvas物件進行繪製,通過unlockCanvasAndPost(mCanvas)方法對畫布內容進行提交。整體程式碼模板如下: 這裡說一個優化的地方,這就是在run方法中。 在我們的draw()方法每一次更新所耗費的時間是不確定的。舉個例子 比如第一次迴圈draw() 耗費了1000毫秒 ,第二次迴圈draw() 耗時2000毫秒。很明顯這樣就會造成執行重新整理時間時快時慢,可能出現卡頓現象。為此最好保證每次重新整理的時間是相同的,這樣可以保證整體畫面過渡流暢。 這裡說一下Thread.yield(): 與Thread.sleep(long millis):的區別: Thread.yield(): 是暫停當前正在執行的執行緒物件 ,並去執行其他執行緒。 Thread.sleep(long millis):則是使當前執行緒暫停引數中所指定的毫秒數然後在繼續執行執行緒。

3.SurfaceView的使用例項

(1)正弦曲線 要繪製一個正弦曲線,只需要不斷修改橫縱座標的值,並讓他們滿足正弦函式即可。因此,我們需要一個Path物件來儲存正弦函式上的座標點,在子執行緒的while迴圈中,不斷改變橫縱座標值。

(2)畫圖板 我們也可以通過使用SurfaceView來實現一個簡單的繪圖板,繪圖的方法與View中進行繪圖所使用的方法一樣,也是通過Path物件記錄手指滑動的路徑來進行繪圖。

4.SurfaceView和View的區別 總的歸納起來SurfaceView和View不同之處有:

1. SurfaceView允許其他執行緒更新檢視物件(執行繪製方法)而View不允許這麼做,它只允許UI執行緒更新檢視物件。

2. SurfaceView是放在其他最底層的檢視層次中,所有其他檢視層都在它上面,所以在它之上可以新增一些層,而且它不能是透明的。

3. 它執行動畫的效率比View高,而且你可以控制幀數。

4. SurfaceView在繪圖時使用l了雙緩衝機制,而View沒有。

31、序列化的作用,以及Android兩種序列化的區別

序列化 (Serialization)將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其當前狀態寫入到臨時或永續性儲存區。以後,可以通過從儲存區中讀取或反序列化物件的狀態,重新建立該物件。 簡單地說:“序列化”就是將執行時的物件狀態轉換成二進位制,然後儲存到流、記憶體或者通過網路傳輸給其他端。

Android面試常問基礎知識點(附詳細解答)

32、差值器,估值器

www.jianshu.com/p/2f19fe1e3… 這篇文章介紹的十分詳細,大家可以系統的學習,瞭解一下。

33、Android中資料儲存方式

1、使用SharedPreferences儲存資料

2、檔案儲存資料

3、SQLite資料庫儲存資料

4、使用ContentProvider儲存資料

5、網路儲存資料

歡迎關注公共號

關注公共號會有更多收穫!

Android面試常問基礎知識點(附詳細解答)

相關文章