第三次学习Rxjava了,记点什么吧。。。
为什么要学习这个东东呢?
经常碰到与安卓系统交互的外部设备是应答式的,就是你一步步的问它,它一步步的回答,例如某厂商的指纹仪进行指纹比对操作,需要的步骤如下:
打开设备-下载指纹特征码到缓冲区A-采集指纹到缓冲区B(循环等待)-再次采集-合成特征码-与缓冲区A对比
类似这样的需要一步步推进的业务流程,通常的做法是异步回调、或者消息订阅,能用是能用,但是怎么说呢?它有点混乱:回调-回调-回调~~,或者:消息-消息-消息~~,半个月后发现之前写的代码阅读起来有点困难,跳来跳去的。
听说rxjava特点是基于事件流、异步,既然有这样的好东西了就学一下吧,可是看next(1);next(2);next(3);这样的例子,完全不得门而入。
本文将完成一个小例子有三个部分,第一个是从网络上下载一个图片,然后展示出来;第二个增加了一个步骤,在下载完毕后添加水印,然后再展示出来;第三个是异常处理。
先看第一个部分:下载图片并展示。
1,由于用到了网络,需要在项目的AndroidManifest.xml文件中添加权限
<uses-permission android:name="android.permission.INTERNET" />
2,布局,仅有一个图像控件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3,主界面Activity
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testNetIo();
}
private static final String PATH = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png";
private void testNetIo() {
Observable.just(PATH).map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String s) throws Throwable {
URL url = new URL(s);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int respCode = conn.getResponseCode();
if (respCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = conn.getInputStream();
return BitmapFactory.decodeStream(inputStream);
}
Log.d(TAG, "反馈其它:" + respCode);
return null;
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Bitmap>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
showProgressDialog();
}
@Override
public void onNext(@NonNull Bitmap bitmap) {
if(bitmap == null){
Log.e(TAG, "onNext反馈null");
return;
}
((ImageView) MainActivity.this.findViewById(R.id.imageView1)).setImageBitmap(bitmap);
}
@Override
public void onError(@NonNull Throwable e) {
Log.d(TAG, "------onError----------" + e);
}
@Override
public void onComplete() {
Log.d(TAG, "-----onComplete-----------");
dismissProgressDialog();
}
});
}
private ProgressDialog progressDialog;
public void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
}
progressDialog.setMessage("your message");
progressDialog.show();
}
public void dismissProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
}
}
代码里使用了已经被抛弃的对话框方法,也没有优化代码,不要去管他,就是简单方便的表达用法就好了。
这样一个简单的例子就完成了,我也不懂为什么要just、为什么要map,就先这样写吧,到目前为止,它能够表达出来的流程性的特点并不明显,接下来增加一个添加水印的步骤,加强一下“流程”、“步骤”。
只需要在map方法的后面,再加一个map方法即可,像这样:
Observable.just(PATH).map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String s) throws Throwable {
URL url = new URL(s);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int respCode = conn.getResponseCode();
if (respCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = conn.getInputStream();
return BitmapFactory.decodeStream(inputStream);
}
Log.d(TAG, "反馈其它:" + respCode);
return null;
}
})
.map(new Function<Bitmap, Bitmap>() {//新添加的map方法
@Override
public Bitmap apply(Bitmap bitmap) throws Throwable {
return waterMarkCover(bitmap, "rxjava入门");
}
})
看吧,要加一个步骤只要写一段map即可。
新添加的水印方法也是网上抄抄的,如下:
private Bitmap waterMarkCover(Bitmap bitmap, String text) {
if (bitmap == null) return null;
Bitmap newImage = bitmap.copy(bitmap.getConfig(), true);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas canvas = new Canvas(newImage);
paint.setColor(Color.RED);
paint.setTextSize(100);
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
canvas.drawText(text, 0, bounds.height(), paint);//bounds.height()是文字高度,你把它换成0试试就知道为什么要用文字高度了
bitmap.recycle();
return newImage;
}
做好这两步基本上可以用上了,再加上异常处理,基本上算入门吧:在两个map之后,加上异常、重试。
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Throwable throwable) throws Throwable {
Log.d(TAG, "发生异常:" + throwable);
if (throwable instanceof NullPointerException) {
Log.d(TAG, "获取内容失败");
} else if (throwable instanceof IOException) {
Log.d(TAG, "请求失败");
tryCount++;
if (tryCount < 3) {
Log.d(TAG, "未满次数,再次请求" + tryCount);
return Observable.just(1).delay(3, TimeUnit.SECONDS);
}
}
Log.d(TAG, "失败了");
return null;
}
});
}
})
当然,出现异常不能直接打个日志就完事,可以顶一个反馈对象用来承载出错信息,我这里直接用null了。
完整的代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.Theme_RxJavaTest1);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private static final String PATH = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png";
private int tryCount = 0;
private void testNetIo() {
Observable<String> obs = Observable.just(PATH);
//Observable.just(PATH)
obs.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String s) throws Throwable {
Log.d(TAG, "-----apply-----------");
URL url = new URL(s);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int respCode = conn.getResponseCode();
if (respCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = conn.getInputStream();
return BitmapFactory.decodeStream(inputStream);
}
Log.d(TAG, "反馈其它:" + respCode);
return null;
}
})
.map(new Function<Bitmap, Bitmap>() {
@Override
public Bitmap apply(Bitmap bitmap) throws Throwable {
Log.d(TAG, "-----apply---2--------");
return waterMarkCover(bitmap, "rxjava入门");
}
})
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Throwable throwable) throws Throwable {
Log.d(TAG, "发生异常:" + throwable);
if (throwable instanceof NullPointerException) {
Log.d(TAG, "获取内容失败");
} else if (throwable instanceof IOException) {
Log.d(TAG, "请求失败");
tryCount++;
if (tryCount < 3) {
Log.d(TAG, "未满次数,再次请求" + tryCount);
return Observable.just(1).delay(3, TimeUnit.SECONDS);
}
}
Log.d(TAG, "失败了");
return null;
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Bitmap>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.d(TAG, "-----onSubscribe-----------");
showProgressDialog();
}
@Override
public void onNext(@NonNull Bitmap bitmap) {
Log.d(TAG, "-----onNext-----------");
if (bitmap == null) {
Log.e(TAG, "onNext反馈null");
return;
}
((ImageView) MainActivity.this.findViewById(R.id.imageView1)).setImageBitmap(bitmap);
}
@Override
public void onError(@NonNull Throwable e) {
Log.d(TAG, "------onError----------" + e);
dismissProgressDialog();
}
@Override
public void onComplete() {
Log.d(TAG, "-----onComplete-----------");
dismissProgressDialog();
}
});
}
private Bitmap waterMarkCover(Bitmap bitmap, String text) {
if (bitmap == null) return null;
Bitmap newImage = bitmap.copy(bitmap.getConfig(), true);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas canvas = new Canvas(newImage);
paint.setColor(Color.RED);
//paint.setTextSize(100);
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
canvas.drawText(text, 0, bounds.height(), paint);//bounds.height()是文字高度,你把它换成0试试就知道为什么要用文字高度了
bitmap.recycle();
return newImage;
}
private ProgressDialog progressDialog;
public void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
}
progressDialog.setMessage("your message");
progressDialog.show();
}
public void dismissProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
}
public void doTest1(View view) {
testNetIo();
}
public void doTest2(View view) {
//Observable.fromArray()
}
}
rxjava的知识点太多了,边学边用。