這可能是最好的RxJava 2.x 教程(完結版)
為什麼要學 RxJava?
提升開發效率,降低維護成本一直是開發團隊永恆不變的宗旨。近兩年來國內的技術圈子中越來越多的開始提及 RxJava,越來越多的應用和麵試中都會有 RxJava ,而就目前的情況,Android 的網路庫基本被 Retrofit + OkHttp 一統天下了,而配合上響應式程式設計 RxJava 可謂如魚得水。想必大家肯定被近期的 Kotlin 炸開了鍋,筆者也在閒暇之時去了解了一番(作為一個與時俱進的有理想的青年怎麼可能不與時俱進?),發現其中有個非常好的優點就是簡潔,支援函數語言程式設計。是的, RxJava 最大的優點也是簡潔,但它不止是簡潔,而且是 隨著程式邏輯變得越來越複雜,它依然能夠保持簡潔
咳咳,要例子,猛戳這裡:給 Android 開發者的 RxJava 詳解
什麼是響應式程式設計
上面我們提及了響應式程式設計,不少新司機對它可謂一臉懵逼,那什麼是響應式程式設計呢?響應式程式設計是一種基於非同步資料流概念的程式設計模式。資料流就像一條河:它可以被觀測,被過濾,被操作,或者為新的消費者與另外一條流合併為一條新的流。
響應式程式設計的一個關鍵概念是事件。事件可以被等待,可以觸發過程,也可以觸發其它事件。事件是唯一的以合適的方式將我們的現實世界對映到我們的軟體中:如果屋裡太熱了我們就開啟一扇窗戶。同樣的,當我們的天氣app從服務端獲取到新的天氣資料後,我們需要更新app上展示天氣資訊的UI;汽車上的車道偏移系統探測到車輛偏移了正常路線就會提醒駕駛者糾正,就是是響應事件。
今天,響應式程式設計最通用的一個場景是UI:我們的移動App必須做出對網路呼叫、使用者觸控輸入和系統彈框的響應。在這個世界上,軟體之所以是事件驅動並響應的是因為現實生活也是如此。
為什麼出了一個系列後還有完結版?
RxJava 這些年可謂越來越流行,而在去年的晚些時候釋出了2.0正式版。大半年已過,雖然網上已經出現了大部分的 RxJava 教程(其實細心的你還是會發現 1.x 的超級多),前些日子,筆者花了大約兩週的閒暇之時寫了 RxJava
2.x 系列教程,也得到了不少反饋,其中就有不少讀者覺得每一篇的教程太短,抑或是希望更多的側重適用場景的介紹,在這樣的大前提下,這篇完結版教程就此誕生,僅供各位新司機採納。
開始
RxJava 2.x 已經按照 Reactive-Streams specification 規範完全的重寫了,maven也被放在了io.reactivex.rxjava2:rxjava:2.x.y
下,所以 RxJava 2.x 獨立於 RxJava 1.x 而存在,而隨後官方宣佈的將在一段時間後終止對 RxJava 1.x 的維護,所以對於熟悉 RxJava 1.x 的老司機自然可以直接看一下 2.x
的文件和異同就能輕鬆上手了,而對於不熟悉的年輕司機,不要慌,本醬帶你裝逼帶你飛,馬上就發車,坐穩了:https://github.com/nanchen2251/RxJava2Examples
你只需要在 build.gradle 中加上:compile 'io.reactivex.rxjava2:rxjava:2.1.1'
(2.1.1為寫此文章時的最新版本)
介面變化
RxJava 2.x 擁有了新的特性,其依賴於4個基礎介面,它們分別是
-
Publisher
-
Subscriber
-
Subscription
-
Processor
其中最核心的莫過於 Publisher
和 Subscriber
。Publisher
可以發出一系列的事件,而 Subscriber
負責和處理這些事件。
其中用的比較多的自然是 Publisher
的 Flowable
,它支援背壓,有興趣的可以看一下官方對於背壓的講解
可以明顯地發現,RxJava 2.x 最大的改動就是對於 backpressure
的處理,為此將原來的 Observable
拆分成了新的Observable
和Flowable
,同時其他相關部分也同時進行了拆分,但令人慶幸的是,是它,是它,還是它,還是我們最熟悉和最喜歡的
RxJava。
Observable
在 RxJava 1.x 中,我們最熟悉的莫過於 Observable
這個類了,筆者在剛剛使用 RxJava 2.x 的時候,建立了 一個Observable
,瞬間一臉懵逼有木有,居然連我們最最熟悉的 Subscriber
都沒了,取而代之的是 ObservableEmmiter
,俗稱發射器。此外,由於沒有了Subscriber
的蹤影,我們建立觀察者時需使用 Observer
。而 Observer
也不是我們熟悉的那個 Observer
,又出現了一個 Disposable
引數帶你裝逼帶你飛。
廢話不多說,從會用開始,還記得 RxJava 的三部曲嗎?
第一步:初始化 Observable
第二步:初始化 Observer
第三步:建立訂閱關係
Observable.create(new ObservableOnSubscribe<Integer>() { // 第一步:初始化Observable
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
Log.e(TAG, "Observable emit 1" + "\n");
e.onNext(1);
Log.e(TAG, "Observable emit 2" + "\n");
e.onNext(2);
Log.e(TAG, "Observable emit 3" + "\n");
e.onNext(3);
e.onComplete();
Log.e(TAG, "Observable emit 4" + "\n" );
e.onNext(4);
}
}).subscribe(new Observer<Integer>() { // 第三步:訂閱
// 第二步:初始化Observer
private int i;
private Disposable mDisposable;
@Override
public void onSubscribe(@NonNull Disposable d) {
mDisposable = d;
}
@Override
public void onNext(@NonNull Integer integer) {
i++;
if (i == 2) {
// 在RxJava 2.x 中,新增的Disposable可以做到切斷的操作,讓Observer觀察者不再接收上游事件
mDisposable.dispose();
}
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError : value : " + e.getMessage() + "\n" );
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete" + "\n" );
}
});
不難看出,RxJava 2.x 與 1.x 還是存在著一些區別的。首先,建立 Observable
時,回撥的是 ObservableEmitter
,字面意思即發射器,並且直接 throws Exception。其次,在建立的 Observer 中,也多了一個回撥方法:onSubscribe
,傳遞引數為Disposable
,Disposable
相當於
RxJava 1.x 中的 Subscription
, 用於解除訂閱。可以看到示例程式碼中,在 i 自增到 2 的時候,訂閱關係被切斷。
07-03 14:24:11.663 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: onSubscribe : false
07-03 14:24:11.664 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: Observable emit 1
07-03 14:24:11.665 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: onNext : value : 1
07-03 14:24:11.666 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: Observable emit 2
07-03 14:24:11.667 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: onNext : value : 2
07-03 14:24:11.668 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: onNext : isDisposable : true
07-03 14:24:11.669 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: Observable emit 3
07-03 14:24:11.670 18467-18467/com.nanchen.rxjava2examples E/RxCreateActivity: Observable emit 4
當然,我們的 RxJava 2.x 也為我們保留了簡化訂閱方法,我們可以根據需求,進行相應的簡化訂閱,只不過傳入物件改為了 Consumer
。
Consumer
即消費者,用於接收單個值,BiConsumer
則是接收兩個值,Function
用於變換物件,Predicate
用於判斷。這些介面命名大多參照了 Java
8 ,熟悉 Java 8 新特性的應該都知道意思,這裡也不再贅述。
執行緒排程
關於執行緒切換這點,RxJava 1.x 和 RxJava 2.x 的實現思路是一樣的。這裡簡單的說一下,以便於我們的新司機入手。
subScribeOn
同 RxJava 1.x 一樣,subscribeOn
用於指定subscribe()
時所發生的執行緒,從原始碼角度可以看出,內部執行緒排程是通過ObservableSubscribeOn
來實現的。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}
ObservableSubscribeOn
的核心原始碼在 subscribeActual
方法中,通過代理的方式使用 SubscribeOnObserver
包裝Observer
後,設定 Disposable
來將 subscribe
切換到 Scheduler
執行緒中。
observeOn
observeOn
方法用於指定下游 Observer
回調發生的執行緒。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
}
執行緒切換需要注意的
RxJava 內建的執行緒排程器的確可以讓我們的執行緒切換得心應手,但其中也有些需要注意的地方。
-
簡單地說,
subscribeOn()
指定的就是發射事件的執行緒,observerOn
指定的就是訂閱者接收事件的執行緒。 -
多次指定發射事件的執行緒只有第一次指定的有效,也就是說多次呼叫
subscribeOn()
只有第一次的有效,其餘的會被忽略。 -
但多次指定訂閱者接收執行緒是可以的,也就是說每呼叫一次
observerOn()
,下游的執行緒就會切換一次。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
Log.e(TAG, "Observable thread is : " + Thread.currentThread().getName());
e.onNext(1);
e.onComplete();
}
}).subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.e(TAG, "After observeOn(mainThread),Current thread is " + Thread.currentThread().getName());
}
})
.observeOn(Schedulers.io())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.e(TAG, "After observeOn(io),Current thread is " + Thread.currentThread().getName());
}
});
輸出:
07-03 14:54:01.177 15121-15438/com.nanchen.rxjava2examples E/RxThreadActivity: Observable thread is : RxNewThreadScheduler-1
07-03 14:54:01.178 15121-15121/com.nanchen.rxjava2examples E/RxThreadActivity: After observeOn(mainThread),Current thread is main
07-03 14:54:01.179 15121-15439/com.nanchen.rxjava2examples E/RxThreadActivity: After observeOn(io),Current thread is RxCachedThreadScheduler-2
例項程式碼中,分別用 Schedulers.newThread()
和 Schedulers.io()
對發射執行緒進行切換,並採用observeOn(AndroidSchedulers.mainThread()
和 Schedulers.io()
進行了接收執行緒的切換。可以看到輸出中發射執行緒僅僅響應了第一個 newThread
,但每呼叫一次 observeOn()
,執行緒便會切換一次,因此如果我們有類似的需求時,便知道如何處理了。
RxJava 中,已經內建了很多執行緒選項供我們選擇,例如有:
-
Schedulers.io()
代表io操作的執行緒, 通常用於網路,讀寫檔案等io密集型的操作; -
Schedulers.computation()
代表CPU計算密集型的操作, 例如需要大量計算的操作; -
Schedulers.newThread()
代表一個常規的新執行緒; -
AndroidSchedulers.mainThread()
代表Android的主執行緒
這些內建的 Scheduler
已經足夠滿足我們開發的需求,因此我們應該使用內建的這些選項,而 RxJava 內部使用的是執行緒池來維護這些執行緒,所以效率也比較高。
操作符
關於操作符,在官方文件中已經做了非常完善的講解,並且筆者前面的系列教程中也著重講解了絕大多數的操作符作用,這裡受於篇幅限制,就不多做贅述,只挑選幾個進行實際情景的講解。
map
map
操作符可以將一個 Observable
物件通過某種關係轉換為另一個Observable
物件。在 2.x 中和 1.x 中作用幾乎一致,不同點在於:2.x 將 1.x 中的 Func1
和 Func2
改為了 Function
和 BiFunction
。
採用 map 操作符進行網路資料解析
想必大家都知道,很多時候我們在使用 RxJava 的時候總是和 Retrofit 進行結合使用,而為了方便演示,這裡我們就暫且採用 OkHttp3 進行演示,配合 map
,doOnNext
,執行緒切換進行簡單的網路請求:
1)通過 Observable.create() 方法,呼叫 OkHttp 網路請求;
2)通過 map 操作符集合 gson,將 Response 轉換為 bean 類;
3)通過 doOnNext() 方法,解析 bean 中的資料,並進行資料庫儲存等操作;
4)排程執行緒,在子執行緒中進行耗時操作任務,在主執行緒中更新 UI ;
5)通過 subscribe(),根據請求成功或者失敗來更新 UI 。
Observable.create(new ObservableOnSubscribe<Response>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Response> e) throws Exception {
Builder builder = new Builder()
.url("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.get();
Request request = builder.build();
Call call = new OkHttpClient().newCall(request);
Response response = call.execute();
e.onNext(response);
}
}).map(new Function<Response, MobileAddress>() {
@Override
public MobileAddress apply(@NonNull Response response) throws Exception {
if (response.isSuccessful()) {
ResponseBody body = response.body();
if (body != null) {
Log.e(TAG, "map:轉換前:" + response.body());
return new Gson().fromJson(body.string(), MobileAddress.class);
}
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<MobileAddress>() {
@Override
public void accept(@NonNull MobileAddress s) throws Exception {
Log.e(TAG, "doOnNext: 儲存成功:" + s.toString() + "\n");
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MobileAddress>() {
@Override
public void accept(@NonNull MobileAddress data) throws Exception {
Log.e(TAG, "成功:" + data.toString() + "\n");
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "失敗:" + throwable.getMessage() + "\n");
}
});
concat
concat
可以做到不交錯的發射兩個甚至多個 Observable 的發射事件,並且只有前一個 Observable
終止(onComplete
) 後才會訂閱下一個 Observable
。
採用 concat 操作符先讀取快取再通過網路請求獲取資料
想必在實際應用中,很多時候(對資料操作不敏感時)都需要我們先讀取快取的資料,如果快取沒有資料,再通過網路請求獲取,隨後在主執行緒更新我們的UI。
concat
操作符簡直就是為我們這種需求量身定做。
利用 concat
的必須呼叫 onComplete
後才能訂閱下一個 Observable
的特性,我們就可以先讀取快取資料,倘若獲取到的快取資料不是我們想要的,再呼叫 onComplete()
以執行獲取網路資料的Observable
,如果快取資料能應我們所需,則直接呼叫 onNext()
,防止過度的網路請求,浪費使用者的流量。
Observable<FoodList> cache = Observable.create(new ObservableOnSubscribe<FoodList>() {
@Override
public void subscribe(@NonNull ObservableEmitter<FoodList> e) throws Exception {
Log.e(TAG, "create當前執行緒:"+Thread.currentThread().getName() );
FoodList data = CacheManager.getInstance().getFoodListData();
// 在操作符 concat 中,只有呼叫 onComplete 之後才會執行下一個 Observable
if (data != null){ // 如果快取資料不為空,則直接讀取快取資料,而不讀取網路資料
isFromNet = false;
Log.e(TAG, "\nsubscribe: 讀取快取資料:" );
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 讀取快取資料:\n");
}
});
e.onNext(data);
}else {
isFromNet = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 讀取網路資料:\n");
}
});
Log.e(TAG, "\nsubscribe: 讀取網路資料:" );
e.onComplete();
}
}
});
Observable<FoodList> network = Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows",10+"")
.build()
.getObjectObservable(FoodList.class);
// 兩個 Observable 的泛型應當保持一致
Observable.concat(cache,network)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FoodList>() {
@Override
public void accept(@NonNull FoodList tngouBeen) throws Exception {
Log.e(TAG, "subscribe 成功:"+Thread.currentThread().getName() );
if (isFromNet){
mRxOperatorsText.append("accept : 網路獲取資料設定快取: \n");
Log.e(TAG, "accept : 網路獲取資料設定快取: \n"+tngouBeen.toString() );
CacheManager.getInstance().setFoodListData(tngouBeen);
}
mRxOperatorsText.append("accept: 讀取資料成功:" + tngouBeen.toString()+"\n");
Log.e(TAG, "accept: 讀取資料成功:" + tngouBeen.toString());
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "subscribe 失敗:"+Thread.currentThread().getName() );
Log.e(TAG, "accept: 讀取資料失敗:"+throwable.getMessage() );
mRxOperatorsText.append("accept: 讀取資料失敗:"+throwable.getMessage()+"\n");
}
});
有時候我們的快取可能還會分為 memory 和 disk ,實際上都差不多,無非是多寫點 Observable ,然後通過 concat 合併即可。
flatMap 實現多個網路請求依次依賴
想必這種情況也在實際情況中比比皆是,例如使用者註冊成功後需要自動登入,我們只需要先通過註冊介面註冊使用者資訊,註冊成功後馬上呼叫登入介面進行自動登入即可。
我們的 flatMap
恰好解決了這種應用場景,flatMap
操作符可以將一個發射資料的 Observable
變換為多個Observables
,然後將它們發射的資料合併後放到一個單獨的 Observable
,利用這個特性,我們很輕鬆地達到了我們的需求。
Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows", 1 + "")
.build()
.getObjectObservable(FoodList.class) // 發起獲取食品列表的請求,並解析到FootList
.subscribeOn(Schedulers.io()) // 在io執行緒進行網路請求
.observeOn(AndroidSchedulers.mainThread()) // 在主執行緒處理獲取食品列表的請求結果
.doOnNext(new Consumer<FoodList>() {
@Override
public void accept(@NonNull FoodList foodList) throws Exception {
// 先根據獲取食品列表的響應結果做一些操作
Log.e(TAG, "accept: doOnNext :" + foodList.toString());
mRxOperatorsText.append("accept: doOnNext :" + foodList.toString()+"\n");
}
})
.observeOn(Schedulers.io()) // 回到 io 執行緒去處理獲取食品詳情的請求
.flatMap(new Function<FoodList, ObservableSource<FoodDetail>>() {
@Override
public ObservableSource<FoodDetail> apply(@NonNull FoodList foodList) throws Exception {
if (foodList != null && foodList.getTngou() != null && foodList.getTngou().size() > 0) {
return Rx2AndroidNetworking.post("http://www.tngou.net/api/food/show")
.addBodyParameter("id", foodList.getTngou().get(0).getId() + "")
.build()
.getObjectObservable(FoodDetail.class);
}
return null;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FoodDetail>() {
@Override
public void accept(@NonNull FoodDetail foodDetail) throws Exception {
Log.e(TAG,
相關推薦
這可能是最好的RxJava 2.x 教程(完結版)
為什麼要學 RxJava?
提升開發效率,降低維護成本一直是開發團隊永恆不變的宗旨。近兩年來國內的技術圈子中越來越多的開始提及 RxJava,越來越多的應用和麵試中都會有 RxJava ,而就目前的情況,Android
的網路庫基本被 Retrofit + OkH
這可能是最好的RxJava 2.x 教程
為什麼要學 RxJava?
提升開發效率,降低維護成本一直是開發團隊永恆不變的宗旨。近兩年來國內的技術圈子中越來越多的開始提及 RxJava ,越來越多的應用和麵試中都會有 RxJava ,而就目前的情況,Android
的網路庫基本被 Retrofit + OkHttp 一統天下了,而配合上響應式
RxJava 2.x 教程及原始碼揭祕(一)入門理解及基本操作符
目錄
前言
Rxjava的介紹
Rxjava的優勢
Rxjava是觀察者模式
Rxjava是裝飾者模式
Observable
Rxjava的操作符
subScribeOn與observeOn切換執行緒
其他操作符
補充
前言
&nbs
RxJava 2.x 教程及原始碼揭祕(三)Rxjava操作符原始碼解析
本文將探究:
知道執行緒排程是怎麼實現的
知道操作符是怎麼實現的
RxJava最強大的莫過於它的執行緒排程 和 花式操作符。
map操作符
map是一個高頻的操作符,我們首先拿他開刀。
例子如下,源頭Observable傳送的是String型別的數字,利用map轉換成
Rxjava 2.x筆記(一)
這麼火的框架,現在才開始學,實在是有點落伍(太落伍)了。因為2.x是獨立於1.x的存在,所以為了儘快趕上時代潮流,本文基於2.x版本,整理一下學習中2.x的知識。因為是做筆記,所以肯定要參考大神的部落格了。(當然,其中的程式碼咱會先敲一遍再貼上的,原來是用筆記本打筆記,當然查詢的時候又不好查
python基礎教程(第二版)---1.4數字和表達式
pan com -1 一行 img 分享 cnblogs 第二版 輸出結果 一、除法
二、取余運算
對於整數:
對於浮點數:
2.75/0.5是為了說明除法結果是5.5
取余的時候(2.75-5*0.5)==0.25就得到了第一行的輸出結果0.25啦
這
OpenAcc社區版安裝教程(Linux版)
下載 mage 操作系統 技術分享 OS tar png 鏈接 blank 下載OpenAcc社區版
1,目前為止的最新版,平臺是Linux,選擇Linux x86-64. 我的服務器系統是CentOs
下載地址鏈接:https://www.pgroup.com/produ
Visual Studio 2017 安裝使用教程(詳細版)
系統設置 -s 分享圖片 代碼 ++ 官網下載 studio 題解 微軟官網 Visual Studio 2017 安裝使用教程(詳細)
本人曾因無法使用vs編寫C語言程序痛苦一個月之久,實乃慚愧,後發現不少同學也同樣存在著相同問題,其原因歸結於網上的各種教程
多多客小程序開源版8步快速安裝教程(圖文版)
開源 小程序開發 微信小程序
1、首先我們打開 https://gitee.com/doodooke/doodoo,下載zip壓縮包
2、進入下載的代碼目錄,打開命令行
3、執行命令安裝依賴yarn
4、創建數據庫
用優啟通製作U盤啟動盤教程(UEFI版)
製作前準備: 1.下載優啟通uefi版到本地磁碟; 2.準備一個能正常使用的U盤,最好大於4G(製作過程會格式化U盤,請先備份好U盤資料)。
第一步:開啟優啟通uefi版,將準備好的u盤插入電腦usb介面,軟體會自動識別並選擇當前插入的u盤,點選“一鍵製作啟動U盤”: 第二步:此時彈出
美圖DPOS以太坊教程(Docker版)
一、前言
最近,需要接觸區塊鏈專案的主鏈開發,在EOS、BTC、ethereum、超級賬本這幾種區塊鏈技術當中,相互對比後,最終還是以go-ethereum為解決方案。 以ethereum為基準去找解決方案,最終找到了2個符合自己要求的方案,分別如下:美圖、gttc。本來是想用gttc的這個解決方案的,但是
百度網站收錄教程(個人版)
讓百度搜索到有兩種方法,一種是等百度主動來爬取你的網站,還有一種是你自己主動提交給百度告訴它來爬取。第一種週期太過漫長,所以本文就講第二種
1.準備網站
首先,想要做SEO的同學,需要考慮清楚所選擇的前端框架。現在主流的angularJs,Vue.js,react.js這
基於UMeng訊息推送測試-demo教程(iOS版)
基於UMeng訊息推送測試-demo教程(iOS版)
眼看著就要過年了(2017本命年終於快過去了臨來嶄新的2018大發年),每個人的心裡就像飛了一樣,這個時候能夠在辦公間待下來的,每個人都是精英,突然想起了那首張含韻的歌《放假了》。
今天是2017年臘月25距離2018年僅有5天,依舊在外
Windows 10 + kali Linux 雙系統安裝教程(詳細版)
準備工具如下:
kali Linux 映象
準備一4G以上的U盤
製作U盤啟動盤工具- Win32DiskImager
新增引導工具-EasyBCD
留出一個空的盤,哪個盤的空間比較大可以壓縮出大概20-50G的空間。
1.磁碟壓縮新建分割槽
開啟計算機管理 》 儲存
嵌入式linux基礎教程(第二版)第一章
linux已經成為很多裝置的作業系統,手機、DVD播放器、電子遊戲機、數碼相機、網路交換機和無線網路裝置、機頂盒、高清電視、藍光DVD播放器、汽車的資訊娛樂中心和很多日常使用的電器等都在使用linux作業系統。linux已經成為很多裝置的嵌入式作業系統
一.為
cmake的使用教程(linux版)(一)
1.安裝cmake
對於ubuntu系統來說,執行下面的命令即可:
$ sudo apt-get install cmake
2.一個簡單的例子
首先建立如下的檔案和目錄,結構如圖:
CMakeLists.txt檔案內容如下:
PROJE
Python繪圖—Matplotlib教程(詳細版)前半部分
一直將matplotlib當做一個工具來用,因為沒有了解到它的特性,所以一直學得不繫統,導致用到的時候經常要查官方文件。這裡翻譯一個官方推薦的matplotlib的介紹文件。
猛戳進入原文連結
文件中包含的內容:
簡介
簡單的例子
matplotlib元
Mybatis 的 PageHelp 外掛的使用教程(SSM版)
效果圖
1. pom.xml 新增PageHelp的jar包
<!-- 引入Mybatis分頁外掛 -->
<dependency>
<groupId&
centos 6.5 6.6 6.7安裝gitlab教程(社群版)
簡單的說安裝gitlab就兩種辦法主要介紹第一種:官網推薦的方法:
1.新建yum源
新建 /etc/yum.repos.d/gitlab-ce.repo,內容為
[gitlab-ce]
name=gitlab-ce
baseurl=http://mirrors.tuna.t
python基礎教程(第二版)
第十章 自帶電池
匯入模組:
import sys
sys.path.append('c:/python27')//告訴程式到這裡找模組
1.1. 若模組是一個程式,則在匯入時只執行一次,後面匯入不在執行,修改後可以用reload函式重新匯入,這