性能優(yōu)化面試:
首先我們要知道性能優(yōu)化解決了什么問題笙各,就是頁面卡頓現(xiàn)象嚴(yán)重。
那么我們就應(yīng)該先去檢測(cè)頁面的加載速度糖声,前面有段時(shí)間我以為activity的oncreate到onresume的運(yùn)行時(shí)間就是頁面的加載時(shí)間身冬,其實(shí)這是錯(cuò)誤的,在源碼中顯示onresume生命周期方法中只表示頁面可以進(jìn)行交互了冯丙,而addview還在以后進(jìn)行調(diào)用才去渲染布局。
然后我找到了其他的方法遭京,iderHandler可以去監(jiān)聽ui線程中渲染消息被處理了后的回調(diào)胃惜,獲取ui線程后再獲取消息隊(duì)列進(jìn)行addIderHander進(jìn)行監(jiān)聽。這樣就能獲取到activity啟動(dòng)后到渲染完頁面的準(zhǔn)確時(shí)間哪雕。超過60ms則定義成頁面卡頓船殉,這樣我們就可以獲取那些頁面出現(xiàn)卡頓現(xiàn)象。
接下來就是真正開始進(jìn)行優(yōu)化了斯嚎,從頁面布局角度出發(fā)利虫,布局層級(jí)結(jié)構(gòu)應(yīng)該扁平化設(shè)計(jì),多使用relativeLayout來開發(fā)堡僻,多使用include標(biāo)簽提高布局復(fù)用糠惫,在xml布局中先把控件設(shè)置成gone,在網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)后再進(jìn)行顯示苦始,去掉不必要的背景寞钥,避免過度重繪的現(xiàn)象慌申,避免出現(xiàn)scollview嵌套listview這樣復(fù)雜現(xiàn)象陌选。
從代碼角度出發(fā),代碼量也不小的情況下蹄溉,這里要把卡頓現(xiàn)象定位到問題代碼上咨油,利用method trace工具來獲取方法的消耗時(shí)間來推斷出那些代碼出現(xiàn)了卡頓。把耗時(shí)操作的代碼放在子線程進(jìn)行柒爵,把一些耗時(shí)資源與無用資源進(jìn)行合理釋放役电,避免創(chuàng)建不必要的對(duì)象,使用lint來減少冗余代碼提高執(zhí)行效率棉胀,listview復(fù)用法瑟,利用線程池來管理線程創(chuàng)建銷毀的開銷。
從內(nèi)存角度出發(fā)唁奢,在這說明下霎挟,內(nèi)存方面對(duì)于頁面卡頓現(xiàn)象來說影響不大,主要是gc操作會(huì)產(chǎn)生一點(diǎn)點(diǎn)的頁面卡頓麻掸。那我們就應(yīng)該減少gc操作酥夭,動(dòng)態(tài)加大分配內(nèi)存,在清單文件里聲明該app加大內(nèi)存分配,圖片資源內(nèi)存優(yōu)化熬北,默認(rèn)8888改成565疙描,盡可能緩存小尺寸的圖片,使用lrucache進(jìn)行緩存圖片資源讶隐,利用dump 操作來獲取內(nèi)存情況起胰,使用leakCanary,檢測(cè)并解決內(nèi)存泄漏問題整份,常見的內(nèi)存泄漏問題待错,匿名內(nèi)部類,內(nèi)部類的默認(rèn)持有外部引用的內(nèi)存泄漏烈评,比如handler.thread.runnable火俄,單例模式下的context引用,單例模式的生命周期是與application一致讲冠,應(yīng)該用context.getapplicatiancontext來使用瓜客,但是在顯示popwindow.dialog時(shí)需要activity的context,所以也要避免在單例模式中顯示彈窗竿开。bitmap.cursor.廣播接受者.在頁面被銷毀的情況下進(jìn)行關(guān)閉與釋放谱仪,避免過度使用static成員變量。
這些操作下來否彩,就大體解決了性能優(yōu)化問題疯攒。
線程池面試:
降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗列荔。
提高響應(yīng)速度敬尺。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行贴浙。
提高線程的可管理性砂吞。線程是稀缺資源,如果無限制的創(chuàng)建崎溃,不僅會(huì)消耗系統(tǒng)資源蜻直,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配袁串,調(diào)優(yōu)和監(jiān)控概而。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ScheduledExecutorService擴(kuò)展ExecutorService接口并增加了schedule方法。調(diào)用schedule方法可以在指定的延時(shí)后執(zhí)行一個(gè)Runnable或者Callable任務(wù)囱修。
corePoolSize:核心線程數(shù)量赎瑰,當(dāng)有新任務(wù)在execute()方法提交時(shí) 如果運(yùn)行的線程少于 corePoolSize,則創(chuàng)建新線程來處理任務(wù)蔚袍,即使線程池中的其他線程是空閑的乡范;
如果線程池中的線程數(shù)量大于等于 corePoolSize 且小于 maximumPoolSize配名,則只有當(dāng)workQueue滿時(shí)才創(chuàng)建新的線程去處理任務(wù);
如果設(shè)置的corePoolSize 和 maximumPoolSize相同晋辆,則創(chuàng)建的線程池的大小是固定的渠脉,這時(shí)如果有新任務(wù)提交略就,若workQueue未滿容握,則將請(qǐng)求放入workQueue中,等待有空閑的線程去從workQueue中取任務(wù)并處理衙四;
如果運(yùn)行的線程數(shù)量大于等于maximumPoolSize霸饲,這時(shí)如果workQueue已經(jīng)滿了为朋,則通過handler所指定的策略來處理任務(wù); maximumPoolSize:最大線程數(shù)量厚脉;workQueue:等待隊(duì)列习寸,當(dāng)任務(wù)提交時(shí),如果線程池中的線程數(shù)量大于等于corePoolSize的時(shí)候傻工,把該任務(wù)封裝成一個(gè)Worker對(duì)象放入等待隊(duì)列霞溪;SynchronousQueue,LinkedBlockingQueue中捆,ArrayBlockingQueue鸯匹。keepAliveTime:線程池維護(hù)線程所允許的空閑時(shí)間。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Worker類設(shè)計(jì)如下:
1泄伪、繼承了AQS類殴蓬,可以方便的實(shí)現(xiàn)工作線程的中止操作;
2蟋滴、實(shí)現(xiàn)了Runnable接口染厅,可以將自身作為一個(gè)任務(wù)在工作線程中執(zhí)行;
3脓杉、當(dāng)前提交的任務(wù)firstTask作為參數(shù)傳入Worker的構(gòu)造方法糟秘;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? runWorker方法是線程池的核心: 1简逮、線程啟動(dòng)之后球散,通過unlock方法釋放鎖,設(shè)置AQS的state為0散庶,表示運(yùn)行中斷蕉堰;
2、獲取第一個(gè)任務(wù)firstTask悲龟,執(zhí)行任務(wù)的run方法屋讶,不過在執(zhí)行任務(wù)之前,會(huì)進(jìn)行加鎖操作须教,任務(wù)執(zhí)行完會(huì)釋放鎖皿渗;
3斩芭、在執(zhí)行任務(wù)的前后,可以根據(jù)業(yè)務(wù)場(chǎng)景自定義beforeExecute和afterExecute方法乐疆;
4划乖、firstTask執(zhí)行完成之后,通過getTask方法從阻塞隊(duì)列中獲取等待的任務(wù)挤土,如果隊(duì)列中沒有任務(wù)琴庵,getTask方法會(huì)被阻塞并掛起,不會(huì)占用cpu資源仰美;通過ExecutorService.submit()方法提交的任務(wù)迷殿,可以獲取任務(wù)執(zhí)行完的返回值。
數(shù)據(jù)庫框架面試:
描述對(duì)應(yīng)要?jiǎng)?chuàng)建表格的對(duì)象的注解 描述對(duì)應(yīng)要?jiǎng)?chuàng)建表格的主鍵ID的注解 描述對(duì)應(yīng)要?jiǎng)?chuàng)建表格的對(duì)象的列字段注解?
創(chuàng)建表操作:獲取要?jiǎng)?chuàng)建表的對(duì)象咖杂,通過反射來獲取表名庆寺,如果表存在數(shù)據(jù)庫或者表名為空,則不進(jìn)行操作诉字,否則進(jìn)行建表操作止邮, 通過反射來拿到表的所有要?jiǎng)?chuàng)建的字段名,利用一個(gè)HashMap進(jìn)行緩存字段名與字段類型奏窑,這里如果不存在字段名就不需要?jiǎng)?chuàng)建了导披,將所有的字段拼接成對(duì)應(yīng)的SQL語句,執(zhí)行對(duì)應(yīng)的創(chuàng)建表操作埃唯。
增刪改查的操作:獲取bean的字段數(shù)組撩匕,先進(jìn)行暴力反射,反正字段是私有字段墨叛,先判斷字段上是否有Column注解止毕,如果有,表示此字段是和列對(duì)應(yīng)漠趁,其注解對(duì)應(yīng)的值就是表示的列名扁凛,然后通過field.get(m)獲取對(duì)象其字段對(duì)應(yīng)的值,然后條件到ContentValues對(duì)象中闯传,values.put(key, value);key是注解Column對(duì)應(yīng)的值谨朝,value是通過反射獲取對(duì)象的值。獲取bean中的Column注解甥绿,然后獲取其值(類名)字币,通過columnvalue=cursor.getColumnIndex(columnName)獲取此列在游標(biāo)中的值,然后通過field.set(bean,columnvalue),設(shè)置給傳進(jìn)來的bean對(duì)象字段值共缕。
LRUcache面試:
LruCache算法就是當(dāng)緩存空間滿了的時(shí)候洗出,將最近最少使用的數(shù)據(jù)從緩存空間中刪除以增加可用的緩存空間來緩存新內(nèi)容,緩存圖片看的是占用的內(nèi)存的大小图谷,每張圖片的占用內(nèi)存也是不一樣的翩活,一次不能這樣算阱洪。因此要重寫sizeOf方法,手動(dòng)將這里改為本次緩存的圖片的大小菠镇。 LruCache通過強(qiáng)引用來緩存一定數(shù)量的值. 每當(dāng)一個(gè)值被訪問的時(shí)候澄峰,這個(gè)值就會(huì)移動(dòng)到緩存隊(duì)列的頭部. 如果插入數(shù)據(jù)時(shí)發(fā)現(xiàn)緩存不夠了,就會(huì)將隊(duì)列中最近訪問次數(shù)最少的數(shù)據(jù)刪掉.可以設(shè)置緩存大小辟犀,一般設(shè)置成4M俏竞。 this.map = new LinkedHashMap(0, 0.75f, true); 初始化LinkedHashMap。第一個(gè)參數(shù)是初始容量堂竟,第二個(gè)參數(shù)是填裝因子魂毁,或叫加載因子,第三個(gè)參數(shù)是排序模式出嘹,true表示在訪問的時(shí)候進(jìn)行排序席楚,否則只在插入的時(shí)候才排序。get和put都可能會(huì)執(zhí)行trimToSize方法中在緩存隊(duì)列中先找到最近最少使用的元素税稼,調(diào)用LinkedHashMap的eldest()方法返回最不經(jīng)常使用的方法烦秩。然后刪掉這個(gè)元素,并減少已使用的緩存空間郎仆。LinkedHashMap集合內(nèi)部本來就有個(gè)排序功能只祠,當(dāng)?shù)谌齻€(gè)參數(shù)是true的時(shí)候,數(shù)據(jù)在被訪問的時(shí)候就會(huì)排序扰肌,這個(gè)排序的結(jié)果就是把最近訪問的數(shù)據(jù)放到集合的最后面抛寝。LinkedHashMap內(nèi)部是使用雙向循環(huán)鏈表來存儲(chǔ)數(shù)據(jù)的。也就是每一個(gè)元素都持有他上一個(gè)元素的地址和下一個(gè)元素的地址曙旭。當(dāng)集合的get方法被調(diào)用時(shí)盗舰,會(huì)調(diào)用這個(gè)方法。 如果accessOrder為true桂躏,就把這個(gè)元素放在集合的最末端钻趋。eldest方法獲取到的就是距離鏈表頂部最近的元素。
kotlin面試:
Kotlin 具有現(xiàn)代靜態(tài)編程語言的很多特點(diǎn)剂习,如類型推斷蛮位、多范式支持、可空性表達(dá)进倍、擴(kuò)展函數(shù)土至、null安全檢測(cè)购对,屬性訪問猾昆,Kotlin的缺點(diǎn) 編譯速度,額外運(yùn)行的大小骡苞,代碼初始可讀性垂蜗。能夠擴(kuò)展一個(gè)類的新功能而無需繼承該類或使用像裝飾者這樣的任何類型的設(shè)計(jì)模式楷扬。 這通過叫做 擴(kuò)展 的特殊聲明完成。Kotlin 支持 擴(kuò)展函數(shù) 和 擴(kuò)展屬性贴见。聲明一個(gè)擴(kuò)展函數(shù)烘苹,我們需要用一個(gè) 接收者類型 也就是被擴(kuò)展的類型來作為他的前綴。由于擴(kuò)展沒有實(shí)際的將成員插入類中片部,因此對(duì)擴(kuò)展屬性來說幕后字段是無效的镣衡。這就是為什么擴(kuò)展屬性不能有初始化器。他們的行為只能由顯式提供的 getters/setters 定義档悠。Kotlin 的類型系統(tǒng)旨在從我們的代碼中消除 NullPointerException廊鸥。NPE 的唯一可能的原因可能是:顯式調(diào)用 throw NullPointerException();使用了下文描述的 !! 操作符辖所;有些數(shù)據(jù)在初始化時(shí)不一致惰说,例如當(dāng):傳遞一個(gè)在構(gòu)造函數(shù)中出現(xiàn)的未初始化的 this 并用于其他地方(“泄漏 this”);超類的構(gòu)造函數(shù)調(diào)用一個(gè)開放成員缘回,該成員在派生中類的實(shí)現(xiàn)使用了未初始化的狀態(tài)吆视;Java 互操作:企圖訪問平臺(tái)類型的 null 引用的成員;用于具有錯(cuò)誤可空性的 Java 互操作的泛型類型酥宴,例如一段 Java 代碼可能會(huì)向 Kotlin 的 MutableList中加入 null啦吧,這意味著應(yīng)該使用 MutableList 來處理它;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
官方文檔:https://hltj.gitbooks.io/kotlin-reference-chinese/content/txt/classes.html