回想起之前有個(gè)面試,一面是問一些項(xiàng)目上的問題,包括金融圖表繪制啊宣虾,包括一些app應(yīng)用難點(diǎn)索赏,這些因?yàn)槎甲鲞^,所以很輕松的通過了聘芜,但是在二面的時(shí)候來了個(gè)CTO,上來的第一個(gè)問題就是進(jìn)程和線程的區(qū)別。
說實(shí)話伤靠,在這次面試之前,我對(duì)這些基礎(chǔ)知識(shí)點(diǎn)啼染,算是毫無準(zhǔn)備宴合。
然后我回想起Android有UI線程和子線程,就說了一句迹鹅,一個(gè)進(jìn)程可以有多個(gè)線程卦洽。。斜棚。
雖然我之后查閱了不少資料阀蒂,但是過了這么久该窗,現(xiàn)在只記得:
進(jìn)程是資源調(diào)度的最小單位
線程是任務(wù)執(zhí)行的最小單位
然而在百度里:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是處理器任務(wù)調(diào)度和執(zhí)行的基本單位蚤霞。還存在資源開銷酗失、包含關(guān)系、內(nèi)存分配昧绣、影響關(guān)系规肴、執(zhí)行過程等區(qū)別。同一進(jìn)程的線程共享本進(jìn)程的地址空間和資源夜畴,而進(jìn)程之間的地址空間和資源相互獨(dú)立奏纪。
=。= 算了我反正是懶得看了斩启,但是在我們?nèi)粘5墓ぷ髦行虻鳎艘恍├细缬羞^AIDL的經(jīng)驗(yàn)(這里說到IPC進(jìn)程間通信,就又出現(xiàn)了一個(gè)高頻知識(shí)點(diǎn)了兔簇,待會(huì)介紹)之外发绢,Android開發(fā)中,多線程主要集中在UI線程(主線程)垄琐,子線程(異步處理耗時(shí)任務(wù))中了边酒。
剛剛提到了進(jìn)程間通信就順帶提一下Linux中的跨進(jìn)程通信
- 管道 :又分為有名和無名 (無名則才能專心練賤)無名可以讓有親戚關(guān)系進(jìn)程之間通信(父子,爺孫)狸窘,有名則沒有上訴限制墩朦,而且都是半雙工(發(fā)送和接收不能同時(shí)進(jìn)行)
- 信號(hào)量:計(jì)數(shù)器,用于控制多線程對(duì)數(shù)據(jù)的訪問翻擒,屬于一種同步機(jī)制
- 信號(hào):通知接收進(jìn)程某個(gè)事件已經(jīng)發(fā)生
- 消息隊(duì)列:消息鏈表氓涣,存放在內(nèi)核中,由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí),UNIX下不同進(jìn)程之間可實(shí)現(xiàn)共享資源的一種機(jī)制
- 共享內(nèi)存:最快的IPC方式(后面再說原因)
- Socket:套接字就不用多說了 C/S 模式
以上是Linux的IPC方式陋气,那Android呢
首先需要知道劳吠,為什么Android App開發(fā)要用進(jìn)程間通信:
幾年前我在杭州面試過一家做圖片直播的公司,除了一些基本問題巩趁,就問到了AIDL痒玩,在當(dāng)時(shí)我只知道通過IPC確實(shí)可以獲取到更多的內(nèi)存資源,而大部分開發(fā)人員也確實(shí)是想通過IPC來獲取更多的內(nèi)存上限议慰。
而其實(shí)我們開發(fā)中也常常用到IPC蠢古,Activity的Intent可以喚起其他應(yīng)用,如通訊錄别凹,電話草讶,短信。番川。到涂。
而像廣播Broadcast則是可以通知所有程序的IPC脊框,像是電量低等廣播
像是Content Provider則給了數(shù)據(jù)訪問的IPC,可以順利訪問到文件或是數(shù)據(jù)庫 (Binder)
Messenger:是基于AIDL實(shí)現(xiàn)的C/S 通信(不需要處理多線程)
以上說了這么多践啄,主要核心就兩個(gè) AIDL 以及 Binder
AIDL:如果你要對(duì)多個(gè)應(yīng)用進(jìn)行IPC浇雹,并且想在服務(wù)里處理多線程,用它就是了
Binder:Binder是一種進(jìn)程間通信機(jī)制(我是機(jī)制=屿讽。=)
- Binder機(jī)制有幾特點(diǎn)昭灵,通過mmap實(shí)現(xiàn)了單次數(shù)據(jù)copy,性能直逼共享內(nèi)存的方式
- C/S架構(gòu)伐谈,更符合很多場景(相對(duì)于共享內(nèi)存)
- 安全性強(qiáng)于傳統(tǒng)IPC烂完,因?yàn)閭鹘y(tǒng)IPC的安全性,是由上層處理的诵棵,而Binder通過進(jìn)程UID/PID可以區(qū)分訪問的用戶到底是誰抠蚣,而其他傳統(tǒng)IPC則是把信息放在請(qǐng)求數(shù)據(jù)中,那樣就有被篡改的風(fēng)險(xiǎn)履澳。
一不小心說的有點(diǎn)多=嘶窄。=,回到進(jìn)程和線程距贷,Android開發(fā)中柄冲,常用的幾個(gè)異步線程用法也說一下把:
最常見的情況就是耗時(shí)任務(wù)是異步的,但是耗時(shí)任務(wù)結(jié)束之后忠蝗,需要通知頁面進(jìn)行修改现横,但是頁面的繪制又需要在UI線程上
- Handler 在Runnable的run方法中發(fā)送消息,在Handler中的handleMessage方法接收消息阁最,并完成ui更新(具體原理說明戒祠,網(wǎng)上又無數(shù)文章)
- **RxJava **可以通過subscribeOn()和observeOn()在線程間進(jìn)行切換
- AsyncTask 本人用的比較少,但是看上去易用性挺強(qiáng)的
- ThreadPoolExecutor 線程池
- kotlin協(xié)程
由于篇幅問題闽撤,這幾種的代碼明天將進(jìn)行說明
1.首先是Handler的用法
// Handler就是把UI任務(wù)放到handleMessage中執(zhí)行 hendleMessage則是在sendMessage后會(huì)被回調(diào)
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
mText.setText("UI操作");
break;
case 2:
mText.setText("UI操作2");
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
// 耗時(shí)操作
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
}
}).start();
2.RxJava 實(shí)現(xiàn)線程切換
Observable<String> stringOb = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
//耗時(shí)操作
emitter.onNext("結(jié)果");
}
});
stringOb.subscribeOn(Schedulers.io()) // 以上發(fā)生在子線程
.observeOn(AndroidSchedulers.mainThread()) // 下面發(fā)生在UI線程
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Throwable {
// Ui操作
System.out.println(s);
}
});
3.AsyncTask 實(shí)現(xiàn)異步任務(wù)
new MyTask().execute();
class MyTask extends AsyncTask<String, String, String> {
@Override
protected String doInBackground(String... params) {
return "耗時(shí)任務(wù)結(jié)果";
}
@Override
protected void onPostExecute(String s) {
// 更新UI
mText.setText(s);
}
}
4.線程池以后會(huì)單獨(dú)說明(根據(jù)阿里Android規(guī)范手冊(cè)中看到的得哆,阿里建議是使用線程池而不是其他方式)
5.kotlin協(xié)程
private suspend fun work(): Long {
// work work
return result
}
launch() {
var result = withContext(Dispather.IO){
work()
}
}
launch {
var deferred = async(Dispather.IO) {// 這里做個(gè)同步
// 耗時(shí)操作
getWebTime()
}
var value = deferred.await()// 這里就會(huì)等待上面結(jié)果 才會(huì)繼續(xù)執(zhí)行
}