年底互聯(lián)網(wǎng)寒冬來(lái)臨集灌,很不幸悔雹,本人公司由于資金鏈斷裂,也加入年底找工作大軍當(dāng)中欣喧。年底工作不好找腌零,特此勉勵(lì)所有有相同經(jīng)歷的同學(xué)們,保持耐心唆阿,互相鼓勵(lì)益涧。
個(gè)人面試了一兩個(gè)星期,以下是我個(gè)人的面試總結(jié)驯鳖,僅供參考闲询,有錯(cuò)懇請(qǐng)?zhí)岢觥?/p>
一.網(wǎng)絡(luò)
1.網(wǎng)絡(luò)協(xié)議 http久免、tcp肥缔、udp懦傍、socket
網(wǎng)絡(luò)分層和協(xié)議關(guān)系如下:
物理層--
數(shù)據(jù)鏈路層--
網(wǎng)絡(luò)層-- IP協(xié)議
傳輸層-- TCP、UDP協(xié)議
會(huì)話層--
表示層-- HTTP協(xié)議拨扶、https鸽捻、ftp
http協(xié)議是基于tcp/ip協(xié)議的一種應(yīng)用丁寄。而 socket封裝了做tcp/ip開(kāi)發(fā)的網(wǎng)絡(luò)接口,
通過(guò)Socket泊愧,我們才能使用TCP/IP協(xié)議。http是短連接盛正,socket是長(zhǎng)連接删咱。
2.tcp三次握手、4次揮手
第一次握手:建立連接豪筝√底蹋客戶端發(fā)送連接請(qǐng)求報(bào)文段,將SYN位置為1续崖,Sequence Number為x敲街;然后,
客戶端進(jìn)入SYN_SEND狀態(tài)严望,等待服務(wù)器的確認(rèn)多艇;
第二次握手:服務(wù)器收到SYN報(bào)文段。服務(wù)器收到客戶端的SYN報(bào)文段像吻,需要對(duì)這個(gè)SYN報(bào)文段進(jìn)行確認(rèn)峻黍,
設(shè)置Acknowledgment Number為x+1(Sequence Number+1);同時(shí)拨匆,自己自己還要發(fā)送SYN請(qǐng)求信息姆涩,
將SYN位置為1,Sequence Number為y惭每;服務(wù)器端將上述所有信息放到一個(gè)報(bào)文段(即SYN+ACK報(bào)文段)中骨饿,
一并發(fā)送給客戶端,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài)台腥;
第三次握手:客戶端收到服務(wù)器的SYN+ACK報(bào)文段宏赘。然后將Acknowledgment Number設(shè)置為y+1,
向服務(wù)器發(fā)送ACK報(bào)文段览爵,這個(gè)報(bào)文段發(fā)送完畢以后置鼻,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài),
完成TCP三次握手蜓竹。
當(dāng)客戶端和服務(wù)器通過(guò)三次握手建立了TCP連接以后箕母,當(dāng)數(shù)據(jù)傳送完畢储藐,肯定是要斷開(kāi)TCP連接的啊。
那對(duì)于TCP的斷開(kāi)連接嘶是,這里就有了神秘的“四次分手”钙勃。
第一次分手:主機(jī)1(可以使客戶端,也可以是服務(wù)器端)聂喇,設(shè)置Sequence Number和Acknowledgment
Number辖源,向主機(jī)2發(fā)送一個(gè)FIN報(bào)文段;此時(shí)希太,主機(jī)1進(jìn)入FIN_WAIT_1狀態(tài)克饶;這表示主機(jī)1沒(méi)有數(shù)據(jù)要發(fā)送給主機(jī)2了;
第二次分手:主機(jī)2收到了主機(jī)1發(fā)送的FIN報(bào)文段誊辉,向主機(jī)1回一個(gè)ACK報(bào)文段Acknowledgment
Number為Sequence Number加1矾湃;主機(jī)1進(jìn)入FIN_WAIT_2狀態(tài);主機(jī)2告訴主機(jī)1堕澄,我“同意”你的關(guān)閉請(qǐng)求邀跃;
第三次分手:主機(jī)2向主機(jī)1發(fā)送FIN報(bào)文段,請(qǐng)求關(guān)閉連接蛙紫,同時(shí)主機(jī)2進(jìn)入LAST_ACK狀態(tài)拍屑;
第四次分手:主機(jī)1收到主機(jī)2發(fā)送的FIN報(bào)文段,向主機(jī)2發(fā)送ACK報(bào)文段坑傅,然后主機(jī)1進(jìn)入TIME_WAIT狀態(tài)僵驰;
主機(jī)2收到主機(jī)1的ACK報(bào)文段以后,就關(guān)閉連接裁蚁;此時(shí)矢渊,主機(jī)1等待2MSL后依然沒(méi)有收到回復(fù),
則證明Server端已正常關(guān)閉枉证,那好矮男,主機(jī)1也可以關(guān)閉連接了。
3.https對(duì)比http
簡(jiǎn)單來(lái)說(shuō)室谚,HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸毡鉴、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,要比http協(xié)議安全秒赤。
HTTPS和HTTP的區(qū)別主要如下:
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é)議安全睦刃。
4.防止抓包
因?yàn)樽グ浖鏔iddle都是需要利用wifi代理袋坑,所以判斷是wifi代理,則不訪問(wèn)數(shù)據(jù)
if(isWifiProxy()){ //true,使用了wifi代理
//不做訪問(wèn)操作
}else{ //flase,正常用戶眯勾,未使用wifi代理
//訪問(wèn)數(shù)據(jù)
}
public static boolean isWifiProxy() {
final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >=
Build.VERSION_CODES.ICE_CREAM_SANDWICH;
String proxyAddress;
int proxyPort;
if (IS_ICS_OR_LATER) {
proxyAddress = System.getProperty("http.proxyHost");
String portStr = System.getProperty("http.proxyPort");
proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
} else {
proxyAddress = android.net.Proxy.getHost(context);
proxyPort = android.net.Proxy.getPort(context);
}
return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);
}
5.防止多并發(fā)請(qǐng)求,比如說(shuō)一個(gè)頁(yè)面多個(gè)請(qǐng)求
Android本來(lái)就是要做并發(fā)請(qǐng)求婆誓,開(kāi)線程池在里面發(fā)網(wǎng)絡(luò)請(qǐng)求吃环,
如果真要防止并發(fā),那就弄個(gè)排隊(duì)的線程池就行了洋幻,參考AsyncTask在高版本的實(shí)現(xiàn)郁轻,就是排隊(duì)。
6.跟后臺(tái)交互網(wǎng)絡(luò)優(yōu)化
1. 比如頻繁調(diào)用的接口文留,可以考慮用長(zhǎng)連接
2. 需要傳輸數(shù)據(jù)的接口可以考慮讓服務(wù)器支持304狀態(tài)好唯,比如etag和last-modified。當(dāng)沒(méi)有變化時(shí)燥翅,不返回?cái)?shù)據(jù)骑篙,從緩存取
3. 讓服務(wù)端把小接口合并成大接口,減少網(wǎng)絡(luò)請(qǐng)求的次數(shù)
7.為何選擇retrofit
1.解耦:比方說(shuō)通過(guò)注解來(lái)配置請(qǐng)求參數(shù)森书,通過(guò)工廠來(lái)生成CallAdapter靶端,Converter
2.注解:通過(guò)注解方式配置請(qǐng)求,代碼簡(jiǎn)介凛膏、使用方便(將一個(gè)http請(qǐng)求抽象出java接口杨名,再通過(guò)動(dòng)態(tài)代理翻譯成http請(qǐng)求)
3.同步異步:支持同步和異步執(zhí)行,只要調(diào)用enqueue/execute即可完成猖毫;
4.自由:更大自由度地支持我們自定義的業(yè)務(wù)邏輯台谍,如自定義Converters,或者搭配rxjava使用
8.retrofit生命周期管理
http://www.reibang.com/p/5f3e1398b3bd
9.http組成
http請(qǐng)求報(bào)文和響應(yīng)報(bào)文都是由以下4部分組成
1.請(qǐng)求行(狀態(tài)行)
2.請(qǐng)求頭
通用報(bào)文包含Date吁断、Connection趁蕊、Cache-Control
請(qǐng)求報(bào)頭通知服務(wù)器關(guān)于客戶端求求的信息坞生,典型的請(qǐng)求頭有:
Host、User-Agent:發(fā)送請(qǐng)求的瀏覽器類型介衔、操作系統(tǒng)等信息
Accept:客戶端可識(shí)別的內(nèi)容類型列表恨胚,用于指定客戶端接收那些類型的信息
Accept-Encoding:客戶端可識(shí)別的數(shù)據(jù)編碼
Accept-Language:表示瀏覽器所支持的語(yǔ)言類型
Connection:允許客戶端和服務(wù)器指定與請(qǐng)求/響應(yīng)連接有關(guān)的選項(xiàng),例如這是為Keep-Alive則表示保持連接炎咖。
Transfer-Encoding:告知接收端為了保證報(bào)文的可靠傳輸赃泡,對(duì)報(bào)文采用了什么編碼方式。
用于服務(wù)器傳遞自身信息的響應(yīng)乘盼,常見(jiàn)的響應(yīng)報(bào)頭:
Location:用于重定向接受者到一個(gè)新的位置升熊,常用在更換域名的時(shí)候
Server:包含可服務(wù)器用來(lái)處理請(qǐng)求的系統(tǒng)信息,與User-Agent請(qǐng)求報(bào)頭是相對(duì)應(yīng)的
實(shí)體報(bào)頭用來(lái)定于被傳送資源的信息绸栅,請(qǐng)求和響應(yīng)消息都可以傳送一個(gè)實(shí)體级野,常見(jiàn)的實(shí)體報(bào)頭為:
Content-Type:發(fā)送給接收者的實(shí)體正文的媒體類型
Content-Lenght:實(shí)體正文的長(zhǎng)度
Content-Language:描述資源所用的自然語(yǔ)言,沒(méi)有設(shè)置則該選項(xiàng)則認(rèn)為實(shí)體內(nèi)容將提供給所有的語(yǔ)言閱讀
Content-Encoding:實(shí)體報(bào)頭被用作媒體類型的修飾符粹胯,它的值指示了已經(jīng)被應(yīng)用到實(shí)體正文的附加內(nèi)容的編碼
Last-Modified:實(shí)體報(bào)頭用于指示資源的最后修改日期和時(shí)間
Expires:實(shí)體報(bào)頭給出響應(yīng)過(guò)期的日期和時(shí)間
3.空行
4.消息主體
10.http1.0和http1.1區(qū)別
http1.0 服務(wù)端向客服端返回響應(yīng)消息時(shí)蓖柔,確定客戶端收到消息后,便會(huì)關(guān)閉連接风纠。
整個(gè)過(guò)程中况鸣,并不保存任何歷史信息和狀態(tài)信息,所以http也被認(rèn)為是無(wú)狀態(tài)的竹观;
http1.1后將關(guān)閉客戶端連接的主動(dòng)權(quán)交還給客戶端镐捧,增加了持久連接支持
11.為什么選擇okhttp
用于替換httpurlconnection;
支持SPDY協(xié)議臭增,允許連接同一主機(jī)所有請(qǐng)求分享一個(gè)socket懂酱;
使用連接池減少請(qǐng)求延遲;
利用響應(yīng)緩存避免重復(fù)請(qǐng)求誊抛;
使用GZIP壓縮下載內(nèi)容
12.https加密過(guò)程列牺,ssl核心
一個(gè)HTTPS請(qǐng)求實(shí)際上包含了兩次HTTP傳輸,可以細(xì)分為8步拗窃。
第一次HTTP請(qǐng)求:
1.客戶端向服務(wù)器發(fā)起HTTPS請(qǐng)求昔园,連接到服務(wù)器的443端口。
2.服務(wù)器端有一個(gè)密鑰對(duì)并炮,即公鑰和私鑰默刚,是用來(lái)進(jìn)行非對(duì)稱加密使用的,服務(wù)器端保存著私鑰逃魄,不能將其泄露荤西,公鑰可以發(fā)送給任何人。
3.服務(wù)器將自己的公鑰發(fā)送給客戶端。
4.客戶端收到服務(wù)器端的公鑰之后邪锌,會(huì)對(duì)公鑰進(jìn)行檢查勉躺,驗(yàn)證其合法性(CA認(rèn)證),如果公鑰合格觅丰,那么客戶端會(huì)生成一個(gè)隨機(jī)值饵溅,這個(gè)隨機(jī)值就是用于進(jìn)行對(duì)稱加密的密鑰,
我們將該密鑰稱之為client key妇萄,即客戶端密鑰蜕企,這樣在概念上和服務(wù)器端的密鑰容易進(jìn)行區(qū)分。然后用服務(wù)器的公鑰對(duì)客戶端密鑰進(jìn)行非對(duì)稱加密冠句,這樣客戶端密鑰就變成密文了
第二次HTTP請(qǐng)求:
1.客戶端會(huì)發(fā)起HTTPS中的第二個(gè)HTTP請(qǐng)求轻掩,將加密之后的客戶端密鑰發(fā)送給服務(wù)器。
2.服務(wù)器接收到客戶端發(fā)來(lái)的密文之后懦底,會(huì)用自己的私鑰對(duì)其進(jìn)行非對(duì)稱解密唇牧,解密之后的明文就是客戶端密鑰,然后用客戶端密鑰對(duì)數(shù)據(jù)進(jìn)行對(duì)稱加密聚唐,這樣數(shù)據(jù)就變成了密文丐重。
然后服務(wù)器將加密后的密文發(fā)送給客戶端。
3.客戶端收到服務(wù)器發(fā)送來(lái)的密文杆查,用客戶端密鑰對(duì)其進(jìn)行對(duì)稱解密弥臼,得到服務(wù)器發(fā)送的數(shù)據(jù)。這樣HTTPS中的第二個(gè)HTTP請(qǐng)求結(jié)束根灯,整個(gè)HTTPS傳輸完成。
總的來(lái)說(shuō)掺栅,對(duì)數(shù)據(jù)進(jìn)行對(duì)稱加密烙肺,對(duì)稱加密所要使用的密鑰通過(guò)非對(duì)稱加密傳輸。
https://yq.aliyun.com/articles/666103
13.自登錄保存用戶信息怎么保證安全性
方案1.token+https:登錄之后由服務(wù)端生成token氧卧,一般是由用戶信息+設(shè)備信息+時(shí)間組成桃笙。token保存在本地,自登錄時(shí)再由
服務(wù)器判斷是有有效和過(guò)期沙绝。網(wǎng)絡(luò)協(xié)議采用https搏明,傳輸過(guò)程是密文傳輸,因此不擔(dān)心竊取
方案2.RSA+隨機(jī)數(shù)(加鹽):將登錄信息+時(shí)間用RSA公鑰加密傳給服務(wù)端闪檬,服務(wù)端私鑰解密判斷時(shí)間再驗(yàn)證登錄星著,驗(yàn)證通過(guò),
生成隨機(jī)salt粗悯,以用戶id為key虚循,緩存salt值。將用戶名+salt用公鑰加密,返回給客戶端保存横缔。下次登錄直接傳加密串铺遂,驗(yàn)證緩存中salt值是否相等。
方案2的好處是不需要https加密保護(hù)茎刚,還可利用清楚salt緩存強(qiáng)制再次登錄襟锐,在加密串失竊后不必修改密碼
二.圖片處理
1.像素格式
ALPHA_8
表示8位Alpha位圖,即A=8,一個(gè)像素點(diǎn)占用1個(gè)字節(jié),它沒(méi)有顏色,只有透明度
ARGB_4444
表示16位ARGB位圖,即A=4,R=4,G=4,B=4,一個(gè)像素點(diǎn)占4+4+4+4=16位膛锭,2個(gè)字節(jié) 粮坞,不推薦,畫(huà)質(zhì)太差
ARGB_8888
表示32位ARGB位圖泉沾,即A=8,R=8,G=8,B=8,一個(gè)像素點(diǎn)占8+8+8+8=32位捞蚂,4個(gè)字節(jié)
RGB_565
表示16位RGB位圖,即R=5,G=6,B=5,它沒(méi)有透明度,一個(gè)像素點(diǎn)占5+6+5=16位,2個(gè)字節(jié)
2.圖片壓縮處理
http://www.reibang.com/p/a52c31b45d6b
3.為什么選擇Glide
1.鏈?zhǔn)讲僮黪尉浚a簡(jiǎn)潔
2.可以加載gif
3.默認(rèn)像素格式使用的是RGB565姓迅,加載imageview所需要的精確尺寸,省內(nèi)存俊马,速度快
4.Glide控制生命周期
加載圖片時(shí)取得context丁存,然后再上面追加一個(gè)fragment,目的是為了拿到activity的生命周期柴我,
在destroy的時(shí)候取消圖片加載任務(wù)
5.bitmap定寬不定高壓縮
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f); //只設(shè)置寬或者高
bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),
bit.getHeight(), matrix, true);
ps:如果是定寬不定高加載解寝,直接使用imageview的scaletype縮放顯示即可
6.緩存策略&Lru算法
圖片緩存策略一般是采取三級(jí)緩存,內(nèi)存緩存艘儒、本地緩存和網(wǎng)絡(luò)緩存
1).內(nèi)存緩存LruCache
初始化,分配內(nèi)存緩存容量
int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
int cacheSizw = maxMemory/8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key,Bitmap bitmap){
return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}
};
然后便是使用時(shí)的添加和獲取方法了
mMemoryCache.put(key.bitmap);
mMemoryCache.get(key);
2).本地緩存 DiskLruCache
初始化可用內(nèi)存和緩存位置
private static final long DISK_CACHE_SIZE = 1024*1024*50;//50M
File diskCacheDisk = getDiskCacheDir(mContext,"bitmap");
if(!diskCacheDisk.exists())
diskcacheDisk.mkdirs();
//參數(shù)分別為緩存位置聋伦,版本號(hào),單個(gè)節(jié)點(diǎn)對(duì)應(yīng)個(gè)數(shù)界睁,緩存大小
mDiskLruCache = DiskLruCache.open(diskCacheDisk,1,1,DISH_CACHE_SIZE);
DiskLruCache的使用需要通過(guò)Editor
new Thread(new Runnable() {
@Override
public void run() {
try {
String imageUrl = "...";
String key = hashKeyForDisk(imageUrl);//MD5,url有特殊字符觉增,不能直接使用
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
//將圖片下載到outputStream
if (downloadUrlToStream(imageUrl, outputStream)) {
editor.commit();
} else {
editor.abort();
}
}
mDiskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
//獲取緩存
String imageUrl = "...";
String key = hashKeyForDisk(imageUrl);
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(is);
mImage.setImageBitmap(bitmap);
}
內(nèi)存緩存和本地緩存都是基于LRU算法,即Least Recently Used最近最少使用算法
LRU底層實(shí)現(xiàn)是基于LinkedHaspMap翻斟,通過(guò)LinkedHaspMap的header的雙向鏈表實(shí)現(xiàn)逾礁,header的after就是最近最少使用的,刪除的都是這個(gè)節(jié)點(diǎn)
https://cloud.tencent.com/developer/article/1340759
三.其他
1.沖突的時(shí)候改寫(xiě)子view
沖突的時(shí)候一般采取外部攔截法访惜,即改寫(xiě)父view嘹履。在父view的onIterceptTouchEvent需要攔截的地方
返回true即可。(一般是ACTION_MOVE里面债热,ACTION_DOWN不可攔截砾嫉,攔了的話事件就傳不到子view了)
也可采取內(nèi)部攔截法,改寫(xiě)子view窒篱。子view沒(méi)有onIterceptTouchEvent焰枢,在dispatchTouchEvent里面
配合parent.requestDisallowInterceptTouchEvent()攔截
public boolean dispatchTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:{
getParent().requestallowInterceptTouchEvent(true); //傳true表示父view不攔截
break;
}
case MotionEvent.ACTION_MOVE:{
if(父view需要此點(diǎn)擊事件){
getParent().requestallowInterceptTouchEvent(false); //傳false表示父view攔截
}
break;
}
}
return super.dispatchTouchEvent(event);
}
此外父view也要默認(rèn)攔截除了ACTION_DOWN以外的其他事件蚓峦,子view調(diào)用requestallowInterceptTouchEvent(false)才能生效
public boolean onIterceptionTouchEvent(MotionEvent event){
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN)
return false;
else
return true;
}
2.handle為什么會(huì)持有activity對(duì)象
在java中非靜態(tài)內(nèi)部類和匿名內(nèi)部類都會(huì)持有當(dāng)前類的外部引用,所以在activity中使用
handler济锄,handler就會(huì)持有activity的隱式引用暑椰。這樣activity無(wú)法被回收,會(huì)導(dǎo)致內(nèi)存泄漏荐绝。
解決辦法:將handler聲明為靜態(tài)內(nèi)部類一汽,并弱引用activity
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
tv_text.setText(msg.what.toString());
}
}
}
3.Application的content和activity service的content有什么不同
Application content是整個(gè)應(yīng)用內(nèi)有效的,activity低滩、service的content是
組件生命周期內(nèi)有效召夹。所以單例類不可以持有activity、service的content,要使用context.getApplicationContext(),防止內(nèi)存泄漏
Android與js的交互
1.webView的loadUrl("javascript.a()"http://javascript定義的方法)
2.定義javascriptIntereface在本地方法給js文件調(diào)用
public class JsObject {
private final static String TAG = "TAG_JsObject";
@JavascriptInterface
public void onSumResult(int result){
Log.d(TAG, "result = " + result);
}
}
Rxbus怎么異步通知
定義RxBus類
public class RxBus {
private final Subject<Object, Object> _bus;
private volatile static RxBus instace;
private RxBus() {
//使用PublishSubject把在訂閱發(fā)生的時(shí)間點(diǎn)之后來(lái)自原始Observable的數(shù)據(jù)發(fā)射給觀察者
_bus = new SerializedSubject<>(PublishSubject.create());
}
public static RxBus getInstace() {
if (instace == null) {
synchronized (RxBus.class) {
if (instace == null)
instace = new RxBus();
}
}
return instace;
}
public void send(Object o) {
_bus.onNext(o);
}
//配合rxlifecycle使用恕沫,管理生命周期
public Observable<Object> toObserverable(LifecycleTransformer lifecycleTransformer) {
return _bus.compose(lifecycleTransformer);
}
}
發(fā)送通知
RxBus.getInstace().send(ActivityEventEntity.REFRESH.getValue());
接收通知
RxBus.getInstace().toObserverable(
bindUntilEvent(ActivityEvent.DESTROY)) //DESTROY時(shí)銷毀
.subscribe(new Action1<Object>() {
@Override
public void call(Object event) {
if ((int) event == ActivityEventEntity.REFRESH.getValue()) {
...
}
}
});
系統(tǒng)啟動(dòng)流程
首先會(huì)啟動(dòng)引導(dǎo)程序bootloader监憎,接著啟動(dòng)init進(jìn)程;
init進(jìn)程啟動(dòng)zygote進(jìn)程婶溯,zygote首先啟動(dòng)systemServer進(jìn)程鲸阔,初始化硬件設(shè)備和服務(wù);
systemServer會(huì)啟動(dòng)ActivityManagerService,WindowManagerService等迄委,AMS的方法resumeTopActivityLocked會(huì)打開(kāi)桌面launcher褐筛。
應(yīng)用啟動(dòng)流程
launcher遠(yuǎn)程調(diào)用ActivityManagerService,要打開(kāi)一個(gè)新的進(jìn)程叙身;
等確認(rèn)可以打開(kāi)新進(jìn)程后渔扎,ActivityManagerService會(huì)請(qǐng)求zygote進(jìn)程為應(yīng)用fork出一個(gè)新進(jìn)程并實(shí)例化Activityhread;
調(diào)用ActivityThread.bindApplication綁定應(yīng)用的application
activity啟動(dòng)過(guò)程
activity啟動(dòng)會(huì)來(lái)到ActivityManagerService類(Binder對(duì)象)信轿,通過(guò)IPC遠(yuǎn)程調(diào)用ActivityThread的內(nèi)部類ApplicationThread晃痴,
通過(guò)scheduleLaunchActivity方法將啟動(dòng)activity的消息交由Handler H處理,
H會(huì)調(diào)用handlerLaunchActivity-performLaunchActivity方法創(chuàng)建和啟動(dòng)Activity對(duì)象
卸載后打開(kāi)一個(gè)頁(yè)面
調(diào)用c層jni接口财忽,fork一個(gè)新進(jìn)程倘核,負(fù)責(zé)監(jiān)聽(tīng)本應(yīng)用是否被卸載了。Linux中父進(jìn)程死了定罢,fork復(fù)制出來(lái)的子進(jìn)程不會(huì)被殺死。
監(jiān)聽(tīng)data/data/下應(yīng)用的緩存文件夾是否還存在旁瘫,不在則調(diào)用android瀏覽器祖凫,打開(kāi)頁(yè)面
系統(tǒng)里面用到的設(shè)計(jì)模式
mvp存在問(wèn)題
1.代碼增多,增加很多類
2.耦合度高酬凳,界面改動(dòng)惠况,涉及到的接口也要變化;
3.P持有V對(duì)象宁仔,如果view銷毀時(shí)稠屠,P還在做耗時(shí)操作,可能會(huì)內(nèi)存泄漏。
解決:在basePresenter加attach和detach方法权埠,attach獲取泛型view,detach釋放view和中止網(wǎng)絡(luò)榨了。activity銷毀時(shí)調(diào)用detach即可(可在baseActivity中實(shí)現(xiàn))
http://www.reibang.com/p/2fd2d5dac94e
mvvm
m-model,v-view,vm-viewmodel。
mvp模式缺陷:p需要持有view對(duì)象攘蔽,要管理生命周期龙屉;
耦合度高,界面改動(dòng)满俗,涉及到的接口也要變化转捕;
p有可能臃腫
而mvvm中vm不需要持有v對(duì)象,通過(guò)databinding可以做到數(shù)據(jù)驅(qū)動(dòng)唆垃,改變數(shù)據(jù)即可改變ui五芝,解耦和更新ui方便(不用切換到主線程),復(fù)用性高
binder過(guò)程
服務(wù)端通過(guò)service提供binder辕万,客戶端通過(guò)asInterface方法將binder轉(zhuǎn)為代理proxy枢步,并通過(guò)它發(fā)起遠(yuǎn)程請(qǐng)求,然后掛起;
binder寫(xiě)入?yún)?shù)蓄坏,然后調(diào)用transact方法价捧,服務(wù)端會(huì)由onTransact方法處理,將結(jié)果寫(xiě)入reply;
最終服務(wù)端返回請(qǐng)求結(jié)果涡戳,喚醒客戶端
serializable和parcelable區(qū)別
serializable是java自帶的结蟋,parcelable是android專用的。
serializable優(yōu)點(diǎn)實(shí)現(xiàn)起來(lái)簡(jiǎn)單渔彰,只需要實(shí)現(xiàn)serializable接口即可嵌屎。缺點(diǎn)使用反射實(shí)現(xiàn)序列化,開(kāi)銷大效率慢易觸發(fā)GC
parcelable優(yōu)點(diǎn)效率快恍涂,缺點(diǎn)實(shí)現(xiàn)復(fù)雜宝惰,需要實(shí)現(xiàn)parcelable接口,然后復(fù)寫(xiě)describeContents和writeToPracel方法再沧,實(shí)例化靜態(tài)化內(nèi)部變量CREATOR尼夺。
handler機(jī)制
當(dāng)調(diào)用handler.sendMessage方法時(shí),會(huì)通過(guò)enqueueMessage方法將發(fā)送的message加入消息隊(duì)列MessageQueue
Looper.loop方法會(huì)一直去輪詢MessageQueue.next
調(diào)用handler.dispatchMessage發(fā)送消息炒瘸,handler收到消息調(diào)用handlerMessage方法處理消息
handler postdely延時(shí)不準(zhǔn)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
handler的sendMessageDelayed方法一開(kāi)始就計(jì)算好了執(zhí)行的時(shí)間點(diǎn)是系統(tǒng)啟動(dòng)至今的時(shí)間+延時(shí)時(shí)間淤堵,
在MessageQueue.next方法判斷消息是否出列時(shí),是根據(jù)當(dāng)前時(shí)間和msg的時(shí)間判斷的
Message next() {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
...
return msg;
}
}
所以如果上一個(gè)消息有耗時(shí)任務(wù)顷扩,占用了延時(shí)任務(wù)執(zhí)行的時(shí)機(jī)拐邪,就會(huì)不準(zhǔn)確
RxJava流程
首先通過(guò)observable.create方法創(chuàng)建被觀察者,其他操作符最后也是返回一個(gè)observable對(duì)象隘截;
然后調(diào)用subscribe方法綁定觀察者扎阶,入?yún)bserver汹胃,方法會(huì)來(lái)到subscribeActual方法,在里面會(huì)創(chuàng)建CreateEmitter發(fā)射器對(duì)象东臀;
繼續(xù)執(zhí)行observable.subscribe(發(fā)射器)着饥,接著觸發(fā)事件源發(fā)射事件。
而subscribeOn和observerOn兩個(gè)切換線程的原理啡邑,切換到子線程是通過(guò)線程池新開(kāi)一個(gè)runnable,在里面執(zhí)行訂閱或觀察贱勃。
切換到主線程則是利用參數(shù)mainThread返回的handler進(jìn)來(lái)postDelayed方法切換的
路由
現(xiàn)在開(kāi)發(fā)都建議模塊化開(kāi)發(fā),即是多module谤逼。好處如下:
1.可以獨(dú)立模塊編譯贵扰,提升速度
2.方便單元測(cè)試
3.有利于團(tuán)隊(duì)并發(fā)開(kāi)發(fā)
多module帶來(lái)不好的地方就是多module要跳轉(zhuǎn)的話就要互相依賴,導(dǎo)致耦合度高流部。為了解決跳轉(zhuǎn)問(wèn)題所以需要引入路由框架戚绕。
路由可以達(dá)到類似web的跳轉(zhuǎn)方式,根據(jù)路徑跳轉(zhuǎn)枝冀。將router配置在common module就可以解耦
使用可以考慮阿里的ARouter框架舞丛。只要在activity前注入@Route(path = "/com/Activity1") ,
使用 ARouter.getInstance().build("/com/Activity1").navigation() 就可以進(jìn)行跳轉(zhuǎn)
啟動(dòng)優(yōu)化
啟動(dòng)分為冷啟動(dòng)(首次啟動(dòng))果漾,熱啟動(dòng)(任務(wù)列表存在該進(jìn)程)球切。優(yōu)化方案:
1.在application和mainactivity的初始化不做耗時(shí)操作,可以開(kāi)線程绒障,也可以延時(shí)操作
2.用導(dǎo)航圖作為windowBackground吨凑,替換掉冷啟動(dòng)系統(tǒng)準(zhǔn)備時(shí)間的白屏或黑屏,優(yōu)化體驗(yàn)
3.返回鍵退出處理為home退出户辱,用熱啟動(dòng)替換冷啟動(dòng)moveTaskToBack(true)
java
1.java的封裝
是面向?qū)ο蟮闹匾瓌t鸵钝。是將對(duì)象的屬性和操作包裝成一個(gè)獨(dú)立的實(shí)體,
并隱藏對(duì)象的內(nèi)部實(shí)現(xiàn)庐镐,向外界提供一些可訪問(wèn)的屬性操作恩商。
比如說(shuō)實(shí)體類提供部分屬性set方法和get方法
2.java四種引用及使用場(chǎng)景
強(qiáng)引用:默認(rèn)調(diào)用就是強(qiáng)引用,永遠(yuǎn)不會(huì)被GC回收
軟引用:當(dāng)內(nèi)存不足時(shí)必逆,會(huì)被GC回收怠堪,用于存儲(chǔ)一些內(nèi)存敏感的緩存
SoftReference<String> softBean = new SoftReference<String>(new String[]{"a", "b", "c"});
String a = softBean.get();
弱引用:不管內(nèi)存足否,只要被發(fā)現(xiàn)名眉,都會(huì)被GC回收粟矿,用于存儲(chǔ)一些內(nèi)存敏感的緩存
WeakReference<String> weakBean = new WeakReference<String>(new String[]{"a", "b", "c"});
String a = weakBean .get();
虛引用:和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收璧针。主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)嚷炉。
ReferenceQueue<String[]> referenceQueue = new ReferenceQueue<String[]>();
PhantomReference<String[]> referent = new PhantomReference<>("a", referenceQueue);
3.線程start和run區(qū)別
run是同步方法渊啰,運(yùn)行在主線程
start才是真正實(shí)現(xiàn)了多線程
重載和重寫(xiě)(kotlin的重載)
1.ArrayList和LinkedList區(qū)別
ArrayList是基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)探橱,而LinkedList是基于雙向鏈表的數(shù)據(jù)結(jié)構(gòu)
隨機(jī)set和get申屹,ArrayList較快,因?yàn)長(zhǎng)inkedList要移動(dòng)指針
add和remove字段隧膏,LinkedList較快哗讥,因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)
2.循環(huán)arraylist刪除方法
方法1:普通倒序循環(huán)
public static void remove(ArrayList<String> list) {
for (int i = list.size() - 1; i >= 0; i--) {
String s = list.get(i);
if (s.equals("bb")) {
list.remove(s);
}
}
}
之所以要采取倒序,是因?yàn)檎虻脑挵恚瑒h除了第一個(gè)“bb”杆煞,下一個(gè)元素會(huì)往左移動(dòng),這次遍歷就錯(cuò)過(guò)了腐泻,
如果也是bb就刪除不了重復(fù)元素决乎。倒序遍歷時(shí)即使發(fā)生元素刪除也不影響后序元素遍歷。
方法2:迭代器循環(huán)刪除()
public static void remove(ArrayList<String> list) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("b")) {
it.remove();
}
}
}
多線程中不能保證兩個(gè)變量修改的一致性派桩,結(jié)果具有不確定性构诚,所以不推薦這種方法
如果是用強(qiáng)循環(huán),并用arraylist的remove方法會(huì)報(bào)ConcurrentModificationException并發(fā)修改異常
4.靜態(tài)方法和實(shí)例方法用sync鎖是鎖住什么
synchronized加在靜態(tài)方法是鎖類铆惑,加在非靜態(tài)方法是鎖對(duì)象范嘱。
1. 如果多線程同時(shí)訪問(wèn)同一類的類鎖(synchronized 修飾的靜態(tài)方法)以及對(duì)象
鎖(synchronized 修飾的非靜態(tài)方法)這兩個(gè)方法執(zhí)行是異步的,原因:類鎖和對(duì)象鎖是兩種不同的鎖员魏。
2. 類鎖對(duì)該類的所有對(duì)象都能起作用丑蛤,而對(duì)象鎖不能悯嗓。
5.單例最好寫(xiě)法
1.雙重檢查+volitile
public class Singleton {
private static volatile Singleton singleton; //1
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) { //2
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
雙重檢查避免大開(kāi)銷获黔,避免兩個(gè)線程同時(shí)進(jìn)入2尔觉,在同步方法里面在檢查一次后添,保證只實(shí)例化一次
volitile避免線程1剛實(shí)例化完盟庞,線程2走到1時(shí)singleton還不是最新的虹蓄,又實(shí)例化一次池凄。
volitile讀寫(xiě)時(shí)都會(huì)到主線程同步最新的變量
2.靜態(tài)內(nèi)部類
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
類的靜態(tài)屬性只會(huì)在第一次加載類的時(shí)候初始化枣察,在類進(jìn)行初始化時(shí)魄眉,別的線程是無(wú)法進(jìn)入的砰盐。
優(yōu)點(diǎn):避免了線程不安全,延遲加載坑律,效率高岩梳。
3.枚舉法
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
不僅能避免多線程同步問(wèn)題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象
6.注解和反射
http://www.reibang.com/p/d4978bbce12a
Android任務(wù)棧分配
被卧瘢活
棧倒序冒泡
Service生命周期啟動(dòng)
a++跟++a
a++是先賦值冀值,再運(yùn)算。a=0;x=a++先將a賦予x宫屠,后再運(yùn)算a=a+1列疗,所以x=0
++a是先運(yùn)算,再賦值浪蹂。a=0;x=++a先是運(yùn)算a=a+1抵栈,然后才是x=a
sleep和wait區(qū)別
sleep是thread的方法告材,沒(méi)有釋放鎖
wait是object方法,釋放了鎖
hashcode是否相等古劲,當(dāng)對(duì)象不相等
hashmap底層
HashMap是基于hashing的原理斥赋,我們使用put(key, value)存儲(chǔ)對(duì)象到HashMap中,
使用get(key)從HashMap中獲取對(duì)象产艾。當(dāng)我們給put()方法傳遞鍵和值時(shí)疤剑,我們先對(duì)鍵調(diào)用hashCode()方法,
返回的hashCode用于找到bucket位置來(lái)儲(chǔ)存Entry對(duì)象
克隆闷堡,復(fù)制一個(gè)對(duì)象
idhandler(空閑handler) activity什么時(shí)候開(kāi)始顯示的
靜態(tài)方法為什么可以全局調(diào)用(不需要外部引用)
單例為什么要雙重檢查
線程池到達(dá)核心數(shù)隘膘,到達(dá)緩存數(shù),參數(shù)之一策略
場(chǎng)景:圖片上傳外再調(diào)評(píng)論接口(鎖)
7.摘星者
transient關(guān)鍵字
rxjava同步方法杠览,取消訂閱關(guān)系
Glide底層
廣汽
HashMap鏈表什么時(shí)候變紅黑樹(shù)
jvm分區(qū)
classloader
為什么用binder
volicate解決什么問(wèn)題
leakcanary原理
mvvm數(shù)據(jù)混亂怎么辦
路由原理
怎么保證一個(gè)線程一個(gè)looper