Android面试题详细整理系列(二)
? ? ?以下這些面試題都是筆者在(2017年1月-2017年3月)這段時間所面試Android工程師的總結(jié)而來,面試的公司包括巨頭xx等,還有新貴公司如dd在線科技,gm金融,zk網(wǎng),momo科技,zbj等,還有小型活力公司如軟都科技,星云顏值,英克科技等,不足之處,還望各位不吝賜教。
1.談一談MVC,MVP,MVVM模式的理解。
第一次遇到這個問題,筆者只能悻悻的回答MVC模式,其他沒法展開說,后來總結(jié)記下來,下次遇到so easy(面試遇到兩次該問題)。
一、 MVC
MVC模式的意思是,軟件可以分成三個部分。
各部分之間的通信方式如下。
1.View 傳送指令到 Controller
2.Controller 完成業(yè)務(wù)邏輯后,要求 Model 改變狀態(tài)
3.Model 將新的數(shù)據(jù)發(fā)送到 View,用戶得到反饋
所有通信都是單向的。
二、MVP
MVP 模式將 Controller 改名為 Presenter,同時改變了通信方向。
1. 各部分之間的通信,都是雙向的。
2. View 與 Model 不發(fā)生聯(lián)系,都通過 Presenter 傳遞。
3. View 非常薄,不部署任何業(yè)務(wù)邏輯,稱為"被動視圖"(Passive View),即沒有任何主動性,而 Presenter非常厚,所有邏輯都部署在那里。
三、MVVM
MVVM 模式將 Presenter 改名為 ViewModel,基本上與 MVP 模式完全一致。
唯一的區(qū)別是,它采用雙向綁定(data-binding):View的變動,自動反映在 ViewModel,反之亦然。
關(guān)于data-binding的用法,可以看看這篇文章?完全掌握Android Data Binding
2.自定義view需要重寫哪些方法,如何設(shè)定自定義view固定寬高比,如何自定義屬性標簽?
自定義view需要重寫onMeasure(),onLayout()(繼承來自ViewGroup時需要),onDraw()。
在OnMeasure()方法中,如果是view的話,view的視圖的最終大小確定通過調(diào)用setMeasuredDimension()來實現(xiàn),在此處可以設(shè)置固定寬高比,如果是viewgroup的話,對其子視圖進行遍歷的measure()過程。
OnLayout()主要作用 :為將整個根據(jù)子視圖的大小以及布局參數(shù)將View樹放到合適的位置上。如果該View不是ViewGroup類型?,調(diào)用setFrame()方法設(shè)置該控件的在父視圖上的坐標軸,如果該View是ViewGroup類型,則對它的每個子View進行l(wèi)ayout()過程。
OnDraw()方法中,首先繪制view背景,接著繪制視圖本身,然后調(diào)用dispatchDraw ()方法繪制子視圖,最后繪制滾動條。
自定義屬性的使用:
一、在res/values文件下定義一個attrs.xml文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ToolBar"> <attr name="buttonNum" format="integer"/> <attr name="itemBackground" format="reference|color"/> </declare-styleable> </resources>
二、在布局xml中如下使用該屬性:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:toolbar="http://schemas.android.com/apk/res/cn.zzm.toolbar" androidrientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <cn.zzm.toolbar.ToolBar android:id="@+id/gridview_toolbar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/control_bar" android:gravity="center" toolbar:buttonNum="5" toolbar:itemBackground="@drawable/control_bar_item_bg"/> </RelativeLayout>三、在自定義組件中,可以如下獲得xml中定義的值:
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ToolBar); buttonNum = a.getInt(R.styleable.ToolBar_buttonNum, 5); itemBg = a.getResourceId(R.styleable.ToolBar_itemBackground, -1); a.recycle();就這么簡單的三步,即可完成對自定義屬性的使用。
3.如何使用gradle打jar包
在android studio中則有點困難,其主要的原因是需要掌握gradle命令形式。接下來就直接上干貨。
首先,在你需要進行分裝jar的模塊進行build.gradle文件下進行以下添加:
然后在Terminal中執(zhí)行g(shù)radle makeJar命令,如果提示:gradle命令不可用。那么你需要在你的系統(tǒng)環(huán)境變量中進行變量的添加:
找到你的gradle插件路徑進行添加,然后再在path屬性的末尾進行
“;%GRADLE_HOME%\bin”添加。
然后輸入 "gradle -v"或者"gradle -version"都可以.
再次運行g(shù)radle makeJar。然后再一長串的編譯后在末尾看到
表示創(chuàng)建jar包成功,在build/libs下可以看到。
4.APP進程被系統(tǒng)殺死后,還能不能接收到broadcast?
首先,答案肯定是不能的。自android 3.1開始,系統(tǒng)自動給所有intent添加了FLAG_EXCLUDE_STOPPED_PACKAGES,導致app處于停止狀態(tài)就不能收到廣播。要想處于停止狀態(tài)的app收到廣播,需要添加FLAG_INCLUDE_STOPPED_PACKAGES這個標記。這樣的話,停止的app應(yīng)該是能夠收到系統(tǒng)廣播了。
5.listview的實現(xiàn)原理及工作原理
這個問題筆者以前沒有關(guān)注過,下來后仔細查看了一番,ListView這么厲害的原因,其中一部分就是因為RecycleBin緩存機制。RecycleBin緩存機制是寫在AbsListView的一個內(nèi)部類。所以ListView繼承于AbsListView,也繼承了這股力量。RecycleBin緩存機制的工作原理:?
ListView每當一項子view滑出界面時,RecycleBin會調(diào)用addScrapView()方法將這個廢棄的子view進行緩存。每當子view滑入界面時,RecycleBin會調(diào)用getScrapView()方法獲取一個廢棄已緩存的view。所以我們再看回Adapter的getView()方法:
@Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = LayoutInflater.from(context).inflate(resourceId, null); ······} else { view = convertView; } ······return view; } 這個convertView是什么?convertView就是RecycleBin緩存機制調(diào)用getScrapView()方法獲取廢棄已緩存的view。getView()方法中有個判斷,if (convertView == null),當convertView為空,也就是沒有廢棄已緩存的view時,將調(diào)用LayoutInflater的inflate()方法加載出來布局view,這個操作是比較耗時的;當convertView不為空時,我們就直接用convertView了,而不需要再次調(diào)用LayoutInflater的inflate()方法加載出來布局view。
ListView的工作原理:
View在顯示到界面的過程中,會進行兩次onMeasure()和onLayout()過程。(如果父視圖的子視圖的個數(shù)為0,就會執(zhí)行一次。否則就會執(zhí)行多次。因為開始時父試圖中是沒有子試圖的。但是當你從xml文件中加載子試圖或者在java代碼中添加子試圖時,父試圖的狀態(tài)會發(fā)生變化,這個變化會引起onlayout甚至是onmeasure。)
第一次onLayout()?:?
ListView在加載子項視圖的時候,先判斷是否有子元素、RecycleBin緩存機制中是否已經(jīng)有緩存視圖了。由于此時ListView是第一次加載,沒有任何視圖,RecycleBin中也沒有任何的緩存記錄,所以ListView就直接進行計算,繪制子view等等一系列操作。
第二次onLayout()?:?
到了第二次onLayout()的時候,要注意,因為在有了第一次onLayout()的過程,ListView現(xiàn)在已經(jīng)加載好了子項視圖了。所以當ListView再次判斷子元素是否為空時,現(xiàn)在子元素不再等于0了。所以這次會進行下面這些操作:?
1. ListView首先調(diào)用RecycleBin緩存機制的fillActiveViews()方法,將第一次onLayout()已經(jīng)加載好的視圖全部緩存到mActiveViews中,然后再detach掉第一次所有加載好的視圖。這樣就解決了第二次onLayout()再次加載視圖的時候,出現(xiàn)數(shù)據(jù)重復的問題。?
2. 巧妙的是,在接下來加載子項視圖的時候,也是先判斷RecycleBin緩存機制中的mActiveViews是否為空,但是因為剛才ListView已經(jīng)把第一次加載好的子視圖全部緩存到了mActiveViews中了,所以此時mActiveViwe并不空,接下來就只要把mActiveViews里面的視圖全部attach到ListView上,這樣ListView中所有子視圖又全部顯示出來了。
ListView的滑動部分工作原理:
滑動部分的機制是寫在AbsListView當中的。那么監(jiān)聽觸控事件是在onTouchEvent()方法當中進行的。onTouchEvent()方法內(nèi)有個switch()條件判斷。?
1. 首先當判斷到我們的動作是滑動時,就計算出我們觸發(fā)event事件手指在Y方向上的位移距離。?
2. 根據(jù)這個距離計算出view是否滑出了界面之外,如果滑出了界面之外,RecycleBin緩存機制就調(diào)用addScrapView()方法將這個View加入到廢棄緩存當中,然后再將這個view進行detach掉。因為這個view已經(jīng)移出界面了,所以沒必要為它保存。?
3. 接下來,所有子視圖就根據(jù)這個距離進行相應(yīng)的偏移。當發(fā)現(xiàn)ListView中最后一個View的底部已經(jīng)移入了屏幕,或者ListView中第一個View的頂部移入了屏幕時,就會對ListView進行填充。?在填充的時候,ListView先調(diào)用RecycleBin緩存機制中的getScrapView()方法來嘗試從廢棄緩存中獲取一個View,如果成功從廢棄緩存中取得一個scrapView時,我們就將這個scrapView傳入getView()方法中,否則就將null傳入getView()方法中。
6.Android中定時器任務(wù)的實現(xiàn)方式?
Android中定時執(zhí)行任務(wù)的3種實現(xiàn)方法:
一、采用Handler與線程的sleep(long)方法(不建議使用,Java的實現(xiàn)方式)
二、采用Handler的postDelayed(Runnable, long)方法(最簡單的android實現(xiàn))
三、采用Handler與timer及TimerTask結(jié)合的方法(比較多的任務(wù)時建議使用)
下面逐一介紹:
一、采用Handle與線程的sleep(long)方法
Handler主要用來處理接受到的消息。這只是最主要的方法,當然Handler里還有其他的方法供實現(xiàn),有興趣的可以去查API,這里不過多解釋。
1. 定義一個Handler類,用于處理接受到的Message。
Handler handler = new Handler() { public void handleMessage(Message msg) { // 要做的事情 super.handleMessage(msg); } };2. 新建一個實現(xiàn)Runnable接口的線程類,如下:
public class MyThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(10000);// 線程暫停10秒,單位毫秒 Message message = new Message(); message.what = 1; handler.sendMessage(message);// 發(fā)送消息 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
3. 在需要啟動線程的地方加入下面語句:
new Thread(new MyThread()).start();
二、采用Handler的postDelayed(Runnable, long)方法
這個實現(xiàn)比較簡單一些。
1. 定義一個Handler類
Handler handler=new Handler(); Runnable runnable=new Runnable() { @Override public void run() { // TODO Auto-generated method stub //要做的事情 handler.postDelayed(this, 2000); } };2. 啟動計時器
handler.postDelayed(runnable, 2000);//每兩秒執(zhí)行一次runnable.
3.?停止計時器
handler.removeCallbacks(runnable);
三、采用Handler與timer及TimerTask結(jié)合的方法
1. 定義定時器、定時器任務(wù)及Handler句柄
private final Timer timer = new Timer(); private TimerTask task; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub // 要做的事情 super.handleMessage(msg); } }; 2. 初始化計時器任務(wù)
task = new TimerTask() { @Override public void run() { // TODO Auto-generated method stub Message message = new Message(); message.what = 1; handler.sendMessage(message); } };
3. 啟動定時器
timer.schedule(task, 2000, 2000); schedule(TimerTask task, long delay, long period):這個方法是說,delay/2000秒后執(zhí)行task, 然后進過period/2000秒再次執(zhí)行task,這個用于循環(huán)任務(wù),執(zhí)行無數(shù)次
4.停止計時器
timer.cancel();
在寫作本篇博客的時候,借鑒了很多同行的博客,在此感謝攻城獅Song,阮一峰,Utopia_Sen,fancsxx等
我是kris,See you next time!!!
Android Studio中g(shù)radle命令指定打jar包...(攻城獅Song)
MVC,MVP 和 MVVM 的圖示? (阮一峰)解析Android ListView工作原理及其緩存機制?(Utopia_Sen)
Android中定時執(zhí)行任務(wù)的3種實現(xiàn)方法? ? (fancsxx)
總結(jié)
以上是生活随笔為你收集整理的Android面试题详细整理系列(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android面试题详细整理系列(一)
- 下一篇: Android面试题详细整理系列(三)