Retrofit

请务必实现自己的诺言!

给自己写在前面的话:

一定要封装一个上乘的OkHttp库!

学习Retrofit!主要原因:项目教程中使用的所有网络请求现在都是Retrofit了,网上都在分析Retrofit源码了,不学不行了!

之前一直使用Okhttp故步自封!

Retrofit -> 相应参考资源

GitHub: https://github.com/square/retrofit
Retrofit: https://square.github.io/retrofit/

添加依赖

implementation ‘com.squareup.retrofit2:retrofit:2.5.0’

知识点:

0:@GET和@POST

在定义的接口中,每一个方法都必须有一个及以上的HTTP ANNOTATION来提供 METHOD 和 相对URL。
有5个内置注释: GET、POST、PUT、DELETE、HEAD.

@GET("users/list")

可以在URL中指定查询参数。

@GET("users/list?sort=desc")

1:@Path

我们可以在方法中动态的更改相对URL:可以使用 @GET或者@POST中的替换块:{path}和方法的参数动态更新请求URL。

替换块是由{}包围的标识符。

用@Path注释对应的参数。

@Path中的字符串必须和替换块中的字符串相同

##原有路径:

https://www.wanandroid.com/article/list/0/json

    @GET("/article/list/{id}/json")
    Call<HomeData> getData(@Path("id") int id);

当然,也可以添加查询参数:

##原有路径:

https://www.wanandroid.com/article/list/0/json?cid=60

    @GET("article/list/{id}/json")
    Call<KnowledgeData> getData(@Path("id") int id, @Query("cid") int cid);

当参数比较复杂的时候,使用@QueryMap

	@GET("group/{id}/users")
	Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
带查询参数例子:

接口:

	public interface KnowledgeDataClient {
	    @GET("article/list/{id}/json")
	    Call<KnowledgeData> getData(@Path("id") int id, @Query("cid") int cid);
	}

方法:

    private void requestData() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.wanandroid.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        KnowledgeDataClient homeDataClient = retrofit.create(KnowledgeDataClient.class);
        Call<KnowledgeData> data = homeDataClient.getData(0,60);
        data.enqueue(new Callback<KnowledgeData>() {
            @Override
            public void onResponse(Call<KnowledgeData> call, Response<KnowledgeData> response) {
                mTvShow.setText(response.body().toString());
            }
            @Override
            public void onFailure(Call<KnowledgeData> call, Throwable t) {

            }
        });
    }

2:@Body – – REQUEST BODY

可以使用@Body注释将对象指定为HTTP请求体。

可以是一个JSON的映射对象,或者是一个RequestBody。

    @POST("GetMetroInfo.do")
    Call<Data> getData(@Body DataRequestBody requestBody);

3:@Headers和@Header

可以使用@Headers注释为方法设置静态标头。

直接设置:

	@Headers("Cache-Control: max-age=640000")
	@GET("widget/list")
	Call<List<Widget>> widgetList();

使用对象来设置多个

	@Headers({
	    "Accept: application/vnd.github.v3.full+json",
	    "User-Agent: Retrofit-Sample-App"
	})
	@GET("users/{username}")
	Call<User> getUser(@Path("username") String username);

也可以使用@Header注释动态更新请求标头。必须为@Header提供相应的参数。如果该值为空,则标头将被省略。

	@GET("user")
	Call<User> getUser(@Header("Authorization") String authorization)

4:@Url

我们有时需要请求的数据有可能不在baseUrl下,这时,我们就可以使用@Url来动态的绑定url

接口:

    public interface RequestData {
        @GET
        Call<ResponseBody> getData(@Url String url);
    }

请求方法:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .build();

    RequestData request = retrofit.create(RequestData.class);
    Call<ResponseBody> call = request.getData("https://www.baidu.com/");

DEMO使用了:GsonConverterFactory

记得添加converter-gson 依赖
implementation ‘com.squareup.retrofit2:converter-gson:2.5.0’

DEMO: <- – GET – ->

Activity:

public class BannerShowActivity extends AppCompatActivity {

    private List<BannerBean.DataBean> data = new ArrayList<>();
    private BannerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner_show);
        ListView mListView = findViewById(R.id.list_view);
        adapter = new BannerAdapter(this, android.R.layout.simple_list_item_1, data);
        mListView.setAdapter(adapter);
        //加载数据
        loadData();
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(BannerShowActivity.this, data.get(position).getImagePath(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void loadData() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.wanandroid.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        BannerClient bannerClient = retrofit.create(BannerClient.class);
        Call<BannerBean> banner = bannerClient.getBanner();
        banner.enqueue(new Callback<BannerBean>() {
            @Override
            public void onResponse(Call<BannerBean> call, Response<BannerBean> response) {
                boolean b = data.addAll(response.body().getData());
                if (b) {
                    adapter.notifyDataSetChanged();
                }
            }
            @Override
            public void onFailure(Call<BannerBean> call, Throwable t) {
            }
        });
    }
}

Adapter:

public class BannerAdapter extends ArrayAdapter<BannerBean> {

    private List<BannerBean.DataBean> mData;
    private Context mContext;
    private int mLayoutResource;

    public BannerAdapter(@NonNull Context context, int resource, List<BannerBean.DataBean> data) {
        super(context, resource);
        mData = data;
        mLayoutResource = resource;
        mContext = context;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        TextView tv;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(mLayoutResource, parent, false);
            tv = convertView.findViewById(android.R.id.text1);
            convertView.setTag(tv);
        } else {
            tv = (TextView) convertView.getTag();
        }
        tv.setText(mData.get(position).getTitle());
        return convertView;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

}

客户端请求接口:

public interface BannerClient {
    @GET("/banner/json")
    Call<BannerBean> getBanner();
}

DEMO: <- – POST – – >

接口:

public interface DataClient {
    @Headers("Accept:application/json")
    @POST("transportservice/action/{path}")
    Call<Data> getData(@Path("path") String path, @Body RequestBody requestBody);
}

Activity主要代码:

    private void requestHomeData() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        DataClient homeDataClient = retrofit.create(DataClient.class);
        RequestBody requestBody = RequestBody.create("{\"Line\":0,\"UserName\":\"user1\"}", MediaType.parse("application/json;charset=utf-8"));
        Call<Data> data = homeDataClient.getData(API.GET_URL, requestBody);
        data.enqueue(new Callback<Data>() {
            @Override
            public void onResponse(Call<Data> call, Response<Data> response) {
                mTvShow.setText(response.body().toString());
            }

            @Override
            public void onFailure(Call<Data> call, Throwable t) {

            }
        });
    }

注意事项:

1: Retrofit2 的baseUrl 必须以 /(斜线) 结束
2: 在Get请求时,如果baseUrl之后没有路径,也要在接口上给注解添加”/”,类似如下代码,否则会报错!
    public interface BlogService {
        @GET("/")
        Call<ResponseBody> getBlog();
    }
3: Retrofit2 的,如果有注解Path,那么,在@GET()中一定不能只写{path},否则会报错:

这样不行:

    @GET("{path}")
    Call<ResponseBody> getBannerData(@Path("path") String path);

应该至少这样写:

    @GET("other/{path}")
    Call<ResponseBody> getBannerData(@Path("path") String path);
4: Retrofit接口一般返回一个Call对象。
5: Retrofit依赖于Okhttp,注意添加Okhttp的依赖

使用OkHttp Log 拦截器

首先,添加依赖:

implementation “com.squareup.okhttp3:logging-interceptor:4.2.1”

然后就是如下代码:

//        使用okhttp日志拦截器
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

//        如果项目在DEBUG阶段,我们使用拦截器来拦截okhttp的请求
        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
            httpLoggingInterceptor.level(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(httpLoggingInterceptor);
        }

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(builder.build())
                .build();

使用OkHttp 拦截器来添加查询参数

        OkHttpClient.Builder okhttpBuilder = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
            @NotNull
            @Override
            public okhttp3.Response intercept(@NotNull Chain chain) throws IOException {
                Request request = chain.request();
                HttpUrl.Builder newHttpUrl = request.url().newBuilder().addQueryParameter("name", "value");
                Request.Builder url = request.newBuilder().url(newHttpUrl.build());
                return chain.proceed(url.build());
            }
        });
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                //ScalarsConverterFactory 使得方法返回值可以接受String(Call的泛型)
                .addConverterFactory(ScalarsConverterFactory.create())
                .client(okhttpBuilder.build())
                .build();

END

点赞