原文链接

在第1,2,3篇中,我大概介绍了RxJava是怎么使用的。下面我会介绍如何在Android中使用RxJava.

RxAndroid

RxAndroid是RxJava的一个针对Android平台的扩展。它包含了一些能够简化Android开发的工具。

首先,AndroidSchedulers提供了针对Android的线程系统的调度器。需要在UI线程中运行某些代码?很简单,只需要使用AndroidSchedulers.mainThread():

retrofitService.getImage(url)    .subscribeOn(Schedulers.io())    .observeOn(AndroidSchedulers.mainThread())    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

如果你已经创建了自己的Handler,你可以使用HandlerThreadScheduler1将一个调度器链接到你的handler上。

接着要介绍的就是AndroidObservable,它提供了跟多的功能来配合Android的生命周期。bindActivity()和bindFragment()方法默认使用AndroidSchedulers.mainThread()来执行观察者代码,这两个方法会在Activity或者Fragment结束的时候通知被观察者停止发出新的消息。

AndroidObservable.bindActivity(this, retrofitService.getImage(url))    .subscribeOn(Schedulers.io())    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);
我自己也很喜欢AndroidObservable.fromBroadcast()方法,它允许你创建一个类似BroadcastReceiver的Observable对象。下面的例子展示了如何在网络变化的时候被通知到:
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);AndroidObservable.fromBroadcast(context, filter)    .subscribe(intent -> handleConnectivityChange(intent));

最后要介绍的是ViewObservable,使用它可以给View添加了一些绑定。如果你想在每次点击view的时候都收到一个事件,可以使用ViewObservable.clicks(),或者你想监听TextView的内容变化,可以使用ViewObservable.text()。
ViewObservable.clicks(mCardNameEditText, false)    .subscribe(view -> handleClick(view));

Retrofit

大名鼎鼎的Retrofit库内置了对RxJava的支持。通常调用发可以通过使用一个Callback对象来获取异步的结果:

@GET("/user/{id}/photo")void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

使用RxJava,你可以直接返回一个Observable对象。
@GET("/user/{id}/photo")Observable<Photo> getUserPhoto(@Path("id") int id);

现在你可以随意使用Observable对象了。你不仅可以获取数据,还可以进行变换。

Retrofit对Observable的支持使得它可以很简单的将多个REST请求结合起来。比如我们有一个请求是获取照片的,还有一个请求是获取元数据的,我们就可以将这两个请求并发的发出,并且等待两个结果都返回之后再做处理:

Observable.zip(    service.getUserPhoto(id),    service.getPhotoMetadata(id),    (photo, metadata) -> createPhotoWithData(photo, metadata))    .subscribe(photoWithData -> showPhoto(photoWithData));

在第二篇里我展示过一个类似的例子(使用flatMap())。这里我只是想展示以下使用RxJava+Retrofit可以多么简单地组合多个REST请求。

遗留代码,运行极慢的代码

Retrofit可以返回Observable对象,但是如果你使用的别的库并不支持这样怎么办?或者说一个内部的内码,你想把他们转换成Observable的?有什么简单的办法没?

绝大多数时候Observable.just() 和 Observable.from() 能够帮助你从遗留代码中创建 Observable 对象:

private Object oldMethod() { ... }public Observable<Object> newMethod() {    return Observable.just(oldMethod());}

上面的例子中如果oldMethod()足够快是没有什么问题的,但是如果很慢呢?调用oldMethod()将会阻塞住他所在的线程。

为了解决这个问题,可以参考我一直使用的方法–使用defer()来包装缓慢的代码:

private Object slowBlockingMethod() { ... }public Observable<Object> newMethod() {    return Observable.defer(() -> Observable.just(slowBlockingMethod()));}

现在,newMethod()的调用不会阻塞了,除非你订阅返回的observable对象。

生命周期

我把最难的不分留在了最后。如何处理Activity的生命周期?主要就是两个问题:
1.在configuration改变(比如转屏)之后继续之前的Subscription。

比如你使用Retrofit发出了一个REST请求,接着想在listview中展示结果。如果在网络请求的时候用户旋转了屏幕怎么办?你当然想继续刚才的请求,但是怎么搞?

2.Observable持有Context导致的内存泄露

这个问题是因为创建subscription的时候,以某种方式持有了context的引用,尤其是当你和view交互的时候,这太容易发生!如果Observable没有及时结束,内存占用就会越来越大。
不幸的是,没有银弹来解决这两个问题,但是这里有一些指导方案你可以参考。

第一个问题的解决方案就是使用RxJava内置的缓存机制,这样你就可以对同一个Observable对象执行unsubscribe/resubscribe,却不用重复运行得到Observable的代码。cache() (或者 replay())会继续执行网络请求(甚至你调用了unsubscribe也不会停止)。这就是说你可以在Activity重新创建的时候从cache()的返回值中创建一个新的Observable对象。

Observable<Photo> request = service.getUserPhoto(id).cache();Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));// ...When the Activity is being recreated...sub.unsubscribe();// ...Once the Activity is recreated...request.subscribe(photo -> handleUserPhoto(photo));

注意,两次sub是使用的同一个缓存的请求。当然在哪里去存储请求的结果还是要你自己来做,和所有其他的生命周期相关的解决方案一延虎,必须在生命周期外的某个地方存储。(retained fragment或者单例等等)。

第二个问题的解决方案就是在生命周期的某个时刻取消订阅。一个很常见的模式就是使用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的订阅。

private CompositeSubscription mCompositeSubscription    = new CompositeSubscription();private void doSomething() {    mCompositeSubscription.add(        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))        .subscribe(s -> System.out.println(s)));}@Overrideprotected void onDestroy() {    super.onDestroy();    mCompositeSubscription.unsubscribe();}

你可以在Activity/Fragment的基类里创建一个CompositeSubscription对象,在子类中使用它。

注意! 一旦你调用了 CompositeSubscription.unsubscribe(),这个CompositeSubscription对象就不可用了, 如果你还想使用CompositeSubscription,就必须在创建一个新的对象了。

两个问题的解决方案都需要添加额外的代码,如果谁有更好的方案,欢迎告诉我。

总结

RxJava还是一个很新的项目,RxAndroid更是。RxAndroid目前还在活跃开发中,也没有多少好的例子。我打赌一年之后我的一些建议就会被看做过时了。

深入浅出RxJava四-在Android中使用响应式编程




RxAndroid是RxJava的扩展, 优雅地处理异步请求. RxAndroid配合Lambda表达式, 精简处理回调, 使程序更具有可读性. Rx作为Android最优秀的开源库之一, 极大地提高生产力, 我们需要掌握. 本文由浅入深, 介绍一些常见的使用方法, 并附有源码.

更多:http://www.wangchenlong.org/

本文代码的GitHub下载地址.

要点包含:
(1) 链式表达式的使用方式.
(2) Lambda的应用.
(3) Rx处理网络请求.
(4) 线程自动管理, 防止内存泄露.
(5) RxBinding绑定控件的异步事件.

基础

当然, 从一个崭新的HelloWorld项目开始.

添加Gradle配置.

123
compile 'com.jakewharton:butterknife:7.0.1'compile 'io.reactivex:rxandroid:1.1.0' // RxAndroidcompile 'io.reactivex:rxjava:1.1.0' // 推荐同时加载RxJava

RxAndroid是本文的核心依赖, 同时添加RxJava. 还有ButterKnife注解库.

Lambda表达式, 是写出优雅代码的关键,参考.

123456789101112
plugins {    id "me.tatarka.retrolambda" version "3.2.4"}android {...    compileOptions {        sourceCompatibility JavaVersion.VERSION_1_8        targetCompatibility JavaVersion.VERSION_1_8    }}

Gradle 2.1+以上, 配置非常简单, 添加一个plugin和一个Java1.8兼容即可.

从主MainActivity跳转至SimpleActivity.

1234567891011121314151617181920212223242526272829303132333435363738
/** * 主Activity, 用于跳转各个模块. * * @author wangchenlong */public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    // 跳转简单的页面    public void gotoSimpleModule(View view) {        startActivity(new Intent(this, SimpleActivity.class));    }    // 跳转复杂的页面    public void gotoMoreModule(View view) {        startActivity(new Intent(this, MoreActivity.class));    }    // 跳转Lambda的页面    public void gotoLambdaModule(View view) {        startActivity(new Intent(this, LambdaActivity.class));    }    // 跳转网络的页面    public void gotoNetworkModule(View view) {        startActivity(new Intent(this, NetworkActivity.class));    }    // 跳转线程安全的页面    public void gotoSafeModule(View view) {        startActivity(new Intent(this, SafeActivity.class));    }}

SimpleActivity中, 创建一个观察者, 收到字符串的返回.

1234567891011121314
    // 观察事件发生    Observable.OnSubscribe mObservableAction = new Observable.OnSubscribe<String>() {        @Override public void call(Subscriber<? super String> subscriber) {            subscriber.onNext(sayMyName()); // 发送事件            subscriber.onCompleted(); // 完成事件        }    };...    // 创建字符串    private String sayMyName() {        return "Hello, I am your friend, Spike!";    }

创建两个订阅者, 使用字符串输出信息.

1234567891011121314151617181920212223242526272829
// 订阅者, 接收字符串, 修改控件Subscriber<String> mTextSubscriber = new Subscriber<String>() {    @Override public void onCompleted() {    }    @Override public void onError(Throwable e) {    }    @Override public void onNext(String s) {        mTvText.setText(s); // 设置文字    }};// 订阅者, 接收字符串, 提示信息Subscriber<String> mToastSubscriber = new Subscriber<String>() {    @Override public void onCompleted() {    }    @Override public void onError(Throwable e) {    }    @Override public void onNext(String s) {        Toast.makeText(SimpleActivity.this, s, Toast.LENGTH_SHORT).show();    }};

在页面中, 观察者接收信息, 发送至主线程AndroidSchedulers.mainThread(), 再传递给订阅者, 由订阅者最终处理消息. 接收信息可以是同步, 也可以是异步.

1234567891011121314
@Override protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_simple);    ButterKnife.bind(this);    // 注册观察活动    @SuppressWarnings("unchecked")    Observable<String> observable = Observable.create(mObservableAction);    // 分发订阅信息    observable.observeOn(AndroidSchedulers.mainThread());    observable.subscribe(mTextSubscriber);    observable.subscribe(mToastSubscriber);}

最基础的RxAndroid使用.


更多

我们已经熟悉了初步的使用方式, 在接着学习一些其他方法, 如

just: 获取输入数据, 直接分发, 更加简洁, 省略其他回调.
from: 获取输入数组, 转变单个元素分发.
map: 映射, 对输入数据进行转换, 如大写.
flatMap: 增大, 本意就是增肥, 把输入数组映射多个值, 依次分发.
reduce: 简化, 正好相反, 把多个数组的值, 组合成一个数据.

来看看这个示例, 设置两个不同类型数组, 作为输入源, 根据不同情况分发数据.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
/** * 更多的RxAndroid的使用方法. * <p> * Created by wangchenlong on 15/12/30. */public class MoreActivity extends Activity {    @Bind(R.id.simple_tv_text) TextView mTvText;    final String[] mManyWords = {"Hello", "I", "am", "your", "friend", "Spike"};    final List<String> mManyWordList = Arrays.asList(mManyWords);    // Action类似订阅者, 设置TextView    private Action1<String> mTextViewAction = new Action1<String>() {        @Override public void call(String s) {            mTvText.setText(s);        }    };    // Action设置Toast    private Action1<String> mToastAction = new Action1<String>() {        @Override public void call(String s) {            Toast.makeText(MoreActivity.this, s, Toast.LENGTH_SHORT).show();        }    };    // 设置映射函数    private Func1<List<String>, Observable<String>> mOneLetterFunc = new Func1<List<String>, Observable<String>>() {        @Override public Observable<String> call(List<String> strings) {            return Observable.from(strings); // 映射字符串        }    };    // 设置大写字母    private Func1<String, String> mUpperLetterFunc = new Func1<String, String>() {        @Override public String call(String s) {            return s.toUpperCase(); // 大小字母        }    };    // 连接字符串    private Func2<String, String, String> mMergeStringFunc = new Func2<String, String, String>() {        @Override public String call(String s, String s2) {            return String.format("%s %s", s, s2); // 空格连接字符串        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_simple);        ButterKnife.bind(this);        // 添加字符串, 省略Action的其他方法, 只使用一个onNext.        Observable<String> obShow = Observable.just(sayMyName());        // 先映射, 再设置TextView        obShow.observeOn(AndroidSchedulers.mainThread())                .map(mUpperLetterFunc).subscribe(mTextViewAction);        // 单独显示数组中的每个元素        Observable<String> obMap = Observable.from(mManyWords);        // 映射之后分发        obMap.observeOn(AndroidSchedulers.mainThread())                .map(mUpperLetterFunc).subscribe(mToastAction);        // 优化过的代码, 直接获取数组, 再分发, 再合并, 再显示toast, Toast顺次执行.        Observable.just(mManyWordList)                .observeOn(AndroidSchedulers.mainThread())                .flatMap(mOneLetterFunc)                .reduce(mMergeStringFunc)                .subscribe(mToastAction);    }    // 创建字符串    private String sayMyName() {        return "Hello, I am your friend, Spike!";    }}

这次简化调用代码, 因为有时候我们对异常并不是很关心,
只要能catch异常即可, 因此流仅仅关注真正需要的部分.

输入字符串, 变换大写, 输出至控件中显示.

123456
// 添加字符串, 省略Action的其他方法, 只使用一个onNext.Observable<String> obShow = Observable.just(sayMyName());// 先映射, 再设置TextViewobShow.observeOn(AndroidSchedulers.mainThread())        .map(mUpperLetterFunc).subscribe(mTextViewAction);

just可以非常简单的获取任何数据, 分发时, 选择使用的线程.
map是对输入数据加工, 转换类型, 输入Func1, 准换大写字母.
Func1代表使用一个参数的函数, 前面是参数, 后面是返回值.
Action1代表最终动作, 因而不需要返回值, 并且一个参数.

输入数组, 单独分发数组中每一个元素, 转换大写, 输入Toast连续显示.

123456
// 单独显示数组中的每个元素Observable<String> obMap = Observable.from(mManyWords);// 映射之后分发obMap.observeOn(AndroidSchedulers.mainThread())        .map(mUpperLetterFunc).subscribe(mToastAction);

from是读取数组中的值, 每次单独分发, 并分发多次, 其余类似.

输入数组, 映射为单独分发, 并组合到一起, 集中显示.

123456
// 优化过的代码, 直接获取数组, 再分发, 再合并, 再显示toast, Toast顺次执行.Observable.just(mManyWordList)        .observeOn(AndroidSchedulers.mainThread())        .flatMap(mOneLetterFunc)        .reduce(mMergeStringFunc)        .subscribe(mToastAction);

这次是使用just分发数组, 则分发数据就是数组, 并不是数组中的元素.
flatMap把数组转换为单独分发,Func1内部使用from拆分数组.
reduce把单独分发数据集中到一起, 再统一分发, 使用Func2.
最终使用Action1显示获得数据. 本次代码也更加简洁.

由此我们可以观察到, Rx的写法可以是多种多样, 合理的写法会更加优雅.

效果

网络请求

Retrofit是网络请求库, 刚推出2.0版本. Rx的一个核心应用就是处理异步网络请求, 结合Retrofit, 会更加方便和简洁.参考.

引入库

1234567
compile 'com.android.support:recyclerview-v7:23.1.1' // RecyclerViewcompile 'com.squareup.retrofit:retrofit:2.0.0-beta2' // Retrofit网络处理compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' // Retrofit的rx解析库compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' // Retrofit的gson库compile 'com.squareup.picasso:picasso:2.5.2' // Picasso网络图片加载

recyclerviewpicasso为了显示.retrofit系列是网络请求.

主页使用一个简单的列表视图, 展示Github的用户信息.

1234567891011121314151617181920212223242526272829303132333435
/** * Rx的网络请求方式 * <p> * Created by wangchenlong on 15/12/31. */public class NetworkActivity extends Activity {    @Bind(R.id.network_rv_list) RecyclerView mRvList; // 列表    @Override protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_network);        ButterKnife.bind(this);        // 设置Layout管理器        LinearLayoutManager layoutManager = new LinearLayoutManager(this);        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);        mRvList.setLayoutManager(layoutManager);        // 设置适配器        UserListAdapter adapter = new UserListAdapter(this::gotoDetailPage);        NetworkWrapper.getUsersInto(adapter);        mRvList.setAdapter(adapter);    }    // 点击的回调    public interface UserClickCallback {        void onItemClicked(String name);    }    // 跳转到库详情页面    private void gotoDetailPage(String name) {        startActivity(NetworkDetailActivity.from(NetworkActivity.this, name));    }}

在列表中提供点击用户信息跳转至用户详情.
NetworkWrapper.getUsersInto(adapter)请求网络, 设置适配器信息.

关键部分, 适配器, 其中包含ViewHolder类和数据类.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
/** * 显示列表 * <p> * Created by wangchenlong on 15/12/31. */public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.UserViewHolder> {    private List<GitHubUser> mUsers; // 用户名集合    private NetworkActivity.UserClickCallback mCallback; // 用户点击项的回调    public UserListAdapter(NetworkActivity.UserClickCallback callback) {        mUsers = new ArrayList<>();        mCallback = callback;    }    public void addUser(GitHubUser user) {        mUsers.add(user);        notifyItemInserted(mUsers.size() - 1); // 最后一位    }    @Override public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View item = LayoutInflater.from(parent.getContext())                .inflate(R.layout.item_network_user, parent, false);        return new UserViewHolder(item, mCallback);    }    @Override public void onBindViewHolder(UserViewHolder holder, int position) {        holder.bindTo(mUsers.get(position));    }    @Override public int getItemCount() {        return mUsers.size();    }    // Adapter的ViewHolder    public static class UserViewHolder extends RecyclerView.ViewHolder {        @Bind(R.id.network_item_iv_user_picture) ImageView mIvUserPicture;        @Bind(R.id.network_item_tv_user_name) TextView mTvUserName;        @Bind(R.id.network_item_tv_user_login) TextView mTvUserLogin;        @Bind(R.id.network_item_tv_user_page) TextView mTvUserPage;        public UserViewHolder(View itemView, NetworkActivity.UserClickCallback callback) {            super(itemView);            ButterKnife.bind(this, itemView);            // 绑定点击事件            itemView.setOnClickListener(v ->                    callback.onItemClicked(mTvUserLogin.getText().toString()));        }        // 绑定数据        public void bindTo(GitHubUser user) {            mTvUserName.setText(user.name);            mTvUserLogin.setText(user.login);            mTvUserPage.setText(user.repos_url);            Picasso.with(mIvUserPicture.getContext())                    .load(user.avatar_url)                    .placeholder(R.drawable.ic_person_black_24dp)                    .into(mIvUserPicture);        }    }    // 用户类, 名称必须与Json解析相同    public static class GitHubUser {        public String login;        public String avatar_url;        public String name;        public String repos_url;    }}

添加数据addUser, 其中notifyItemInserted通知更新.
可以自动生成Json解析类的网站.

首先创建`Retrofit``服务, 通过服务获取数据, 再依次分发给适配器.

123456789101112131415161718192021222324252627282930313233
/** * 用户获取类 * <p> * Created by wangchenlong on 15/12/31. */public class NetworkWrapper {    private static final String[] mFamousUsers =            {"SpikeKing", "JakeWharton", "rock3r", "Takhion", "dextorer", "Mariuxtheone"};    // 获取用户信息    public static void getUsersInto(final UserListAdapter adapter) {        GitHubService gitHubService =                ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);        Observable.from(mFamousUsers)                .flatMap(gitHubService::getUserData)                .subscribeOn(Schedulers.newThread())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(adapter::addUser);    }    // 获取库信息    public static void getReposInfo(final String username, final RepoListAdapter adapter) {        GitHubService gitHubService =                ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);        gitHubService.getRepoData(username)                .flatMap(Observable::from)                .subscribeOn(Schedulers.newThread())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(adapter::addRepo);    }}

网络请求无法在主线程上执行, 需要启动异步线程, 如Schedulers.newThread().
使用工厂模式ServiceFactory创建服务, 也可以单独创建服务.

创建Retrofit服务的工厂类.

123456789101112131415
/** * 工厂模式 * <p> * Created by wangchenlong on 15/12/31. */public class ServiceFactory {    public static <T> T createServiceFrom(final Class<T> serviceClass, String endpoint) {        Retrofit adapter = new Retrofit.Builder()                .baseUrl(endpoint)                .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 添加Rx适配器                .addConverterFactory(GsonConverterFactory.create()) // 添加Gson转换器                .build();        return adapter.create(serviceClass);    }}

这是Retrofit 2.0的写法, 注意需要添加Rx和Gson的解析.

设置网络请求的Url.

12345678910111213141516
/** * GitHub的服务 * <p> * Created by wangchenlong on 15/12/31. */public interface GitHubService {    String ENDPOINT = "https://api.github.com";    // 获取个人信息    @GET("/users/{user}")    Observable<UserListAdapter.GitHubUser> getUserData(@Path("user") String user);    // 获取库, 获取的是数组    @GET("/users/{user}/repos")    Observable<RepoListAdapter.GitHubRepo[]> getRepoData(@Path("user") String user);}

显示用户

详情页面与主页类似, 参考代码, 不做细说.

线程安全

Rx的好处之一就是可以防止内存泄露, 即根据页面生命周期, 处理异步线程的结束. 可以使用RxLifecycle库处理生命周期.

Activity类继承RxAppCompatActivity, 替换AppCompatActivity.

启动一个循环线程.

1234567891011121314151617181920212223242526272829303132
/** * Rx的线程安全 * <p> * Created by wangchenlong on 15/12/31. */public class SafeActivity extends RxAppCompatActivity {    private static final String TAG = "DEBUG-WCL: " + SafeActivity.class.getSimpleName();    @Bind(R.id.simple_tv_text) TextView mTvText;    @Override protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_simple);        ButterKnife.bind(this);        Observable.interval(1, TimeUnit.SECONDS)                .observeOn(AndroidSchedulers.mainThread())                .compose(bindToLifecycle()) // 管理生命周期, 防止内存泄露                .subscribe(this::showTime);    }    private void showTime(Long time) {        mTvText.setText(String.valueOf("时间计数: " + time));        Log.d(TAG, "时间计数器: " + time);    }    @Override    protected void onPause() {        super.onPause();        Log.w(TAG, "页面关闭!");    }}

继承RxAppCompatActivity, 添加bindToLifecycle方法管理生命周期. 当页面onPause时, 会自动结束循环线程. 如果注释这句代码, 则会导致内存泄露.

RxBinding

RxBinding是Rx中处理控件异步调用的方式, 也是由Square公司开发, Jake负责编写. 通过绑定组件, 异步获取事件, 并进行处理. 编码风格非常优雅.

除了RxJava, 再添加RxBinding的依赖.

1234
// RxBindingcompile 'com.jakewharton.rxbinding:rxbinding:0.3.0'compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'

Toolbar和Fab, 两个较新的控件.

123456789101112131415161718192021222324252627282930313233343536
<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical" tools:context=".BindingActivity">    <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">        <android.support.v7.widget.Toolbar android:id="@+id/rxbinding_t_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:popupTheme="@style/AppTheme.PopupOverlay" tools:targetApi="21"/>    </android.support.design.widget.AppBarLayout>    <include layout="@layout/content_rxbinding"/>    <android.support.design.widget.FloatingActionButton android:id="@+id/rxbinding_fab_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email"/></android.support.design.widget.CoordinatorLayout>

两个EditText控件, 对比传统方法和RxBinding方法.

12345678910111213141516171819202122232425262728293031323334353637383940
<?xml version="1.0" encoding="utf-8"?><LinearLayout 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" android:orientation="vertical" android:padding="@dimen/activity_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".BindingActivity" tools:showIn="@layout/activity_binding">    <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/usual_approach"/>    <EditText android:id="@+id/rxbinding_et_usual_approach" android:layout_width="match_parent" android:layout_height="48dp" android:hint="@null"/>    <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/reactive_approach"/>    <EditText android:id="@+id/rxbinding_et_reactive_approach" android:layout_width="match_parent" android:layout_height="48dp" android:hint="@null"/>    <TextView android:id="@+id/rxbinding_tv_show" android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout>

使用ButterKnife注入控件, 使用RxBinding方式绑定控件, 异步监听事件.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
/** * Rx绑定 * <p> * Created by wangchenlong on 16/1/25. */public class BindingActivity extends AppCompatActivity {    @Bind(R.id.rxbinding_t_toolbar) Toolbar mTToolbar;    @Bind(R.id.rxbinding_et_usual_approach) EditText mEtUsualApproach;    @Bind(R.id.rxbinding_et_reactive_approach) EditText mEtReactiveApproach;    @Bind(R.id.rxbinding_tv_show) TextView mTvShow;    @Bind(R.id.rxbinding_fab_fab) FloatingActionButton mFabFab;    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_binding);        ButterKnife.bind(this);        initToolbar(); // 初始化Toolbar        initFabButton(); // 初始化Fab按钮        initEditText(); // 初始化编辑文本    }    // 初始化Toolbar    private void initToolbar() {        // 添加菜单按钮        setSupportActionBar(mTToolbar);        ActionBar actionBar = getSupportActionBar();        // 添加浏览按钮        if (actionBar != null) {            actionBar.setDisplayHomeAsUpEnabled(true);        }        RxToolbar.itemClicks(mTToolbar).subscribe(this::onToolbarItemClicked);        RxToolbar.navigationClicks(mTToolbar).subscribe(this::onToolbarNavigationClicked);    }    // 点击Toolbar的项    private void onToolbarItemClicked(MenuItem menuItem) {        String m = "点击\"" + menuItem.getTitle() + "\"";        Toast.makeText(this, m, Toast.LENGTH_SHORT).show();    }    // 浏览点击    private void onToolbarNavigationClicked(Void v) {        Toast.makeText(this, "浏览点击", Toast.LENGTH_SHORT).show();    }    @Override public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.menu_rxbinding, menu);        return super.onCreateOptionsMenu(menu);    }    // 初始化Fab按钮    private void initFabButton() {        RxView.clicks(mFabFab).subscribe(this::onFabClicked);    }    // 点击Fab按钮    private void onFabClicked(Void v) {        Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "点击Snackbar", Snackbar.LENGTH_SHORT);        snackbar.show();        RxSnackbar.dismisses(snackbar).subscribe(this::onSnackbarDismissed);    }    // 销毁Snackbar, event参考{Snackbar}    private void onSnackbarDismissed(int event) {        String text = "Snackbar消失代码:" + event;        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();    }    // 初始化编辑文本    private void initEditText() {        // 正常方式        mEtUsualApproach.addTextChangedListener(new TextWatcher() {            @Override            public void beforeTextChanged(CharSequence s, int start, int count, int after) {            }            @Override public void onTextChanged(CharSequence s, int start, int before, int count) {                mTvShow.setText(s);            }            @Override public void afterTextChanged(Editable s) {            }        });        // Rx方式        RxTextView.textChanges(mEtReactiveApproach).subscribe(mTvShow::setText);    }}

Toolbar使用RxToolbar监听点击事件; Snackbar使用RxSnackbar监听;
EditText使用RxTextView监听; 其余使用RxView监听.

OK, That’s all. Enjoy it!

原始地址:
http://www.wangchenlong.org/2016/03/20/1603/207-rxjava-first/
欢迎Follow我的GitHub, 关注我的简书,微博,CSDN,掘金,Slides.
我已委托“维权骑士”为我的文章进行维权行动. 未经授权, 禁止转载, 授权或合作请留言.







更多相关文章

  1. Android退出当前应用程序的方法
  2. Android(安卓)ANR异常及解决方法
  3. 【Android】Handler机制源码解析
  4. android耗时任务_handler
  5. Android(安卓)面试题
  6. Android(安卓)SQLite使用详解和多线程并发访问
  7. Android实现文件上传下载【xUtils工具实现】
  8. Android(安卓)高手进阶教程(十三)之----Android(安卓)数据库SQLi
  9. cocos2d jsb 打包 Android(安卓)APK

随机推荐

  1. 一个问题解决。开心。哈哈。。。
  2. Android数据库操作总结
  3. [置顶] Android热补丁技术—dexposed原理
  4. Java / Android(安卓)监控进程Crash异常
  5. [置顶] 安卓视频播放器 一行代码快速实现
  6. 打造Android的中文Siri语音助手(一)——
  7. Android静态代码扫描-自定义规则
  8. 对于android触摸事件模型的一些理解
  9. AsyncTask 研究一 怎么使用
  10. Android之可以做的两件坏事---破解锁屏密