系列文章
RxJava系列之簡介和觀察者設計模式
RxJava系列之上游與下游
RxJava系列之常用創建型操作符
RxJava系列之常用變換操作符
RxJava系列之常用過濾操作符
RxJava系列之常用條件操作符
RxJava系列之常用合併操作符
RxJava系列之常用異常操作符
RxJava系列之線程切換實戰
RxJava系列之背壓模式
RxJava系列之配合Retrofit
RxJava系列之泛型高級
RxJava系列之手寫create操作符
RxJava系列之手寫create操作符增加泛型限定
RxJava系列之手寫just操作符
RxJava系列之手寫map操作符
RxJava系列之手寫切換線程
線程切換
前面的文章都是寫的demo代碼,本篇文章可以實戰使用RxJava的方式加載網絡圖片。RxJava可以方便的設置上游和下游的執行線程,從而達到異步處理的作用。
如何切換線程
RxJava默認執行在主線程。如果需要切換上游執行線程,使用subscribeOn操作符。如果需要切換下游執行線程,使用observeOn操作符。
/**
* todo 異步線程區域
* Schedulers.io() :代表io流操作,網絡操作,文件流,耗時操作
* Schedulers.newThread() : 比較常規的,普普通通
* Schedulers.computation() : 代表CPU 大量計算 所需要的線程
*
* todo main線程 主線程
* AndroidSchedulers.mainThread() : 專門爲Android main線程量身定做的
*
* todo 給上游分配多次,只會在第一次切換,後面的不切換了(忽略)
* todo 給下游分配多次,每次都會去切換
*
* @param view
*/
public void r01(View view) {
// RxJava如果不配置,默認就是主線程main
// 上游
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
Log.d(TAG, "上游 subscribe: " + Thread.currentThread().getName());
e.onNext("");
}
}).subscribeOn(Schedulers.io()) // todo 給上游配置異步線程 // 給上游分配多次,只會在第一次切換,後面的不切換了
.subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
.subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
.subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
.subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
// result: io 異步線程
.observeOn(AndroidSchedulers.mainThread()) // todo 給下游配置 安卓主線程 // 給下游分配多次,每次都會去切換
.observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
.observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
.observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
.observeOn(Schedulers.io()) // 切換一次線程
// result: io 異步線程
.subscribe(new Consumer<String>() { // 下游簡化版
@Override
public void accept(String s) throws Exception {
Log.d(TAG, "下游 subscribe: " + Thread.currentThread().getName());
}
});
}
是否指定異步線程的影響
如果未指定異步線程,則上游發一次,下游接收一次。上游發一次,下游接收一次。
如果指定了異步線程,則符合多線程的執行順序,具有不確定性。
/**
* todo 同步 和 異步 執行流程
* @param view
*/
public void r02(View view) {
// TODO 默認情況下 上游和下游都是main線程的情況下
// 上游
/*Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
Log.d(TAG, "subscribe: 上游發送了一次 1 ");
e.onNext(1);
Log.d(TAG, "subscribe: 上游發送了一次 2 ");
e.onNext(2);
Log.d(TAG, "subscribe: 上游發送了一次 3 ");
e.onNext(3);
}
}).subscribe(new Consumer<Integer>() { // 下游簡化版
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "下游 accept: " + integer);
}
});*/
/**
* 默認情況下 同步的想象
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 1
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 1
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 2
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 2
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 3
* 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 3
*/
// TODO 配置好異步線程
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
Log.d(TAG, "subscribe: 上游發送了一次 1 ");
e.onNext(1);
Log.d(TAG, "subscribe: 上游發送了一次 2 ");
e.onNext(2);
Log.d(TAG, "subscribe: 上游發送了一次 3 ");
e.onNext(3);
}
}).subscribeOn(Schedulers.io()) // 給上游分配 異步線程
.observeOn(AndroidSchedulers.mainThread()) // 給下游分配 主線程
.subscribe(new Consumer<Integer>() { // 下游簡化版
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "下游 accept: " + integer);
}
});
/**
* 配置了 異步後
* 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 1
* 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 2
* 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 3
* 09-05 16:26:03.574 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 1
* 09-05 16:26:03.574 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 2
* 09-05 16:26:03.575 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 3
*/
}
常規方式加載網絡圖片
使用常規方式加載圖片,代碼四分五裂,沒有串起來。增加或刪減功能維護比較困難。
private final String PATH = "http://pic33.nipic.com/20131007/13639685_123501617185_2.jpg";
/**
* todo 不使用RxJava去下載圖片
* @param view
*/
public void r03(View view) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("正在下載中...");
progressDialog.show();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
URL url = new URL(PATH);
URLConnection urlConnection = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
httpURLConnection.setConnectTimeout(5000);
int responseCode = httpURLConnection.getResponseCode();
if (HttpURLConnection.HTTP_OK == responseCode) {
Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
Message message = handler.obtainMessage();
message.obj = bitmap;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
imageView.setImageBitmap(bitmap);
// 隱藏加載框
if (progressDialog != null)
progressDialog.dismiss();
return false;
}
});
RxJava的方式加載網絡圖片
加載網絡圖片這件事被封裝成一個事件流,增加與刪減功能維護起來非常清楚。
/**
* todo 使用RxJava去下載圖片
* @param view
*/
public void r04(View view) {
// 起點
// 上游 被觀察者 Observable
Observable.just(PATH) // 內部發射
// 加載網路圖片,String Path 變換 Bitmap
.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String s) throws Exception {
try {
Thread.sleep(2000);
URL url = new URL(PATH);
URLConnection urlConnection = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
httpURLConnection.setConnectTimeout(5000);
int responseCode = httpURLConnection.getResponseCode();
if (HttpURLConnection.HTTP_OK == responseCode) {
Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
})
. // 給圖片加水印
.map(new Function<Bitmap, Bitmap>() {
@Override
public Bitmap apply(Bitmap bitmap) throws Exception {
// 給圖片加水印
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(30);
Bitmap bitmapSuccess = drawTextToBitmap(bitmap, "同學們大家好", paint, 60, 60);
return bitmapSuccess;
}
})
// 比如:增加一個 日誌紀錄功能,只需要添加要給 變換操作符
.map(new Function<Bitmap, Bitmap>() {
@Override
public Bitmap apply(Bitmap bitmap) throws Exception {
Log.d(TAG, "apply: 下載的Bitmap 是這個樣子的" + bitmap);
return bitmap;
}
})
.subscribeOn(Schedulers.io()) // todo 給上游分配 異步線程
.observeOn(AndroidSchedulers.mainThread()) // todo 給下游分配 主線程
.subscribe(new Observer<Bitmap>() { // 下游
@Override
public void onSubscribe(Disposable d) {
progressDialog = new ProgressDialog(MainActivity8.this);
progressDialog.setMessage("RxJava下載圖片中..");
progressDialog.show();
}
@Override
public void onNext(Bitmap bitmap) {
if (imageView != null)
imageView.setImageBitmap(bitmap);
}
@Override
public void onError(Throwable e) { // 發生異常
// if (imageView != null)
// imageView.setImageResource(R.mipmap.ic_launcher); // 下載錯誤的圖片
}
@Override
public void onComplete() { // 終點
if (progressDialog != null)
progressDialog.dismiss();
}
});
}
//圖片上繪製文字
private Bitmap drawTextToBitmap(Bitmap bitmap, String text, Paint paint, int paddingLeft, int paddingTop) {
Bitmap.Config bitmapConfig = bitmap.getConfig();
paint.setDither(true); // 獲取跟清晰的圖像採樣
paint.setFilterBitmap(true);// 過濾一些
if (bitmapConfig == null) {
bitmapConfig = Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
canvas.drawText(text, paddingLeft, paddingTop, paint);
return bitmap;
}
總結
1.異步線程區域
Schedulers.io() :代表io流操作,網絡操作,文件流,耗時操作
Schedulers.newThread() : 比較常規的,普普通通
Schedulers.computation() : 代表CPU 大量計算 所需要的線程
2.AndroidSchedulers.mainThread() : 專門爲Android main線程量身定做的
3.給上游分配多次,只會在第一次切換,後面的不切換了(忽略)
4.給下游分配多次,每次都會去切換
5.如果不配置異步線程,上游發一次,下游接收一次,上游發一次,下游接收一次,上游發一次,下游接收一次
6.配置好異步線程,就是異步的表現
7.傳統下載圖片的寫法,容易四分五裂
8.RxJava下載圖片,基於事件流編程,一條鏈子,起點和終點