Android學(xué)習(xí)改造升級(jí)漫漫之路


升級(jí)gradle之后發(fā)現(xiàn) 打包出來(lái)的apk體積突然大了將近一倍。

在主工程的AndroidManifest.xml配置中,在<application>標(biāo)簽,添加android:extractNativeLibs=true屬性,打包APK時(shí),是否對(duì)so庫(kù)進(jìn)行壓縮的控制屬性為?android:extractNativeLibs。

handler的原理

簡(jiǎn)單來(lái)說(shuō)Handler中只有一個(gè)Looper,Handler將Message發(fā)送到Looper的消息隊(duì)列中,即MessageQueue,等待Looper的循環(huán)讀取Message,處理Message,然后調(diào)用Message的target,即附屬的Handler的dispatchMessage()方法,將該消息回調(diào)到handleMessage()方法中,然后完成更新UI操作。

Android中為什么Looper.loop()不會(huì)卡死主線程

Looper.loop()方法會(huì)循環(huán)調(diào)用消息隊(duì)列messageQueue.next()方法來(lái)讀取消息,當(dāng)messageQueue中沒(méi)有消息時(shí)主線程會(huì)阻塞在message.next()內(nèi)部的messageQueue.nativePollOnce()函數(shù)調(diào)用中,類(lèi)似于Object.wait(),但會(huì)讓出cpu執(zhí)行權(quán),讓cpu休眠直到下次有消息進(jìn)入

談?wù)凩ist,Set,Map的區(qū)別

List中存儲(chǔ)的數(shù)據(jù)是有順序的,并且值允許重復(fù);Map中存儲(chǔ)的數(shù)據(jù)是無(wú)序的,它的鍵是不允許重復(fù)的,但是值是允許重復(fù)的;Set中存儲(chǔ)的數(shù)據(jù)是無(wú)順序的,并且不允許重復(fù),但元素在集合中的位置是由元素的hashcode決定,即位置是固定的(Set集合是根據(jù)hashcode來(lái)進(jìn)行數(shù)據(jù)存儲(chǔ)的,所以位置是固定的,但是這個(gè)位置不是用戶可以控制的,所以對(duì)于用戶來(lái)說(shuō)set中的元素還是無(wú)序的)

談?wù)凙rrayList和LinkedList的區(qū)別?

ArrayList是基于數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList是基于鏈表的數(shù)據(jù)結(jié)構(gòu)。

ArrayList適用于查詢操作,LinkedList適用于插入和刪除操作

請(qǐng)簡(jiǎn)述 LinkedHashMap 的工作原理和使用方式?

查看LinkedHashMap源碼發(fā)現(xiàn)是繼承HashMap實(shí)現(xiàn)Map接口。也就是HashMap的方法LinkedMap都有。

LinkHashMap與HashMap的主要區(qū)別是:LinkedHashMap是有序的,hashmap是無(wú)序的。LinkedHashMap通過(guò)維護(hù)一個(gè)雙向鏈表實(shí)現(xiàn)有序,也正是因?yàn)橐S護(hù)這個(gè)鏈表,內(nèi)存上有更大的開(kāi)銷(xiāo)。

補(bǔ)充下有序和無(wú)序:我們說(shuō)的無(wú)序是插入順序和輸出順序不一致。 補(bǔ)充一下鏈表結(jié)構(gòu)和順序結(jié)構(gòu):線性結(jié)構(gòu)分為順序結(jié)構(gòu),和鏈表結(jié)構(gòu)。

順序結(jié)構(gòu):在內(nèi)存中是一塊完整有序內(nèi)存。所以我們?cè)诓樵兊臅r(shí)候時(shí)候直接索引index,便可找到要查詢的數(shù)據(jù),速度非???,缺點(diǎn)是插入刪除慢。有點(diǎn)類(lèi)似班級(jí)排隊(duì)時(shí)(一列縱隊(duì)),每個(gè)人都知道自己在第幾個(gè)位置。老師只要說(shuō)第三個(gè)位置,那這個(gè)同學(xué)立馬知道老師要找的是自己。這時(shí)候要插入一個(gè)同學(xué)到第二個(gè)位置,所以之前第二個(gè)位置開(kāi)始往后的每個(gè)同學(xué)的位置都要+1。所以比較慢。

鏈表結(jié)構(gòu):通過(guò)結(jié)點(diǎn)頭記錄該結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn)和下一個(gè)下一個(gè)結(jié)點(diǎn)(就是傳統(tǒng)的雙鏈表,單鏈表就是只記錄下一個(gè)結(jié)點(diǎn),循環(huán)鏈表就是最后一個(gè)結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn)指向第一個(gè)結(jié)點(diǎn))。正是因?yàn)檫@種關(guān)系,所以鏈表結(jié)構(gòu)不需要一塊完整的內(nèi)存,而且插入刪除相對(duì)快,但是查詢相對(duì)慢。但是因?yàn)橐S護(hù)結(jié)點(diǎn)頭,所以內(nèi)存開(kāi)銷(xiāo)相對(duì)大一點(diǎn)。有點(diǎn)類(lèi)似于班級(jí)排隊(duì)時(shí),每個(gè)人雖然不知道自己的位置,但是知道自己前面是誰(shuí)和后面是誰(shuí)。當(dāng)要插入一個(gè)同學(xué)b時(shí)到c前面時(shí),只要c 同學(xué)記住自己之前是a,現(xiàn)在換成b.b記住自己前面是a,后面是c。所以想對(duì)來(lái)說(shuō)插入很快。刪除類(lèi)似。但是當(dāng)老師按位置查詢時(shí),就要先從第一個(gè)開(kāi)始計(jì)數(shù),知道找到老師要找的數(shù)字。所以查詢慢。

如何實(shí)現(xiàn)多線程中的同步?

多線程同步和異步不是一回事。幾種情況,

就是大家說(shuō)的synchronized 他可以保證原子性,保證多個(gè)線程在操作同一方法時(shí)只有一個(gè)線程可以持有鎖,并且操作該方法,

就是手動(dòng)調(diào)用讀寫(xiě)鎖,

手動(dòng)操作線程的wait和notify

volatile我記得是沒(méi)有原子性的,他可以保證內(nèi)存可見(jiàn)性,在多線程的情況下保證每個(gè)線程的數(shù)據(jù)都是最新的

synchronized和volatile關(guān)鍵字的區(qū)別?

1.volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀?。籹ynchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問(wèn)該變量,其他線程被阻塞住。

2.volatile僅能使用在變量級(jí)別;synchronized則可以使用 在變量、方法、和類(lèi)級(jí)別的volatile僅能實(shí)現(xiàn)變量的修改可見(jiàn)性,不能保證原子性;而synchronized則可以保證變量的修改可見(jiàn)性和原子性

3.volatile不會(huì)造成線程的阻塞;synchronized可能會(huì)造成 線程的阻塞。

4.volatile標(biāo)記的變量不會(huì)被編譯器優(yōu)化;synchronized標(biāo) 記的變量可以被編譯器優(yōu)化

請(qǐng)談?wù)?Thread 中 run() 與 start()的區(qū)別?

run() 和普通的成員方法一樣,可以被重復(fù)調(diào)用。但是如果單獨(dú)調(diào)用 run 方法,則不是在子線程中執(zhí)行。start()這個(gè)方法只能被調(diào)用一次。調(diào)用這個(gè)方法后 程序會(huì)啟動(dòng)一個(gè) 新的線程來(lái) 執(zhí)行 run 方法。注意 :調(diào)用start 后,線程處于可運(yùn)行狀態(tài)(并沒(méi)有運(yùn)行),一旦得到 cup 時(shí)間片,就開(kāi)始執(zhí)行run 方法,run 方法結(jié)束后,線程則立即終止。

談?wù)劸€程阻塞的原因?

1、線程執(zhí)行了Thread.sleep(int n)方法,線程放棄CPU,睡眠n毫秒然后恢復(fù)運(yùn)行。

2、線程要執(zhí)行一段同步代碼,由于無(wú)法獲得相關(guān)的同步鎖,只好進(jìn)入阻塞狀態(tài),等到獲得了同步鎖,才能恢復(fù)運(yùn)行。

3、線程執(zhí)行了一個(gè)對(duì)象的wait()方法,進(jìn)入阻塞狀態(tài),只有等到其他線程執(zhí)行了該對(duì)象的notify()或notifyAll()方法,才可能將其喚醒。

4、線程執(zhí)行I/O操作或進(jìn)行遠(yuǎn)程通信時(shí),會(huì)因?yàn)榈却嚓P(guān)的資源而進(jìn)入阻塞狀態(tài)。例如,當(dāng)線程執(zhí)行System.in.read()方法時(shí),如果用戶沒(méi)有向控制臺(tái)輸入數(shù)據(jù),則該線程會(huì)一直等讀到了用戶的輸入數(shù)據(jù)才從read()方法返回。進(jìn)行遠(yuǎn)程通信時(shí),在客戶程序中,線程在以下情況可能進(jìn)入阻塞狀態(tài)。

5、請(qǐng)求與服務(wù)器建立連接時(shí),即當(dāng)線程執(zhí)行Socket的帶參數(shù)的構(gòu)造方法,或執(zhí)行Socket的connect()方法時(shí),會(huì)進(jìn)入阻塞狀態(tài),直到連接成功,此線程才從Socket的構(gòu)造方法或connect()方法返回。

6、線程從Socket的輸入流讀取數(shù)據(jù)時(shí),如果沒(méi)有足夠的數(shù)據(jù),就會(huì)進(jìn)入阻塞狀態(tài),直到讀到了足夠的數(shù)據(jù),或者到 達(dá)輸入流的末尾,或者出現(xiàn)了異常,才從輸入流的read()方 法返回或異常中斷。輸入流中有多少數(shù)據(jù)才算足夠呢?這要看線程執(zhí)行的read()方法的類(lèi)型。int read(); 只要輸入流中有一個(gè)字節(jié),就算足夠。int read(byte[] buff); 只要輸入流中的字節(jié)數(shù)目與參數(shù)buff數(shù)組的長(zhǎng)度相同,就算足夠。String readLine(); 只要輸入流中郵一行字符串,就算足夠。值得注意的是,InputStream類(lèi)并沒(méi)有readLine方法,在過(guò)濾流BufferedReader類(lèi)中才有此方法。

7、線程向Socket的輸出流寫(xiě)一批數(shù)據(jù)時(shí),可能會(huì)進(jìn)入阻塞狀態(tài),等到輸出了所有的數(shù)據(jù),或者出現(xiàn)異常,才從輸出流的write()方法返回或異常中斷。

8、調(diào)用Socket的setSoLinger()方法設(shè)置了關(guān)閉Socket的延遲時(shí)間,那么當(dāng)線程執(zhí)行Socket的close方法時(shí),會(huì)進(jìn)入阻塞狀態(tài),直到底層Socket發(fā)送完所有剩余數(shù)據(jù),或者超過(guò)了setSoLinger()方法設(shè)置的延遲時(shí)間,才從close()方法返回。

Service如何進(jìn)行?;睿?br>

1、利用系統(tǒng)廣播拉活 2、利用系統(tǒng)service拉活 3、利用Native進(jìn)程拉活<Android5.0以后失效> fork進(jìn)行監(jiān)控主進(jìn)程 4、利用native拉活 5、利用JobScheduler機(jī)制拉活<Android5.0以后> 6、利用賬號(hào)同步機(jī)制拉活

簡(jiǎn)單介紹下ContentProvider是如何實(shí)現(xiàn)數(shù)據(jù)共享的?

ContentProvider(內(nèi)容提供者):對(duì)外提供了統(tǒng)一的訪問(wèn)數(shù)據(jù)的接口。 ContentResolver(內(nèi)容解析者):通過(guò)URI的不同來(lái)操作不同的ContentProvider中的數(shù)據(jù)。 ContentObserver(內(nèi)容觀察者):觀察特定URI引起的數(shù)據(jù)庫(kù)的變化。通過(guò)ContentResolver進(jìn)行注冊(cè),觀察數(shù)據(jù)是否發(fā)生變化及時(shí)通知刷新頁(yè)面(通過(guò)Handler通知主線程更新UI)。

請(qǐng)簡(jiǎn)述 Http 與 Https 的區(qū)別?

HTTP協(xié)議傳輸?shù)臄?shù)據(jù)都是未加密的,也就是明文的,因此使用HTTP協(xié)議傳輸隱私信息非常不安全,為了保證這些隱私數(shù) 據(jù)能加密傳輸,于是網(wǎng)景公司設(shè)計(jì)了SSL(Secure SocketsLayer)協(xié)議用于對(duì)HTTP協(xié)議傳輸?shù)臄?shù)據(jù)進(jìn)行加密,從而就誕生了HTTPS。 1、https協(xié)議需要到ca申請(qǐng)證書(shū),一般免費(fèi)證書(shū)較少,因而需要一定費(fèi)用。 2、http是超文本傳輸協(xié)議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協(xié)議。 3、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。 4、http的連接很簡(jiǎn)單,是無(wú)狀態(tài)的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,比http協(xié)議安全。 最后一點(diǎn)在Android 9.0 如果用http進(jìn)行傳輸,需要在application節(jié)點(diǎn)下設(shè)置android:usesCleartextTraffic="true"

TCP、UDP、socket理解

tcp需要三次握手,四次揮手才能將一串消息發(fā)送出去。udp是無(wú)連接的,線程不安全的,發(fā)送消息效率更高,不管消息是否到達(dá)

而socket一般用于即時(shí)通訊

view的繪制原理

主要是三個(gè)方法,OnMeasure(繪制視圖大小.?精確模式(MeasureSpec.EXACTLY,MeasureSpec.AT_MOST) onLayout(視圖展示的位置)ondraw(繪制)

簡(jiǎn)述view的事件分發(fā)機(jī)制

主要三個(gè)方法去處理;是否消費(fèi)(方法名太長(zhǎng))-是否攔截 -是否處理

android主要有幾種動(dòng)畫(huà)形式?

屬性動(dòng)畫(huà),幀動(dòng)畫(huà),補(bǔ)間動(dòng)畫(huà)(平移、漸變、旋轉(zhuǎn)、縮放)

android中activity啟動(dòng)模式有幾種?

默認(rèn)模式(new一個(gè)創(chuàng)建一次)

棧頂復(fù)用(這個(gè)activity是在棧頂,再次啟動(dòng)這個(gè)activity就直接復(fù)用,不創(chuàng)建新的activity)

棧內(nèi)復(fù)用(任務(wù)棧中存在這個(gè)activity,拉出來(lái)置頂復(fù)用)

全局單例模式(升級(jí)版棧內(nèi)復(fù)用)

RxJava常用操作符

創(chuàng)建型操作符:

create操作符已經(jīng)用到就是創(chuàng)建被觀察者

just是不斷地將事件添加到任務(wù)隊(duì)列中

fromArray:跟just基本一樣就是可以直接接收數(shù)組

interval:創(chuàng)建一個(gè)按固定時(shí)間間隔發(fā)射整數(shù)序列的?Observable,相當(dāng)于定時(shí)器

range:創(chuàng)建發(fā)射指定范圍的整數(shù)序列的 Observable,可以拿來(lái)替代 for 循環(huán),發(fā)射一個(gè)范圍內(nèi)的有

序整數(shù)序列

repeat:創(chuàng)建一個(gè) N 次重復(fù)發(fā)射特定數(shù)據(jù)的 Observable

變換操作符:

map:將 Observable 轉(zhuǎn)換為一個(gè)新的 Observable 對(duì)象并發(fā)射,觀察者將收到新的 Observable 處理

flatMap:該操作符將 Observable 發(fā)射的數(shù)據(jù)集合變換為 Observable 集合,然后將這些 Observable發(fā)射的數(shù)據(jù)平坦化地放進(jìn)一個(gè)單獨(dú)的 Observable

cast:強(qiáng)制將數(shù)據(jù)轉(zhuǎn)換成另外一個(gè)類(lèi)型

concatMap:該操作符功能與 flatMap 操作符一致;不過(guò),它解決了 flatMap 交叉問(wèn)題,提供了一種能夠把發(fā)射的值連續(xù)在一起的函數(shù),而不是合并它們

buffer:buffer 操作符將源 Observable 變換為一個(gè)新的 Observable,這個(gè)新的 Observable 每次發(fā)射一組列表值而不是一個(gè)一個(gè)發(fā)射,和 buffer 操作符類(lèi)似的還有 window 操作符,只不過(guò) window操作符發(fā)射的是 Observable 而不是數(shù)據(jù)列表。

groupBy:該操作符用于分組元素,將源 Observable 變換成一個(gè)發(fā)射 Observables 的新 Observable,它們中的每一個(gè)新 Observable 都發(fā)射一組指定的數(shù)據(jù)。

過(guò)濾操作符:

filter:filter 操作符是對(duì)源 Observable 產(chǎn)生的結(jié)果自定義規(guī)則進(jìn)行過(guò)濾,只有滿足條件的結(jié)果才會(huì)

提交給訂閱者

elementAt:elementAt 操作符用來(lái)返回指定位置的數(shù)據(jù)

distinct:distinct 操 作 符 用 來(lái) 去 重 , 其 只 允 許 還 沒(méi) 有 發(fā) 射 過(guò) 的 數(shù) 據(jù) 項(xiàng) 通 過(guò)

skip、take:skip 操作符將源 Observable 發(fā)射的數(shù)據(jù)過(guò)濾掉前 n 項(xiàng);而 take 操作符則只取前 n 項(xiàng);

ignoreElements:ignoreElements 操作符忽略所有源 Observable 產(chǎn)生的結(jié)果,只把 Observable 的 onSubscribe和 onComplete 事件通知給訂閱者

組合操作符:

startWith:startWith 操作符會(huì)在源 Observable 發(fā)射的數(shù)據(jù)前面插上一些數(shù)據(jù)

merge:merge 操作符將多個(gè) Observable 合并到一個(gè) Observable 中進(jìn)行發(fā)射,merge 可能會(huì)讓合并的Observable 發(fā)射的數(shù)據(jù)交錯(cuò)。

concat:將多個(gè) Obserbavle 發(fā)射的數(shù)據(jù)進(jìn)行合并發(fā)射。concat 嚴(yán)格按照順序發(fā)射數(shù)據(jù),前一個(gè)

Observable 沒(méi)發(fā)射完成是不會(huì)發(fā)射后一個(gè) Observable 的數(shù)據(jù)的

zip:zip 操作符合并兩個(gè)或者多個(gè) Observable 發(fā)射出的數(shù)據(jù)項(xiàng),根據(jù)指定的函數(shù)變換它們,并發(fā)

射一個(gè)新值。

combineLastest:當(dāng)兩個(gè) Observable 中的任何一個(gè)發(fā)射了數(shù)據(jù)時(shí),使用一個(gè)函數(shù)結(jié)合每個(gè) Observable 發(fā)射的最近數(shù)據(jù)項(xiàng),并且基于這個(gè)函數(shù)的結(jié)果發(fā)射數(shù)據(jù)。

輔助操作符:

delay:delay 操作符讓原始 Observable 在發(fā)射每項(xiàng)數(shù)據(jù)之前都暫停一段指定的時(shí)間段。

這個(gè)測(cè)試需要在Android環(huán)境下,不能在java環(huán)境下測(cè)試,否則沒(méi)有效果。

Do: Do 系列操作符就是為原始 Observable 的生命周期事件注冊(cè)一個(gè)回調(diào),當(dāng) Observable 的某個(gè)

事件發(fā)生時(shí)就會(huì)調(diào)用這些回調(diào)

subscribeOn、observeOn:

subscribeOn 操作符用于指定 Observable 自身在哪個(gè)線程上運(yùn)行,如果 Observable 需要執(zhí)行耗時(shí)操作,一般可以讓其在新開(kāi)的一個(gè)子線程上運(yùn)行。

observerOn 用來(lái)指定 Observer 所運(yùn)行的線程,也就是發(fā)射出的數(shù)據(jù)在哪個(gè)線程上使用。一般情況下會(huì)指定在主線程中運(yùn)行,這樣就可以修改UI。

timeout:如果原始 Observable 過(guò)了指定的一段時(shí)長(zhǎng)沒(méi)有發(fā)射任何數(shù)據(jù),timeout 操作符會(huì)以一個(gè)

onError 通知終止這個(gè) Observable,或者繼續(xù)執(zhí)行一個(gè)備用的 Observable。

錯(cuò)誤處理操作符:

catch:catch 操作符攔截原始 Observable 的 onError 通知,將它替換為其他數(shù)據(jù)項(xiàng)或數(shù)據(jù)序列,讓

產(chǎn)生的 Observable 能夠正常終止或者根本不終止。RxJava 將 catch 實(shí)現(xiàn)為以下 3 個(gè)不同的操作符:

retry:retry 操作符不會(huì)將原始 Observable 的 onError 通知傳遞給觀察者,它會(huì)訂閱這個(gè) Observable,再給它一次機(jī)會(huì)無(wú)錯(cuò)誤地完成其數(shù)據(jù)序列。 retry 總是傳遞 onNext 通知給觀察者,由于重新訂閱,這可能會(huì)造成數(shù)據(jù)項(xiàng)重復(fù)。RxJava 中的實(shí)現(xiàn)為 retry 和 retryWhen。

布爾操作符:

all:操作符根據(jù)一個(gè)函數(shù)對(duì)源 Observable 發(fā)射的所有數(shù)據(jù)進(jìn)行判斷,最終返回的結(jié)果就是這

個(gè)判斷結(jié)果。這個(gè)函數(shù)使用發(fā)射的數(shù)據(jù)作為參數(shù),內(nèi)部判斷所有的數(shù)據(jù)是否滿足我們定義好的

判斷條件。如果全部都滿足則返回 true,否則就返回 false。

contains、isEmpty:contains 操作符用來(lái)判斷源 Observable 所發(fā)射的數(shù)據(jù)是否包含某一個(gè)數(shù)據(jù)。如果包含該數(shù)據(jù),會(huì)返回 true;如果源 Observable 已經(jīng)結(jié)束了卻還沒(méi)有發(fā)射這個(gè)數(shù)據(jù),則返回 false。isEmpty操作符用來(lái)判斷源 Observable 是否發(fā)射過(guò)數(shù)據(jù)。

條件操作符:

amb:amb 操作符對(duì)于給 定兩個(gè) 或多個(gè) Observable,它只 發(fā)射首先發(fā)射數(shù) 據(jù)或通知 的那個(gè)

Observable 的所有數(shù)據(jù)。

defaultIfEmpty:發(fā)射來(lái)自原始 Observable 的數(shù)據(jù)。如果原始 Observable 沒(méi)有發(fā)射數(shù)據(jù),就發(fā)射一個(gè)默認(rèn)數(shù)據(jù)

轉(zhuǎn)換操作符:

toList:toList 操作符將發(fā)射多項(xiàng)數(shù)據(jù)且為每一項(xiàng)數(shù)據(jù)調(diào)用 onNext 方法的 Observable 發(fā)射的多項(xiàng)數(shù)

據(jù)組合成一個(gè) List,然后調(diào)用一次 onNext 方法傳遞整個(gè)列表。

toSortedList:toSortedList 操作符類(lèi)似于 toList 操作符;不同的是,它會(huì)對(duì)產(chǎn)生的列表排序,默認(rèn)是自然升序。如果發(fā)射的數(shù)據(jù)項(xiàng)沒(méi)有實(shí)現(xiàn) Comparable 接口,會(huì)拋出一個(gè)異常。當(dāng)然,若發(fā)射的數(shù)據(jù)項(xiàng)

沒(méi)有實(shí)現(xiàn) Comparable 接口,可以使用 toSortedList(Func2)變體,其傳遞的函數(shù)參數(shù) Func2 會(huì)作

用于比較兩個(gè)數(shù)據(jù)項(xiàng)。

toMap:toMap 操作符收集原始 Observable 發(fā)射的所有數(shù)據(jù)項(xiàng)到一個(gè) Map(默認(rèn)是 HashMap),然

后發(fā)射這個(gè) Map。

如何排查OOM?

簡(jiǎn)單來(lái)說(shuō),就是程序的內(nèi)存不夠了,掛掉了,Android Studio 中集成的 Profiler 可以分析 APP 內(nèi)存??梢允褂?Dump 功能方便的查看當(dāng)前的內(nèi)存快照,一般來(lái)說(shuō)bitmap被view持有,造成OOM的情況較多

內(nèi)存泄漏?

什么是內(nèi)存泄漏? 通常我們認(rèn)為,在運(yùn)行的程序中,如果一個(gè)無(wú)法訪問(wèn)的對(duì)象卻仍然占用著內(nèi)存空間,即為此對(duì)象造成了內(nèi)存泄漏

ANR的排查方法

1.當(dāng)您的 Activity 位于前臺(tái)時(shí),您的應(yīng)用在 5 秒鐘內(nèi)未響應(yīng)輸入事件或 BroadcastReceiver(如按鍵或屏幕輕觸事件)。

2.雖然前臺(tái)沒(méi)有 Activity,但您的 BroadcastReceiver 用了相當(dāng)長(zhǎng)的時(shí)間仍未執(zhí)行完畢。

一般可以通過(guò)監(jiān)控日志查找分析

Intent傳輸數(shù)據(jù)大小限制

intent傳輸數(shù)據(jù)的大小受Binder的限制,理論上上限是1M,實(shí)際512K以上就會(huì)報(bào)錯(cuò),

解決辦法

減少傳輸數(shù)據(jù)量

Intent通過(guò)綁定一個(gè)Bundle來(lái)傳輸,這個(gè)可以超過(guò)1M,不過(guò)也不能過(guò)大

通過(guò)內(nèi)存共享,使用靜態(tài)變量或者使用EventBus等類(lèi)似的通信工具

通過(guò)文件共享????

深拷貝和淺拷貝的區(qū)別

深拷貝和淺拷貝是只針對(duì)Object和Array這樣的引用數(shù)據(jù)類(lèi)型的。淺拷貝只復(fù)制指向某個(gè)對(duì)象的指針,而不復(fù)制對(duì)象本身,新舊對(duì)象還是共享同一塊內(nèi)存。但深拷貝會(huì)另外創(chuàng)造一個(gè)一模一樣的對(duì)象,新對(duì)象跟原對(duì)象不共享內(nèi)存,修改新對(duì)象不會(huì)改到原對(duì)象。

LRUCache 原理

LruCache 中 Lru 算法的實(shí)現(xiàn)就是通過(guò) LinkedHashMap 來(lái)實(shí)現(xiàn)的。LruCache 中將 LinkedHashMap 的順序設(shè)置為 LRU 順序來(lái)實(shí)現(xiàn) LRU 緩存,通過(guò)調(diào)用get/put等方法控制緩存大小,這樣當(dāng)內(nèi)存緩存達(dá)到設(shè)定的最大值時(shí),將鏈表頭部的對(duì)象(近期最少用到的)移除。

什么是MVVM

Model代表數(shù)據(jù)模型,數(shù)據(jù)和業(yè)務(wù)邏輯都在Model層中定義,View代表UI視圖,負(fù)責(zé)數(shù)據(jù)的展示,ViewModel負(fù)責(zé)監(jiān)聽(tīng)Model中數(shù)據(jù)的改變并且控制視圖的更新,處理用戶交互操作。實(shí)現(xiàn)了數(shù)據(jù)雙向綁定

從ActivityA 啟動(dòng) ActivityB的生命周期

A.onCreate —> A.onStart —> A.onResume —>A.onPause —>B.onCreate —> B.onStart —> B.onResume—> A.onSaveInstanceState —> A.onStop

EventBus原理

主要解決多線程和多activity、fragment之間的通信問(wèn)題,原理是通過(guò)注解和反射實(shí)現(xiàn),將方法保存在公共隊(duì)列中供其調(diào)用,首先在register()的時(shí)候,通過(guò)反射把當(dāng)前類(lèi)的所有方法遍歷,然后把帶有@Subscribe注解的方法保存在隊(duì)列中,在調(diào)用的時(shí)候發(fā)送post方法,與隊(duì)列中的方法進(jìn)行匹配,這里只匹配方法的參數(shù),如果一樣的話就掉起該方法

gradle中minsdkversion、compilesdkversion、targetsdkversion的區(qū)別

compileSdkVersion 告訴 Gradle 用哪個(gè) Android SDK 版本編譯你的應(yīng)用。minSdkVersion 則是應(yīng)用可以運(yùn)行的最低要求?targetSdkVersion 是 Android 提供向前兼容的主要依據(jù),在應(yīng)用的 targetSdkVersion 沒(méi)有更新之前系統(tǒng)不會(huì)應(yīng)用最新的行為變化。

內(nèi)存優(yōu)化技巧

1.謹(jǐn)慎使用 Service。IntentService 是 Service 的一個(gè)子類(lèi),在它的內(nèi)部有一個(gè)工作線程來(lái)處理耗時(shí)任務(wù),當(dāng)任務(wù)執(zhí)行完后,IntentService 就會(huì)自動(dòng)停止

2.選擇優(yōu)化后的數(shù)據(jù)容器。Android 提供了幾個(gè)優(yōu)化后的數(shù)據(jù)容器,包括 SparseArray、SparseBooleanArray 以及 LongSparseArray。

3.小心代碼抽象。抽象會(huì)導(dǎo)致更多的代碼需要被執(zhí)行,也就是需要更多的時(shí)間和把更多的代碼映射到內(nèi)存中

4.使用 protobuf 作為序列化數(shù)據(jù)。Protocol buffers 是 Google 設(shè)計(jì)的,它可以對(duì)結(jié)構(gòu)化的數(shù)據(jù)序列化,與 XML 類(lèi)似,不過(guò)比 XML 更小,更快,而且更簡(jiǎn)單

5.Apk 瘦身。有些資源和第三方庫(kù)會(huì)在我們不知情的情況下大量消耗內(nèi)存,Android Studio 提供了 R8 和 ProGuard 幫助我們縮小 Apk,去掉不必要的資源

6.使用 Dagger2 進(jìn)行依賴注入。Dagger2 是在編譯期生成代碼,而不是用反射實(shí)現(xiàn)的,這樣就避免了反射帶來(lái)的內(nèi)存開(kāi)銷(xiāo),而是在編譯期生成代碼

7.謹(jǐn)慎使用第三方庫(kù)。這些第三方庫(kù)包括日志、分析、圖片加載、緩存以及其他框架,都有可能帶來(lái)性能問(wèn)題

Android輕量級(jí)數(shù)據(jù)SparseArray詳解

由于SparseArray中Key存儲(chǔ)的是數(shù)組形式,因此可以直接以int作為Key。避免了HashMap的裝箱拆箱操作,性能更高且int的存儲(chǔ)開(kāi)銷(xiāo)遠(yuǎn)遠(yuǎn)小于Integer,SparseArray采用了延遲刪除的機(jī)制,通過(guò)將刪除KEY的Value設(shè)置DELETED,方便之后對(duì)該下標(biāo)的存儲(chǔ)進(jìn)行復(fù)用,使用二分查找,時(shí)間復(fù)雜度為O(LogN),如果遇到頻繁刪除,不會(huì)觸發(fā)gc機(jī)制,導(dǎo)致mSize 遠(yuǎn)大于有效數(shù)組長(zhǎng)度,造成性能損耗

一般使用場(chǎng)景key為整型;不需要頻繁的刪除;元素個(gè)數(shù)相對(duì)較少

DataBinding原理分析

使用了觀察者設(shè)計(jì)模式,可以看到單項(xiàng)綁定和雙向綁定的區(qū)別就是“@”和“{}”之間多了個(gè)“=”,簡(jiǎn)化Activity/Fragment中部分操作View的代碼,讓我們更加關(guān)注業(yè)務(wù)邏輯的實(shí)現(xiàn)

Android藍(lán)牙通信

有兩種常見(jiàn)的方式,一種是socket方式,另一種是通過(guò)GATT Server(Android 5.0以后)通信。通過(guò)Bluetooth?API執(zhí)行掃描、配對(duì)、連接、關(guān)閉等操作

wait和sleep的區(qū)別

wait會(huì)釋放鎖、sleep不會(huì)

wait是Object的方法,sleep是Thread的方法

線程池的工作機(jī)制

如果當(dāng)前運(yùn)行的線程少于corePoolSize(核心線程數(shù)),則創(chuàng)建新線程來(lái)執(zhí)行任務(wù)(注意,執(zhí)行這一步驟需要獲取全局鎖)。

如果運(yùn)行的線程等于或多于corePoolSize,則將任務(wù)加入BlockingQueue。

如果無(wú)法將任務(wù)加入BlockingQueue(隊(duì)列已滿),則創(chuàng)建新的線程來(lái)處理任務(wù)(注意,執(zhí)行這一步驟需要獲取全局鎖)。

如果創(chuàng)建新線程將使當(dāng)前運(yùn)行的線程超出maximumPoolSize,任務(wù)將被拒絕,并調(diào)用RejectedExecutionHandler.rejectedExecution()方法。

注意:wait()、notify()、notifyAll()必須在synchronized中使用,否則會(huì)報(bào)異常:java.lang.IllegalMonitorStateException: object not locked by thread before notifyAll()

實(shí)現(xiàn)線程的方式

繼承Thread、實(shí)現(xiàn)Runable、實(shí)現(xiàn)Callable

Android常用動(dòng)畫(huà)

屬性動(dòng)畫(huà)、補(bǔ)間動(dòng)畫(huà)(平移、漸變、旋轉(zhuǎn)、縮放)、幀動(dòng)畫(huà)

Android常用存儲(chǔ)方式

SharePreferences、SQLite、Contert Provider、File、網(wǎng)絡(luò)存儲(chǔ)

mmkv存儲(chǔ)原理

基于 mmap 的高性能通用 key-value 組件,底層序列化/反序列化使用 protobuf 實(shí)現(xiàn),性能高,穩(wěn)定性強(qiáng)

Android依賴版本統(tǒng)一管理

通過(guò)自定義config.build文件可以實(shí)現(xiàn)依賴的統(tǒng)一管理,對(duì)于組件化開(kāi)發(fā)的工程提供維護(hù)便利

Android本地廣播和全局廣播的區(qū)別

1、本地廣播:發(fā)送的廣播事件不被其他應(yīng)用程序獲取,也不能響應(yīng)其他應(yīng)用程序發(fā)送的廣播事件。本地廣播只能被動(dòng)態(tài)注冊(cè),不能靜態(tài)注冊(cè)。動(dòng)態(tài)注冊(cè)或方法時(shí)需要用到LocalBroadcastManager。

2、全局廣播:發(fā)送的廣播事件可被其他應(yīng)用程序獲取,也能響應(yīng)其他應(yīng)用程序發(fā)送的廣播事件(可以通過(guò) exported–是否監(jiān)聽(tīng)其他應(yīng)用程序發(fā)送的廣播 在清單文件中控制) 全局廣播既可以動(dòng)態(tài)注冊(cè),也可以靜態(tài)注冊(cè)

Gradle中混淆的使用

一個(gè)是 Java?代碼的混淆,另外一個(gè)是資源的壓縮,如keep?命令指的是一系列以?-keep?開(kāi)頭的命令,它主要用來(lái)保留 Java 中不需要進(jìn)行混淆的元素

為什么onCreate和onResume方法中不可以獲取View寬高

是因?yàn)檫€沒(méi)執(zhí)行View的繪制流程

view.post之所以能夠拿到寬高,是因?yàn)樵诶L制之前,會(huì)將獲取寬高的任務(wù)放到Handler的消息隊(duì)列,等到View的繪制結(jié)束之后,便會(huì)執(zhí)行

子線程發(fā)消息到主線程進(jìn)行更新 UI,有幾種方式

handler 、AsyncTask、EventBus,廣播,view.post, runinUiThread,其實(shí)本質(zhì)上就2種:handler機(jī)制 + 廣播

子線程中能不能 new handler?為什么

必須可以。子線程 可以new 一個(gè)mainHandler,然后發(fā)送消息到UI Thread。

AsyncTask(異步任務(wù))的工作原理

AsyncTask是對(duì)Handler和線程池的封裝,使用它可以更新用戶界面,當(dāng)然,這里的更新操作還是在主線程中完成的,但由于AsyncTask內(nèi)部包含了一個(gè)Handler,所以可以發(fā)送消息給主線程,讓他更新UI,另外AsyncTask內(nèi)還包含了一個(gè)線程池,避免了不必要的創(chuàng)建和銷(xiāo)毀線程的開(kāi)銷(xiāo)

談?wù)剬?duì)Android?NDK的理解

NDK是一系列工具的················集合.NDK提供了一系列的工具,幫助開(kāi)發(fā)者快速開(kāi)發(fā)C或C++的動(dòng)態(tài)庫(kù),并能自動(dòng)將so和java應(yīng)用一起打包成apk.這些工具對(duì)開(kāi)發(fā)者的幫助是巨大的.NDK集成了交叉編譯器,并提供了相應(yīng)的mk文件隔離CPU,平臺(tái),ABI等差異,開(kāi)發(fā)人員只需要簡(jiǎn)單修改 mk文件(指出"哪些文件需要編譯","編譯特性要求"等),就可以創(chuàng)建出so.NDK可以自動(dòng)地將so和Java應(yīng)用一起打包,極大地減輕了開(kāi)發(fā)人員的打包工作.NDK提供了一份穩(wěn)定,功能有限的API頭文件聲明.Google明確聲明該API是穩(wěn)定的,在后續(xù)所有版本中都穩(wěn)定支持當(dāng)前發(fā)布的API.從該版本的NDK中看出,這些 API支持的功能非常有限,包含有:C標(biāo)準(zhǔn)庫(kù)(libc),標(biāo)準(zhǔn)數(shù)學(xué)庫(kù)(libm ),壓縮庫(kù)(libz),Log庫(kù)(liblog).

我只是啟動(dòng)一個(gè)應(yīng)用程序,為什么Application的onCreate執(zhí)行了多次?

在啟動(dòng)應(yīng)用程序的時(shí)候,linux中調(diào)用fork創(chuàng)建的子進(jìn)程,將共享父進(jìn)程的代碼空間,復(fù)制父進(jìn)程數(shù)據(jù)空間,此時(shí)子進(jìn)程會(huì)獲得父進(jìn)程的所有變量的一份拷貝。如果這個(gè)時(shí)候第三方框架會(huì)啟動(dòng)新的進(jìn)程,那么也會(huì)執(zhí)行接下來(lái)的Application的代碼,所以會(huì)執(zhí)行多次了

為什么ArrayMap比HashMap更適合Android開(kāi)發(fā)

HashMap在存放數(shù)據(jù)的時(shí)候,無(wú)論存放的量是多少,首先是會(huì)生成一個(gè)Entry對(duì)象,這個(gè)就比較浪費(fèi)內(nèi)存空間,而ArrayMap只是把數(shù)據(jù)插入到數(shù)組中,不用生成新的對(duì)象,存放大量數(shù)據(jù)的時(shí)候,ArrayMap性能上就不如HashMap,因?yàn)锳rrayMap使用的是二分查找法找的下標(biāo),當(dāng)數(shù)據(jù)多了下標(biāo)值找起來(lái)時(shí)間就花的久,此外還需要將所有數(shù)據(jù)往后移再插入數(shù)據(jù),而HashMap只要插入到鏈表或者樹(shù)后面即可

為什么Arrays.asList后往里add數(shù)據(jù)會(huì)報(bào)錯(cuò)

java.util.ArrayList里面,我們看下里面的代碼是存在add方法的,我們?cè)倩仡^再去看看asList生成的List是在java.util.Arrays包里面的,而這里面的ArrayList我們看到了,并沒(méi)有去實(shí)現(xiàn)List接口,所以也就沒(méi)有add,get等方法,另外在kotlin里面,我們會(huì)看到一個(gè)細(xì)節(jié),當(dāng)你敲完Arrays.asList的時(shí)候,編譯器會(huì)提示你,可以轉(zhuǎn)換成listof函數(shù),而這個(gè)還是我們知道生成的list都是只能讀取,不能往里寫(xiě)數(shù)據(jù)

Thread.sleep(0)到底“睡沒(méi)睡”

其實(shí)在Android操作系統(tǒng)中,每個(gè)線程使用cpu資源都是有優(yōu)先級(jí)的,優(yōu)先級(jí)高的才有資格使用,而操作系統(tǒng)則是在一個(gè)線程釋放cpu資源以后,重新計(jì)算所有線程的優(yōu)先級(jí)來(lái)重新分配cpu資源,所以sleep真正的意義不是暫停,而是在接下去的時(shí)間內(nèi)不參與cpu的競(jìng)爭(zhēng),等到cpu重新分配完資源以后,如果優(yōu)先級(jí)沒(méi)變,那么繼續(xù)執(zhí)行,所以sleep(0)秒的真正含義是觸發(fā)cpu資源重新分配

Android 中 Intent 采用了什么設(shè)計(jì)模式?

采用了原型模式

原型模式的好處在于方便地拷貝某個(gè)實(shí)例的屬性進(jìn)行使用、又不會(huì)對(duì)原實(shí)例造成影響,其邏輯在于對(duì)?Cloneable?接口的實(shí)現(xiàn)

Android中對(duì)象的內(nèi)存回收機(jī)制

對(duì)象的內(nèi)存回收機(jī)制由垃圾回收器來(lái)實(shí)現(xiàn)。垃圾回收器是一個(gè)系統(tǒng)級(jí)別的服務(wù),它會(huì)定期掃描應(yīng)用的內(nèi)存空間,檢查哪些對(duì)象已經(jīng)不再被引用,然后將這些對(duì)象標(biāo)記為垃圾,并回收它們所占用的內(nèi)存空間。 對(duì)象的內(nèi)存回收機(jī)制主要涉及以下幾個(gè)方面:

1.引用計(jì)數(shù)

2.標(biāo)記清除

3.復(fù)制算法

4.標(biāo)記整理

Android中如何實(shí)現(xiàn)插件動(dòng)態(tài)加載

實(shí)現(xiàn)插件化主要有兩種方式:動(dòng)態(tài)加載和靜態(tài)加載,其中動(dòng)態(tài)加載是指在應(yīng)用程序運(yùn)行時(shí)加載插件,而靜態(tài)加載是指在編譯時(shí)加載插件。

Andorid中如何實(shí)現(xiàn)數(shù)據(jù)加密

1. 對(duì)稱加密(如 AES、DES、3DES)

2.非對(duì)稱加密(如 RSA、DSA)

3.消息摘要(如 MD5、SHA)

Java中實(shí)現(xiàn)線程同步的方式

synchronized關(guān)鍵字:使用synchronized關(guān)鍵字可以對(duì)共享資源進(jìn)行同步,使得在同一時(shí)間只有一個(gè)線程可以訪問(wèn)該資源。synchronized可以修飾方法或代碼塊,保證了線程的互斥訪問(wèn)和可見(jiàn)性。

Lock接口:Lock是Java5中引入的一個(gè)新的同步機(jī)制,通過(guò)Lock和Unlock方法來(lái)對(duì)共享資源進(jìn)行加鎖和釋放操作,與synchronized相比,可以更加靈活地控制同步范圍。

ReentrantLock類(lèi):ReentrantLock是Lock接口的一個(gè)實(shí)現(xiàn)類(lèi),功能比synchronized更加強(qiáng)大,包括可重入鎖、可中斷鎖、公平性、實(shí)現(xiàn)Condition等功能。

volatile關(guān)鍵字:使用volatile關(guān)鍵字修飾的變量在多個(gè)線程之間可見(jiàn),即一個(gè)線程對(duì)該變量做出的修改,會(huì)被其他線程立即看到,并更新自己的緩存。volatile關(guān)鍵字可以保證線程之間的可見(jiàn)性。

AtomicInteger類(lèi):AtomicInteger是Java中提供的一個(gè)原子性變量類(lèi),它可以保證多個(gè)線程同時(shí)對(duì)一個(gè)整數(shù)變量進(jìn)行加減操作時(shí)的原子性。

synchronized塊和Lock接口實(shí)現(xiàn)讀寫(xiě)鎖:在讀寫(xiě)分離的場(chǎng)景中,可以使用synchronized塊或Lock接口的實(shí)現(xiàn)類(lèi)ReadWriteLock來(lái)實(shí)現(xiàn)讀寫(xiě)鎖,從而提高程序的并發(fā)性和性能。

Movie原生實(shí)現(xiàn)gif圖動(dòng)態(tài)顯示

Android 方法超過(guò)64K

當(dāng)項(xiàng)目越做越大,方法數(shù)越來(lái)越多的時(shí)候,會(huì)不可避免的遇到方法數(shù)超過(guò)64K的錯(cuò)誤,這是因?yàn)閱蝹€(gè)dex文件索引的方法是由short類(lèi)型來(lái)保存的。而short的長(zhǎng)度范圍是(-32768~32767),即總共65536個(gè)。那么如何解決?

?1.精簡(jiǎn)方法數(shù)量,刪除無(wú)用的類(lèi)、方法、第三方庫(kù)。(治標(biāo)不治本,業(yè)務(wù)代碼增加,始終會(huì)達(dá)到臨界值)?2.使用ProGuard去掉一些未使用的代碼 (同解決方法1)?3.插件化方案??4.分割Dex

?
多進(jìn)程android webview,Android上多進(jìn)程中使用webview的問(wèn)題

在Applicationd類(lèi)的onCreate或者onBaseContextAttached方法中加入

public void onBaseContextAttached(Context base) {

super.onBaseContextAttached(base);

initWebViewDataDirectory(this);

}

/**

* 得到進(jìn)程名稱

* @param context

* @return

*/

public static String getProcessName(Context context) {

try {

if (context == null)

return null;

ActivityManager manager = (ActivityManager)

context.getSystemService(Context.ACTIVITY_SERVICE);

for (ActivityManager.RunningAppProcessInfo processInfo :

manager.getRunningAppProcesses()) {

if (processInfo.pid == android.os.Process.myPid()) {

return processInfo.processName;

}

}

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 為webView設(shè)置目錄后綴

* @param context

*/

@RequiresApi(api = Build.VERSION_CODES.P)

public static void initWebViewDataDirectory(Context context) {

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

String processName = getProcessName(context);

if (!context.getPackageName().equals(processName)) {//判斷是否是默認(rèn)進(jìn)程名稱

WebView.setDataDirectorySuffix(processName);

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容