到底什么情況下可以使用 RxJava ? 我們需要知道不是使用 RxJava 而是響應式編程,好吧,那到底什么時候可以響應式編程?按道理來講,我們寫任何代碼都可以采用響應式編程的思想,只不過是有沒有必要而已。羅列一些使用場景:
- 防止按鈕重復點擊;
RxView.clicks(mClearContent).debounce(300, TimeUnit.MILLISECONDS)
- EditText 添加 addTextChangedListener
RxTextView.textChanges(mUserNameEt)
- 延遲執(zhí)行
Observable.timer(3, TimeUnit.SECONDS)
- 不斷輪詢
Observable.interval(3, 3, TimeUnit.SECONDS)
- 減少頻繁調(diào)用
RxTextView.textChanges(mUserNameEt).debounce(1200, TimeUnit.MILLISECONDS)
- RxPermission請求權(quán)限
rxPermissions.request(Manifest.permission.CAMERA)
2.RxPermission 源碼分析
上面的用法真的是少得可憐,可能是我也用得比較少,其實還有一些如 RxBus + RxRelay,OkHttp + RxJava + Retrofit,MVP + Dragger + RxJava 等等,但這些目前一時半會還講不清楚,希望后面分析完 Retrofit 源碼后能說清楚一些皮毛。我們還是老老實實來看看 RxPermission 的源碼?不是說好講使用怎么又講源碼,為什么非得跟源碼過不去?待會后面你就知道了。先看下使用(申請相機權(quán)限):
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.request(Manifest.permission.CAMERA).subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean aBoolean) throws Exception {
if(aBoolean){
// 有權(quán)限
}else {
// 沒權(quán)限
}
}
});
回想一下我們在不用任何框架的時候,去申請權(quán)限那得多少代碼啊~,關(guān)鍵是還需要重載 Activity 的 onRequestPermissionsResult() ,都懷疑人生了。后來自己好不容易寫了個注解權(quán)限框架,但是當看到 RxPermission 就棄坑了,看看怎么實現(xiàn)的:
public Observable<Boolean> request(final String... permissions) {
return Observable.just(TRIGGER).compose(ensure(permissions));
}
public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
return new ObservableTransformer<T, Boolean>() {
@Override
public ObservableSource<Boolean> apply(Observable<T> o) {
return request(o, permissions)
// Transform Observable<Permission> to Observable<Boolean>
.buffer(permissions.length)
.flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {
@Override
public ObservableSource<Boolean> apply(List<Permission> permissions) throws Exception {
if (permissions.isEmpty()) {
// Occurs during orientation change, when the subject receives onComplete.
// In that case we don't want to propagate that empty list to the
// subscriber, only the onComplete.
return Observable.empty();
}
// 如果有用戶沒有授權(quán)的權(quán)限,返回false
// Return true if all permissions are granted.
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
// 返回授權(quán)成功
return Observable.just(true);
}
});
}
};
}
@TargetApi(Build.VERSION_CODES.M)
private Observable<Permission> requestImplementation(final String... permissions) {
// 根據(jù)需要申請的權(quán)限創(chuàng)建一個 Observable 集合
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
// 用來存放沒有授予的權(quán)限
List<String> unrequestedPermissions = new ArrayList<>();
// In case of multiple permissions, we create an Observable for each of them.
// At the end, the observables are combined to have a unique response.
for (String permission : permissions) {
mRxPermissionsFragment.log("Requesting permission " + permission);
// 把需要申請的權(quán)限加入集合
if (isGranted(permission)) {
// Already granted, or not Android M
// Return a granted Permission object.
list.add(Observable.just(new Permission(permission, true, false)));
continue;
}
if (isRevoked(permission)) {
// Revoked by a policy, return a denied Permission object.
list.add(Observable.just(new Permission(permission, false, false)));
continue;
}
PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);
// Create a new subject if not exists
if (subject == null) {
unrequestedPermissions.add(permission);
subject = PublishSubject.create();
mRxPermissionsFragment.setSubjectForPermission(permission, subject);
}
list.add(subject);
}
// 如果有權(quán)限沒有授權(quán),去申請
if (!unrequestedPermissions.isEmpty()) {
String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
requestPermissionsFromFragment(unrequestedPermissionsArray);
}
return Observable.concat(Observable.fromIterable(list));
}
// 調(diào)用自己額外創(chuàng)建的 Fragment 的申請權(quán)限方法
@TargetApi(Build.VERSION_CODES.M)
void requestPermissionsFromFragment(String[] permissions) {
mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
mRxPermissionsFragment.requestPermissions(permissions);
}
源碼其實很簡單,就那么幾個類,我們之所以不需要在 Activity 中重寫 onRequestPermissionsResult 方法了,那是因為 RxPermission 自己額外創(chuàng)建了一個 Fragment ,加載到了我們的 Activity 中,so easy 。
3.RxLogin 庫封裝
最后我們根據(jù)響應式編程的思想,參照 RxPermission 的源碼思想,自己動手來打造一個 RxLogin 第三方登錄框架。我一般都是用友盟第三方的:
public class UserLoginActivity extends Activity implements UMAuthListener {
private UMShareAPI mUmShareAPI;
public static final String PLATFORM_KEY = "PLATFORM_KEY";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUmShareAPI = UMShareAPI.get(this);
mUmShareAPI.doOauthVerify(this,SHARE_MEDIA.QQ, this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data);
}
@Override
public void onStart(SHARE_MEDIA share_media) {
}
@Override
public void onComplete(SHARE_MEDIA share_media, int i, Map<String, String> map) {
// 獲取參數(shù)用戶信息,調(diào)用后臺接口,請求登錄
}
@Override
public void onError(SHARE_MEDIA share_media, int i, Throwable throwable) {
// 獲取等三方賬號信息出錯
}
@Override
public void onCancel(SHARE_MEDIA share_media, int i) {
// 取消第三方登錄
}
}
上面這種寫法,我們初級剛?cè)腴T的時候一般會這么寫,這樣寫到底行不行,也不說不行,也不說不對。但是一般我們不會直接依賴第三方(隔離),而且其實還會多出來很多方法像 onActivityResult() 、onStar()、onCancel() 等等一些我們可能都不需要或者說不想關(guān)注,但是又不得已,哎。
下面我們自己動手寫一個RxLogin,一方面不與第三方的框架直接依賴(隔離),還有一方面我們過濾掉不關(guān)心的方法,還有一方面我們項目中如果有 Retrofit 、MVP 這些那會更加完美一些,當然還有一些其他方面。需要額外新建 5 個類,5個類有點多,如果你想放棄:
public class RxLogin implements UMAuthListener {
private Activity activity;
private RxLoginResult mLoginResult;
private PublishSubject<RxLoginResult> mLoginEmitter;
static UMAuthListener STATIC_LISTENER;
private RxLogin(Activity activity){
this.activity = activity;
mLoginResult = new RxLoginResult();
STATIC_LISTENER = this;
mLoginEmitter = PublishSubject.create();
}
public Observable<RxLoginResult> doOauthVerify(final RxLoginPlatform platform) {
mShareResult.setPlatform(platform);
Intent intent = new Intent(activity,RxLoginActivity.class);
intent.putExtra(PLATFORM_KEY,platform);
activity.startActivity(intent);
activity.overridePendingTransition(0,0);
List<Observable<RxLoginResult>> list = new ArrayList<>(1);
list.add(mLoginEmitter);
return Observable.concat(list);
}
@Override
public void onStart(SHARE_MEDIA share_media) {
}
@Override
public void onComplete(SHARE_MEDIA share_media, int i, Map<String, String> map) {
mLoginResult.setIsSucceed(true);
mLoginResult.setUserInfo(map);
mLoginEmitter.onNext(mLoginResult);
}
@Override
public void onError(SHARE_MEDIA share_media, int i, Throwable throwable) {
mLoginResult.setIsSucceed(false);
mLoginEmitter.onNext(mLoginResult);
throwable.printStackTrace();
}
@Override
public void onCancel(SHARE_MEDIA share_media, int i) {
mLoginResult.setIsSucceed(false);
mLoginEmitter.onNext(mLoginResult);
}
static final SHARE_MEDIA platformChange(RxLoginPlatform platform){
switch (platform){
case Platform_QQ:
return SHARE_MEDIA.QQ;
case Platform_WX:
return SHARE_MEDIA.WEIXIN;
}
return SHARE_MEDIA.WEIXIN;
}
public static RxLogin create(Activity activity){
return new RxLogin(activity);
}
}
看下最后的使用:
public class UserLoginActivity extends Activity implements UMAuthListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// RxLogin 使用方式如下:
RxLogin.create(this)
// 第三QQ登錄
.doOauthVerify(RxLoginPlatform.Platform_QQ)
.subscribe(new Consumer<RxLoginResult>() {
@Override
public void accept(RxLoginResult rxLoginResult) throws Exception {
if(rxLoginResult.isSucceed()){
// 調(diào)用后臺接口,請求登錄
}
}
});
}
}