最新Android面試總結(jié)

年底互聯(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棘幸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子倦零,更是在濱河造成了極大的恐慌误续,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扫茅,死亡現(xiàn)場(chǎng)離奇詭異蹋嵌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)葫隙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門栽烂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人恋脚,你說(shuō)我怎么就攤上這事腺办。” “怎么了糟描?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵怀喉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我船响,道長(zhǎng)躬拢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任见间,我火速辦了婚禮聊闯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘米诉。我一直安慰自己菱蔬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拴泌,像睡著了一般犹褒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弛针,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音李皇,去河邊找鬼削茁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掉房,可吹牛的內(nèi)容都是我干的茧跋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼卓囚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瘾杭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哪亿,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粥烁,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蝇棉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體讨阻,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年篡殷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钝吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡板辽,死狀恐怖奇瘦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情劲弦,我是刑警寧澤耳标,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站邑跪,受9級(jí)特大地震影響麻捻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜呀袱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一贸毕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夜赵,春花似錦明棍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沸版。三九已至,卻和暖如春兴蒸,著一層夾襖步出監(jiān)牢的瞬間视粮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工橙凳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蕾殴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓岛啸,卻偏偏與公主長(zhǎng)得像钓觉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坚踩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容