mvc mvp mvvm
mvc是最基礎(chǔ)的架構(gòu),最開始是從web引用過來的,但是在android里面,C就在activity里,activity其實是屬于view層,本身是不能做大量業(yè)務(wù)邏輯處理的,mvc在安卓中就會顯得笨拙,使activity變的繁雜不易維護和拓展,但是小項目又比較合適,正因為大項目使用mvc把activity作用變得不易維護所以衍生出了mvp,mvp是把activity里的業(yè)務(wù)抽出來放到 Presenter里面,把activity的位置還原到view層,去實現(xiàn)用戶的所有操作接口,但是這里又會有個問題,presenter和activity也就是view的交互會很頻繁,而這些操作都是用接口連接的,所以項目會增加很多接口文件,雖然現(xiàn)在有控件可以直接一鍵生成這樣的接口,但是每次增加一個業(yè)務(wù)的時候就會比較繁瑣,會寫很多接口和實現(xiàn)類,但是總的來說用mvp在中大型項目上用的時候可能覺得麻煩一點,但是在單元測試,拓展,維護上會方便很多,而且一個presenter可以用于多個activity,可重用性更高,mvvm是改良版的mvp,它用viewmodel代替了presenter,用dadabinding 做view和viewmode的數(shù)據(jù)綁定,數(shù)據(jù)發(fā)生改變view就會跟著改變,因為livedata 生命周期通過lifecycle和activity綁定,所以一定程度上app了頁面的內(nèi)存泄露問題,mvvm是現(xiàn)在主流的架構(gòu)也是Google主推的架構(gòu)
多線程
http網(wǎng)絡(luò)請求多線程,照片上傳多線程,人像比對多線程 多線程主要是要處理線程之間的共享變量,比如幾個線程同時請求,而請求結(jié)果需要做一個回調(diào),每個線程請求成功需要做一個計數(shù)器,而這個計數(shù)器就是共享變量,多線程操作共享變量需要加鎖synconizied,并且用volatile修飾保證變量操作的唯一性
join:調(diào)用join就是調(diào)用當(dāng)前線程的wait方法,等待join線程執(zhí)行完畢在調(diào)用當(dāng)前線程的notify方法恢復(fù)執(zhí)行,join方法必須要在線程運行之后才有效
如何保證你的線程執(zhí)行完
對于需要鎖屏后繼續(xù)執(zhí)行的任務(wù),優(yōu)先使用 WorkManager或前臺服務(wù),而不是依賴 Activity 生命周期內(nèi)的普通線程。
kotlin
很多只是寫法變了,關(guān)鍵字變了,邏輯還是差不多的,比如switch變成了when,for循環(huán)變成for in,還有就是一些權(quán)限修飾符變了,默認(rèn)權(quán)限是final,open標(biāo)識可繼承,可重寫,增加了新的權(quán)限internal標(biāo)識在當(dāng)前module中使用用起來比java方便,省掉了很多代碼,比如那種非空判斷只需要語句中加個問號就行,邏輯都是差不多的
kotlin沒有static的變量,只能用companion派生對象關(guān)鍵字修飾,全局靜態(tài)對象用Object關(guān)鍵字(Util工具類)const常量
高階函數(shù)
forEach 遍歷數(shù)組
map 數(shù)組轉(zhuǎn)換
flatmap 多維數(shù)組轉(zhuǎn)換
let 變量定義作用域,非空判斷 返回最后一行
with 傳入對象,作用域最后一行是返回值
run 只接受lambda函數(shù) 返回最后一行
apply,返回函數(shù)本身作用域,減少重復(fù)引用 不改變值
tcp/ip http
這些協(xié)議原理還是很深的,首先他們的層級是不一樣的,ip協(xié)議是用于網(wǎng)絡(luò)層的,tcp是傳輸層,http是應(yīng)用層,然后tcp又涉及到三次握手,對于這些協(xié)議我沒有研究的很深,平常用的多的還是http協(xié)議,http協(xié)議是建立在tcp上的一種應(yīng)用,他的特點就是每次發(fā)出請求都需要服務(wù)端回送響應(yīng),建立連接和關(guān)閉連接叫做一次連接 可以參考(https://blog.csdn.net/DavidStar1988/article/details/80400020)
rxjava
zip 合并observable
其他操作符https://blog.csdn.net/chaoyangsun/article/details/80373203
創(chuàng)建類型
concat 將Observable依次發(fā)送
merge 交錯,不按順序發(fā)送
ust 原樣發(fā)射
from 將數(shù)組分成單個依次發(fā)射
Interval 間隔發(fā)射
timer 延遲發(fā)射
轉(zhuǎn)換類型
map 轉(zhuǎn)換數(shù)據(jù) 比如給我的是url,但是我需要的是bitmap,這時候就用map
filter 篩選,按條件篩選,比如篩選出非空的url
flatmap 相對于map,可以多對多,比如在轉(zhuǎn)換數(shù)據(jù)的集合里進行拆分
just 和from區(qū)別: just是設(shè)置什么就返回什么,from是把數(shù)組分成單獨的發(fā)送
https://blog.csdn.net/moonpure/article/details/78402347
retrofit
其實retrofit不是一個網(wǎng)絡(luò)請求庫,它是一個請求庫上面的一個封裝,建立在okhttp上的一個庫,特點就是你可以用注解去構(gòu)建一個網(wǎng)絡(luò)請求,他先是用接口去實現(xiàn)一個帶有注解的類,然后用retrofit的create去創(chuàng)建一個實體,我看了一下源碼,這個create里面是用了動態(tài)代理來實現(xiàn)的他就是通過反射在一個class方法的前后增加邏輯代碼,這些邏輯代碼就是用到okhttp的庫,最后生成一個請求的request,里面設(shè)計感還是很強的,工廠模式,適配器模式還提供了rxjava的支持,和rxjava結(jié)合還是用的很爽的http://www.itdecent.cn/p/a8b98e66b1b8
Glide
和其他圖片加載框架差不多,glide都有三級緩存,但是glide比較強大的是能加載gif,還有g(shù)lide的with方法傳入的context會進行檢查,他會添加一個隱藏的fragment來監(jiān)聽生命周期,達(dá)到自動回收圖片的目的
Volley
volley的封裝比較簡單是輕量級的,volley也支持okhttp,但是volley不支持post大文件數(shù)據(jù)
Dagger
dagger是一個注解處理器,幫助項目解耦少用new關(guān)鍵字,和spring的作用差不多,但是他是編譯期間通過apt插件預(yù)生成一些代碼,而spring是在運行期間,它主要可以幫助你做一些初始化的操作,缺點就是編譯時間邊長了,每次運行要多很長時間http://www.itdecent.cn/p/cc54c5ec9897
事件分發(fā)
// 以ACTION_DOWN事件為例:
- 該View的onTouchEvent()返回false
- 該View的父ViewGroup收到返回的false
- 父ViewGroup會自己嘗試處理事件(調(diào)用自己的onTouchEvent())
- 如果父ViewGroup的onTouchEvent()也返回false
- 事件會繼續(xù)向上傳遞,直到:
- 某個父ViewGroup的onTouchEvent()返回true(處理事件)
- 或傳遞到Activity的onTouchEvent()
數(shù)據(jù)庫增加字段
sqllite只支持增加字段,刪除字段只能重新建表,方法就是把表重命名,新建一張帶有該字段的表,然后復(fù)制數(shù)據(jù)
數(shù)據(jù)庫框架比較
Room, sqlite
數(shù)據(jù)庫性能優(yōu)化
數(shù)據(jù)庫操作基本由三個過程,創(chuàng)建事務(wù),執(zhí)行sql,提交,這三個過程,但是我們批量操作,就變成創(chuàng)建事務(wù),執(zhí)行n個sql,提交,這樣效率會提高很多,
在sql語句數(shù)量比較多的情況下,用stringbuilder代替string拼接,這樣會提高空間分配效率
查詢時根據(jù)需要返回盡量少的字段,可以減少內(nèi)存的消耗
查詢結(jié)束后及時關(guān)閉cursor
如果條件允許的話可以在建表的時候記住columnIndex,它是一個int類型下標(biāo)值,不用每次查詢都去調(diào)用getColumnIndex,這個在數(shù)據(jù)量龐大的查詢條件下會比較節(jié)省時間
調(diào)用數(shù)據(jù)庫的時候要用子線程防止卡ui,但是由于sqllite是不支持多線程并發(fā)訪問的,兩個線程同時插入會拋出異常,雖然是有事務(wù)控制,可以保證數(shù)據(jù)庫的完整性,但是不能保證線程執(zhí)行的準(zhǔn)確性,也就是說有的線程可能會調(diào)用失敗或者請求出現(xiàn)線程繁忙錯誤,所以sqlite最好保證單線程執(zhí)行
一般情況下,sqlite是多進程可以操作的,不會報錯,但是不能保證插入順序,原則上需要在一個進程里去操作,正確的做法是,啟用一個, 單獨的進程通過contentprovider或者其他ipc向其他進程提供接口,通過這個單獨的進程統(tǒng)一操作
ipc
為什么需要多進程:
Android對進程的內(nèi)存分配是有限的,用多進程讓核心服務(wù)和UI分離,可以為用戶提供更好的程序穩(wěn)定性,提升用戶體驗 開啟多進程,用多進程分擔(dān)內(nèi)存壓力,用前臺service保證adj的值最小,最不容易回收
內(nèi)存分配 管理內(nèi)存分配的是LMK LowMemoryKiller,通過adb命令proc/pid/com_score_adj可以看到一個應(yīng)用的adj值,值越大,越容易被回收,通常切換到后臺會變大
-
多進程會出現(xiàn)的問題:
共享內(nèi)存失效,單例,靜態(tài)變量成員失效,同步鎖失效,application多個,sharepreferce可靠性下降 -
應(yīng)用場景:
音樂播放器,定位服務(wù) 開啟多進程的方式:在四大組件注冊文件中指定 android:process
-
多進程的通信方式:
Bundle 常用于四大組件通訊
文件共享,多進程操作同一個文件交換數(shù)據(jù) 但是不支持并發(fā)
Messenger 輕量級的AIDL ,需要基于Handler
Socket
AIDL 寫aidl接口文件,在服務(wù)里通過Binder拿到接口對象傳遞數(shù)據(jù),注冊服務(wù),客戶端綁定服務(wù)轉(zhuǎn)換aidl接口獲取數(shù)據(jù)
ContentProvider 例如我們的應(yīng)用訪問系統(tǒng)的通訊錄數(shù)據(jù)
recycleview
嵌套scrollview卡頓,重新scrollview攔截一下事件
嵌套顯示不全item,外面再包一層relativelayout 增加一個descendantfcusability屬性
布局優(yōu)化
避免層級太多
開發(fā)者模式查看是否過度繪制
使用include標(biāo)簽重用layout
viewstub標(biāo)簽 (調(diào)用的時候才加載布局)
不要在onDraw中頻繁創(chuàng)建對象和做耗時操作
socket
創(chuàng)建服務(wù)端socket連接對象,指定ip和端口號,子線程進行
監(jiān)聽socket流,獲取服務(wù)端返回數(shù)據(jù)
發(fā)送數(shù)據(jù)也是利用InputStream的write字節(jié)
websocket
是基于類似于http的應(yīng)用層協(xié)義,使用jar包WebSocketClient連接服務(wù)器
Android動畫
view動畫 移動,平移,縮放,旋轉(zhuǎn)
drawable動畫 幀動畫,一幀一幀的
property動畫 view動畫升級版,可以渲染還沒加載的view
Handler機制
Handler創(chuàng)建Looper,Looper創(chuàng)建MessageQueue Looper消息隊列處理者用死循環(huán)不斷去 MessageQueue 消息隊列拿到message然后又調(diào)用handler去處理, 類似于生產(chǎn)者消費者http://www.itdecent.cn/p/87ee9b5206df
性能優(yōu)化
1 anr主線程不能處理耗時操作
2 內(nèi)存溢出,bitmap優(yōu)化
3 內(nèi)存抖動,頻繁創(chuàng)建臨時對象
4 內(nèi)存泄漏
-
使用LeakCanary檢測:
運行后會在另外一個Leaks的app中顯示,該庫會監(jiān)聽onDistory方法,把activity實例放在弱引用里實時訪問,找出內(nèi)存泄露的變量和類
-
android studio app profiler工具:
可以查看當(dāng)前運行app的CPU,內(nèi)存,網(wǎng)絡(luò)的占用和消耗,可以看當(dāng)前頁面所在類的實例,可以調(diào)試內(nèi)存泄漏和占用比例
webview引起的內(nèi)存泄露,手動創(chuàng)建,手動銷毀,不要在xml使用
線程銷毀
context單例引用
handler
弱引用
SQLite使用完記得關(guān)閉鏈接
5 ui布局復(fù)雜
6 view重復(fù)繪制
7 懶加載setUserVisibleHint
http://www.itdecent.cn/p/3e44250ca2de
activity啟動模式
standard
默認(rèn)啟動模式,start多少就開啟多少
singletop
棧頂唯一,允許有多個實例,但是不允許疊加,通知欄跳轉(zhuǎn)
singleTask
棧唯一,但是start的時候如果此activity之上有新的activity會被銷毀,會調(diào)用此activity的onNewIntent方法,主界面
singleInstance
棧唯一,一般用于系統(tǒng)應(yīng)用,launcher
Android 678910 的差異
6.0 運行時權(quán)限,休眠模式,app進入休眠模式會斷開網(wǎng)絡(luò),殺掉一些空置進程
7.0 分屏多任務(wù),VR,通知欄快捷回復(fù),優(yōu)化編輯器減少安裝速度,文件加密
8.0 畫中畫,Notifcation Dots,自適應(yīng)桌面圖標(biāo),后臺進程限制,運行時權(quán)限優(yōu)化,安裝位置來源apk權(quán)限
9.0 WIFI RTT室內(nèi)精準(zhǔn)定位,凹凸屏支持,消息通知優(yōu)化,多攝像頭api,ImageDecoder,神經(jīng)網(wǎng)絡(luò)api,NFC api
10.0 暗黑模式,桌面模式(投影),5Gapi,折疊屏支持,手勢導(dǎo)航,后臺程序位置獲取權(quán)限,神經(jīng)網(wǎng)絡(luò)1.2,文件沙盒
http://www.itdecent.cn/p/88409d6f5795
activity fragment互傳值
// Activity
class MainActivity : AppCompatActivity() {
// Activity 自己的 ViewModel
private val activityViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityViewModel.message.value = "Hello from Activity"
// 添加 Fragment
supportFragmentManager.beginTransaction()
.add(R.id.container, FragmentA())
.commit()
}
}
// Fragment A
class FragmentA : Fragment() {
// 獲取 Activity 的 ViewModel(共享)
private val sharedViewModel: MainViewModel by activityViewModels()
// 獲取 Fragment 自己的 ViewModel
private val fragmentViewModel: FragmentAViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 可以訪問 Activity 設(shè)置的數(shù)據(jù)
println(sharedViewModel.message.value) // 輸出: "Hello from Activity"
// 修改共享數(shù)據(jù)
sharedViewModel.message.value = "Modified by FragmentA"
}
}
// Fragment B
class FragmentB : Fragment() {
// 同樣獲取 Activity 的 ViewModel
private val sharedViewModel: MainViewModel by activityViewModels()
// Fragment 自己的 ViewModel
private val fragmentViewModel: FragmentBViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 看到 FragmentA 修改的數(shù)據(jù)
println(sharedViewModel.message.value) // 輸出: "Modified by FragmentA"
// Fragment 各自的 ViewModel 互不影響
println(fragmentViewModel.fragmentData.value) // FragmentB 自己的數(shù)據(jù)
}
}
外部獲取自定義view的寬高
不能直接獲取,view沒有進行onmeasure方法獲取到是0,有兩種方法
——自己調(diào)用view的onmeasure測量
——需要設(shè)置一個監(jiān)聽,View.ViewTreeObserver.addGlobalLayoutLinstener在這個回調(diào)里獲取寬高
hashcode
hashcode是用對象的字段算出來的一個數(shù)字,用來比較兩個對象是否相等,但是他需要和 equals一起使用,當(dāng)hashcode相等的時候比較equals,這樣比較更高效 當(dāng)hashcode相等時兩個對象不一定相等
StringBuffer和StringBuilder
stringbuffer是線程安全,stringbuilder不能多線程訪問,但是stringbuilder速度更快,另外string因為每次都是新建對象,所以string也是線程安全
接口的意義
接口主要為了彌補類不能實現(xiàn)多繼承的缺點,有了接口一個類可以實現(xiàn)多個接口的操作
內(nèi)部類,靜態(tài)內(nèi)部類,局部內(nèi)部類
內(nèi)部類可以隱藏一些外部類的變量和方法,不讓調(diào)用者知道
靜態(tài)內(nèi)部類不依賴外部對象,但是可以指向表達(dá)一種從屬關(guān)系
局部類只是在方法里面使用
設(shè)計模式
- 單例模式
- 工廠模式(工廠方法,抽象工廠)
- 策略模式
- 代理模式 (動態(tài)代理靜態(tài)代理)
- 觀察者模式
- 責(zé)任鏈模式
模塊化組件化與插件化和熱修復(fù)
模塊化是把基礎(chǔ)業(yè)務(wù)分成一個模塊,其他業(yè)務(wù)引用基礎(chǔ)業(yè)務(wù),這些模塊都在同一個工程
組件化是指一個app有多個module,每個module負(fù)責(zé)一部分業(yè)務(wù),每個業(yè)務(wù)組件可以單獨打包也可以做依賴庫,最后打包成為一個apk,
插件化是指一個項目分為一個宿主和多個插件,每個插件是一個apk,最后這個宿主app加載多個apk,可以實現(xiàn)動態(tài)加載
熱修復(fù)是指為一個業(yè)務(wù)或者一個class單獨進行加載,已達(dá)到修復(fù)bug的目的
排序算法
- 冒泡排序
int a = [2,1,5,9,5]
int temp = 0;
for (int i = 0; i < a.length-1; i++) {
for (int j = 0;j < a.length-1-i; j++) {
if (a[j] > a[j+1]) {
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
原理,像擠跑跑一樣,一個一個擠出來,第二層for循環(huán)每一次選出一個最大值,外面一層for循環(huán)控制第二層循環(huán)的排序范圍
-
選擇排序
image.png
總結(jié),就是把里面的循環(huán)每次找到一個最小值,外面的循環(huán)控制輪詢范圍
glide 和 Picasso的選擇
| 特性 | glide | Picasso |
|---|---|---|
| gif加載 | 支持gif加載 | 不支持 |
| 生命周期 | 支持 | 不支持 |
| 內(nèi)存 | 根據(jù)image尺寸 | full 尺寸 |
| 加載時間 | 慢-先改變尺寸 | 快-直接加載到內(nèi)存 |
圖片加載時,傳入Context對象或者fragment對象,glide會生成一個無UI的fragment,因為glide無法從傳入的context中獲取生命周期 通過這個fragment可以拿到傳入context相同的生命周期,從而實現(xiàn)了對生命周期變化做出相同的暫停和銷毀操作
總結(jié):小項目用Picasso,功能簡單,加載速度快,大項目用glide,功能配置豐富,對內(nèi)存優(yōu)化比較好,對于內(nèi)存方面,glide會對不同尺寸的圖片緩存多份緩存,
而Picasso都是同一份緩存,根據(jù)尺寸要求在內(nèi)存中進行resize,所以下載速度Picasso>glide,內(nèi)存加載速度glide> Picasso
activity啟動過程
首先從點擊桌面圖標(biāo)開始,launcher進程通過binder ipc向system service發(fā)起通信startActivity,system service進程的AMS(activity manager service)會判斷app進程是否已經(jīng)創(chuàng)建,若沒有創(chuàng)建則通過binder ipc向zygote通信fork app進程, app進程會通過ipc向ams 發(fā)起通信 請求attachapplication 和得到schedulelancheractivity請求然后和主線程通信,主線程開始調(diào)用activity oncreate app正式啟動
- 用戶點擊圖標(biāo)
↓ - Launcher → AMS: startActivity() // Binder調(diào)用
↓ - AMS檢查進程 → 不存在
↓ - AMS → Zygote: fork新進程 // Socket通信
↓ - App進程啟動
↓ - ActivityThread.main()啟動
↓ - App進程 → AMS: attachApplication() // 注冊自己
↓ - AMS收到注冊 → 處理attachApplicationLocked()
↓ - AMS → App: bindApplication() // 告訴App可以初始化了
↓ - App進程收到BIND_APPLICATION消息
↓ - handleBindApplication() // 核心!
↓
├─ 11.1 創(chuàng)建LoadedApk
↓
├─ 11.2 創(chuàng)建Application對象(反射)
↓
├─ 11.3 Application.attachBaseContext() // 第一次
↓
├─ 11.4 安裝ContentProvider
↓
│ ├─ 創(chuàng)建所有ContentProvider實例
↓ │
│ └─ ContentProvider.onCreate() // 每個Provider
↓
└─ 11.5 Application.onCreate() // 第二次
↓ - Application初始化完成
↓ - AMS查找要啟動的Activity
↓ - AMS → App: scheduleLaunchActivity() // 啟動Activity
↓ - App進程收到LAUNCH_ACTIVITY消息
↓ - handleLaunchActivity() // 核心!
↓
├─ 16.1 創(chuàng)建Activity實例(反射)
↓
├─ 16.2 Activity.attach() // 關(guān)聯(lián)Context
↓
├─ 16.3 Instrumentation.callActivityOnCreate()
↓
└─ 16.4 Activity.onCreate() // 用戶可見
↓ - 后續(xù)生命周期:onStart() → onResume()
↓ - 界面顯示
協(xié)成
什么是協(xié)成
協(xié)成是輕量級線程,建立在線程之上,和線程的區(qū)別就是它不會阻塞任務(wù),而是掛起任務(wù),當(dāng)一個任務(wù)出現(xiàn)阻塞的時候這個線程會被掛起,掛起之后這個線程還可以去執(zhí)行其他任務(wù),而這個任務(wù)阻塞結(jié)束后又會被線程執(zhí)行,相當(dāng)于是把線程進行分段,相當(dāng)于用最少的線程做更多的事情,節(jié)約資源,協(xié)成的另外一個好處就是它的調(diào)用是同步的,不需要回調(diào)函數(shù),只需要return回結(jié)果,看上去更簡潔易懂
如何開啟協(xié)成
- launch
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(5000L) // 延遲一段時間
println("main: I'm tired of waiting!")
job.cancel() // 取消該作業(yè)
job.join() // 等待作業(yè)執(zhí)行結(jié)束
println("main: Now I can quit.")
launch 返回的是job對象 只能控制協(xié)成狀態(tài),不能返回數(shù)據(jù)結(jié)構(gòu),異步方法需要用suspend修飾
- async
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
async 返回的是defferred對象,可以控制協(xié)成狀態(tài)可以返回數(shù)據(jù)結(jié)構(gòu),defferred是Job的子類
以上兩種方式都可以用Dispather 指定協(xié)成在那個線程運行
