目錄
-
安卓的多線程
- 進程和線程的區別
- 進程是CPU資源分配的最小單位,線程的CPU調度的最小單位;
- 進程之間不能共享資源,線程之間可以共享所在進程的地址空間和資源;
- 一個進程中可以有多個線程,一個線程只能屬於一個進程;
- 進程可以開啓線程和其他進程
- 創建線程的三種方式
- 繼承Thread類重寫run方法;
- 實現Runnable接口重寫run方法;
- 實現Callable重寫call方法;
可以提供一個返回值
Call方法可以拋出一個異常
可以通過運行Callable得到的Future對象監聽目標線程調用call方法的結果,得到返回值;
- 對線程池的理解
- 線程池在任務未到來之前會創建一定數量的線程放在空閒隊列中,這些線程都是處於睡眠狀態;當請求到來之後,會給其分配一個空閒線程,然後運行;如果沒有空閒線程,會創建新線程,如果最大線程數滿了,就會拋出異常.
- 作用:
- 是爲了儘可能減少對象創建和銷燬的次數,提高性能,減少CPU資源的消耗,可以控制線程數量,避免內存消耗過度.
- 當線程調度任務發生異常時,會重新創建一個線程來代替異常線程;
- 如何創建:使用ThreadPoolExecutor
- 線程的狀態
- New:新建狀態,還沒有調用start
- Runnable:可運行狀態,調用start進入可運行狀態
- Blocked:阻塞狀態,被鎖阻塞,暫時不活動
- Synchronized:關鍵字修飾的方法或代碼塊獲取鎖時的狀態
- Waiting:等待狀態,不運行任何代碼,等待線程調度器調度,sleep,wait用來暫停當前線程的執行,任何其他線程都可以中斷當前線程的睡眠;
- Timed Waiting:超時等待,在指定時間自行返回;
- Terminated:終止狀態,包括正常終止和異常終止;
- wait和sleep的區別
- Wait用於線程間的通信,如果等待且其他線程被喚醒時,它會釋放鎖;
- Sleep僅是釋放CPU資源或者讓當前線程停止執行一段時間,並不會釋放鎖;
- 線程中斷
- Java中提供了線程中斷機制相關的interrupt()方法,他將中斷標識位設置爲true,具體是否要中斷由程序來判斷;
- New和Terminal不理會線程中斷請求;
- Runnable和Blocked調用interrupt會將標誌位設置爲true;
- Waiting和Timed Waiting會發生InterruptedException異常;
- Thread爲什麼不能用stop方法停止線程
- 會拋出ThreadDeath異常;
- 釋放該線程持有的所有的鎖,數據可能會被破壞;
- 同步方法和同步代碼塊
當多個線程同時操作一個可共享的資源變量時,就會導致數據不準確,解決方法:
-
- 同步方法.即用synchronized關鍵字修飾的方法,在調用此方法前,需要獲得內置鎖,否則處於阻塞狀態;synchronized也可以修飾靜態方法,調動此靜態方法,會鎖住整個類;
- 同步代碼塊:synchronized修飾的語句塊,
- 使用特殊域變量volatile實現線程同步,他會保證修改的值立即更新到主存中,即內存可見性;
- 使用重入鎖:
- ReentrantLock()方法,創建一個ReentrantLock實例;
- lock()獲得鎖;
- unlock()釋放鎖;
- 使用局部變量實現線程同步:使用ThreadLocal來管理變量,每一個線程都有該變量的副本,副本之間獨立
- 線程通信機制
- 四大角色
- Message:消息的載體,常用的四個字段:arg1,arg2,what,obj
- 四大角色
Obj攜帶Object對象,其他三個攜帶整形數據
-
-
- MessageQueue:消息隊列,每個線程只有一個
- Looper:調用loop()方法,進入到一個無限循環(沒有新消息就阻塞),每當MessQueue有消息就取出來,每個線程只有一個Looper
- Handler:發送和處理消息
- ThreadLocal:每個線程私有的本地存儲區域,存儲對象
-
- 原子性,可見性,有序性
- 原子性:某一個操作不可分割,比如a=0是原子操作,a++不是原子操作,非原子操作都存在線程安全問題
- 可見性:指線程之間的可見性,如用volatile修飾的內容具有可見性,但不能保證其原子性,因爲volatile只能使用在變量級別,不能對方法進行修飾;
- 有序性:線程執行的順序按代碼的先後順序,java內存模型中,允許編譯器和處理器對指令進行重排序,這樣會影響到多線程併發執行的正確性.所以可以通過volatile和synchronized以及Lock保證一定的有序性.
- 死鎖產生的四個必要條件
- 互斥條件,一個資源一段時間內僅能夠被一個進程所佔有;
- 請求和保持條件:進程已經持有了至少一個資源,但又提出了新的資源請求,而該資源被其他進程所佔有,此時請求會被阻塞,但自己的資源又保持不放;
- 不可剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,只能自己主動釋放;
- 循環等待條件:若干進程形成循環等待資源的關係,環路中每一個進程所佔有的資源被另一個進程所申請.
- 死鎖的避免
系統對進程所發出的每一個能夠滿足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源.
- 控制線程的個數
- 使用semphore來控制,acquire來請求一個線程,信號量-1,release()釋放一個信號量,信號量+1
- 使用線程池executor,new一個CachedThreadPool固定線程;
- ThreadPoolExecutor的六個參數:
- CorePoolsize:核心池大小
- Maxnumpoolsize最大池大小,工作隊列滿了之後放這裏,滿了就會報異常
- Workqueue:工作隊列:核心池放不下的會先放在這裏
-
Java基礎知識
- Java中堆和棧的理解
- 堆內存:
- 用於存儲java中的對象和數組,當我們new一個對象或者創建一個數組,就會在堆內存中開闢一塊空間;
- 生存期不必告訴編譯器,存取速度較慢;
- 堆是所有線程共享的一塊公共區域,對象都在堆裏創建,但爲了提升效率,線程會從堆中拷貝一個緩存到自己的棧中;
- 棧內存:
- 主要用於執行程序,比如基本類型的變量和對象的引用變量;
- 棧數據數據大小和生存期是確定的,存取速度比堆快;
- 棧是一塊和線程相關的內存區域,每個線程都有自己的棧內存,用於存儲本地變量,方法參數和棧調用;
- 堆內存:
- JVM運行時的內存分佈
- 線程共享:
- 方法區:存放已被加載的類信息,常量,靜態常量;
- 堆:java內存最大的一塊,所有對象實例,數組都存放在java堆,gc回收的地方;
- 線程私有:
- 虛擬機棧:java虛擬棧:存放基本數據類型,對象的引用,方法出口等;
- 本地方法棧
- 程序計數器
- 線程共享:
- 四種引用:
- 強引用:若內存中的對象具有強引用,即時內存不足,拋異常,垃圾回收器也不會回收;如果顯示地設置爲null,或超出生命週期範圍,就可以考慮回收;
- 軟引用:如果內存空間足夠,GC不會回收,內存空間不足,就會回收;如果軟引用被gc回收,虛擬機就會把這個軟引用加入到與之關聯的引用隊列中;
- 弱引用:gc一旦發現只具有弱引用的對象,就會回收;並把這個弱引用加入到與之相關聯的引用隊列中;
- 虛引用:並不會決定對象的生命週期,且必須和引用隊列聯合使用;
- equals和hashCode的區別:
- 兩個對象equals,hashCode一定相等
- HashCode不相等,一定不equals
- String,StringBuffer,StringBuilder的區別
- String字符串常量,不可變,每次改變相當於生成一個新的對象;
- StringBuilder字符串變量,線程不安全,效率高於StringBuffer;
- StringBuffer,字符串變量,線程安全;
- 內部類
- 成員內部類:位於外部類成員位置的類,可以使用外部類中的成員變量和成員方法;
- class Outer {
- private int age = 20;
- //成員位置
- class Inner {
- public void show() {
- System.out.println(age);
- }
- }
- }
- 局部內部類:定義在在方法和作用域中
- class Outer {
- public void method(){
- class Inner {
- }
- }
- }
- 匿名內部類:無構造方法
- 靜態內部類:由static修飾的類,不能使用外圍類的非static的變量和方法
- 作用:每個內部類都能獨立地繼承一個接口的實現,內部類解決java只能單繼承
- 成員內部類:位於外部類成員位置的類,可以使用外部類中的成員變量和成員方法;
- 靜態代理和動態代理的區別:
- 靜態代理:由程序員創建或者由特定工具自動生成源代碼,在程序運行前,代理類的.class文件就存在;
- 在程序運行時,運用反射機制動態創建;
- Java反射機制
- 在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意一個屬性和方法;
-
集合
- HashMap和HashTable的區別
- HashMap支持key和value爲null,HashTable不允許,是因爲HashMap對null進行了特殊處理,將null的hashCode值定爲0
- hashMap不是線程安全,HashTable是線程安全的;
- hashMap實現線程安全的方式爲Collections.synchronized(new HashMap());
- HashMap默認長度爲16,擴容爲原先的2倍,HashTable默認長度爲11,擴容爲原來的2n+1;
- HashMap繼承AbstractMap,HashTable繼承自Dictionary
- HashMap和HashSet的區別
- HashMap實現了map接口,hashmap存儲鍵值對;hashset實現了set接口,僅存儲對象;
- Hashmap使用put()方法添加,使用鍵對象來計算hashcode值;hashset是使用add()方法將元素放入set中,使用成員對象來計算hashcode值;
- Hashmap比較快,因爲是使用唯一的鍵來獲取對象,hashset比較慢,因爲要比較兩個對象是否相同;
- Hashmap和Hashset如何判斷元素重複
- Hashmap可以判斷key是否相同,也可以判斷value是否相同;
- Hashset不能添加重複的元素,當調用add()方法時,首先會計算hashCode值,如果相同就調用equals方法看是否返回true,如果爲true說明元素已經存在;
- Arraylist和vector的區別
- Vector加入了同步機制,線程安全,arraylist線程不安全;
- Vector加入了synchronized同步機制,效率相對慢一點;
-
網絡相關面試題
- TCP/IP五層模型:應用層,傳輸層,網絡層,數據鏈路層,物理層
- 三次握手
- 第一次:建立連接時,客戶端發送syn包(seq=j)到服務器,並進入SYN_SENT狀態,等待服務器確認;
- 第二次:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(seq=k);
- 客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
- 爲什麼是三次而不是兩次:爲了數據傳輸,TCP協議的通信雙方都必須維護一個序列號,三次握手的過程即通信雙方互相告知序列號起始值的過程;
- 握手過程中包中不包括數據,握手完畢之後,纔開始傳送數據.
- 四次揮手
- 第一次:客戶端發送報文告訴服務器沒有數據要發送了;
- 第二次:服務器收到,告訴客戶端收到
- 第三次:服務端向客戶端發送報文,請求關閉連接;
- 第四次:客戶端收到關閉連接的請求,向服務端發送報文,服務端關閉連接;
- 爲什麼不是三次:第二次揮手只是確認客戶端的結束報文段,不代表服務端的數據已經傳輸完畢了,這個傳輸完畢的時間不確定,可能會使得客戶端長時間得不到響應,造成資源浪費
- TCP和UDP的區別
- TCP面向連接,UDP無連接;
- TCP提供可靠的服務,UDP不保證可靠交付;
- TCP面向字節流,UDP面向報文;
- TCP全雙工可靠信道,UDP是不可靠信道;
- TCP連接是點到點,UDP是支持一對一,多對多;
- http協議
- 基於請求和響應模式的無連接,無狀態,應用層協議
- 簡單快速:協議簡單,通信速度快;
- 靈活:成功傳輸任意類型的數據對象,由Content-Type標記;
- 無連接:每次處理一個請求,處理完成即斷開;
- 無狀態:對事物處理沒有記憶功能;
- http是應用層的協議,底層基於TCP/IP協議
- get和post的區別
- Get參數通過url傳遞,post是在requset body中傳遞;
- Get在url傳遞的參數有長度限制,post沒有;
- Get參數會暴露在url上,安全性不如post;
- Get只接受ASCII字符,post無限制;
- Get回退時無害,post會再次請求;
- Get只能進行url編碼,post支持多種編碼方式;
- https
- 是http的安全版;
- 應用層http和傳輸層tcp中間加多了一層TLS/SSL加密套件,https就是應用層將數據給到TLS/SSL,然後將數據加密後,再給到TCP進行傳輸;
- 加密算法
- 對稱加密:加密和解密是同一個密鑰;(代表:DES)
- 非對稱加密:A和B各有兩把鑰匙,一把公鑰,一把私鑰,A用B公鑰進行加密,B用B的私鑰進行解密,反過來一樣;(代表:RSA)
- Volley
- 2.3之前使用HttpClient,之後使用HttpUrlConnection;
- 適合進行數據量不大但通信頻繁的網絡操作;對於大數據量的操作,比如文件下載,表現很糟糕,因爲volley會把返回的流導入內存中,下載大文件會發生內存溢出;
- Volley執行的過程:默認情況下,volley會開啓四個網絡調度線程和一個緩存調度線程;首先請求會加入緩存隊列,緩存調度線程如果找到該請求的緩存就直接讀取該緩存並解析,否則,這個請求就加入網絡隊列;
-
- Volley爲什麼不適合上傳大文件:適合小且頻率高的?
- Volley的網絡請求線程池默認大小爲4,可以併發4個請求,大於4個,會排在隊列中,併發量小所以適合數據小頻率高的請求;
- 下載文件會把流存入內存中,導致內存溢出;
- Volley爲什麼不適合上傳大文件:適合小且頻率高的?
- Okhttp
- 最大併發量爲64
- 使用連接池技術,支持5個併發的socket連接,默認keepAlive時間爲5分鐘,解決tcp握手和揮手的效率問題;
- 利用相應緩存避免重複的網絡請求;
- 請求失敗,自動重連;
- 支持SPDY協議,是一種基於TCP的應用層協議;
- Okhttp的缺點:
- 消息回來需要切到主線程自己去寫;
- 調用比較複雜,需要進行封裝;
- 緩存失敗:爲了信息傳輸的安全性,對請求進行加密.可能導致緩存系統失效;
- Okhttp用到哪些設計模式:
- Builder設計模式,如構建對象OkhttpClient,參數類型相同且很多參數可以爲空時,可以用Builder模式完成;
- 工廠方法模式:如源碼中的接口Call;
- 單例模式
- 觀察者模式如EventListener,監聽請求和響應;
- 策略模式;
- 責任鏈模式,如攔截器;
- Retrofit
- Retrofit底層是基於okhttp實現的,使用運行時註解的方式工作;
- 通過java接口與註解來描述網絡請求,並用動態代理生成網絡請求的request,然後通過client來調用相應的網絡框架(默認爲okhttp)去發起網絡請求,並將返回的response通過converterFactory轉換爲相應的數據model,最後通過callAdapter轉換成其他數據方式;
- 流程:
- 解析註解->配置網絡請求參數;
- 動態代理->生成請求對象;
- 網絡請求適配器->將請求對象進行平臺適配;
- 網絡請求執行器->發送請求;
- 數據轉換器->解析返回數據;
- 回調執行器->切換線程;
- 主線程->處理結果;
- 優點
- 可以配置不同的http client來實現網絡請求,如okhttp,httpclient;
- 參數註解可以定製;
- 支持同步,異步,rxjava;
- 超級解耦;
- 可以配置不同的反序列化工具來解析數據,如json,xml;
-
性能優化
- 圖片如何三級緩存?
- 三級緩存:加載某張圖片時,先去LruCache中尋找圖片,沒有則去SoftReference中取出圖片使用,沒有則去文件系統中尋找,沒有則去網上加載圖片;
- Lrucache是存儲最近,最後使用的圖片,最近最少使用的會被移除出去;當被移除的時候,會將圖片添加到softreference中,軟引用不會影響系統運行;
- Bitmap如何處理大圖,如何預防OOM?
- 調整圖片大小;
- BitMapFactory的Options參數,通過inSampleSize參數可以對一個圖片進行採樣縮放;
- 及時回收不用的圖片資源:將暫時不用的圖片recycle();
- 緩存圖片到內存中:LruCache專門處理圖片緩存;
- 調整圖片大小;
- 內存回收機制和GC算法?
- 內存判定對象可回收的兩種機制:
- 引用計數算法:被引用計數加一,引用失效,計數減一,計數器爲0就是不可以再被使用的,難以解決對象之間相互循環引用;
- 可達性分析法:通過一系列GCRoots的對象作爲起始點,從這些節點向下搜索,剩餘的就是不可達的;
- GC回收算法:
- 分代收集算法:根據對象存活週期,將Java堆分爲新生代和老年代
- 新生代:大批對象死去,只有少量存活,使用複製算法只需複製少量存活對象即可;
- 複製算法:把可用內存把容量劃分爲大小相同的兩塊,每次只使用其中一塊,當這一塊內存用盡後,把還存活着的對象複製到另一塊,再把這一塊內存空間一次清理掉,
- 老年代:對象存活率高,使用標記-清理算法或者標記整理算法,只需標記較少的回收對象即可
- 標記-清理算法:首先標記出所有需要回收的對象,然後統一清理;但會導致導致大量的內存碎片;
- 標記-整理算法:標記出所有需要回收的對象,然後把所有存活的對象整理到一端,不會產生內存碎片;
- 新生代:大批對象死去,只有少量存活,使用複製算法只需複製少量存活對象即可;
- 分代收集算法:根據對象存活週期,將Java堆分爲新生代和老年代
- 內存判定對象可回收的兩種機制:
- 內存溢出和內存泄漏的區別:
- 內存溢出:程序在申請內存時,沒有足夠內存空間供其使用,出現out of memory,Android系統爲每一個應用申請到的內存有限;
- 內存泄漏:指程序在申請內存後,被某個對象一直持有,無法釋放已經申請的內存空間;內存泄漏的累積後果很嚴重;
- 內存溢出的原因
- 發生地點:堆內存和java虛擬棧中
- 堆內存溢出:
- 生產者消費者模型,
- 如註冊後忘記註銷;
- 添加到隊列,忘記控制隊列大小;
- 循環引用
- Fastjson轉json
- 生產者消費者模型,
- 棧內存溢出:遞歸
- 內存泄漏的原因
- 根本原因:對象的生命存活時間不一致
- 發生地點:方法區,堆,java虛擬棧
- 單例模式
- 案例:單例構造方法中傳遞的context爲某一個activity,在activity中調用了單例的構造方法;
- 泄漏原因:activity生命週期比單例類短,無法釋放,導致泄漏;
- 解決:Context傳遞應用Application的Context,即單例對象的生命週期和Application一樣長,不會出現內存泄漏的問題;
- 靜態變量導致的內存泄漏:
- 案例:一個activity定義了一個靜態變量;
- 泄漏原因:靜態變量存儲在方法區,生命週期從類加載到整個進程結束,一旦靜態變量初始化後,他所持有的引用,知道進程結束後纔會釋放;
- 解決方法:在onDestroy中將靜態變量賦值爲null;
- 非靜態內部類導致的內存泄漏:
- 案例:
- Handler裏面調用Activity的方法,Handler會被底層的ThreadLocal綁定,所以生命週期比Activity週期長;
- activity中開了一個線程去請求數據,然後退出activity ,因爲線程持有activity的引用,導致activity不能及時回收;
- 泄漏原因:非靜態內部類默認會持有外部類的引用,當非靜態內部類對象的生命週期比外部類對象的生命週期長時,就會導致內存泄漏;
- 解決方法:Handler提取出來,引用Activity時用WeakReference包裝起來;或者把Handler聲明爲靜態類.因爲靜態內部類不會持有外部類的引用;
- 案例:
- 未取消註冊回調導致的內存泄漏
- 在activity中註冊廣播,在銷燬activity時沒有取消註冊;
- 泄漏原因:廣播會一直存在系統中,一樣持有activity的引用,導致內存泄漏;
- 解決方法:及時取消註冊;
- 集合中的對象未清理造成內存泄漏:
- 案例:一個對象被添加到了集合中,該對象是不能被gc回收的;
- 資源未關閉導致內存泄漏:
- 案例:WebView
- 沒有有效利用已有的對象,容易造成泄漏,例如,使用過的Bitmap對象沒有調用recycle()方法釋放內存,構造Adapter沒有使用緩存的convertView對象;
- 哪些工具檢測內存泄漏:
- DDMS-Heap
- 實時查看程序中的內存的使用信息;
- MAT分析方式
- Histogram,列出內存中每個對象的名字,數量和大小;
- Dominator Tree將所有內存中的對象按大小排序;
- DDMS-Heap
- 如何優化ListView
- 複用convertView
- 當convertView不爲空時對其進行復用;
- 定義存儲控件引用類ViewHolder
- 在第一次創建時convertView將控件找出,在第二次複用時,直接使用convertView的getTag()獲得這些控件;
- 數據的分批和分頁加載
- 分批加載
- 分頁加載
- 圖片的處理
- 要用WeakReference來存儲與獲取圖片信息;
- 獲取到圖片時,對圖片進行壓縮存在內存中;
- 在getView()方法中做圖片轉換時,產生的中間變量要及時釋放;
- 複用convertView
-
View相關
- View的繪製過程:
- OnMeasure,測量視圖大小,從頂層父View到子View遞歸調用measure方法,measure又回調onMeasure();
- OnLayout():確定View的位置,進行頁面佈局;從頂層父View向子View遞歸調用view.layout方法,即父View根據上一步measure子View所得到的佈局大小和佈局參數,將子View放在合適的位置上;
- onDraw():繪製視圖,
- 繪製背景background.draw(canvas)
- 繪製自己(onDraw)
- 繪製children(dispatchDraw)
- 繪製裝飾(onDrawScrollBars)
- View事件分發機制:
- DispatchTouchEvent:用於進行事件的分發;如果事件能夠傳遞給當前View,那麼此方法一定會被調用,返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent的影響,表示是否消耗當前事件;
- onInterceptTouchEvent:在上訴方法內部調用,對事件進行攔截,該方法只在ViewGroup中有;一旦攔截,則執行ViewGroup中的onTouchEvent,在ViewGroup中處理事件,而不接着分發View.
- onTouchEvent,在dispatchTouchEvent中調用,用來處理點擊事件,返回結果表示是否消耗當前事件;
- 如何解決View的事件衝突:
- 外部攔截法:點擊事件都要經過父容器的攔截處理,如果父容器需要此事件就攔截,否則不攔截,需要重寫父容器的onInterceptTouchEvent方法,在內部做出相應的攔截;
- 內部攔截法:指父容器不攔截任何事件,將所有事件都傳遞給子容器,如果子容器需要就直接消耗,否則交由父容器進行處理,需要配合requestDisallowInterceptTouchEvent方法;
- Scroller怎麼實現View的彈性滑動?
- 在MotionEvent.ACTION_UP事件觸發時調用startScroll()方法,該方法並沒有進行實際的滑動操作,而是記錄滑動相關量
- 接着調用invalidate/postinvalidate()方法,請求view重繪,導致view.draw方法被執行;
- 在view重繪後會在draw方法中調用computeScroll方法,而computeScroll又會向Scroller獲取當前的scrollX和scrollY,然後通過scrollTo實現滑動;接着又調用postInvalidate方法進行第二次重繪,和之前一樣,反覆導致View進行小幅度的滑動;
- Invadate和postinvalidate的區別:
- Invalidate和postinvalidate都用於刷新View,主要區別是:
- invalidate在主線程中調用,若在子線程中需要配合handler;
- Postinvalidate可在子線程中直接調用;
- SurfaceView和View的區別
- View要在UI線程進行刷新,SurfaceView可以在子線程中進行刷新;
- View適合於主動刷新,Surface適合於被動刷新,如頻繁刷新,而View頻繁刷新可能會阻塞主線程;
- SurfaceView底層雙緩衝機制,而View沒有,適合於頻繁刷新,刷新時數據處理量很大的頁面(如視頻播放界面)
- 自定義View如何考慮機型適配
- 合理使用wrap_content,match_parent
- 儘可能使用RelativeLayout
- 針對不同的機型,使用不同的佈局文件放在對應的目錄下,android會自動匹配;
- 儘量使用.9圖片;
- 使用與密度無關的像素單位dp,sp;
- 切圖的時候切分辨率大的圖;
-
進程
- 如何開啓多進程?
- 在AndroidManifest中給四大組件指定屬性,android:process開啓多進程模式;
- 在內存允許的條件下可以開啓N個進程;
- 爲何需要IPC?多進程通信可能出現的問題
- 通過開啓多進程獲取更大內存空間,兩個或多個應用之間共享數據
- 造成的問題:
- 靜態成員和單例模式完全失效;獨立的虛擬機造成;
- 線程同步機制完全失效;獨立的虛擬機造成;
- SharePreferences的可靠性下降;Sp不支持兩個進程併發進行讀寫,有一定機率導致數據丟失;
- Application會多次創建;新進程會分配獨立的虛擬機;
- 進程間通信的方式:
- Intent:只能傳輸Bundle所支持的數據類型,四大組件之間的通信
- AIDL:Android中最常用的,使用稍微複雜,需要注意線程同步;
- ContentProvider:進程間的大量數據共享,主要對外提供數據的CRUD操作;
- Socket:常用於網絡通信中,只能傳輸原始的字節流;
- Binder的優點
- 傳輸效率高,可操作性高:傳輸效率影響因素主要是內存拷貝的次數,Binder只需一次拷貝;
- 實現C/S架構方便:Server和Client端獨立,穩定性較好;
- 安全性較高;
-
其他的補充知識點
- Service啓動方式
- 通過startService(),會經歷onCreate->onStartCommand,stopService的時候會調用onDestroy,如果是調用者直接退出而沒有調用stopService時,Service會一直在後臺運行,下次調用者起來仍然可以stopService;
- 通過bindService,Service只會運行onCreate,這個時候調用者和Service綁定在一起,調用者退出Service也會onUnbind->onDestroy;
- 如果先是start,然後bind時就直接onbind
- 如果先是bind,再start,就直接startCommand
- 兩個同時用,需要先onUnbind -> 然後StopService();
- EventBus的理解
- EventBus是一款Android優化的發佈/訂閱消息總線,它簡化了組件和組件,組件和後臺線程間的通信
- 三個元素:
- Event:事件
- Subscriber:事件訂閱者,接收特定的Event事件
- Publisher:事件發佈者,同於通知Subscriber有事件發生
- 實現了觀察者設計模式,優點爲代碼簡潔優雅,使用方便,開銷小,有助於代碼解耦;
- 生命週期的執行:
- Activity A到Activity B的:A的onPause() -> B 的onCreate() -> onStart() -> onResume() -> A的onStop();
- 從B返回A:B的onPause() -> A的onRestart() -> onStart() -> onResume() -> B的onStop() -> onDestroy();
- 退出A : onPause() -> onStop() ->onDestroy();
- 屬性動畫(Property Animation)和(Tween Animation)的區別:
- 補間動畫只是改變View的顯示效果,不會真正改變View的屬性;屬性動畫會改變View的實際屬性值;
- 補間動畫只是針對於View,屬性動畫可以不作於View;
- ListView和RecyclerView的區別;
- ViewHolder:ListView中ViewHolder需要自己定義,也可以不定義;recyclerview中viewholder必須定義;
- LayoutManager:R提供了根據了更加豐富的佈局管理,包括linearlayoutmanager支持水平和豎直方向;StaggeredGridLayoutManager支持瀑布流;GridLayoutManager支持網格展示;
- ItemAnimator:R中用於添加,刪除,移動的動畫效果;
- ItemDecoration:R默認不加間隔符,使用這個來實現間隔符;
- Java中的集中變量修飾符
- Public修飾的變量爲公有變量,如果公有變量又在一個共有類中,這個變量可以被所有包中的所有類訪問;
- Protected保護訪問修飾符:若在一個共有類中,可以被所在類本身,同一個包中的所有類,其他包中該類的子類;
- 默認訪問修飾符:同一個包中的所有類;
- Private:所在類訪問;
- ANR
- Application Not Responsing ,即應用無響應;
- 原因:
- 當前的事件沒有機會得到處理;
- 當前事件正在處理,沒有及時完成;
- 案例:
- 點擊事件或者鍵盤輸入事件在5s內無法得到響應;
- BroadCastReceiver的onReceive()函數運行在主線程中,10s內無法得到處理;
- 前臺服務在20s無法完成處理,後臺服務200s內沒有執行完畢;
- ContentProvider的publish在10s內沒有完畢;
- 解決:
- 把io操作放在工作線程中處理,減少耗時操作和錯誤操作,如網絡請求,數據庫操作,Socket操作,文件讀寫等放在子線程;
- 主線程的Looper.loop()一直無限循環爲什麼不會造成ANR?
- looper.loop() 不斷地接收事件、處理事件,每一個點擊觸摸或者說Activity的生命週期都是運行在 Looper.loop() 的控制之下,如果它停止了,應用也就停止了。只能是某一個消息或者說對消息的處理阻塞了 Looper.loop();也就說我們的代碼其實就是在這個循環裏面去執行的,當然不會阻塞了。
- 一個線程中有幾個Handler,幾個Looper,幾個MessageQueue
- 一個線程只能有一個Looper,因爲Thread中使用ThreadLocal<Looper>去存儲當前線程的Looper,ThreadLocal底層是hashmap,key值是線程本身,所以只有一個looper;
- 一個messageQueue,因爲一個線程只有一個looper
- 一個線程可以存在多個handler,可以在發送消息的時候使用message.setTarget(handler)來設置當前message的target handler;
- 數據庫如何高效插入數據
- 在try中開啓事務
- 在finally中關閉事務
- Activity的狀態保存和恢復
- 保存:當Activity A 在前臺時,按下home鍵,切換其他的應用,橫豎屏切換,系統會調用onSavedInstanceState()方法;
- 恢復:onRestoreInstanceState()
- Fragment的add和remove
- Remove是移除fragment,當fragment不加入back stack時,remove會一直調用到onDetach,加入到back stack時,會走到onDestroyView;
- 當fragment被另一個fragment調用replace時,並且壓入back stack時,View會銷燬,fragment本身沒有銷燬,也就是調用到了ondestroyView();
- Fragment的oncreateview多次執行,需要用一個rootView記錄一下oncreateView返回的View,下一次調用判斷一下rootView是否爲null
- Fragment之間數據的傳遞
-
- MainFragment mainFragment =
- (MainFragment) getActivity()
- .getSupportFragmentManager()
- .findFragmentByTag("mainFragment");//另一個fragment的tag
- 接口回調
- EventBus
- 引入EventBus:添加依賴
- 註冊事件接收者,在接收Fragment中進行註冊EventBus.getDefault().register(this);
- 發送事件: EventBus.getDefault().post(mDatas.get(position));
- 定義事件類型
- 接收事件並處理:
- @Subscribe
- public void onEvent(String data) {
- bt_main.setText(data);
- }
- 註銷事件接收:
- @Override
- public void onDestroy() {
- super.onDestroy();
- EventBus.getDefault().unregister(this);
- }
-
- Activity與Service之間
- 通過startService()來啓動服務,不過不好掌控
- 使用bindService(),就可以在ServiceConnection中獲取Service的實例,就實現了activity中獲取到service的實例對象,就可以調用service方法;
- 對ContentProvider的理解
- 結構化方式存放的數據,以相對安全的方式來封裝數據並且提供簡易的處理機制,提供了不同進程間數據交互的標準化接口;
- 說說ContentProvider、ContentResolver、ContentObserver 之間的關係
- ContentProvider是內容提供者,對其他應用進行數據共享,其他應用可以通過ContentProvider對你應用中的數據進行增刪查改;
- ContentResolver內容解析者,作用是按照一定的規則對內容提供者的數據解析;
- ContentObserver:內容觀察者,目的是觀察特定uri引起的數據庫的變化,既而做一些相應的處理,類似於數據庫中的觸發器;
- 描述一下BroadcastReceiver的理解
- 使用了設計模式中的觀察者模式,基於消息的發佈/訂閱事件模型,將廣播的發送者和接收者解耦;
- 註冊的方式:
- 靜態註冊:在AM中註冊,不受任何組件的生命週期影響;
- 動態註冊:組件結束前,必須註銷;
- 最好在onresume中註冊,onpause中註銷,否則造成內存泄漏;
- 方式廣播:intent通過sendBroadCast發送出去;
- 類型:
- 普通廣播:開發者自身定義intent的廣播
- 系統廣播:涉及到手機的開機,網絡變化,拍照等都會相應的廣播;
- 有序廣播:廣播接收者按照先後順序來接收;按照priority屬性大小排序,相同的話就是動態註冊的優先;
- 本地廣播:安全性高,效率高;exported設置爲false,非本應用的廣播不接收,指定該廣播接收器所在的包名,使用封裝好的LocalBroadcastManager使用方式與全局廣播相同,只是註冊和註銷的context變爲了LocalBroadcastManager的實例;
- 如何導入已有的數據庫
- 把源數據庫放入res目錄下,建立一個dbmanager,然後通過FileinputStream讀取數據庫,再用FileOutputStream把讀取到的數據寫入到data/data/包名下;
- LinearLayout和RelativeLayout的區別:
- RelativeLayout比LinearLayout慢是因爲會讓子View調用兩次measure過程,而LinearLayout只需要一次,如果LinearLayout有weight屬性時,也需要兩次;
- 不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是Relat..;
- 儘量使用padding而不是margin
- 屏幕適配的知識點
- 屏幕尺寸:指對角線的長度,單位是英寸,一英寸=2.54釐米
- 屏幕分辨率:1px=1像素,屏幕分辨率越高,顯示效果越好;
- 屏幕像素密度:每英寸的像素點數;計算公式爲豎的分辨率的平方+橫的分辨率的平方開根號然後除以英寸數;
- 常見的
- Mdpi:像素密度:120-160 hdpi 160 -240 xhdpi 240 -320 xxhdpi 320 - 480 xxxdpi 480-640
- 常用適配方案:
- 使用wrap_content,match,weight
- 自定義像素適配,以美工的設計尺寸爲原始尺寸,根據不同設備的密度來計算出寬和高;
- 百分比適配
- 插值器和估值器
- 插值器:Interpolator:一個接口,設置屬性值從初始值到結束值的變化規律,
- 在動畫效果的XML代碼中設置插值器屬性
- 在java代碼中設置
- 估值器:TypeEvaluator,一個接口,插值器決定值的變化規律,即決定的是變化趨勢,接下來的具體變化數值交給估值器;
- 屬性動畫特有的屬性;
- 協助插值器實現非線性運動的動畫效果;
- 插值器:Interpolator:一個接口,設置屬性值從初始值到結束值的變化規律,
- Android的數據存儲方式
- SharePreference,SQLite,Content Provider,File,網絡存儲
- SharePreference本質是一個xml文件,存儲一些參數設置
- SQLite輕量級的數據庫支持SQL語法,Android還提供了一個SQLIteDataBaseHelper的類,提供了一些操作數據庫的api;
- Content Provider 實現數據共享;
- FIle: IO存儲方式,用於存儲數量大的數據,更新數據很難;