美文网首页Android优秀开源AndroidMVP项目
像小白一样学习mvp+retrofit2

像小白一样学习mvp+retrofit2

作者: Souv | 来源:发表于2016-11-16 09:48 被阅读292次

-1-什么是retrofit2

Retrofit是一个不错的网络请求库

A type-safe REST client for Android and Java

好吧,说人话就是:一个用于网络请求的库。跟我们用到的OKhttp、Vellory、AysncHttp是一样一样的作用。

-2-废话

此demo采用mvp+retrofit2的形式展示,如果不熟悉mvp的同学请参考

像小白一样学习MVP

有的好事者肯定就问了,现在主流不是mvp+retrofit2+rxjava吗,怎么你只介绍前面2个的结合呢。是的我就只介绍mvp+retrofit2原因是什么不怕告诉你,因为看了rxjava能大概知道原理和作用,但是能直接上手的案例真的不多,也因为本人天资愚钝所以还是打算一步一步来比较好。

如果有人同学想学习rxjava,请参考扔物线这位大神的给 Android 开发者的 RxJava 详解文章,说实话我至少看了3遍,已经了解一些了,下一步就会上手写demo,也相信自己能搞定。

-3-实战

1 项目包结构

项目包结构

熟悉mvp的同学都知道model、presenter、view就不做介绍了。同时我自己新建了一个retrofit包名负责retrofit的所有功能。

当然此结构由优化的地方,因demo,所以不做过度累述。

这是一个请求通过网络获取网络天气信息的demo。请求连接为

http://api.avatardata.cn/Weather/Query?key=xxxxxxx&cityname=长沙

先看效果图


运行前 运行中 运行后

2 添加依赖

在使用之前,你必须先导入必要的jar包,以androidStudio为例:

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

注意:本demo使用retrofit2,而不是retrofit1.x

3 mvp中的M

/**
 * 描述:mvp中的m
 * 作者:dc on 2016/11/15 14:48
 * 邮箱:597210600@qq.com
 */
public interface WeatherModel {
    /**  异步加载数据  **/
    void loadWeather(String key , String cityId);

    /**  同步加载数据   **/
    void synLoadWeather(String key, String cityName);

    /**  设置监听器  **/
    void setWeatherListener(WeatherModelImp.LoadWeatherInfoListener listener);
}

里面有3个方法,一个异步加载数据、一个同步加载数据。一个监听器方法(作用是通过interface返回请求结果,如果用上Rxjava就不需要这么麻烦了)。

然后新建一个类实现该接口

import android.content.Context;
import android.util.Log;

import com.souv.rxjava.Constan;
import com.souv.rxjava.bean.WeatherInfoBean;
import com.souv.rxjava.retrofit.LoadWeatherServer;

import java.io.IOException;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * 描述:mvp中的m实现接口
 * 作者:dc on 2016/11/15 14:48
 * 邮箱:597210600@qq.com
 */
public class WeatherModelImp implements WeatherModel {
private static final String TAG = WeatherModelImp.class.getSimpleName();

/**  对象定义  **/
private Context mContext = null;
private LoadWeatherInfoListener loadWeatherInfoListener = null;

public WeatherModelImp(Context context){
    mContext = context;
}

@Override
public void loadWeather(String key, String cityId) {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constan.WHETHER_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
    Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
    call.enqueue(new Callback<WeatherInfoBean>() {
        @Override
        public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
            String result = "";
            try{
                String errorResult = response.errorBody().string();
            }catch (Exception e){
                e.printStackTrace();
            }
            WeatherInfoBean weatherBean = response.body();
            Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
            loadWeatherInfoListener.loadWeatherSuccess(weatherBean);
        }

        @Override
        public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
            //请求失败
            t.printStackTrace();
            loadWeatherInfoListener.loadWeatherFial();
        }
    });
}

@Override
public void synLoadWeather(String key, String cityId) {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constan.WHETHER_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
    Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key, cityId);

    try {
        Response<WeatherInfoBean> responseBody = call.execute();
        WeatherInfoBean weatherBean = responseBody.body();
        Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
        loadWeatherInfoListener.synLoadWeatherSuccess(weatherBean);
    } catch (IOException e) {
        e.printStackTrace();
        loadWeatherInfoListener.synLoadWeatherFail();
    }
}

@Override
public void setWeatherListener(LoadWeatherInfoListener listener) {
    loadWeatherInfoListener = listener;
}

public interface LoadWeatherInfoListener{
    void loadWeatherSuccess(WeatherInfoBean bean);  //异步加载数据成功

    void loadWeatherFial(); //异步加载数据失败

    void synLoadWeatherSuccess(WeatherInfoBean bean);  //同步加载数据成功

    void synLoadWeatherFail();   //同步加载数据失败
}

我们仔细看下loadWeather方法和synLoadWeather方法。分别是异步请求和同步请求

Retrofit2使用

异步请求

Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
    call.enqueue(new Callback<WeatherInfoBean>() {
        @Override
        public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
            
        }

        @Override
        public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
            //请求失败
            t.printStackTrace();
        }

以上代码发起了一个在后台线程的请求并从response 的response.body()方法中获取一个结果对象。注意这里的onResponse和onFailure方法是在主线程中调用的。

我建议你使用enqueue,它最符合 Android OS的习惯。

同步请求

Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key, cityId);

    try {
        Response<WeatherInfoBean> responseBody = call.execute();
        WeatherInfoBean weatherBean = responseBody.body();
       
    } catch (IOException e) {
        e.printStackTrace();
    }

以上的代码会阻塞线程,因此你不能在安卓的主线程中调用,不然会面临NetworkOnMainThreadException。如果你想调用execute方法,请在后台线程执行。

然后我们来看下retrofit的URL定义方式。

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
 * 描述:retrofit
 * 作者:dc on 2016/11/15 15:16
 * 邮箱:597210600@qq.com
 */
public interface LoadWeatherServer {

//http://api.avatardata.cn/
//http://api.avatardata.cn/Weather/Query?key=75bfe88f27a34311a41591291b7191ce&cityname=%E9%95%BF%E6%B2%99
    @GET("Weather/Query?")
    Call<WeatherInfoBean> loadRetrofitWeatherInfo(@Query("key") String key,@Query("cityname") String cityName);
}

......

//引用
Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Constan.WHETHER_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

......

//URL
public static final String WHETHER_URL = "http://api.avatardata.cn/";

因retrofit重新定义了新的URL形式,所以URL形式有不同的表示。

下面引用几个案例

URL案例-1
URL案例-2
URL案例-3

显然我喜欢第二个案例

对于 Retrofit 2.0中新的URL定义方式,这里是我的建议:

  • Base URL: 总是以 /结尾

  • @Url: 不要以 / 开头

如:

     @GET("Weather/Query?")
    Call<WeatherInfoBean> loadRetrofitWeatherInfo(@Query("key") String key,@Query("cityname") String cityName);


//引用
Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(http://api.avatardata.cn/)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

Retrofit请求

LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
    Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
    call.enqueue(new Callback<WeatherInfoBean>() {
        @Override
        public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
            String result = "";
            try{
                String errorResult = response.errorBody().string();
            }catch (Exception e){
                e.printStackTrace();
            }
            WeatherInfoBean weatherBean = response.body();
            Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
            loadWeatherInfoListener.loadWeatherSuccess(weatherBean);
        }

        @Override
        public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
            //请求失败
            t.printStackTrace();
            loadWeatherInfoListener.loadWeatherFial();
        }
    });

前面说到enqueue是异步请求,其中Response是请求响应的结果体。

在Retrofit 2.0中,不管 response 是否能被解析。onResponse总是会被调用。但是在结果不能被解析的情况下,response.body()会返回null。别忘了处理这种情况。

如果response存在什么问题,比如404什么的,onResponse也会被调用。你可以从response.errorBody().string()中获取错误信息的主体。

好了,然后我们在来看

MVP中的V

/**
 * 描述:MVP中的V
 * 作者:dc on 2016/11/15 16:02
 * 邮箱:597210600@qq.com
 */
public interface WeatherView {

void showWeather(String weather);  //异步显示天气信息

void showWeatherTemp(String weatherTemp); //异步显示天气温度信息

void loadWeatherFail(); //异步加载天气信息失败

void startLoadWeatherInfo();  //开始加载天气信息

void synShowWeather(String weather);  //同步显示天气信息

void synShowWeatherTemp(String weatherTemp);//同步显示天气温度信息

void synoadWeatherFail();   //同步加载天气信息失败
}

也没什么好解释的,只要是提供接口给MVP中的P,让其调用。接口主要是处理控件事件。因为上面提到过retrofit异步加载是在主线程中的,同步加载时开启了一个新线程的所以我在不同的请求方式下也分了不同的显示方法。这之后我们会在MVP中的P看到

MVP中的P

/**
 * 描述:
 * 作者:dc on 2016/11/15 16:02
 * 邮箱:597210600@qq.com
 */
public class WeatherPresenter implements WeatherModelImp.LoadWeatherInfoListener{
private static final String TAG = WeatherPresenter.class.getSimpleName();

private WeatherModelImp weatherModelImp = null;
private WeatherView weatherView = null;
private Handler handler = new Handler();

public WeatherPresenter(Context context , WeatherView view){
    weatherModelImp = new WeatherModelImp(context);
    weatherView = view;
    weatherModelImp.setWeatherListener(this);
}

/**
 * 异步获取天气数据
 * @param key
 * @param cityName
 */
public void loadWeatherInfo(String key ,String cityName){
    weatherView.startLoadWeatherInfo();
    weatherModelImp.loadWeather(key, cityName);

}

/**
 * 同步获取天气数据
 * @param key
 * @param cityName
 */
public void synLoadWeatherInfo(String key ,String cityName){
    weatherView.startLoadWeatherInfo();
    weatherModelImp.synLoadWeather(key, cityName);
}

@Override
public void loadWeatherSuccess(WeatherInfoBean bean) {
    if(null != bean){
        //显示当前温度
        String temperature = bean.getResult().getRealtime().getWeather().getTemperature();
        String info = bean.getResult().getRealtime().getWeather().getInfo();

        weatherView.showWeather(info);
        weatherView.showWeatherTemp(temperature);
    }
}

@Override
public void loadWeatherFial() {
    weatherView.loadWeatherFail();
}

@Override
public void synLoadWeatherSuccess(WeatherInfoBean bean) {
    //显示当前温度
    final String temperature = bean.getResult().getRealtime().getWeather().getTemperature();
    final String info = bean.getResult().getRealtime().getWeather().getInfo();
    //同步请求,启动的新线程,所以在现实界面的时候需要在主线程中
    handler.post(new Runnable() {
        @Override
        public void run() {
            weatherView.synShowWeather(info);
            weatherView.synShowWeatherTemp(temperature);
        }
    });
}
@Override
public void synLoadWeatherFail() {
    //同步请求,启动的新线程,所以在现实界面的时候需要在主线程中
    handler.post(new Runnable() {
        @Override
        public void run() {
            weatherView.synoadWeatherFail();
        }
    });
}
}

也不过大解释,MVP中的P主要是M和V的桥接。这里面引用了接口的方式来返回请求天气结果。个人觉得还是过于麻烦,所以说Rxjava还是很有必要学习的

MainActivity

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.souv.rxjava.presenter.WeatherPresenter;
import com.souv.rxjava.view.WeatherView;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity implements WeatherView {
private static final String TAG = MainActivity.class.getSimpleName();
@Bind(R.id.main_weather_tv)
TextView mainWeatherTv;
@Bind(R.id.main_weather_temp_tv)
TextView mainWeatherTempTv;
@Bind(R.id.main_weather_getweather_btn)
Button mainWeatherGetweatherBtn;
@Bind(R.id.main_weather_syn_getweather_btn)
Button mainWeatherSynGetweatherBtn;

/**
 * 对象定义
 **/
private WeatherPresenter weatherPresenter = null;
private WaitDialog waitDialog = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);

    init();

}

private void init() {
    waitDialog = new WaitDialog(MainActivity.this, android.R.style.Theme_Translucent_NoTitleBar);
    waitDialog.setCancelable(true);
    weatherPresenter = new WeatherPresenter(MainActivity.this, this);
}

@OnClick(R.id.main_weather_getweather_btn)
public void loadWeather() {
    weatherPresenter.loadWeatherInfo("75bfe88f27a34311a41591291b7191ce", "长沙");
}

@OnClick(R.id.main_weather_syn_getweather_btn)
public void synLoadWeather() {
    //以上的代码会阻塞线程,因此你不能在安卓的主线程中调用,不然会面临NetworkOnMainThreadException。如果你想调用execute方法,请在后台线程执行。
    new Thread(new Runnable() {
        @Override
        public void run() {
            weatherPresenter.synLoadWeatherInfo("75bfe88f27a34311a41591291b7191ce", "长沙");
        }
    }).start();

}

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

@Override
public void showWeather(String weather) {
    mainWeatherTv.setText(weather);
}

@Override
public void showWeatherTemp(String weatherTemp) {
    waitDialog.cancel();
    waitDialog.dismiss();
    mainWeatherTempTv.setText(weatherTemp + "℃");
}

@Override
public void loadWeatherFail() {
    Toast.makeText(MainActivity.this, "获取天气信息失败", Toast.LENGTH_SHORT).show();
}

@Override
public void startLoadWeatherInfo() {
    waitDialog.setWaitText("开始请求天气信息");
    waitDialog.show();
}

@Override
public void synShowWeather(String weather) {
    mainWeatherTv.setText(weather);
}

@Override
public void synShowWeatherTemp(String weatherTemp) {
    waitDialog.cancel();
    waitDialog.dismiss();
    mainWeatherTempTv.setText(weatherTemp + "℃");
}

@Override
public void synoadWeatherFail() {
    Toast.makeText(MainActivity.this, "获取天气信息失败", Toast.LENGTH_SHORT).show();
}
}

也没什么好解释的,主要是extends MVP中的V,用来实现视图的操作。同时使用到MVP中的P的实例来调用P中的方法。

总结

Retrofit2还有很多知识需要学习,上面的demo只是冰山一角,但是却能清晰的知道他的使用方式,如何使用。只要是入了门那么之后的深入我相信是不会很难的。因为学习了Retrofit所以我觉得RXjava真的很有必要,也很适合跟Retrofit一起使用。如果可以你也一定要学习新的知识。


慢慢努力做好身边所有的事

求知若饥,虚心若愚

相关文章

  • 像小白一样学习mvp+retrofit2

    -1-什么是retrofit2 Retrofit是一个不错的网络请求库 A type-safe REST clie...

  • 像小白一样学习MVP

    MVP与MVC MVP是从MVC的延伸。为什么要会出现MVP模式:那我们得了解一下MVC模式到底是一个怎样的模式:...

  • 像小白一样学习android jni编程

    什么是jni Android系统不允许一个纯粹使用C/C++的程序出现,它要求必须是通过Java代码嵌入Nativ...

  • 6月,减压小画

    5.31. 手动笑一个![憨笑] 6.2. 小白兔的逆袭。 6.1. 像专业人士一样学习规则,才能像艺术家一样打破...

  • 大马和小白马

    一天早上,天空睛朗,大马和小白马比赛跑步。 小白马像闪电一样飞快的跑,大马也像闪电一样,它们不分上下,一会大马...

  • 像小白鲸一样孤单

    今天下午在肯德基玩,开始有几个小朋友一起玩,后来小朋友都走了,只剩下小M一人。 小M说:“妈妈,我像小白鲸一样孤单...

  • 像学游泳一样学习机器学习——转行小白学习笔记

    为什么说像游泳一样学习机器学习? 游泳怎么学?实践中学。 看书学不会游泳,看视频学不会游泳,用救生圈游还是学不会。...

  • 小白的开始,改变了我。

    hi,你们好。我是小白训练营45期24班六组12号W. 像一切有老师有作业的学习一样,我迎来了小白的毕业,在大神的...

  • 增强学习 Q-learning

    对于小白菜来说,首先是了解Q-learning的基本原理,最好是像学习bp,学习CNN一样可以将一条计算走通,这里...

  • 如何快速学习区块链运用

    区块链是一个新生事物,好多小白需要学习,来抓住这个风口,这个未来的大趋势。 学习,你会像当年做学生一样认真吗?既然...

网友评论

  • 0573da934361:很有帮助,非常感谢。不过个人觉得LoadWeatherInfoListener这个interface应该放在WeatherModel中。
    Souv:嗯,放model接口中,是要稍好点,谢谢。
    还有这篇文章已经很low了,我重新写了一篇,你看下。http://www.jianshu.com/p/426864584518
  • owant:为何Presenter层有handle感觉不符合规律呀。Presenter应该是可以脱SDK的。Model层也是
    Souv:是的,这是最开始入门的时候写的,之后并没有如此了。

本文标题:像小白一样学习mvp+retrofit2

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