Android Rxjava2.0+Retrofit2.0+Mvp封装直接拿去用 简单demo演示3分钟彻底学会

Android 超越官方 MVC架构 MVP架构 MVVM架构 一网打尽;

https://blog.csdn.net/WHB20081815/article/details/68948856

 

什么是mvp,MVC的区别

MVP的优缺点

1.MVP架构中,解除了View和Model间的耦合,使它们不能相互访问,核心的业务逻辑都集中在Presenter中。

MVP 模式将Activity 中的业务逻辑全部分离出来,让Activity 只做 UI 逻辑的处理,所有跟Android API无关的业务逻辑由 Presenter 层来完成。

2.降低耦合,方便维护

MVP在实现代码简洁的同时,额外增加了大量的接口、类,不方便进行管理

 

Model进行一步操作的时候,获取结果通过Presenter会传到View的时候,出现View引用的空指针异常。

Presenter和View相互持有引用,解除不及时的话容易出现内存泄漏。

 

MVP模式的应用

2.1 model层描述和具体代码

提供我们想要展示在view层的数据和具体登陆业务逻辑处理的实现,

 

 

package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:模拟登陆的操作的接口,实现类为LoginModelImpl.相当于MVP模式中的Model层
 */
public interface LoginModel {
    void login(String username, String password, OnLoginFinishedListener listener);
}

来源: https://www.jianshu.com/p/9d40b298eca9

 

 

 

package com.nsu.edu.androidmvpdemo.login;

import android.os.Handler;
import android.text.TextUtils;
/**
 * Created by Anthony on 2016/2/15.
 * Class Note:延时模拟登陆(2s),如果名字或者密码为空则登陆失败,否则登陆成功
 */
public class LoginModelImpl implements LoginModel {

    @Override
    public void login(final String username, final String password, final OnLoginFinishedListener listener) {

        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                boolean error = false;
                if (TextUtils.isEmpty(username)){
                    listener.onUsernameError();//model层里面回调listener
                    error = true;
                }
                if (TextUtils.isEmpty(password)){
                    listener.onPasswordError();
                    error = true;
                }
                if (!error){
                    listener.onSuccess();
                }
            }
        }, 2000);
    }
}

 

 

2.2 view层描述和具体代码

负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment以及View的子类体现在了这一 层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。本层所需要做的操作就是在每一次有相应交互的时候,调用presenter的相关方法就行。(比如说,button点击)

 

package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:登陆View的接口,实现类也就是登陆的activity
 */
public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

 

 

package com.nsu.edu.androidmvpdemo.login;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.nsu.edu.androidmvpdemo.R;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:MVP模式中View层对应一个activity,这里是登陆的activity
 */
public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);

        presenter = new LoginPresenterImpl(this);
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void navigateToHome() {
// TODO       startActivity(new Intent(this, MainActivity.class));
        Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show();
//        finish();
    }

    @Override
    public void onClick(View v) {
        presenter.validateCredentials(username.getText().toString(), password.getText().toString());
    }

}

 

2.3 presenter层描述和具体代码

Presenter扮演着view和model的中间层的角色。获取model层的数据之后构建view层;也可以收到view层UI上的反馈命令后分发处理逻辑,交给model层做业务操作。它也可以决定View层的各种操作。

 

 

package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:登陆的Presenter 的接口,实现类为LoginPresenterImpl,完成登陆的验证,以及销毁当前view
 */
public interface LoginPresenter {
    void validateCredentials(String username, String password);

    void onDestroy();
}
package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:
 * 1 完成presenter的实现。这里面主要是Model层和View层的交互和操作。
 * 2  presenter里面还有个OnLoginFinishedListener,
 * 其在Presenter层实现,给Model层回调,更改View层的状态,
 * 确保 Model层不直接操作View层。如果没有这一接口在LoginPresenterImpl实现的话,
 * LoginPresenterImpl只 有View和Model的引用那么Model怎么把结果告诉View呢?
 */
public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {
    private LoginView loginView;
    private LoginModel loginModel;

    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();
    }

    @Override
    public void validateCredentials(String username, String password) {
        if (loginView != null) {
            loginView.showProgress();
        }

        loginModel.login(username, password, this);
    }

    @Override
    public void onDestroy() {
        loginView = null;
    }

    @Override
    public void onUsernameError() {
        if (loginView != null) {
            loginView.setUsernameError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (loginView != null) {
            loginView.setPasswordError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (loginView != null) {
            loginView.navigateToHome();
        }
    }
}

 

 

1 Activity做了一些UI初始化的东西并需要实例化对应LoginPresenter的引用和实现 LoginView的接口,监听界面动作
2 登陆按钮按下后即接收到登陆的事件,在onClick里接收到即通过LoginPresenter的引用把它交给LoginPresenter处理。LoginPresenter接收到了登陆的逻辑就知道要登陆了
3 然后LoginPresenter显示进度条并且把逻辑交给我们的Model去处理,也就是这里面的LoginModel,(LoginModel的实现类LoginModelImpl),同时会把OnLoginFinishedListener也就是LoginPresenter自身传递给我们的Model(LoginModel)。
4 LoginModel处理完逻辑之后,结果通过OnLoginFinishedListener回调通知LoginPresenter
5 LoginPresenter再把结果返回给view层的Activity,最后activity显示结果



MVP总结:

presenter封装view和 Model

Activity里面封装presenter和view

 

mvp更好的写法:

https://www.jianshu.com/p/063126f807d0

RxJava2.0+Retrofit2.0结合使用

1 //rxjava +retrofit

    implementation 'io.reactivex.rxjava2:rxjava:2.1.1'

    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'

    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

    implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'

    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合rxjava2

    implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'//拦截器

1.构建RetrofitClient客户端.

public static RetrofitClient getInstance(Context context, String url) {
    if (context != null) {
        mContext = context;
    }
    sNewInstance = new RetrofitClient(context, url);
    return sNewInstance;
}

private RetrofitClient(Context context) {

    this(context, null);
}

private RetrofitClient(Context context, String url) {

    if (TextUtils.isEmpty(url)) {
        url = baseUrl;
    }
    okHttpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(
                    new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
            .cookieJar(new NovateCookieManger(context))
            .addInterceptor(new BaseInterceptor(mContext))
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .build();
    Retrofit retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .baseUrl(url)
            .build();
    apiService = retrofit.create(ApiService.class);
}

 

2.

ApiService

请求网络的API接口类,这里你可以增加你需要的请求接口,也可复用已经实现的几个方法。

/**
 * Created by Tamic on 2016-07-08.
 */
public interface ApiService {

    public static final String Base_URL = "http://ip.taobao.com/";
    /**
     *普通写法
     */
    @GET("service/getIpInfo.php/")
    Observable<ResponseBody> getData(@Query("ip") String ip);

    @GET("{url}")
    Observable<ResponseBody> executeGet(
            @Path("url") String url,
            @QueryMap Map<String, String> maps);


    @POST("{url}")
    Observable<ResponseBody> executePost(
            @Path("url") String url,
            @QueryMap Map<String, String> maps);

    @Multipart
    @POST("{url}")
    Observable<ResponseBody> upLoadFile(
            @Path("url") String url,
            @Part("image\\\\"; filename=\\"image.jpg") RequestBody avatar);


    @POST("{url}")
    Observable<ResponseBody> uploadFiles(
            @Path("url") String url,
            @Path("headers") Map<String, String> headers,
            @Part("filename") String description,
            @PartMap()  Map<String, RequestBody> maps);

    @Streaming
    @GET
    Observable<ResponseBody> downloadFile(@Url String fileUrl);

}

 

public void getData(Subscriber<ResponseBody> subscriber, String ip) {
    apiService.getData(ip)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

public void get(String url, Map headers, Map parameters, Subscriber<ResponseBody> subscriber) {
    apiService.executeGet(url, headers, parameters)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

public void post(String url, Map headers, Map parameters, Subscriber<ResponseBody> subscriber) {
    apiService.executePost(url, headers, parameters)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

 

    大家发现上面的指定生产线程和消费线程的步骤有点麻烦,每个api都得进行指定线程,那么可以利用rxJava的转换器写一个Transformer
    Observable.Transformer schedulersTransformer() {
        return new Observable.Transformer() {


            @Override
            public Object call(Object observable) {
                return ((Observable)  observable).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
1

    那么api可以这样优化了:

    public Subscription getData(Subscriber<IpResult> subscriber, String ip) {
        return apiService.getData(ip)
                .compose(schedulersTransformer())
                .subscribe(subscriber);
    }

RX的Transformer用法就是上面的

 

我们要对所以返回结果进行预处理,新建一个DefaultTransformer继承Observable.Transformer,预处理无非就是对status_code进行判断和解析,不同的错误返回不同的错误信息。有个操作符compose。因为我们在每一个请求中都会处理status_code以及使用一些操作符,比如用observeOn和subscribeOn来切换线程。RxJava提供了一种解决方案:Transformer(转换器),一般情况下就是通过使用操作符compose()来实现。


 

这里我们使用map操作符把Obserable<BaseEntity<T>>,转换成为Observable<T>在内部对status_code进行了预处理。这里当状态码不等于10000就表示请求出错抛出异常。这里的ApiException是我们自定义的一个异常类,用来处理服务器返回的异常。

 

public class DefaultTransformer<T> implements Observable.Transformer<T, T> {
    @Override
    public Observable<T> call(Observable<T> tObservable) {
        return tObservable
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1<T, T>() {// 通用错误处理,判断code
                    @Override
                    public T call(T t) {
                        if (((BaseEntity<T>)t).getStatus_code() != 10000) {
                            throw new ApiException(((BaseEntity<T>)t).getStatus_code(), ((BaseEntity<T>)t).getError_msg());
                        }
                        return t;
                    }
                });
    }

    public static <T> DefaultTransformer<T> create() {
        return new DefaultTransformer<>();
    }
}

来源: https://www.jianshu.com/p/9364a9577c41

 

3.构建基础拦截器

 

责任链模式:很明显,在okhttp中的拦截器模块,执行过程用到。OkHttp3 的拦截器链中, 内置了5个默认的拦截器,分别用于重试、请求对象转换、缓存、链接、网络读写(责任链模式:为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。)

 

Interceptor 的设计是一种分层的思想,每个 Interceptor 就是一层。分层简化了每一层的逻辑,每层只需要关注自己的责任,而各层之间通过约定的接口/协议进行合作(面向接口编程思想),共同完成复杂的任务。

--------------------- 

本来

RetryAndFollowUpInterceptor:负责重定向。

BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。

CacheInterceptor:负责读取缓存以及更新缓存。

ConnectInterceptor:负责与服务器建立连接。

CallServerInterceptor:负责从服务器读取响应的数据。

 

 

包含cookies的管理还有拦截器(自定义拦截器)

1.日志拦截器

2.缓存拦截器

3.网络拦截器:添加下载的头。拦截器

okHttpClient = new OkHttpClient.Builder()
        .addNetworkInterceptor(
                new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
        .cookieJar(new NovateCookieManger(context))
        .cache(cache)
        .addInterceptor(new BaseInterceptor(headers))
        .addInterceptor(new CaheInterceptor(context))
        .addNetworkInterceptor(new CaheInterceptor(context))

 

 

构建基础拦截器

用来设置基础header,这里是通过MAP键值对来构建,将heder加入到Request中。

/**
 * BaseInterceptor,use set okhttp call header
 * Created by Tamic on 2016-06-30.
 */
public class BaseInterceptor implements Interceptor {

    private Map<String, String> headers;

    public BaseInterceptor(Map<String, String> headers) {
        this.headers = headers;
    }

    @Override
    public Response intercept(Chain chain) throws    IOException {

        Request.Builder builder = chain.request()
                .newBuilder();
        if (headers != null && headers.size() > 0) {
            Set<String> keys = headers.keySet();
            for (String headerKey : keys) {
                builder.addHeader(headerKey,   headers.get(headerKey)).build();
            }
        }
        return chain.proceed(builder.build());

    }
}

最后调用时如下:

btn_get.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        Map<String, String> maps = new HashMap<String, String>();
        maps.put("ip", "21.22.11.33");

        //"http://ip.taobao.com/service/getIpInfo.php?ip=21.22.11.33";
        RetrofitClient.getInstance(MainActivity.this).createBaseApi().get("service/getIpInfo.php"
                , maps, new BaseSubscriber<IpResult>(MainActivity.this) {


                    @Override
                    public void onError(ResponeThrowable e) {


                        Log.e("Lyk", e.getMessage());
                        Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();

                    }

                    @Override
                    public void onNext(IpResult responseBody) {

                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();
                    }
                });
    }
});


btn_post.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        Map<String, String> maps = new HashMap<String, String>();

        maps.put("ip", "21.22.11.33");
        //"http://ip.taobao.com/service/getIpInfo.php?ip=21.22.11.33";
        RetrofitClient.getInstance(MainActivity.this).createBaseApi().post("service/getIpInfo.php"
                , maps, new BaseSubscriber<ResponseBody>(MainActivity.this) {

                    @Override
                    public void onError(ResponeThrowable e) {
                        Log.e("Lyk", e.getMessage());
                        Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();

                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();
                    }
                });
    }
});

 

接下来的话就是管理生命周期了。有个专门的库可以管理生命周期的叫

RxLifecycle,可以去看看。不过我们不用这个,我们在BaseActivity里面写。

 

public abstract class BaseActivity extends AppCompatActivity {

    private CompositeSubscription mCompositeSubscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        onUnsubscribe();
    }
    /**
     * 所有rx订阅后,需要调用此方法,用于在detachView时取消订阅
     */
    protected void addSubscription(Subscription subscribe) {
        if (mCompositeSubscription == null)
            mCompositeSubscription = new CompositeSubscription();
        mCompositeSubscription.add(subscribe);
    }

    /**
     * 取消本页面所有订阅
     */
    protected void onUnsubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }

}

我们在请求数据的地方调用addSubscription(Subscription subscribe)方法,当activity在onDestroy()的时候就取消订阅了。


 

RxJava + Retrofit2.0的项目实战完美封装

参考博客:

https://blog.csdn.net/silenceoo/article/details/75037576

 

最好的

https://blog.csdn.net/u013651026/article/details/79294396

 

 

其实 Retrofit传统的API调用与okHTTP功能和使用上没有什么本质的区别,它的强大之处在于与RxJava结合使用。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值