网络请求程序员手机移动程序开发

Retrofit知道这些就差不多了

2017-03-08  本文已影响515人  Android开发哥

引言

最近项目没什么进度,一直在学习IOS跟复习PHP。Android这边也没怎么搞了。不过这东西可不能拉下啊。二话不说赶紧上Github学习下新的框架。就看到了两个比较火的RXJavaRetrofit。于是就开始啃一下Retrofit文档。文档可谓是短小精悍。。。一开始硬是没看懂。下面就把自己看懂的部分说一下吧.

Retrofit到底是个啥

Retrofit是一套RESTful架构的Android(Java)客户端实现,基于注解,提供JSON to POJO(Plain Ordinary Java Object,简单Java对象),POJO to JSON,网络请求(POST,GET,PUT,DELETE等)封装。
从别人那里偷来的定义,看清来应该已经足够清晰了吧?至于什么是RESTful?不要问我,因为我对这个概念也比较模糊。。。

没时间解释了,赶紧上车

我们这里跟官方文档不一样,我们先获取最简单的网络数据吧。就是获取String类型的数据。
然后我们再去结合Gson去直接把返回的数据转成模型

环境

第一步:导入库

compile 'com.squareup.retrofit2:retrofit:2.1.0'

没啥好说的

第二部:编写服务接口

刚开始看这种做法有点奇葩,可能是本人的见识不足,勿喷。。。。不过后来发现这样写是真的屌。。自己慢慢去体会。

/**
 * Created by August on 16/7/29.
 * 获取Github首页的HTML源码
 */
public interface GithubService {
    @GET("/")
    Call<String> getGitHubIndexSource();
}

上面只是获取HTML源码,别想太多。。。。

第三部:直接调用

Retrofit retrofit = new Retrofit
        .Builder()
        .baseUrl("https://github.com/")
        .addConverterFactory(new Converter.Factory() {
            @Override
            public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
                return new Converter<ResponseBody, String>() {
                    @Override
                    public String convert(ResponseBody value) throws IOException {
                        return value.string();
                    }
                };
            }
        })
        .build();
GithubService githubService = retrofit.create(GithubService.class);
Call<String> indexSource = githubService.getGitHubIndexSource();
indexSource.enqueue(new Callback<String>() {
    @Override
    public void onResponse(Call<String> call, Response<String> response) {
        Log.i("TAG", response.body());
    }

    @Override
    public void onFailure(Call<String> call, Throwable t) {
        Log.i("TAG", t.getMessage());
    }
});

我们成功就可以打印出结果了。看到这里你可能会想暴揍一顿笔者,然后头也不回地离开。。。我第一次也有这样的想法,不过你得耐心看下去,真的。。。因为此处我们使用了最基础的Converter所以代码写起来有点多。但是作为程序员要懂得封装。待会我们就可以使用GsonConverter编写出简介的代码。

嗯?整个过程就大概像我上面那样说的。。。

除了Get还有什么呢?

我们都知道除了Get的请求方法,常见的还有Post(携带字段数据)Multipart(携带文件数据与字段数据等)

Post

@POST(URLConstant.SEND_LOGIN_CODE)
@FormUrlEncoded
Call<BaseResponseEntity> sendCode(@Field("phone") String phone);

Post中我们使用Field来指定参数字段

Multipart

@Multipart
@POST(URLConstant.COMPLETE_DATA)
Call<BaseObjResponseEntity<UserEntity>> completeData(@Part("user_token") RequestBody userToken,
                                                              @Part MultipartBody.Part icon,
                                                              @Part("nick_name") RequestBody nickName);

这里的尽管userToken跟nickName都是字符串类型,但是在Multipart中需要包装一下.重要的方法就是RequestBody.create(),其中包含多个重载的函数.大家可以使用下面的工具类来生成对应参数

/**
 * Created by August on 2017/2/10.
 * 文件上传帮助类
 */

public class MultiPartHelper {

    /**
     * 构造字段信息参数
     *
     * @param content
     * @return
     */
    public static RequestBody create(String content) {
        return RequestBody.create(MediaType.parse("multipart/form-data"), content);
    }


    /**
     * 构造单个文件上传
     *
     * @param key
     * @param file
     * @return
     */
    public static MultipartBody.Part create(String key, File file) {
        return MultipartBody.Part.createFormData(key, file.getName(), create(file));
    }

    public static RequestBody create(File file) {
        return RequestBody.create(MediaType.parse("multipart/form-data"), file);
    }

    public static MultipartBody.Part create(String key, String filePath) {
        return create(key, new File(filePath));
    }

    /**
     * 构造多个文件上传参数
     *
     * @param fileList
     * @return
     */
    public static List<MultipartBody.Part> create(List<String> fileList) {
        List<MultipartBody.Part> partList = new ArrayList<>();
        for (String filename : fileList) {
            File file = new File(filename);
            RequestBody imageBody = create(file);
            partList.add(MultipartBody.Part.createFormData(file.getName(), file.getName(), imageBody));
        }
        return partList;
    }

}

一起装逼一起飞

上面使用原生的CallBack我们已经受够了。。。一坨坨的代码如翔一样挥之不去。。。我们结合别人封装的GsonConverter来写一个从请求模型的转换吧
我们使用一个天气的API来获取数据吧。自己注册开发者身份

第一步:还是导入库(这是新的库。。。)

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

因为我们前面选择的是Retrofit2.1.0的库。所以我们选择的converter-gson的版本也要对应上,我在学习的过程中就遇到过版本不对上的话就出错了。可能版本之间差异还是比较大的。还有让你添加库,你丫的别把Retrofit2.1.0给删掉。。。

第二步:编写模型

我们可以找到我们需要获取的数据的返回格式

获取城市列表返回数据

{
    errNum: 0,
    errMsg: "success",
    retData: [
        {
            province_cn: "北京",  //省
            district_cn: "北京",  //市
            name_cn: "朝阳",    //区、县 
            name_en: "chaoyang",  //城市拼音
            area_id: "101010300"  //城市代码
        },
        {
            province_cn: "辽宁",
            district_cn: "朝阳",
            name_cn: "朝阳",
            name_en: "chaoyang",
            area_id: "101071201"
        }
    ]
}

获取指定天气信息返回数据

{
errNum: 0,
errMsg: "success",
retData: {
   city: "北京", //城市
   pinyin: "beijing", //城市拼音
   citycode: "101010100",  //城市编码   
   date: "15-02-11", //日期
   time: "11:00", //发布时间
   postCode: "100000", //邮编
   longitude: 116.391, //经度
   latitude: 39.904, //维度
   altitude: "33", //海拔 
   weather: "晴",  //天气情况
   temp: "10", //气温
   l_tmp: "-4", //最低气温
   h_tmp: "10", //最高气温
   WD: "无持续风向",  //风向
   WS: "微风(<10m/h)", //风力
   sunrise: "07:12", //日出时间
   sunset: "17:44" //日落时间
  }    
}

从上面我们可以看出,我们需要建立4个模型。

所以我们的模型代码就像下面那样

CityInfo模型

public class CityInfo {
    @SerializedName("province_cn")
    private String provinceCN;
    @SerializedName("district_cn")
    private String districtCN;
    @SerializedName("name_cn")
    private String nameCN;
    @SerializedName("name_en")
    private String nameEN;
    @SerializedName("area_id")
    private String areaID;

    public String getProvinceCN() {
        return provinceCN;
    }

    public void setProvinceCN(String provinceCN) {
        this.provinceCN = provinceCN;
    }

    public String getDistrictCN() {
        return districtCN;
    }

    public void setDistrictCN(String districtCN) {
        this.districtCN = districtCN;
    }

    public String getNameCN() {
        return nameCN;
    }

    public void setNameCN(String nameCN) {
        this.nameCN = nameCN;
    }

    public String getNameEN() {
        return nameEN;
    }

    public void setNameEN(String nameEN) {
        this.nameEN = nameEN;
    }

    public String getAreaID() {
        return areaID;
    }

    public void setAreaID(String areaID) {
        this.areaID = areaID;
    }
}

CityListResult模型

/**
 * Created by August on 16/7/29.
 * 城市查询结果模型
 */
public class CityListResult {
    @SerializedName("errNum")
    private int errNum;
    @SerializedName("errMsg")
    private String errMsg;
    @SerializedName("retData")
    private List<CityInfo> retData;

    public int getErrNum() {
        return errNum;
    }

    public void setErrNum(int errNum) {
        this.errNum = errNum;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }

    public List<CityInfo> getRetData() {
        return retData;
    }

    public void setRetData(List<CityInfo> retData) {
        this.retData = retData;
    }
}

Weather模型


/**
 * Created by August on 16/7/29.
 * 指定天气结果模型
 */
package demo.august1996.top.retrofitnewssample.model;

import com.google.gson.annotations.SerializedName;

/**
 * Created by August on 16/7/29.
 * 指定天气结果模型
 */
public class Weather {
    @SerializedName("city")
    private String city;
    @SerializedName("pinyin")
    private String pinyin;
    @SerializedName("citycode")
    private long cityCode;
    @SerializedName("date")
    private String date;
    @SerializedName("time")
    private String time;
    @SerializedName("postCode")
    private long postCode;
    @SerializedName("longitude")
    private double longitude;
    @SerializedName("latitude")
    private double latitude;
    @SerializedName("altitude")
    private double altitude;
    @SerializedName("weather")
    private String weather;
    @SerializedName("temp")
    private int temp;
    @SerializedName("l_tmp")
    private int l_tmp;
    @SerializedName("h_tmp")
    private int h_tmp;
    @SerializedName("WD")
    private String WD;
    @SerializedName("WS")
    private String WS;
    @SerializedName("sunrise")
    private String sunrise;
    @SerializedName("sunset")
    private String sunset;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }

    public long getCityCode() {
        return cityCode;
    }

    public void setCityCode(long cityCode) {
        this.cityCode = cityCode;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public long getPostCode() {
        return postCode;
    }

    public void setPostCode(long postCode) {
        this.postCode = postCode;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getAltitude() {
        return altitude;
    }

    public void setAltitude(double altitude) {
        this.altitude = altitude;
    }

    public String getWeather() {
        return weather;
    }

    public void setWeather(String weather) {
        this.weather = weather;
    }

    public int getTemp() {
        return temp;
    }

    public void setTemp(int temp) {
        this.temp = temp;
    }

    public int getL_tmp() {
        return l_tmp;
    }

    public void setL_tmp(int l_tmp) {
        this.l_tmp = l_tmp;
    }

    public int getH_tmp() {
        return h_tmp;
    }

    public void setH_tmp(int h_tmp) {
        this.h_tmp = h_tmp;
    }

    public String getWD() {
        return WD;
    }

    public void setWD(String WD) {
        this.WD = WD;
    }

    public String getWS() {
        return WS;
    }

    public void setWS(String WS) {
        this.WS = WS;
    }

    public String getSunrise() {
        return sunrise;
    }

    public void setSunrise(String sunrise) {
        this.sunrise = sunrise;
    }

    public String getSunset() {
        return sunset;
    }

    public void setSunset(String sunset) {
        this.sunset = sunset;
    }
}

WeatherResult模型


/**
 * Created by August on 16/7/29.
 * 获取特定城市返回结果模型
 */
public class WeatherResult {
    @SerializedName("errNum")
    private int errNum;
    @SerializedName("errMsg")
    private String errMsg;
    @SerializedName("retData")
    private Weather retData;

    public int getErrNum() {
        return errNum;
    }

    public void setErrNum(int errNum) {
        this.errNum = errNum;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }

    public Weather getRetData() {
        return retData;
    }

    public void setRetData(Weather retData) {
        this.retData = retData;
    }
}

前面的例子中使用的是String类型的结果,这次使用的是特定模型的结果

编写服务接口

/**
 * Created by August on 16/7/28.
 * 获取天气相关的服务
 */
public interface WeatherService {
    @Headers("apikey:ffe27ca85324881f8414e8c1ab28xxxx")
    @GET("/apistore/weatherservice/citylist")
    Call<CityListResult> getCityList(@Query("cityname") String cityName);

    @Headers("apikey:ffe27ca85324881f8414e8c1ab28xxxx")
    @GET("/apistore/weatherservice/cityid")
    Call<WeatherResult> getWeather(@Query("cityid") String cityID);
}

上面第一个就是获取城市列表与指定城市天气结果的定义

@GET("https://api.github.com/users/{user}/repos")
Call<String> getGitHub(@Path("user") String user);

调用走起!

我们通过一个简单的ListView去展示城市列表,然后点击城市显示相应的天气信息。


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ListView mListView;
    private CityListResult mResult;
    private ArrayList<String> mDatas = new ArrayList<>();
    private ArrayAdapter<String> mAdapter;
    private WeatherService mWeatherService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.getBtn).setOnClickListener(this);
        mWeatherService = new Retrofit.Builder()
                .baseUrl("http://apis.baidu.com")
                .addConverterFactory(GsonConverterFactory.create())
                .build().create(WeatherService.class);
        mListView = (ListView) findViewById(R.id.mListView);
        mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, final int i, long l) {

                mWeatherService.getWeather(String.valueOf(mResult.getRetData().get(i).getAreaID()))
                        .enqueue(new Callback<WeatherResult>() {
                    @Override
                    public void onResponse(Call<WeatherResult> call, final Response<WeatherResult> response) {
                        MainActivity.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                if (response.body().getErrNum() == 0) {

                                    Toast.makeText(MainActivity.this, response.body().getRetData().getWeather(), Toast.LENGTH_SHORT).show();

                                } else {
                                    Toast.makeText(MainActivity.this, response.body().getErrMsg(), Toast.LENGTH_SHORT).show();
                                }
                            }
                        });
                    }

                    @Override
                    public void onFailure(Call<WeatherResult> call, Throwable t) {
                        t.printStackTrace();
                    }
                });
            }
        });
    }

    @Override
    public void onClick(View view) {
        mWeatherService.getCityList("广东").enqueue(new Callback<CityListResult>() {
            @Override
            public void onResponse(Call<CityListResult> call, final Response<CityListResult> response) {
                mResult = response.body();
                if (mResult.getErrNum() == 0) {
                    mDatas.clear();
                    for (int i = 0; i < mResult.getRetData().size(); i++) {
                        mDatas.add(mResult.getRetData().get(i).getNameCN());
                    }
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAdapter.notifyDataSetChanged();
                        }
                    });
                } else {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, response.body().getErrMsg(), Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }

            @Override
            public void onFailure(Call<CityListResult> call, Throwable t) {
                t.printStackTrace();
            }
        });
    }
}

看到这么恶心的代码。。。把我自己也吓到了,其实并不多【尴尬死了】。主要的就是

获取服务

mWeatherService = new Retrofit.Builder()
        .baseUrl("http://apis.baidu.com")
        .addConverterFactory(GsonConverterFactory.create())
        .build().create(WeatherService.class);
mListView = (ListView) findViewById(R.id.mListVie

创建请求

mWeatherService.getWeather(String.valueOf(mResult.getRetData().get(i).getAreaID())).enqueue(this);

结果解析

response.body();
上一篇下一篇

猜你喜欢

热点阅读