Java String 有多長?
2個方面
棧 String longString = "aaaa....aaaa"; 字節(jié)數(shù)65535炊琉,字節(jié)碼的格式是UTF8
實際是65534苔咪,javac源碼 length<65535
kotlin是65535
String longString = "叁叁肆那";
一個漢字占3個字節(jié),漢字個數(shù)65535/3悼泌,是沒問題的
源碼 length>65535
主要受字節(jié)碼影響松捉,Latin是65534,非Latin是65535
也會方法區(qū)大小限制
堆 byte[] bytes = loadFromFile("test.txt");
String string = new String(bytes);
通過new出String的對象馆里,理論上最大長度是 Integer.MAX_VALUE
Java虛擬機指令newarray的限制
ArrayList的源碼中
Some VM reserve some header words in an array
最大長度是 Integer.MAX_VALUE - 8
也會受到堆內(nèi)存大小的限制
匿名內(nèi)部類
匿名內(nèi)部類名字是 包名+$1 $2
編譯器生成
為什么可以new 匿名內(nèi)部類
子類可以訪問父類的構(gòu)造方法
泛型
類型擦除 從編譯的細節(jié)
類型擦除 對運行時的影響
類型擦除 對反射的影響
對比類型不擦除的語言
為什么java選擇類型擦除?
類型擦除的好處?
運行時內(nèi)存負擔小
兼容性好 1.5之后才有泛型
類型擦除的問題?
基本類型無法作為泛型實參 所以需要裝箱和拆箱 開銷比較大
泛型類型無法用作方法重載
泛型類型無法當做真實類型使用 編譯之后被擦除了,T都是object
靜態(tài)方法無法引用泛型參數(shù) 泛型參數(shù)只有類實例化之后才知道隘世,所以static方法不知道
類型強轉(zhuǎn)運行時開銷 需要強轉(zhuǎn) 編譯時擦除泛型
泛型簽名
獲取泛型
onActivityResult使用很麻煩,為什么不設(shè)計成回調(diào)
B回到A鸠踪,A會被銷毀,會重新創(chuàng)建對象的實例,所以回調(diào)不能使用
解決方案:
activity嵌套fragment,用fragment里的onActivityrResult里的引用丰捷。因為fragment在activity創(chuàng)建之后生成,所以拿到fragment里新的對象實例少漆,fragment持有的對象的activity检访,必然是新的卖氨。
唯一性五嫂,view的唯一可以是id孩灯,通過findviewbyId獲取
fragment的唯一性可以考mWho掀亩,通過反射拿到
如何停止一個線程
線程stop已經(jīng)被廢棄
線程不能直接停止炼七,讀寫操作容易crash,資源不能清理枝誊;也不能暫停皮壁,一直占用cpu,其它線程會一直等待。
但是任務(wù)可以停止
Interrupt
volatitle Boolean標志位
如何寫出線程安全的程序
什么是線程安全?
可變資源(內(nèi)存)線程間共享
解決方案:
不共享資源 重入函數(shù) threadLocal(final 避免存儲大量對象 用完之后及時移除對象)
禁止重排序 final(定義的變量不可以被修改 方法绘闷,類不可被復(fù)寫,繼承)volatile(可見性)
保證可見性 fina volatile 加鎖(鎖釋放會強制將緩存刷新到主內(nèi)存)
保證原子性(簡單理解 運行步驟不可拆分 +運算可被拆分成3個步驟,所以不具有原子性)
保證原子性的方法:
加鎖,保證操作的互斥性
使用CAS指令(如Unsafe.compareAndSwapInt)
使用原子數(shù)值類型(如AtomicInteger)
使用原子屬性更新器(如AtomicReferenceFiledUpdater)
ConcurrentHashMap如何實現(xiàn)并發(fā)訪問
ConcurrentHashMap 類中包含兩個靜態(tài)內(nèi)部類 HashEntry 和 Segment侦鹏。HashEntry 用來封裝映射表的鍵 / 值對跨释;Segment 用來充當鎖的角色,每個 Segment 對象守護整個散列映射表的若干個桶它碎。每個桶是由若干個 HashEntry 對象鏈接起來的鏈表套腹。一個 ConcurrentHashMap 實例中包含由若干個 Segment 對象組成的數(shù)組
優(yōu)化so包
動態(tài)下發(fā)so包
捕獲Native異常
Activity的啟動流程
如何跨App啟動Activity
uid exported intentFilter 拒接服務(wù)漏洞
Handler
post為什么能切換主線程?
調(diào)用主線程的handler,thread run()方法
threadLocal是什么?
是map,ThreadLocalMap,key是線程名.currentThread
threadLocal是全局唯一的,泛型是looper,Looper是全局唯一的,不能new,只有Looper.prepare()保證唯一
一個線程只綁定一個looper,一個looper只綁定一個mQueue
post,sendMessage,sendEmptyMessageAtTime最終都調(diào)用enqueueMessage
發(fā)送消息
handler.sendMessage(msg),enqueueMessage壓入消息隊列 max_pool_size=50
取消息
Looper.loop()輪詢器 , msg.target.dispatchMessage,msg.target是handler
Looper.loop()為什么不會ANR?
Binder.clearCallingIdentity() native方法
在for循環(huán)中,Trace.traceBegin(traceTag,msg.target.getTraceName(msg))調(diào)用nativeTraceBegin native方法
調(diào)用2個native方法回收釋放
消息隊列優(yōu)化
枚舉類型 復(fù)用 重復(fù)消息過濾 互斥消息取消
如何避免OOM
使用合適的數(shù)據(jù)結(jié)構(gòu)
內(nèi)存復(fù)用 建立一個內(nèi)存池 Message pool
避免使用枚舉 對象 24Bytes
Bitmap 合適分辨率 放在合適的目錄(mdpi)bitmap的重采樣和復(fù)用配置
謹慎使用多進程 預(yù)加載一些配置和資源
對圖片進行緩存
網(wǎng)絡(luò)/磁盤/內(nèi)存
LRU 最近最少使用 LinkeHashMap 線程安全synchronized 將被訪問的元素移到鏈表尾部,迭代器訪問第一個元素最老
LFU 頻率使用最多的
圖片占用內(nèi)存的大小
jpg 用RGB_565 沒有透明通道
使用inSampleize采樣 大圖->小圖
使用矩陣變換來放大圖片 小圖->大圖
使用.9圖圖做背景
不使用圖片 優(yōu)先使用VectorDrawable
Binder
AIDL Android Interface Definition Language Android接口定義語言
打開binder driver="/dev/binder"
buffer創(chuàng)建 binder_open(driver,128*1024)
開辟內(nèi)存映射 128kmmap
ServiceMananger啟動
打包Parcel中,數(shù)據(jù)寫入binder設(shè)備 writeTransactionData , copy_from_user
服務(wù)注冊,添加到鏈表svclist中
定義主線程中的線程池
循環(huán)從mIn和mOut中取出讀寫請求 buffer是256字節(jié),發(fā)到binder設(shè)備中
案列:三方登錄
client 創(chuàng)建AIDL,定義接口方法(與server同一包名),創(chuàng)建service,用于service端回調(diào)通信
server 創(chuàng)建AIDL,定義接口方法(與client同一包名),創(chuàng)建service,用于接收client端發(fā)送的消息
client serviceManager server
PackageManagerService
http://www.reibang.com/p/cb0555d62b9c
在Systemserver中啟動
ApplicationPackageManager IPackageManager(mPM) AIDL
安裝
有界面安裝
PackageInstallerActivity PackageUtil.getPackageInfo(sourceFile) startInstall() InstallAppProgress.class(通過handler broadcastReceiver 回調(diào)消息)
無界面安裝
調(diào)用C adb_commandline(buff4096) install_app(獲取手機內(nèi)部存儲路徑 sd卡存儲路徑) 通過shell:pm PackageManagerService handler發(fā)送消息 默認安裝4次,4次不成功即失敗
無界面安裝 .png
ActivityManagerService
調(diào)用2次binder機制 App進程->系統(tǒng)進程->App進程
App啟動流程
換膚
思路
1.
替換textcolor 和 background
自定義Textview Button Imageview LinearLayout
重寫AppCompatViewInflater
在onCreate中重寫Factory2
系統(tǒng)API UIMode判斷是白天黑夜
2.
將resource打包apk
反射獲取AssetManager,new resource,加載path拜英,拿到皮膚包的包名
apk內(nèi)resources.arsc映射文件盛垦,資源name必須一一對應(yīng)湿弦,通過資源id叉跛,獲取name柒傻,type
插件化
Hook
類加載 createClassLoader
資源加載 AssetManager
四大組件 activity hook Intent service hook AMP 動態(tài)代理
廣播 解析插件Manifest 將靜態(tài)廣播轉(zhuǎn)成動態(tài) 毕赐遥活只能在宿主中
shadow
靜態(tài)代理
Tinker熱修復(fù)
代碼 dexDiff算法
資源 Entry的BSDiff
46831行代碼
算法機制
old new 先根據(jù)索引排序冒窍,只要old < new delete = replace > add
1.獲取系統(tǒng)的classLoader pathList
2.構(gòu)造dexElements
3.將補丁dex注入到系統(tǒng)dexElements的最前面
sp原理 mmkv
ArrayMap ConcurrentHashMap分段加鎖
2級緩存 磁盤 內(nèi)存
讀數(shù)據(jù)會阻塞 加鎖
commit會阻塞 apply開啟線程異步
MMKV 是基于 mmap 內(nèi)存映射的 key-value 組件遏匆,底層序列化/反序列化使用 protobuf 實現(xiàn)瘦馍,性能高洞豁,穩(wěn)定性強犀变。
內(nèi)存準備
通過 mmap 內(nèi)存映射文件榨呆,提供一段可供隨時寫入的內(nèi)存塊罗标,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)負責(zé)將內(nèi)存回寫到文件积蜻,不必擔心 crash 導(dǎo)致數(shù)據(jù)丟失闯割。
數(shù)據(jù)組織
數(shù)據(jù)序列化方面我們選用 protobuf 協(xié)議,pb 在性能和空間占用上都有不錯的表現(xiàn)竿拆。
寫入優(yōu)化
考慮到主要使用場景是頻繁地進行寫入更新宙拉,我們需要有增量更新的能力。我們考慮將增量 kv 對象序列化后丙笋,append 到內(nèi)存末尾谢澈。
空間增長
使用 append 實現(xiàn)增量更新帶來了一個新的問題,就是不斷 append 的話御板,文件大小會增長得不可控锥忿。我們需要在性能和空間上做個折中。
socket TCP UDP
TCP socket
ServerSocket accept() 阻塞 inputstream
ClientSocket IP 端口號 outputstream
流的方式發(fā)送
UDP socket
DatagramSocket receive() 阻塞
ClientSocket InetAddress IP 端口號 send
包的方式發(fā)送 最大長度 65535-8
UDP 不可靠 是服務(wù)端抓取數(shù)據(jù)
單播 廣播 多播
Https為什么安全
加密 TLS
對身份進行驗證 證書
kotlin協(xié)程
協(xié)程就是個線程框架
協(xié)程的掛起本質(zhì)就是線程切出去再切回來
是輕量級的線程怠肋,解決并發(fā)問題
CoroutineScope創(chuàng)建協(xié)程
coroutineScope.launch(Dispatchers.IO) {
...
}
withContext 這個函數(shù)可以切換到指定的線程敬鬓,并在閉包內(nèi)的邏輯執(zhí)行結(jié)束之后,自動把線程切回去繼續(xù)執(zhí)行 消除了并發(fā)代碼在協(xié)作時的嵌套
suspend 『掛起』是非阻塞式的 掛起的對象是協(xié)程 只是一個提醒 我是一個耗時函數(shù),我被我的創(chuàng)建者用掛起的方式放在后臺運行列林,所以請在協(xié)程里調(diào)用我
什么是掛起瑞你?掛起,就是一個稍后會被自動切回來的線程調(diào)度操作
啟動一個協(xié)程可以使用 launch 或者 async 函數(shù)希痴,協(xié)程其實就是這兩個函數(shù)中閉包的代碼塊者甲。
kotlin inline
減少方法壓棧,出棧,進而減少資源消耗
也就是說inline關(guān)鍵字實際上增加了代碼量,但是提升了性能砌创,而且增加的代碼量是在編譯期執(zhí)行的虏缸,對程序可讀性不會造成影響
APT javapont
注解目標是在類 方法 參數(shù)
編譯期 運行時
extends AbstractProessor
內(nèi)存抖動
循環(huán) 頻繁調(diào)用導(dǎo)致GC頻繁
自我介紹
您好,我叫湯文斌嫩实,在上家公司擔任Android leader 刽辙,帶領(lǐng)3人團隊,負責(zé)公司5個項目甲献,3個已經(jīng)上線宰缤,2個是公司內(nèi)部項目。上線的項目平均日活在1w左右晃洒,崩潰率維持在1%慨灭。主要負責(zé)產(chǎn)品和前后端溝通,架構(gòu)搭建和技術(shù)選型球及,以及新技術(shù)的布道氧骤。
APP瘦身優(yōu)化
代碼優(yōu)化 混淆 第三方庫 移除無用代碼
資源優(yōu)化 混淆 壓縮 刪除冗余資源
so優(yōu)化 只用arm包,最小
列表卡頓優(yōu)化
布局 不要嵌套
圖片 不要太大 滑動取消加載
線程 使用線程池
jetpack
lifecycle 解決代碼入侵 解決內(nèi)存泄漏 解決統(tǒng)一管理
liveData和lifecycle綁定 可以感應(yīng)生命周期 不會崩潰
viewmodel 管理數(shù)據(jù) 重建不丟失
GC
可達性分析吃引,尋找GCRoot
GCRoot對象 Java虛擬機棧 方法區(qū) 活躍的 Native 引用的對象存在
堆內(nèi)存分配不足 systen.gc()
classLoader 雙親委派模式
先找父類筹陵,父類找不到自身才會執(zhí)行實際的類加載過程
自定義classLoader 重寫findClass 調(diào)用defineClass方法將字節(jié)碼轉(zhuǎn)成Class對象
Android中classLoader PathClassLoader DexClassLoader
PathClassLoader用來加載系統(tǒng)apk和被安裝到手機的apk內(nèi)的dex文件
DexClassLoader可以從SD卡上加載包含class.dex的.jar和.apk文件
LeakCanary原理
weakReference ReferenceQueue
頁面需要被回收,就將其包裝到weakReference中镊尺,并且在weakReference的構(gòu)造器中傳入自定義的ReferenceQueue
將包裝的weakReference做一個標記key朦佩,并且在一個強引用Set中添加相應(yīng)的key記錄
主動觸發(fā)GC,遍歷ReferenceQueue鹅心,刪除對應(yīng)的Set記錄
如果Set中依然有對象吕粗,則內(nèi)存泄漏