请务必实现自己的诺言!
给自己写在前面的话:
一定要封装一个上乘的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