Dragger2与MVP与Retrofit实战

1 篇文章 0 订阅
1 篇文章 0 订阅

MVP模块

MVP与MVC区别,有何应用场景

      在我们传统的mvc开发中经常会遇到M(javabean),V(layout,activity),C(activity),这里activity里会有大量的界面操作,业务逻辑操作。随着业务的拓展,后期如果修改一个地方,后续的修改非常的庞大,所以我们想着如果可以就把业务逻辑操作单独的放到另外一个地方,这样不仅美观上,后期拓展上,而且重复利用率上也能得到提升,mvp恰巧可以解决这个问题,我们可以把繁杂的业务逻辑放到p层开发,这样不仅是activity层显得不那么臃肿,而且不同的activity,fragment都可以使用重复的逻辑操作,减少相同逻辑的代码量。

如何写mvp

      下面看一个简单的登录模块。
      如果用mvc做的话

 mUsername = (EditText) findViewById(R.id.username);
        mPassword = (EditText) findViewById(R.id.password);
        dialog = new ProgressDialog(this);

    }

    /**
     * 按钮点击
     *
     * @param view
     */
    public void login(View view) {
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();

        boolean checkUserInfo = checkUserInfo(username,password);

        if(checkUserInfo){
            dialog.show();
            final User user=new User();
            user.username=username;
            user.password=password;
            new Thread(){
                @Override
                public void run() {
                    UserLoginNet net=new UserLoginNet();

                    if(net.sendUserLoginInfo(user)){
                        // 登陆成功
                        success();
                    }else{
                        //登陆失败
                        failed();
                    }

                }
            }.start();
        }else{
            Toast.makeText(MainActivity.this, R.string.check_info, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 检验用户输入——界面相关逻辑处理
     * @param username
     * @param password
     * @return
     */
    private boolean checkUserInfo(String username, String password) {
        if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)){
            return false;
        }
        return true;
    }

    /**
     * 登陆成功
     */
    public void success(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 登陆成功
                dialog.dismiss();
                Toast.makeText(MainActivity.this, getString(R.string.welcome)+mUsername.getText().toString(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 登陆失败
     */
    public void failed(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 登陆失败
                dialog.dismiss();
                Toast.makeText(MainActivity.this, R.string.login_fail, Toast.LENGTH_SHORT).show();
            }
        });
    }

可以看出这里面业务逻辑代码和界面操作都融合在一起,那么我们有没有方式把业务逻辑抽取到p层,不仅美观上,后期拓展上,而且重复利用率上也能得到提升。

 /**
     * 钮点击
     *
     * @param view
     */
    public void login(View view) {
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();

        boolean checkUserInfo = checkUserInfo(username, password);

        if (checkUserInfo) {
            dialog.show();
            presenter.login(username, password);
        } else {
            Toast.makeText(MainActivity.this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
        }
    }


    /**
     * 检验用户输入——界面相关逻辑处理
     *
     * @param username
     * @param password
     * @return
     */
    private boolean checkUserInfo(String username, String password) {
        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            return false;
        }
        return true;
    }

    /**
     * 登陆成功
     */
    public void success() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 登陆成功
                dialog.dismiss();
                Toast.makeText(MainActivity.this, "欢迎回来:" + mUsername.getText().toString(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 登陆失败
     */
    public void failed() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 登陆失败
                dialog.dismiss();
                Toast.makeText(MainActivity.this, "用户名或密码输入有误", Toast.LENGTH_SHORT).show();
            }
        });
    }

在P层做业务逻辑处理

public MainActivityPresenter(MainActivity activity) {
        this.activity = activity;

        // 网络访问:
        // 第一步:创建Builder,指定baseUrl和数据解析工具
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl(Constant.BASEURL);
        builder.addConverterFactory(GsonConverterFactory.create());// Gson解析

        // 第二步:创建Retrofit
        Retrofit retrofit = builder.build();

        // 第三步:指定请求方式(get或post)和参数,通过定以接口的形式指定
        // 第三步通过接口com.itheima.mvp.presenter.api.ResponseInfoAPI

        // 第四步:将Retrofit和第三步的联网参数联系起来
        api = retrofit.create(ResponseInfoAPI.class);

    }

    /**
     * 用户登陆
     *
     * @param username
     * @param password
     */
    public void login(String username, String password) {
        Call<ResponseInfo> call = api.login(username, password);
        call.enqueue(new Callback<ResponseInfo>() {
            @Override
            public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {
                // 处理服务器回复内容
                if (response != null) {
                    if (response.isSuccessful()) {
                        // 登陆成功
                        activity.success();
                    }else{
                        // 错误提示
                    }
                }else{
                    // 错误提示
                    onFailure(call,new RuntimeException("服务器忙请稍后重试"));
                }
            }

            @Override
            public void onFailure(Call<ResponseInfo> call, Throwable t) {
                // 异常处理
                //登陆失败
                activity.failed();
            }
        });
    }


    public void login1(String username, String password) {

        final User user = new User();
        user.username = username;
        user.password = password;
        new Thread() {
            @Override
            public void run() {
                UserLoginNet net = new UserLoginNet();

                if (net.sendUserLoginInfo(user)) {
                    // 登陆成功
                    activity.success();
                } else {
                    //登陆失败
                    activity.failed();
                }

            }
        }.start();
    }

在Activity里面只需要拿到P层类的实例,调用login方法即刻
那么我们如何拿到Presenter实例?通过Presenter p=new Presenter();
答案肯定可以,但是不好,为什么?看下面详解为什么要用Dragger2。

dragger2框架

为什么要用dragger2

     mvp设计模式中我们的activity层中需要拿到p层的对象,那么在开发中有可能我们很多activity或者fragment都可能需要拿到这个p层对象的实例,这样就存在一个耦合度的问题,耦合度太高,假如我们p层类的构造发生变化我们要在很多地方都得进行修改,这就是耦合度太高的弊端,如何解决这个问题?这种思想不仅在MVP会遇到,在编程中解耦是亘古不变的话题,那么我们思考可以通过哪些方式解决这个耦合度的问题?

1:利用配置文件,使用反射的方式获取到需要加载的对象
譬如,清淡文件中的Mainactivity加载的时候,安卓就是要通过配置文件中packagename+class反射的方式加载Mainactivity对象。
2:设计模式,单例,工厂,观察者。
譬如通过单例获取到对象,我们只需要在那个方法里获取到对象的创建,修改的话只需要修改单例这个方法,假如类的结构发生变化我们只需要修改单例的对象就可以了。工厂也是,我们只需要修改工厂类就可以修改了(譬如不同的fragmen切换的时候我们可以通过工厂设计模式生产对象)

那我们除了上述的方式以外我们设想能不能有个容器,这个容器在我们需要用到某个对象的时候帮我们创建好需要的对象,像对象工厂一样,并且帮我们管理这些对象。沿着这种思想,dragger2应运而生。。。

dragger2
1:在容器中创建好需要的对象
2:获取到MainActivity实例做赋值的动作

Dagger2是为android和java平台提供的在编译时进行依赖注入的框架。
编译时:编译时生成代码(rebuild),我们完胜所需对象的注入,假设是反射,应该是什么时候起作用。
很多的依赖注入都是通过反射去做的,这个dagger2的好处在于没用用反射,而是在编译时就做好了注入,这样解决了反射带来的在性能和开发上的问题。
上面说在编译期就完成了注入,下面我们在rebuild以后看下文件目录,多生成了Presenter工厂类和Component实例类
这里写图片描述
下面我们下看都做了什么?
MainActivityModule_ProvideMainActivityPresenterFactory类

public final class MainActivityModule_ProvideMainActivityPresenterFactory implements Factory<MainActivityPresenter> {
    private final MainActivityModule module;

    public MainActivityModule_ProvideMainActivityPresenterFactory(MainActivityModule module) {
        assert module != null;

        this.module = module;
    }

    public MainActivityPresenter get() {
        return (MainActivityPresenter)Preconditions.checkNotNull(this.module.provideMainActivityPresenter(), "Cannot return null from a non-@Nullable @Provides method");
    }

    public static Factory<MainActivityPresenter> create(MainActivityModule module) {
        return new MainActivityModule_ProvideMainActivityPresenterFactory(module);
    }
}

DaggerMainActivityComponent类

private Provider<MainActivityPresenter> provideMainActivityPresenterProvider;
    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private DaggerMainActivityComponent(DaggerMainActivityComponent.Builder builder) {
        assert builder != null;

        this.initialize(builder);
    }

    public static DaggerMainActivityComponent.Builder builder() {
        return new DaggerMainActivityComponent.Builder(null);
    }

    private void initialize(DaggerMainActivityComponent.Builder builder) {
        this.provideMainActivityPresenterProvider = MainActivityModule_ProvideMainActivityPresenterFactory.create(builder.mainActivityModule);
        this.mainActivityMembersInjector = MainActivity_MembersInjector.create(this.provideMainActivityPresenterProvider);
    }

    public void in(MainActivity activity) {
        this.mainActivityMembersInjector.injectMembers(activity);
    }

    public static final class Builder {
        private MainActivityModule mainActivityModule;

        private Builder() {
        }

        public MainActivityComponent build() {
            if(this.mainActivityModule == null) {
                throw new IllegalStateException(MainActivityModule.class.getCanonicalName() + " must be set");
            } else {
                return new DaggerMainActivityComponent(this, null);
            }
        }

        public DaggerMainActivityComponent.Builder mainActivityModule(MainActivityModule mainActivityModule) {
            this.mainActivityModule = (MainActivityModule)Preconditions.checkNotNull(mainActivityModule);
            return this;
        }
    }
}

关键就在于里面的in方法,这里的injectMembers方法进行了关联。

public void in(MainActivity activity) {
        this.mainActivityMembersInjector.injectMembers(activity);
    }
深入解析dragger2

1:指定创建对象的类和方法。
在MainActivityModule中用@Module注解指定创建对象的类
然后通过@Provides方法指定创建对象的方法。

@Module
public class MainActivityModule {


    private MainActivity activity;

    public MainActivityModule(MainActivity activity) {
        this.activity = activity;
    }

    @Provides
    public MainActivityPresenter provideMainActivityPresenter(){
        return  new MainActivityPresenter(activity);
    }
}

2:指定接受者
创建好的对象赋值给指定目标,我们需要通过@Inject告诉dragger2容器。
把已经创建好的对象赋值给谁,

@Inject
MainActivityPresenter presenter;

3:将创建好的对象和接受者联系在一起
通过@Component注解指定这个工作由哪个接口来完成。
这个接口中我们可以看到组件和接受者。
我们先定义一个接口
@Component (modules = MainActivityModule.class)
public interface MainActivityComponent {
void in(MainActivity activity);
}
在activity中我们会创建这个接口对应的实例

MainActivityComponent component = DaggerMainActivityComponent.builder().mainActivityModule(new MainActivityModule(this)).build();
        component.in(this);

可以看出返回的就是Component对应的实例。这个实例中完成了关联

接下来我们断电进区看一看究竟:

在MainActivity中

 MainActivityComponent component = DaggerMainActivityComponent.builder().mainActivityModule(new MainActivityModule(this)).build();
        component.in(this);

断点进去
DaggerMainActivityComponent类中

@Override
  public void in(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
  }

再进入MainActivity_MembersInjector类中

@Override
public void injectMembers(MainActivity instance) {
  if (instance == null) {
    throw new NullPointerException("Cannot inject members into a null reference");
  }
  instance.presenter = presenterProvider.get();
}

这里写图片描述

这里写图片描述

可以看出一遍是拿到presenter的引用,一边拿到module应用然后把module对象,而这个module类是用于创建presenter对象的。这样就轻易的把二者关联到一起了。

dragger2的使用
引入配置
添加dagger2的依赖
compile 'com.google.dagger:dagger:2.6'
编译时生成代码的插件配置(android-apt)
project的gradle中添加
    buildscript {
        dependencies {
                classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }
    }
apt插件的使用
    modle的gradle中添加
        apply plugin: 'com.neenbedankt.android-apt'
关联Dagger2
    dependencies {
        apt 'com.google.dagger:dagger-compiler:2.6'
    }
开始我犯了一个错误就是
添加apply plugin: 'com.neenbedankt.android-apt'
// 使用apt插件为Dagger2生成代码
apt 'com.google.dagger:dagger-compiler:2.6'
的时候我没有在module中添加,而是在app的 module添加了以至于一直没有注入成功,所以大家在使用的哪个module下面需要注入就在哪个module下添加
{
apply plugin: 'com.neenbedankt.android-apt' // 注释处理
}
{
 compile 'com.google.dagger:dagger:2.0.2'
 compile 'com.google.dagger:dagger-compiler:2.0.2'
}

然后在project中grade中配置
{
 dependencies {
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

使用步骤分五步走:
这里写图片描述

retrofit的使用

Retrofit的介绍

Retorfit是一个功能强大的联网工具。可以看成是OKHttp+数据解析(json、xml等)的组合。
它可以自动的对我们返回的数据进行解析,这里的解析工具有很多种,

Converters can be added to support other types. Six sibling modules adapt popular serialization libraries for your convenience.

Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

需要在代码中配置一下就可以规定用哪种解析工具,一般常用的有Gson,SimpleXml,Jackson

一、 使用手册
1. 引入配置
λ 添加Retrofit依赖:
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
λ 使用Gson进行数据解析
compile ‘com.google.code.gson:gson:2.2.4’
λ 将Retorfit与Gson关联
compile ‘com.squareup.retrofit2:converter-gson:2.1.0’

测试链接http://localhost:8080/TakeoutService/login?username=“zhang san”&password=”bj”

 //网络访问,创建builder和数据解析工具
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://localhost:8080/");
        //确定解析工具
        builder.addConverterFactory(GsonConverterFactory.create());

        //第二步:创建retrofit
        Retrofit retrofit = builder.build();

        //第三步:指定请求方式,和参数,通过以接口形式指定。

        //第四步:将retrofit与第三步的联网参数联系在一起
        //相当于通过retrofit给ResponseInfoApi接口创建一个实例
        api = retrofit.create(ResponseInfoApi.class);

然后获取到ResponseInfoApi实例对象api,通过这个对象定义的方法返回一个Call对象,用于操作网络
Call login = api.login(username, password);

由于之前我们就规定好的接口方法。

//"TakeoutService/login"
  @GET("TakeoutService/login")
    Call<ResponseInfo> login(@Query("username") String username, @Query("password") String password);

可以看出返回的是一个Call对象的范型,范型的类型就是返回的数据解析后的bean类型,所以我们得提前用一个bean来接收。

public void login(String username, String password) {
        Call<ResponseInfo> login = api.login(username, password);
        //异步
        login.enqueue(new Callback<ResponseInfo>() {
            @Override
            public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {
                //服务器成功回复操作
//                ResponseInfo body = response.body();
                if (response != null) {
                    if (response.isSuccessful()) {
                        activity.success();
                    } else {
                        //请求提示
                    }
                } else {
                    onFailure(call, new RuntimeException("服务器请求异常"));
                }
                activity.success();
            }

            @Override
            public void onFailure(Call<ResponseInfo> call, Throwable t) {
                //服务器失败操作
                activity.faile();
            }
        });
    }
替换原则:
1@Path - 替换参数
@GET("/group/{id}/users")
public Call<List<User>> groupList(@Path("id") int groupId);
2@Query - 添加查询参数
@GET("/group/{id}/users")
public Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
3@QueryMap - 如果有多个查询参数,把它们放在Map中
@GET("/group/{id}/users")
public Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IntroductionProgressManager 一行代码即可监听 App 中所有网络链接的上传以及下载进度,包括 Glide 的图片加载进度,实现原理类似 EventBus,你可在 App 中的任何地方,将多个监听器,以 Url 地址作为标识符,注册到本框架,当此 Url 地址存在下载或者上传的动作时,框架会主动调用所有使用此 Url 地址注册过的监听器,达到多个模块的同步更新.框架的构思和实现可以看这篇文章Feature使用简单,只需一行代码即可实现进度监听.多平台支持,支持 Okhttp , Retrofit , Glide ,使用 Okhttp 原生 Api ,不存在兼容问题.低耦合,实际请求端和进度接收端并不存在直接或间接的关联关系,即可以在 App 任何地方接收进度信息.侵入性低,使用本框架你并不需要更改之前进行上传或下载的代码,即使用或不使用本框架并不会影响到原有的代码.多端同步,同一个数据源的上传或下载进度可以指定多个不同的接收端,少去了使用 EventBus 实现多个端口同步更新进度.自动管理监听器,少去了手动注销监听器的烦恼.默认运行在主线层,少去了切换线程的烦恼.轻量级框架,不包含任何三方库,体积极小.Download compile 'me.jessyan:progressmanager:1.2.5'UsageStep 1 // 构建 OkHttpClient 时,将 OkHttpClient.Builder() 传入 with() 方法,进行初始化配置  OkHttpClient = ProgressManager.getInstance().with(new OkHttpClient.Builder())                 .build();Step 2 // Glide 下载监听  ProgressManager.getInstance().addResponseListener(IMAGE_URL, getGlideListener()); // Okhttp/Retofit 下载监听  ProgressManager.getInstance().addResponseListener(DOWNLOAD_URL, getDownloadListener()); // Okhttp/Retofit 上传监听  ProgressManager.getInstance().addRequestLisenter(UPLOAD_URL, getUploadListener());ProGuard -keep class me.jessyan.progressmanager.** { *; }  -keep interface me.jessyan.progressmanager.** { *; }About MeEmail: jess.yan.effort@gmail.comHome: http://jessyan.me掘金: https://gold.xitu.io/user/57a9dbd9165abd0061714613简书: http://www.jianshu.com/u/1d0c0bc634db
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值