Android什么是函数,什么是函数响应式编程(JavaAndroid版本)
什么是函數(shù)響應(yīng)式編程(Java&Android版本)
函數(shù)響應(yīng)式編程(FRP)為解決現(xiàn)代編程問(wèn)題提供了全新的視角。一旦理解它,可以極大地簡(jiǎn)化你的項(xiàng)目,特別是處理嵌套回調(diào)的異步事件,復(fù)雜的列表過(guò)濾和變換,或者時(shí)間相關(guān)問(wèn)題。
我將盡量跳過(guò)對(duì)函數(shù)響應(yīng)式編程學(xué)院式的解釋(網(wǎng)絡(luò)上已經(jīng)有很多),并重點(diǎn)從實(shí)用的角度幫你理解什么是函數(shù)響應(yīng)式編程,以及工作中怎么應(yīng)用它。本文將圍繞函數(shù)響應(yīng)式編程的一個(gè)具體實(shí)現(xiàn)RxJava,它可用于Java和Android。
開(kāi)始
我們以一個(gè)真實(shí)的例子來(lái)開(kāi)始講解函數(shù)響應(yīng)式編程怎么提高我們代碼的可讀性。我們的任務(wù)是通過(guò)查詢GitHub的API,首先獲取用戶列表,然后請(qǐng)求每個(gè)用戶的詳細(xì)信息。這個(gè)過(guò)程包括兩個(gè)web 服務(wù)端點(diǎn):
https://api.github.com/users - 獲取用戶列表;
https://api.github.com/users/{username} -獲取特定用戶的詳細(xì)信息,例如https://api.github.com/users/mutexkid。
舊的風(fēng)格
下面例子你可能已經(jīng)很熟悉了:它調(diào)用web service,使用回調(diào)接口將成功的結(jié)果傳遞給下一個(gè)web service請(qǐng)求,同時(shí)定義另一個(gè)成功回調(diào),然后發(fā)起下一個(gè)web service請(qǐng)求。你可以看到,這會(huì)導(dǎo)致兩層嵌套的回調(diào):
//The "Nested Callbacks" Way
public void fetchUserDetails() {
//first, request the users...
mService.requestUsers(new Callback() {
@Override
public void success(final GithubUsersResponse githubUsersResponse,
final Response response) {
Timber.i(TAG, "Request Users request completed");
final synchronized List githubUserDetails = new ArrayList();
//next, loop over each item in the response
for (GithubUserDetail githubUserDetail : githubUsersResponse) {
//request a detail object for that user
mService.requestUserDetails(githubUserDetail.mLogin,
new Callback() {
@Override
public void success(GithubUserDetail githubUserDetail,
Response response) {
Log.i("User Detail request completed for user : " + githubUserDetail.mLogin);
githubUserDetails.add(githubUserDetail);
if (githubUserDetails.size() == githubUsersResponse.mGithubUsers.size()) {
//we‘ve downloaded‘em all - notify all who are interested!
mBus.post(new UserDetailsLoadedCompleteEvent(githubUserDetails));
}
}
@Override
public void failure(RetrofitError error) {
Log.e(TAG, "Request User Detail Failed!!!!", error);
}
});
}
}
@Override
public void failure(RetrofitError error) {
Log.e(TAG, "Request User Failed!!!!", error);
}
});
}
盡管這不是最差的代碼-至少它是異步的,因此在等待每個(gè)請(qǐng)求完成的時(shí)候不會(huì)阻塞-但由于代碼復(fù)雜(增加更多層次的回調(diào)代碼復(fù)雜度將呈指數(shù)級(jí)增長(zhǎng))因此遠(yuǎn)非理想的代碼。當(dāng)我們不可避免要修改代碼時(shí)(在前面的web service調(diào)用中,我們依賴前一次的回調(diào)狀態(tài),因此它不適用于模塊化或者修改要傳遞給下一個(gè)回調(diào)的數(shù)據(jù))也遠(yuǎn)非容易的工作。我們親切的稱這種情況為“回調(diào)地獄”。
RxJava的方式
下面讓我們看看使用RxJava如何實(shí)現(xiàn)相同的功能:
public void rxFetchUserDetails() {
//request the users
mService.rxRequestUsers().concatMap(Observable::from)
.concatMap((GithubUser githubUser) ->
//request the details for each user
mService.rxRequestUserDetails(githubUser.mLogin)
)
//accumulate them as a list
.toList()
//define which threads information will be passed on
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
//post them on an eventbus
.subscribe(githubUserDetails -> {
EventBus.getDefault().post(new UserDetailsLoadedCompleteEvent(githubUserDetails));
});
}
如你所見(jiàn),使用函數(shù)響應(yīng)式編程模型我們完全擺脫了回調(diào),并最終得到了更短小的程序。讓我們從函數(shù)響應(yīng)式編程的基本定義開(kāi)始慢慢解釋到底發(fā)生了什么,并逐漸理解上面的代碼,這些代碼托管在GitHub上面。
從根本上講,函數(shù)響應(yīng)式編程是在觀察者模式的基礎(chǔ)上,增加對(duì)Observables發(fā)送的數(shù)據(jù)流進(jìn)行操縱和變換的功能。在上面的例子中,Observables是我們的數(shù)據(jù)流通所在的管道。
回顧一下,觀察者模式包含兩個(gè)角色:一個(gè)Observable和一個(gè)或者多個(gè)Observers。Observable發(fā)送事件,而Observer訂閱和接收這些事件。在上面的例子中,.subscribe()函數(shù)用于給Observable添加一個(gè)Observer,并創(chuàng)建一個(gè)請(qǐng)求。
構(gòu)建一個(gè)Observable管道
對(duì)Observable管道的每個(gè)操作都將返回一個(gè)新的Observable,這個(gè)新的Observable的內(nèi)容要么和操作前相同,要么就是經(jīng)過(guò)轉(zhuǎn)換的。這種方式使得我們可以對(duì)任務(wù)進(jìn)行分解,并把事件流分解成小的操作,接著把這些Observables拼接起來(lái)從而完成更復(fù)雜的行為或者重用管道中的每個(gè)獨(dú)立的單元。我們對(duì)Observable的每個(gè)方法調(diào)用會(huì)被加入到總的管道中以便我們的數(shù)據(jù)在其中流動(dòng)。
下面首先讓我們從搭建一個(gè)Observable開(kāi)始,來(lái)看一個(gè)具體的例子:
Observable sentenceObservable = Observable.from(“this”, “is”, “a”, “sentence”);
這樣我們就定義好了管道的第一個(gè)部分:Observable。在其中流通的數(shù)據(jù)是一個(gè)字符串序列。首先要認(rèn)識(shí)到的是這是沒(méi)有實(shí)現(xiàn)任何功能的非阻塞代碼,僅僅定義了我們想要完成什么事情。Observable只有在我們“訂閱”它之后才會(huì)開(kāi)始工作,也就是說(shuō)給它注冊(cè)一個(gè)Observer之后。
Observable.subscribe(new Action1() {
@Override
public void call(String s) {
System.out.println(s);
}
});
到這一步Observable才會(huì)發(fā)送由每個(gè)獨(dú)立的Observable的from()函數(shù)添加的數(shù)據(jù)塊。管道會(huì)持續(xù)發(fā)送Observables直到所有Observables都被處理完成。
變換數(shù)據(jù)流
現(xiàn)在我們得到正在發(fā)送的字符串流,我們可以按照需要對(duì)這些數(shù)據(jù)流進(jìn)行變換,并建立更復(fù)雜的行為。
Observable sentenceObservable = Observable.from(“this”, “is”, “a”, “sentence”);
sentenceObservable.map(new Func1() {
@Override
public String call(String s) {
return s.toUpperCase() + " ";
}
})
.toList()
.map(new Func1, String>() {
@Override
public String call(List strings) {
Collections.reverse(strings);
return strings.toString();
}
})
//subscribe to the stream of Observables
.subscribe(new Action1() {
@Override
public void call(String s) {
System.out.println(s);
}
});
一旦Observable被訂閱了,我們會(huì)得到“SENTENCE A IS THIS”。上面調(diào)用的.map函數(shù)接受Func1類的對(duì)象,該類有兩個(gè)范型類型參數(shù):一個(gè)是輸入類型(前一個(gè)Observable的內(nèi)容),另一個(gè)是輸出類型(在這個(gè)例子中,是一個(gè)經(jīng)過(guò)大寫(xiě)轉(zhuǎn)換,格式化并用一個(gè)新的Observable實(shí)例包裝的字符串,最終被傳遞給下一個(gè)函數(shù))。如你所見(jiàn),我們通過(guò)可重用的管道組合實(shí)現(xiàn)更復(fù)雜的功能。
上面的例子中,我們還可以使用Java8的lambda表達(dá)式來(lái)進(jìn)一步簡(jiǎn)化代碼:
Observable.just("this", "is", "a", "sentence").map(s -> s.toUpperCase() + " ").toList().map(strings -> {
Collections.reverse(strings);
return strings.toString();
});
在subscribe函數(shù)中,我們傳遞Action1類對(duì)象作為參數(shù),并以String類型作為范型參數(shù)。這定義了訂閱者的行為,當(dāng)被觀察者發(fā)送最后一個(gè)事件后,處理后的字符串就被接收到了。這是.subscribe()函數(shù)最簡(jiǎn)單的重載形式(參見(jiàn)https://github.com/ReactiveX/RxJava/wiki/Observable#establishing-subscribers可以看到更復(fù)雜的函數(shù)重載簽名)。
這個(gè)例子展示了變換函數(shù).map()和聚合函數(shù).toList(),在操縱數(shù)據(jù)流的能力方面這僅僅是冰山一角(所有可用的數(shù)據(jù)流操作函數(shù)可見(jiàn)https://github.com/ReactiveX/RxJava/wiki),但它顯示了基本概念:在函數(shù)響應(yīng)式編程中,我們可以通過(guò)實(shí)現(xiàn)了數(shù)據(jù)轉(zhuǎn)換或者數(shù)據(jù)操作功能的管道中獨(dú)立的單元來(lái)轉(zhuǎn)換數(shù)據(jù)流。根據(jù)需要我們可以在其他由Observables組成的管道復(fù)用這些獨(dú)立的單元。通過(guò)把這些Observable單元拼接在一起,我們可以組成更復(fù)雜的特性,但同時(shí)保持它們作為易于理解和可修改的可組合邏輯小單元。
使用Scheduler管理線程
在web service例子中,我們展示了如何使用RxJava發(fā)起網(wǎng)絡(luò)請(qǐng)求。我們談及轉(zhuǎn)換,聚合和訂閱Observable數(shù)據(jù)流,但我們沒(méi)有談及Observable數(shù)據(jù)流的web請(qǐng)求是怎樣實(shí)現(xiàn)異步的。
這就屬于FRP編程模型如何調(diào)用Scheduler的范疇了-該策略定義了Observable流事件在哪個(gè)線程中發(fā)生,以及訂閱者在哪個(gè)線程消費(fèi)Observable的處理結(jié)果。在web service例子中,我們希望請(qǐng)求在后臺(tái)線程中進(jìn)行,而訂閱行為發(fā)生在主線程中,因此我們?nèi)缦露x:
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
//post them on an eventbus
.subscribe(githubUserDetails -> {
EventBus.getDefault().post(new UserDetailsLoadedCompleteEvent(githubUserDetails));
});
Observable.subscribeOn(Scheduler scheduler)函數(shù)指定Observable的工作需要在指定的Scheduler線程中執(zhí)行。Observable.observeOn(Scheduler scheduler)指定Observable在哪個(gè)Scheduler線程觸發(fā)訂閱者們的onNext(),onCompleted(),和onError()函數(shù),并調(diào)用Observable的observeOn()函數(shù),傳遞正確的Scheduler給它。
下面是可能會(huì)用到Scheduler:
Schedulers.computation():用于計(jì)算型工作例如事件循環(huán)和回調(diào)處理,不要在I/O中使用這個(gè)函數(shù)(應(yīng)該使用Schedulers.io()函數(shù));
Schedulers.from(executor):使用指定的Executor作為Scheduler;
Schedulers.immediate():在當(dāng)前線程中立即開(kāi)始執(zhí)行任務(wù);
Schedulers.io():用于I/O密集型工作例如阻塞I/O的異步操作,這個(gè)調(diào)度器由一個(gè)會(huì)隨需增長(zhǎng)的線程池支持;對(duì)于一般的計(jì)算工作,使用Schedulers.computation();
Schedulers.newThread():為每個(gè)工作單元?jiǎng)?chuàng)建一個(gè)新的線程;
Schedulers.test():用于測(cè)試目的,支持單元測(cè)試的高級(jí)事件;
Schedulers.trampoline():在當(dāng)前線程中的工作放入隊(duì)列中排隊(duì),并依次操作。
通過(guò)設(shè)置observeOn和subscribeOn調(diào)度器,我們定義了網(wǎng)絡(luò)請(qǐng)求使用哪個(gè)線程(Schedulers.newThread())。
下一步
我們已經(jīng)在本文中涵蓋了很多基礎(chǔ)內(nèi)容,到這里你應(yīng)該對(duì)函數(shù)響應(yīng)式編程如何工作有了很好的認(rèn)識(shí)。請(qǐng)查看并理解本文介紹的工程,它托管在GitHub上面,閱讀RxJava文檔并檢出rxjava-koans工程,以測(cè)試驅(qū)動(dòng)的方式掌握函數(shù)響應(yīng)式編程范型。
總結(jié)
以上是生活随笔為你收集整理的Android什么是函数,什么是函数响应式编程(JavaAndroid版本)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c语言我国有13亿人口,计算机二级循环结
- 下一篇: android传输注册数据异常,andr