過年回來到現(xiàn)在也一個月了弧满,這段時間一直沒寫文章婆跑,這是因為我準備換工作了,一直在面試庭呜,也面試了四五家滑进,但是效果都不是很好,雖然如此募谎,但也算收獲了一些經(jīng)驗扶关,我就將我面試遇到的問題記錄下來,與大家一起分享吧数冬。(本人是做游戲sdk的节槐,所以一些問題會偏向于sdk的,如果不找sdk方向的工作可以忽略其中的一些問題)
一拐纱、面試基礎(chǔ)
1铜异、自我介紹
這個大家自己可以好好看一下網(wǎng)上的一些攻略,自己組織一個好一點的自我介紹戳玫,主要是要把個人信息熙掺,之前做過什么介紹清楚,這個就看自我發(fā)揮了咕宿。
2币绩、之前做過的項目或者工作經(jīng)歷,遇到了什么難點府阀,解決了什么問題缆镣,技術(shù)上得到了哪些提高(這個必問,希望大家準備好)
這個問題是必問的试浙,而且感覺還挺重要的董瞻,大家面試前先準備好幾個技術(shù)難點,哪怕項目中沒用過也沒關(guān)系田巴。
3钠糊、介紹一下之前項目中常用的第三方框架
二、java部分
一壹哺、ArrayList和HashMap實現(xiàn)原理抄伍,他們的特性是什么。
ArrayList:底層數(shù)據(jù)結(jié)構(gòu)是Object數(shù)組管宵,查詢快截珍,增刪慢攀甚,查詢是根據(jù)數(shù)組下標直接查詢速度快,增刪需要移動后邊的元素和擴容岗喉,速度慢秋度。線程不安全,元素單個钱床,效率高荚斯。(如果要線程安全的List集合可以用Vector)
HashMap:jdk1.8之前的數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表,1.8之后是數(shù)組+鏈表+紅黑樹查牌,當鏈表長度小于8的時候使用的還是數(shù)組+鏈表鲸拥,當鏈表長度超過8時轉(zhuǎn)換為紅黑樹,HashMap元素成對僧免,元素可為空刑赶,線程不安全,new HashMap的時候初始化默認大小為16懂衩。(如果要線程安全的Map集合可以用HashTable或ConcurrentHashMap)
二撞叨、java四大引用
強引用(StrongReference):JVM 寧可拋出 OOM ,也不會讓 GC 回收具有強引用的對象
軟引用(SoftReference):只有在內(nèi)存空間不足時浊洞,才會被回收的對象
弱引用(WeakReference):在 GC 時牵敷,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當前內(nèi)存空間足夠與否法希,都會回收它的內(nèi)存
虛引用(PhantomReference):任何時候都可以被GC回收枷餐,當垃圾回收器準備回收一個對象時,如果發(fā)現(xiàn)它還有虛引用苫亦,就會在回收對象的內(nèi)存之前毛肋,把這個虛引用加入到與之關(guān)聯(lián)的引用隊列中。程序可以通過判斷引用隊列中是否存在該對象的虛引用屋剑,來了解這個對象是否將要被回收润匙。可以用來作為GC回收Object的標志唉匾。
三孕讳、java抽象類和接口,有什么區(qū)別
抽象類:在Java中被abstract關(guān)鍵字修飾的類稱為抽象類巍膘,被abstract關(guān)鍵字修飾的方法稱為抽象方法厂财,抽象方法只有方法的聲明,沒有方法體峡懈。抽象類的特點:
a璃饱、抽象類不能被實例化只能被繼承;
b逮诲、包含抽象方法的一定是抽象類帜平,但是抽象類不一定含有抽象方法;
c梅鹦、抽象類中的抽象方法的修飾符只能為public或者protected裆甩,默認為public;
d齐唆、一個子類繼承一個抽象類嗤栓,則子類必須實現(xiàn)父類抽象方法,否則子類也必須定義為抽象類箍邮;
e茉帅、抽象類可以包含屬性、方法锭弊、構(gòu)造方法堪澎,但是構(gòu)造方法不能用于實例化,主要用途是被子類調(diào)用味滞。
接口:Java中接口使用interface關(guān)鍵字修飾樱蛤,特點為:
a、接口可以包含變量剑鞍、方法昨凡;變量被隱士指定為public static final,方法被隱士指定為public abstract(JDK1.8之前)蚁署;
b便脊、接口支持多繼承,即一個接口可以extends多個接口光戈,間接的解決了Java中類的單繼承問題哪痰;
c、一個類可以實現(xiàn)多個接口久妆;
d妒御、JDK1.8中對接口增加了新的特性:(1)、默認方法(default method):JDK 1.8允許給接口添加非抽象的方法實現(xiàn)镇饺,但必須使用default關(guān)鍵字修飾乎莉;定義了default的方法可以不被實現(xiàn)子類所實現(xiàn),但只能被實現(xiàn)子類的對象調(diào)用奸笤;如果子類實現(xiàn)了多個接口惋啃,并且這些接口包含一樣的默認方法,則子類必須重寫默認方法监右;(2)边灭、靜態(tài)方法(static method):JDK 1.8中允許使用static關(guān)鍵字修飾一個方法,并提供實現(xiàn)健盒,稱為接口靜態(tài)方法绒瘦。接口靜態(tài)方法只能通過接口調(diào)用(接口名.靜態(tài)方法名)称簿。
相同點
(1)都不能被實例化 (2)接口的實現(xiàn)類或抽象類的子類都只有實現(xiàn)了接口或抽象類中的方法后才能實例化。
不同點
(1)接口只有定義惰帽,不能有方法的實現(xiàn)憨降,java 1.8中可以定義default方法體,而抽象類可以有定義與實現(xiàn)该酗,方法可在抽象類中實現(xiàn)授药。
(2)實現(xiàn)接口的關(guān)鍵字為implements,繼承抽象類的關(guān)鍵字為extends呜魄。一個類可以實現(xiàn)多個接口悔叽,但一個類只能繼承一個抽象類。所以爵嗅,使用接口可以間接地實現(xiàn)多重繼承娇澎。
(3)接口強調(diào)特定功能的實現(xiàn),而抽象類強調(diào)所屬關(guān)系睹晒。
(4)接口成員變量默認為public static final九火,必須賦初值,不能被修改册招;其所有的成員方法都是public岔激、abstract的。抽象類中成員變量默認default是掰,可在子類中被重新定義虑鼎,也可被重新賦值;抽象方法被abstract修飾键痛,不能被private炫彩、static、synchronized和native等修飾絮短,必須以分號結(jié)尾江兢,不帶花括號。
四丁频、設(shè)計模式六大原則杉允,能否給我說說Android中至少3個用到設(shè)計模式的例子
1、單一職責原則:有且只有一個原因會引起類的變化席里。
2叔磷、接口隔離原則。
3奖磁、里氏替換原則改基。
4、依賴倒置原則咖为。
5秕狰、迪米特法則稠腊。
6、開閉原則鸣哀。
23種設(shè)計模式:
創(chuàng)建型5個:單例模式架忌、建造者模式、原型模式诺舔、工廠方法、抽象工廠
行為型11個:策略模式备畦、狀態(tài)模式低飒、觀察者模式、中介者模式懂盐、訪問者模式褥赊、迭代器模式、模板方法莉恼、備忘錄模式拌喉、命令模式、解釋器模式俐银、責任鏈模式
結(jié)構(gòu)型模式7個:適配器模式尿背、裝飾器模式、代理模式捶惜、外觀模式田藐、橋接模式、組合模式吱七、享元模式
OKHttp內(nèi)部采用責任鏈模式來完成每個interceptor攔截器的調(diào)用
定義:使得多個對象都有機會處理請求汽久,從而避免了請求的發(fā)送者和接收者之間的耦合關(guān)系。將 這些對象連成一條鏈踊餐,并沿著這條鏈傳遞該請求景醇,直到有對象處理該請求為止。
優(yōu)點: 將請求者和處理者關(guān)系解耦吝岭,使代碼更加靈活三痰。
缺點: 在鏈中遍歷尋找請求處理者中,如果處理者太多窜管,會影響性能酒觅。
RxJava的觀察者模式
定義:定義對象間一種一對多的依賴關(guān)系,使得每當一對象狀態(tài)發(fā)生改變時微峰,所有依賴它的對象 會得到通知并自動更新舷丹。
應(yīng)用:listView的Adapter的notifyDataSetChanged方法,廣播蜓肆,事件總線機制颜凯。
觀察者模式主要的作用是對象解耦谋币,將觀察者和被觀察者分隔開,只依賴于observer(觀察員) 和observable(可觀察的)抽象症概。
優(yōu)點: 1.觀察者和被觀察者是抽象耦合蕾额,以應(yīng)對業(yè)務(wù)變化; 2.增強系統(tǒng)的靈活性和可擴展性彼城。
缺點: 在Java中通知是順序執(zhí)行诅蝶,如果觀察者卡頓,會影響整體的執(zhí)行效率募壕。這種情況下调炬,一般建議采 用異步的方式。
listview/gridview的適配器模式
1.使用場景: 1.接口不兼容 2.想建立一個可以重復(fù)使用的類 3.需要統(tǒng)一的輸出接口舱馅,而輸入端類型不可知缰泡。
優(yōu)點: 1.更好的復(fù)用性:復(fù)用現(xiàn)有的功能; 2.更好的擴展性:擴展現(xiàn)有的功能代嗤。
缺點: 過多的使用適配器棘钞,會使系統(tǒng)凌亂不堪,不易于整體把握干毅。如:明明調(diào)用的是A接口宜猜,內(nèi)部適配 的卻是B接口的實現(xiàn),如果系統(tǒng)出現(xiàn)太多這種情況硝逢,無異于一場災(zāi)難宝恶。
Context外觀模式
使用場景: 為復(fù)雜子系統(tǒng)提供一個簡單接口。
優(yōu)點: 1.隱藏子系統(tǒng)實現(xiàn)細節(jié)趴捅,減少客戶和子系統(tǒng)的耦合垫毙,能夠擁抱變化; 2.外觀類對子系統(tǒng)接口封裝拱绑,使子系統(tǒng)更加容易使用综芥。
缺點: 1.外觀類接口膨脹; 更多免費Android開發(fā)視頻課程猎拨,2.外觀類沒有遵循開閉原則膀藐,當業(yè)務(wù)出現(xiàn)變更時,可能需要修改外觀類红省。
AlertDialog额各、Notification 源碼使用了建造者模式完成參數(shù)的初始化
優(yōu)點:1.良好的封裝性,隱藏內(nèi)部實現(xiàn)細節(jié)吧恃; 2.建造者獨立虾啦,容易擴展。
缺點: 會產(chǎn)生多余的Builder對象和Director對象,消耗內(nèi)存傲醉。
安卓應(yīng)用主題是抽象工廠模式的最好體現(xiàn)
安卓應(yīng)用有兩套主題:LightTheme亮色主題和DarkTheme暗色主題蝇闭。主題之下有各種與之相關(guān)的 UI元素。創(chuàng)建主題也要隨之創(chuàng)建各種UI元素硬毕,這就是抽象工廠模式的最好體現(xiàn)呻引。
優(yōu)點: 分離接口和實現(xiàn),面向接口編程吐咳。在具體實現(xiàn)中解耦逻悠,同時基于接口和實現(xiàn)的分離,使得產(chǎn)品類 切換更加容易韭脊,靈活童谒。
缺點: 1.類文件爆炸性增加 2.新的產(chǎn)品類不易擴展
五、一個線程幾個loop
三乾蓬、Android部分
1惠啄、65536錯誤是怎么回事
該錯誤主要是由于我們打包后classes.dex文件里方法數(shù)量超出的65536個慎恒。
預(yù)防方法主要是少寫方法任内,多用基類,能不用兼容包的就不要用兼容包的,引入jar或library工程時注意下它們的方法數(shù)融柬,但是如果實在沒辦法死嗦,就用這個方法解決:
第一步:在項目的grade文件里面的defaultConfig閉包下添加: multiDexEnabled true
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
multiDexEnabled true
第二步:在dependencies下添加依賴 compile 'com.android.support:multidex:1.0.0'
dependencies {
compile 'com.android.support:multidex:1.0.0'
第三步:自定義繼承于Application的類,并重寫protected void attachBaseContext(Context base)方法粒氧,調(diào)用 MultiDex.install(this)初始化越除,最后記得在Manifest清單里注冊自定義的application類,如圖:
public class MyApplication extends Application{
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
2外盯、什么是內(nèi)存泄漏摘盆、什么是內(nèi)存溢出,該怎么應(yīng)對
內(nèi)存溢出(OOM):指的是申請不到足夠的內(nèi)存饱苟;
原因:1孩擂、內(nèi)存一次性開銷過大(加載巨圖)。2箱熬、內(nèi)存持續(xù)性開銷(循環(huán)創(chuàng)建對象)类垦。3、內(nèi)存回收不及時(內(nèi)存開銷過快城须,GC頻率跟不上開銷速度等)蚤认。4、內(nèi)存無法回收(內(nèi)存泄漏導(dǎo)致內(nèi)存溢出)糕伐。
解決方案:1砰琢、調(diào)整圖像大小。2、盡可能不在循環(huán)中申請內(nèi)存氯析。3亏较、及時回收圖像。
內(nèi)存泄露(Leak):無法釋放已申請的內(nèi)存掩缓;
在Java中雪情,判斷對象是否仍在使用的方法是:引用計數(shù)法,可達性分析你辣。
原因:1巡通、靜態(tài)變量(單例模式)。2舍哄、監(jiān)聽器宴凉。3、內(nèi)部類(內(nèi)部類持有外部引用)表悬。4弥锄、資源對象未關(guān)閉。5蟆沫、容器中的對象沒有清理籽暇。6、webview饭庞。
避免內(nèi)存泄漏方法:
1戒悠、不要在匿名內(nèi)部類中進行異步操作
2、將非靜態(tài)內(nèi)部類轉(zhuǎn)為靜態(tài)內(nèi)部類 + WeakReference(弱引用)的方式
3舟山、使用Context時绸狐,盡量使用Application 的 Context
4、盡量避免使用static 成員變量累盗。另外可以考慮lazy初始化
5寒矿、為webView開啟另外一個進程,通過AIDL與主線程進行通信若债,WebView所在的進程可以根據(jù)業(yè)務(wù)的需要選擇合適的時機進行銷毀符相,從而達到內(nèi)存的完整釋放
6、及時關(guān)閉資源拆座。Bitmap 使用后調(diào)用recycle()方法
兩者關(guān)系:內(nèi)存泄露 → 剩余內(nèi)存不足 → 后續(xù)申請不到足夠內(nèi)存 →內(nèi)存溢出主巍。
3、什么是Android的模塊化挪凑、組件化孕索、插件化
模塊化:一個程序按照其功能做拆分,分成相互獨立的模塊(例如:登陸躏碳,注冊)搞旭。模塊化的具體實施方法分為插件化和組件化。
組件化:是將一個app分成多個模塊,每個模塊都是一個組件(module)肄渗,開發(fā)的過程中我們可以讓這些組件相互依賴或者單獨調(diào)試部分組件镇眷,但是最終發(fā)布的時候?qū)⑦@些組件合并成一個統(tǒng)一的apk,這就是組件化開發(fā)翎嫡。
插件化:插件化開發(fā)和組件化不同欠动,插件化開發(fā)就是將整個app拆分成很多模塊,每個模塊都是一個apk(組件化的每個模塊是一個lib),最終打包的時候?qū)⑺拗鱝pk和插件apk分開打包惑申,插件apk通過動態(tài)下發(fā)到宿主apk具伍,這就是插件化。
4圈驼、淺述APK的打包流程
第一步 aapt階段: 資源打包工具人芽,將res資源文件打包成R.java文件和res文件。
第二步 aidl階段: 這個階段處理.aidl文件绩脆,生成對應(yīng)的Java接口文件萤厅。
第三步 Java Compiler階段:通過Java Compiler編譯R.java、Java接口文件靴迫、Java源文件惕味,生成.class文件。
第四步 dex階段: 通過dex2.jar將.class文件處理生成class.dex矢劲。
第五步 apkbuilder階段:將classes.dex赦拘、res文件夾慌随、AndroidManifest.xml打包成apk文件芬沉。
第六步 Jarsigner階段:對apk文件加簽,形成一個可以運行的apk阁猜。
5丸逸、打apk包時,V1剃袍、V2簽名的區(qū)別是什么
v1簽名是對jar進行簽名黄刚,V2簽名是對整個apk簽名:官方介紹就是:v2簽名是在整個APK文件的二進制內(nèi)容上計算和驗證的,v1是在歸檔文件中解壓縮文件內(nèi)容民效。
二者簽名所產(chǎn)生的結(jié)果:
v1:在v1中只對未壓縮的文件內(nèi)容進行了驗證憔维,所以在APK簽名之后可以進行很多修改——文件可以移動,甚至可以重新壓縮畏邢。即可以對簽名后的文件在進行處理
v2:v2簽名驗證了歸檔中的所有字節(jié)业扒,而不是單獨的ZIP條目,如果您在構(gòu)建過程中有任何定制任務(wù)舒萎,包括篡改或處理APK文件程储,請確保禁用它們,否則您可能會使v2簽名失效,從而使您的APKs與Android 7.0和以上版本不兼容章鲤。
根據(jù)實際開發(fā)的經(jīng)驗總結(jié):
一定可行的方案: 只使用 v1 方案
不一定可行的方案:同時使用 v1 和 v2 方案
對 7.0 以下一定不行的方案:只使用 v2 方案
6摊灭、Handler
什么是handler:
Handler是Android SDK來處理異步消息的核心類。
子線程與主線程通過Handler來進行通信败徊。子線程可以通過Handler來通知主線程進行UI更新帚呼。
7、ListView和RecylerView的區(qū)別皱蹦,以及如何優(yōu)化
1萝挤、 緩存不同:
ListView是2級緩存,RecyclerView比ListView多兩級緩存根欧,支持多個離ItemView緩存怜珍,支持開發(fā)者自定義緩存處理邏輯,支持所有RecyclerView共用同一個RecyclerViewPool(緩存池)凤粗。
2酥泛、adapter不同
ListView有自帶的Adapter,例如ArrayAdapter等嫌拣,而RecylerView所有的adapter必須由自己實現(xiàn)柔袁。
3、布局不同
ListView布局較為單一异逐,只有縱向布局捶索,RecylerView橫向、縱向灰瞻、表格腥例、瀑布流都可以實現(xiàn)。
4酝润、刷新區(qū)別
ListView中通常使用全局刷新函數(shù)notifyDataSetChanged()來刷新ListView中的所有數(shù)據(jù)燎竖,這是一個非常耗費資源的行為,RecyclerView則可以實現(xiàn)數(shù)據(jù)的局部刷新要销,例如notifyItemChanged()函數(shù)等构回。
5、 動畫區(qū)別:
在RecyclerView封裝的類中已經(jīng)自帶了很多內(nèi)置的動畫API疏咐,而ListView則需要自己實現(xiàn)纤掸。
6、item點擊事件:
ListView提供了setOnItemClickListener()這樣的item點擊事件浑塞,而RecylerView沒有借跪,需要自己實現(xiàn)。
ListView的優(yōu)化:
優(yōu)化方式一:
convertView的復(fù)用缩举,進行布局的復(fù)用垦梆。
優(yōu)化方式二:
ViewHolder的使用匹颤,避免每次都findviewById。
優(yōu)化方式三:
使用分段加載托猩。
優(yōu)化方式四:
使用分頁加載印蓖。
RecylerView的優(yōu)化:
8、EventBus
EventBus是一種用于Android的事件發(fā)布-訂閱總線京腥,它簡化了應(yīng)用程序內(nèi)各個組件之間進行通信的復(fù)雜度赦肃,尤其是碎片之間進行通信的問題,可以避免由于使用廣播通信而帶來的諸多不便公浪。
三個角色:
Event:事件他宛,它可以是任意類型,EventBus會根據(jù)事件類型進行全局的通知欠气。
Subscriber:事件訂閱者厅各,在EventBus 3.0之前我們必須定義以onEvent開頭的那幾個方法,分別是onEvent预柒、onEventMainThread队塘、onEventBackgroundThread和onEventAsync,而在3.0之后事件處理的方法名可以隨意取宜鸯,不過需要加上注解@subscribe憔古,并且指定線程模型,默認是POSTING淋袖。
Publisher:事件的發(fā)布者鸿市,可以在任意線程里發(fā)布事件。一般情況下即碗,使用EventBus.getDefault()就可以得到一個EventBus對象焰情,然后再調(diào)用post(Object)方法即可。
四個線程:
POSTING:默認拜姿,表示事件處理函數(shù)的線程跟發(fā)布事件的線程在同一個線程烙样。
MAIN:表示事件處理函數(shù)的線程在主線程(UI)線程冯遂,因此在這里不能進行耗時操作蕊肥。
BACKGROUND:表示事件處理函數(shù)的線程在后臺線程,因此不能進行UI操作蛤肌。如果發(fā)布事件的線程是主線程(UI線程)壁却,那么事件處理函數(shù)將會開啟一個后臺線程,如果果發(fā)布事件的線程是在后臺線程裸准,那么事件處理函數(shù)就使用該線程展东。
ASYNC:表示無論事件發(fā)布的線程是哪一個,事件處理函數(shù)始終會新建一個子線程運行炒俱,同樣不能進行UI操作盐肃。
9爪膊、ANR問題
ANR全稱:Application Not Responding,也就是應(yīng)用程序無響應(yīng)砸王。
以下四個條件都可以造成ANR發(fā)生:
InputDispatching Timeout:5秒內(nèi)無法響應(yīng)屏幕觸摸事件或鍵盤輸入事件
BroadcastQueue Timeout :在執(zhí)行前臺廣播(BroadcastReceiver)的onReceive()函數(shù)時10秒沒有處理完成推盛,后臺為60秒。
Service Timeout :前臺服務(wù)20秒內(nèi)谦铃,后臺服務(wù)在200秒內(nèi)沒有執(zhí)行完畢耘成。
ContentProvider Timeout :ContentProvider的publish在10s內(nèi)沒進行完成。
造成ANR的原因及解決辦法:
1驹闰、主線程阻塞或主線程數(shù)據(jù)讀取瘪菌。解決辦法:使用子線程處理耗時任務(wù)或者阻塞任務(wù)
2晕翠、CPU滿負荷叹阔,I/O阻塞。解決辦法:文件讀寫或者數(shù)據(jù)庫操作放在子線程镀虐。
3屹培、內(nèi)存不足疆栏。解決辦法:優(yōu)化內(nèi)存,防止內(nèi)存泄漏惫谤。
4壁顶、各大組件ANR。解決辦法:各大組件的生命周期也應(yīng)避免耗時操作溜歪。
10若专、android設(shè)備的唯一標識問題
沒有IMEI授權(quán)就用android_id和MAC地址,但是android6.0之后MAC地址統(tǒng)一返回02:00:00:00:00:00蝴猪。
有IMEI授權(quán)就用imei碼调衰。
android10不能用imei,用OAID吧自阱。
以上的是主流的方法嚎莉,還有MEID,ESN沛豌,IMSI等趋箩。
Android設(shè)備唯一標識
11、sdk打渠道包方法
12加派、APP從啟動到看到第一個頁面