美文网首页Android开发Android开发经验谈
Live-client-2-RxJava、Retrofit、Ro

Live-client-2-RxJava、Retrofit、Ro

作者: G63HH | 来源:发表于2019-08-16 20:45 被阅读7次

    在Android项目中,采用了MVP的架构,MVP架构主要是为了解决以往MVC架构下,在Activity中处理业务逻辑导致的耦合性强的问题。MVP架构的介绍。在上一篇文章(Live-client-1-UI界面的设计)中,已经完成大部分的UI界面的设计,再编写对应的Activity和fragment就大致完成了MVP中的View视图层,接下来要做的是Model层和Presenter层的设计和实现。

    在项目中选用了Room,这个Google官方的数据库框架,也是Jetpack架构组件库中的一员,通常和Databinding、LiveData、ViewModel框架使用,实现MVVM架构。但是在本项目中并没有这么做, 而是将Room替代以往复杂的Sqlite操作,并利用Room数据库框架提供可以直接返回RxJava的Flowable可观察对象的特性,来实现数据库的访问,并返回Flowable可观察对象提供给Rxjava进行处理。

    既然提及到了Rxjava,那么Rxjava又是什么呢?Rxjava是一个基于事件、通过使用观察者序列来编写异步代码的库。Rxjava将链式编程风格和异步结合在一起,并基于观察者模式。在Android中使用Rxjava,能快速实现线程切换、异步回调、消息处理等日常编码中出现的难题,同时使得代码逻辑更加清晰明了。

    Retrofit的官方描述

    A type-safe HTTP client for Android and Java

    一种适用于Android和Java的类型安全的HTTP客户端。通过官方的介绍,可以得知Retrofit也是支持链式调用,同时也支持将请求的结果转为Flowable可观察对象,因此通过Retrofit网络框架请求得来的网络数据也可以通过Rxjava来进行处理。

    于是,我们就可以通过Rxjava来处理本地数据库和网络请求得到的数据,供Presenter层进行处理。

    Model层

    Model层要做的东西其实还挺多的:

    1. 首先要定义实体类;
    2. 根据实体类创建Room数据库框架所需要的Dao、Database,完成数据库的定义和创建;
    3. 创建数据接口,实现本地、网络数据接口,最后创建数据仓库Repository类来组合本地数据和网络数据。
    mvp-1.png

    但是在这个项目中,我将网络请求部分从Repository中分离,直接提供接口给Presenter,这样做会加重Presenter层的负担,但是在逻辑上更加清晰,明确需要从网络中请求时就直接调用retrofit的接口,而不是向以往一样从repository中调用网络数据接口发起retrofit的请求。尽管逻辑清晰了, 但是会使得Model和Presenter的耦合性增加,因此不太推荐这种写法,在后期可能会对该部分进行重构。


    mvp-2.png

    Model目录如下:


    Model目录.png

    实体类

    定义一个User实体类,包含用户名、账号、密码三个属性,同时作为Room框架中的实体,要添加@Entity注解。

    **
     * 用户表
     * @author Ljh 2019/7/10 15:49
     */
    @Entity
    public class User {
    
        private String name;
    
        @NonNull
        @PrimaryKey
        private String account;
    
        @NonNull
        private String password;
    
        public User() {
        }
    
        @Ignore
        public User(String name, @NonNull String phone, @NonNull String password) {
            this.name = name;
            this.account = phone;
            this.password = password;
        }
    
       // ...  get/set方法省略
    }
    

    Dao

    UserDao编写数据访问的相关方法及返回值,要添加@Dao注解将类标记为数据访问对象。代码示例

    /**
     * @author Ljh 2019/7/10 16:26
     */
    @Dao
    public interface UserDao {
    
        /**
         * 按照主键phone查询User,通过rxjava的single
         *
         * @param account 用户账号:手机/email
         * @return 返回单个user,如果找不到onError(EmptyResultSetException.class)
         */
        @Query("SELECT * FROM user where account =:account")
        Single<User> getUserByPhone(String account);
    
        /**
         * 添加用户
         *
         * @param user 用户
         * @return 返回
         */
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        Completable insertUser(User user);
    
        /**
         * 更新用户
         *
         * @param user 用户信息
         * @return 返回成功与否
         */
        @Update(onConflict = OnConflictStrategy.REPLACE)
        Completable updateUser(User user);
    
        /**
         * 删除用户
         *
         * @param account 用户账号 手机/email
         * @return 返回成功与否
         */
        @Query("DELETE FROM user where account = :account")
        Completable deleteUser(String account);
    }
    

    在最初接触Room框架时,Room仅仅支持对@Query查询出来的数据提供Flowable可观察对象,在Version 2.1.0-alpha01时,就添加对@Insert、@Update、@Delete等操作结果的观察,也就是说在Version 2.1.0-alpha01后的版本的Room配合Rxjava使用才不会那么别扭,可以观察所有的数据库操作,然后放入Rxjava中操作。


    Room-Version 2.1.0-alpha01.png

    数据库创建

    创建数据库时要使用单例模式,避免创建多个数据库实例,导致内存泄漏。需要使用@Database()注解配置数据实体类、数据库版本等信息。

    /**
     * @author Ljh 2019/7/10 17:42
     */
    @Database(entities = {User.class, LiveInfo.class}, version = 1, exportSchema = false)
    public abstract class LiveDataBase extends RoomDatabase {
        public static final String TAG = LiveDataBase.class.getSimpleName();
        public static LiveDataBase INSTANCE;
    
        public abstract UserDao userDao();
    
        public abstract LiveInfoDao liveInfoDao();
    
        private static final Object sLock = new Object();
    
        public static LiveDataBase getInstance() {
            synchronized (sLock) {
                if (INSTANCE == null) {
                    Log.d(TAG, "Create Database");
                    INSTANCE = Room.databaseBuilder(MyApplication.getMyApplicationContext(),
                            LiveDataBase.class, "Live.db")
                            .build();
                }
                return INSTANCE;
            }
        }
    }
    

    完成以上步骤之后,就完成了实体类、数据表、数据库和数据库CRUD的接口。接下来要看数据接口怎么设计。

    数据接口与数据仓库Repository

    编写数据接口的目的是为了在数据库CRUD接口之上再次进行封装、处理,然后提供给Presenter层使用,其本质还是CRUD等操作。UserDataSource如下:

    public interface UserDataSource {
    
        Single<User> getUserByPhone(String account);
    
        Completable insertUser(User user);
    
        Completable updateUser(User user);
    
        Completable deleteUser(String account);
    }
    

    本地数据源UserLocalDataSource:

    public class UserLocalDataSource implements UserDataSource {
    
        private static UserLocalDataSource INSTANCE;
    
        private UserDao mUserDao;
    
    
        public UserLocalDataSource(UserDao userDao) {
            this.mUserDao = userDao;
        }
    
        public static UserLocalDataSource getInstance(UserDao userDao) {
            if (INSTANCE == null) {
                INSTANCE = new UserLocalDataSource(userDao);
            }
            return INSTANCE;
        }
    
        public static void destroyInstance() {
            INSTANCE = null;
        }
    
        @Override
        public Single<User> getUserByPhone(String account) {
            return mUserDao.getUserByPhone(account)
                    .subscribeOn(Schedulers.io());
        }
    
        @Override
        public Completable insertUser(User user) {
            return mUserDao.insertUser(user)
                    .subscribeOn(Schedulers.io());
        }
    
        @Override
        public Completable updateUser(User user) {
            return mUserDao.updateUser(user)
                    .subscribeOn(Schedulers.io());
        }
    
        @Override
        public Completable deleteUser(String account) {
            return mUserDao.deleteUser(account)
                    .subscribeOn(Schedulers.io());
        }
    }
    

    UserRepository数据仓库:

    /**
     * @author Ljh 2019/7/15 10:04
     */
    public class UserRepository implements UserDataSource {
    
        private static UserRepository INSTANCE;
    
        private final UserLocalDataSource mUserLocalDataSource;
    
        private UserRepository() {
            this.mUserLocalDataSource = UserLocalDataSource.getInstance(LiveDataBase.getInstance().userDao());
        }
    
        public static UserRepository getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new UserRepository();
            }
            return INSTANCE;
        }
    
        @Override
        public Single<User> getUserByPhone(String account) {
            return mUserLocalDataSource.getUserByPhone(account);
        }
    
        @Override
        public Completable insertUser(User user) {
            return mUserLocalDataSource.insertUser(user);
        }
    
        @Override
        public Completable updateUser(User user) {
            return mUserLocalDataSource.updateUser(user);
        }
    
        @Override
        public Completable deleteUser(String account) {
            return mUserLocalDataSource.deleteUser(account);
        }
    }
    

    可以看到,所有的操作只是对数据库Dao接口的调用,再封装一个数据仓库的单例模式。如果这个简单的应用没有后续的扩展和改善的话,那么像数据仓库中不处理网络请求的写法,其实有很多部分都是冗余的,没有必要的。因为UserRepository中只是简单的调用了UserLocalDataSource,而UserLocalDataSource又只是简单的调用了数据库Dao的接口。如果添加一个UserRemoteDataSource专门用来处理网络请求及其响应数据,然后在UserRepository中进行本地、网络的逻辑处理,那么这样的Model层设计还算有点用处。

    View层与Presenter层

    View层和Presenter层的关联比较紧密,这里就一同讲述。每一个View和Presenter都有一些相同的方法,我们将该部分方法抽出,分离成接口,让每个View和Presenter来实现。

    BaseView

    在BaseView这个接口中,需要将每个Presenter和当前view进行绑定,也需要在每个View中显示Toast,于是BaseView的设计如下:

    public interface BaseView<T> {
        void setPresenter(T presenter);
        void showToast(String message);
    }
    

    通过一个模板类的设计,可以将view和不同的Presenter进行绑定。

    BasePresenter

    在BasePresenter中则较为简单,只是提供了一个start()方法,给每个Presenter进行一些初始化的操作。

    public interface BasePresenter {
        void start();
    }
    

    Login功能模块:


    login-功能模块.png

    LoginContract

    下面以登录这个功能来讲解MVP架构中,View和Presenter的实现。

    Contract的意思是契约,通过LoginContract类,来详细定义View和Presenter中所需要的方法:

    1. 在Presenter方面:首先在登录注册功能中,肯定要有一个登录方法、一个注册方法来完成这两项功能,其次,在注册的时候,需要获取验证码进行注册,于是又要定义一个发送验证码的方法。
    2. 在View方面:由于要在一个页面中转换登录和注册框,因此需要一个显示登录框的方法、一个显示注册框的方法;由于背景使用了Bing的每日一图,于是又定义一个显示每日一图的方法;在请求验证码时,通常要避免用户不断请求验证码,导致验证码的时效发生变化和避免增加服务器的负担,需要设置一个倒计时方法。

    这时,可能会有这样的疑问:为什么还要设置一个契约接口来定义上述的方法呢?其实目的是为了将View和Presenter的所有方法集中在一起,便于查看。同时,通过setPresenter()方法的实现,可以将view和presenter联系在一起,相互调用对方的方法,给对方提供调用接口,在逻辑上更加清晰。
    代码示例:

    public interface LoginContract {
    
        interface View extends BaseView<Presenter> {
            //显示Bing每日一图作为全局背景
            void showBingPicBg(String address);
    
            //显示注册框
            void showRegister();
    
            //显示登录框
            void showLogin();
    
            //对页面的返回进行设置
            void setResult(String result, String msg);
    
            //取消短信倒计时
            void cancelTimer();
        }
    
        interface Presenter extends BasePresenter {
            //使用账号密码登录
            void login(String phone, String password, String accountType);
    
            //通过user和验证码注册
            void register(User user, String code, String accountType);
    
            //发送验证码
            void sendMessage(String phone, String accountType);
        }
    }
    

    LoginFragment

    创建一个实现了LoginContract.View接口的Fragment,并通过单例模式获取该Fragment,避免重复创建View带来的花销。
    在Fragment的onCreateView方法中,通过LayoutInflater来查找和加载Fragment对应的ui布局,并给ui布局中的控件进行初始化。

    public class LoginFragment extends Fragment implements LoginContract.View, View.OnClickListener {
        public static final String TAG = LoginFragment.class.getSimpleName();
    
        private LoginContract.Presenter mPresenter;
      
        //... 省略UI控件
    
        //验证码倒计时
        private CountDownTimer timer = new CountDownTimer(60 * 1000, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                codeBt.setEnabled(false);
                codeBt.setText("(" + millisUntilFinished / 1000 + ")");
            }
    
            @Override
            public void onFinish() {
                codeBt.setEnabled(true);
                codeBt.setText("重新获取验证码");
            }
        };
    
        public static LoginFragment newInstance() {
            return new LoginFragment();
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View root = inflater.inflate(R.layout.fragment_login, container, false);
          // ... 省略控件的初始化  
            mPresenter.start();
            return root;
        }
    
        @Override
        public void showBingPicBg(final String address) {
            try {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Glide.with(getContext()).load(address).into(bgImage);
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 显示注册框
         * <p>
         * 显示验证码,显示undo返回键,修改文字
         */
        @Override
        public void showRegister() {}
    
        @Override
        public void setResult(String result, String msg) {
            if (result.equals("LOGIN_SUCCESS")) {
                Log.d(TAG, "setResult: LOGIN_SUCCESS");
                getActivity().setResult(1);
                getActivity().finish();
            } else {
                Log.d(TAG, "setResult: LOGIN_ERROR");
            }
        }
    
        /**
         * 显示登录框
         * <p>
         * 隐藏验证码、隐藏undo返回、显示登录按钮
         */
        @Override
        public void showLogin() {}
    
        @Override
        public void cancelTimer() {
            timer.cancel();
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    codeBt.setEnabled(true);
                    codeBt.setText("获取验证码");
                }
            });
        }
    
        @Override
        public void setPresenter(LoginContract.Presenter presenter) {
            mPresenter = presenter;
        }
    
        @Override
        public void showToast(final String message) {}
    
        @Override
        public void onClick(View v) {
            String account = "";
            String password = "";
            String code = "";
            switch (v.getId()) {
                case R.id.bt_login_login:
                    account = accountEt.getText().toString();
                    password = passwordEt.getText().toString();
                   //... 省略输入验证      
                    mPresenter.login(account, password, ACCOUNT_TYPE);
                    break;
                case R.id.bt_login_register:
                           //... 省略输入验证        
                            mPresenter.register(user, code, ACCOUNT_TYPE);
                    }
                    break;
                case R.id.bt_login_undo:
                    showLogin();
                    break;
                case R.id.bt_login_code:
                    //发送短信验证码
                    account = accountEt.getText().toString();
                        //验证码倒计时开始
                        timer.start();
                        mPresenter.sendMessage(account, ACCOUNT_TYPE);
                    break;
            }
        }
    }
    

    LoginPresenter

    LoginPresenter要继承LoginContract中的Presenter接口,实现接口中的功能,并在构造函数中创建一个View对象(LoginContact中的View),完成view与presenter的绑定。

    public class LoginPresenter implements LoginContract.Presenter {
        public static final String TAG = LoginPresenter.class.getSimpleName();
    
        private final LoginContract.View mView;
    
        private UserRepository mUserRepository;
    
        public LoginPresenter(LoginContract.View view) {
            mUserRepository = UserRepository.getInstance();
            mView = view;
            mView.setPresenter(this);
        }
    
        @Override
        public void login(final String account, String password, String accountType) {
            LiveService liveService = LiveService.Factory.create();
            Disposable subscribe = liveService.login(account, MD5Util.encrypt(password), accountType)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .subscribe(new Consumer<Result<String>>() {
                        @Override
                        public void accept(Result<String> stringResult) throws Exception {
                            if (stringResult.getState().equals("200")) {   //登录成功
                                mView.setResult("LOGIN_SUCCESS", stringResult.getMsg());
                                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(MyApplication.getMyApplicationContext()).edit();
                                editor.putString(Constant.SP_ACCOUNT, account);
                                editor.putBoolean(Constant.IS_LOGIN, true);
                                editor.commit();
                            } else if (stringResult.getState().equals("100")) {
                                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(MyApplication.getMyApplicationContext()).edit();
                                editor.putBoolean(Constant.IS_LOGIN, false);
                                editor.commit();
                                mView.setResult("LOGIN_ERROR", stringResult.getMsg());
                                mView.showToast(stringResult.getMsg());
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            mView.showToast("网络异常,请稍后重试");
                        }
                    });
        }
    
        @Override
        public void register(final User user, String code, String accountType) {
            user.setPassword(MD5Util.encrypt(user.getPassword()));
            LiveService liveService = LiveService.Factory.create();
            liveService.register(user.getAccount(), user.getPassword(), user.getName(), code, accountType)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .subscribe(new Consumer<Result<String>>() {
                        @Override
                        public void accept(Result<String> stringResult) throws Exception {
                            if (stringResult == null) {
                                mView.showToast("网络异常,请稍后重试");
                                return;
                            }
                            String state = stringResult.getState();
                            String msg = stringResult.getMsg();
                            if (state.equals("200")) {
                                //注册成功,将该用户保存到数据库中,并将phone保存到sp中,跳转到登录界面
                                //将sp中的phone加载到登录界面中的phone中
                                mUserRepository.insertUser(user);
                                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(MyApplication.getMyApplicationContext()).edit();
                                editor.putString(Constant.SP_ACCOUNT, user.getAccount());
                                editor.commit();
                                //子线程中能显示吗?
                                mView.showToast(msg);
                                mView.showLogin();
                            } else if (state.equals("100")) {
                                //注册失败,提醒用户注册失败原因
                                mView.showToast(msg);
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.d(TAG, "accept: " + throwable.getMessage());
                            mView.showToast("网络异常,请稍后重试");
                        }
                    });
        }
    
        @Override
        public void sendMessage(String account, String accountType) {
            if (TextUtils.isEmpty(account)) {
                mView.showToast("手机号不能为空");
                return;
            }
            LiveService liveService = LiveService.Factory.create();
            liveService.sendMessage(account, accountType)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Result<String>>() {
                        @Override
                        public void accept(Result<String> stringResult) throws Exception {
                            if (stringResult.getState().equals("200")) {
                                //获取验证码成功
                                mView.showToast("验证码已发送到您的手机");
                            } else {
                                mView.showToast(stringResult.getMsg());
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.d(TAG, "accept: " + throwable.getMessage());
                            mView.showToast("网络错误,稍后重试");
                            mView.cancelTimer();
                        }
                    });
        }
    
        @Override
        public void start() {
            loadBingPic();
        }
    
        /**
         * 加载每日一图。
         * <p>
         * 获取新的每日一图,成功,则加载到ImageView中,作为loginActivity的背景
         * 获取不成功,则加载原有的每日一图。
         */
        private void loadBingPic() {
            BingPicService bingPicService = BingPicService.Factory.create();
            bingPicService.getBingPic()
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .subscribe(new Consumer<BingResult>() {
                        @Override
                        public void accept(BingResult bingResult) throws Exception {
                            if (bingResult == null) { //获取不到新的必应每日一图,则用旧图取代
                                showOldBingPicBg();
                                return;
                            }
                            String urlTemp = bingResult.getImages().get(0).getUrl();
                            if (TextUtils.isEmpty(urlTemp)) {//获取不到新的必应每日一图,则用旧图取代
                                showOldBingPicBg();
                                return;
                            } else {
                                String newBingPicURL = "http://s.cn.bing.net" + urlTemp;
                                SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getMyApplicationContext());
                                String oldBingPicURL = preferences.getString("BING_PIC", null);
                                if (oldBingPicURL == null || !oldBingPicURL.equals(newBingPicURL)) { //地址不相等
                                    SharedPreferences.Editor editor = preferences.edit();
                                    editor.putString("BING_PIC", newBingPicURL);
                                    editor.commit();
                                }
                                mView.showBingPicBg(newBingPicURL);
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.d(TAG, "onFailure: ");
                            showOldBingPicBg();
                        }
                    });
        }
    }
    

    在上面的代码中,可以看到login方法中调用了LoginService.Fractory.create()方法创建了一个LiveService,而这个LiveService又是什么呢?这个LiveService就是前面所提到的Retrofit的网络调用接口。调用LiveService的login方法,发起登录的网络请求,同时通过链式调用将网络请求在子线程中进行,请求完成后将登录结果放置到SharePreference中,并调用View中对应的方法,进行页面的控制。

    LiveService

    Retrofit网络请求的实现,就是通过定义一个接口来完成。这个接口中有不同注解标注的方法,定义了Http请求中的GET、POST、PUT、DELETE等请求方式。而在这个接口中,最为重要的创建Retrofit对象实例,通过在接口中创建工厂类的方式,来创建Retrofit。

    public interface LiveService {
    
        /**
         * 用户登录 - 表单
         *
         * @param account  用户账号:手机/Email
         * @param password 密码
         * @return 返回登录情况
         */
        @FormUrlEncoded
        @POST("login")
        Flowable<Result<String>> login(@Field("account") String account,
                                       @Field("password") String password,
                                       @Field("type") String accountType);
    
        /**
         * 注册 - 表单
         *
         * @param account  用户账号:手机/Email
         * @param password 密码
         * @param name     用户名称(默认与手机号相同)
         * @param code     验证码
         * @return 返回注册情况
         */
        @FormUrlEncoded
        @POST("register")
        Flowable<Result<String>> register(@Field("account") String account,
                                          @Field("password") String password,
                                          @Field("name") String name,
                                          @Field("code") String code,
                                          @Field("type") String accountType);
    
        /**
         * 注销
         *
         * @return 返回注销情况
         */
        @GET("logout")
        Flowable<Result<String>> logout();
    
        /**
         * 发送验证码 - 表单
         *
         * @param account 用户账号:手机/Email
         * @return 返回发送验证码情况
         */
        @FormUrlEncoded
        @POST("message")
        Flowable<Result<String>> sendMessage(@Field("account") String account,
                                             @Field("type") String accountType);
    
        /**
         * 获取最新版本信息
         *
         * @return
         */
        @GET("version")
        Flowable<Result<String>> getVersion();
    
        /**
         * 下载差分包
         * 使用streaming方式,retrofit不会一次性将ResponseBody读取如内存,否则用以造成OOM
         *
         * @param url 下载的差分包的地址
         * @return 返回值使用ResponseBody之后会对ResponseBody进行读取
         */
        @GET
        @Streaming
        Flowable<ResponseBody> downloadApk(@Url String url);
    
        class Factory {
            static boolean isDebug = false;
            static OkHttpClient client = new OkHttpClient
                    .Builder()
                    .connectTimeout(2, TimeUnit.SECONDS)
                    .readTimeout(5, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS)
                    .addInterceptor(new AddCookiesInterceptor()) //这部分
                    .addInterceptor(new ReceivedCookiesInterceptor()) //这部分
                    .build();
    
    
            public static LiveService create() {
                String url = isDebug ? "本地地址" : "服务器地址";
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(url)
                        .client(client)
                        .addConverterFactory(GsonConverterFactory.create())
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                        .build();
                return retrofit.create(LiveService.class);
            }
        }
    }
    

    编写了LiveService后,Presenter中就完成了登录中数据的本地和网络请求。

    MVP模式中的M、V、P都已经实现了,那么问题又来了,如何将Presenter和View相互匹配呢?上述的过程只是将V、P实现了,那他们之间的桥梁要怎么搭建呢?答案就是通过Activity来实现。

    LoginActivity

    在LoginActivity中创建Fragment和Presenter,然后在Fragment中绑定Presenter,这样就将V、P之间的桥梁搭建了起来。

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (Build.VERSION.SDK_INT >= 21) { //android 5.0以上系统,状态栏也显示图片
                View decorView = getWindow().getDecorView();
                decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                getWindow().setStatusBarColor(Color.TRANSPARENT);
            }
            setContentView(R.layout.activity_login);
    
            LoginFragment loginFragment = (LoginFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
            if (loginFragment == null) {
                loginFragment = LoginFragment.newInstance();
                ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), loginFragment, R.id.contentFrame);
            }
    
            mPresenter = new LoginPresenter(loginFragment);
        }
    

    总结

    MVP架构下,有许多种变换形式,比如使用原始的数据库操作、使用Litepal数据库、接口回调的方式实现异步请求、Arouter实现页面跳转等,可以根据自身的需求来选择架构的实现形式。

    相关文章

      网友评论

        本文标题:Live-client-2-RxJava、Retrofit、Ro

        本文链接:https://www.haomeiwen.com/subject/sxdfsctx.html