前几天学习了Retrofit相关知识,网络请求这块一般很抵触去学,但是又像是一个宝箱诱惑着你去探索尝试,于是,看了官方两篇基础教程,先简单实现一下访问豆瓣或者知乎网站的数据,并将其用Listview或者RecyclerView显示出来。先放上最后的效果图:
话不多说,让我们边学边做。
一. Retrofit概述:
1.Retrofit是什么?
Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求,其源码详见OkHttp Github。官方描述是这样的:A type-safe REST client for Android and Java.
对于Java和Android来讲,它是一款安全的REST客户端。从定义来讲可能比较晦涩难懂,最好的方式就是通过实战来挖掘它的用法。
2.Retrofit用来干嘛?
用注解去描述HTTP请求,同时Retrofit默认集成URL参数替换和查询参数.除此之外它还支持 Multipart请求和文件上传。开发一个自己的用于请求 REST API 的类型安全的网络请求库是一件很痛苦的事情:你需要处理很多功能,比如建立连接,处理缓存,重连接失败请求,线程,响应数据的解析,错误处理等等。从另一方面来说,Retrofit 是一个有优秀的计划,文档和测试并且经过考验的库,它会帮你节省你的宝贵时间以及不让你那么头痛。
二. Retrofit的用法:
pom.xml
或者build.gradle
中定义Retrofit和它的依赖关系。当运行命令去构建你的项目时,构建系统会在你的项目里下载相应的库。 我们建议用OkHttp搭配Retrofit,OKHttp同样需要定义Okio依赖。
package com.app.ui; /** * Created by MuFeng on 2017/4/18. * 应用网络请求的工具类 */ public class ApiUtils { public static final String BASE_URL = "http://news-at.zhihu.com/api/4/"; }
GET
, POST
, PUT
, PATCH
, 以及 DELETE
。在这里,
我们将执行一个
GET
请求。
/** * Created by MuFeng on 2017/4/18. * 创建网络请求的接口 */ public interface WebRequestService { @GET("news/latest") Call<NewsInfo> getRequest();//从服务器端过滤数据,获取传入变量对应的数据 }
/** * Created by MuFeng on 2017/4/18. * 适配 */ public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{ private Context context; List<String> titleData; List<String> bmpData; private MyItemListener myItemListener;//item的点击监听器 public NewsAdapter(Context context, List<String> titleData, List<String> bmpData){ this.titleData=titleData; this.bmpData=bmpData; this.context=context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater layoutInflater=LayoutInflater.from(context); View view=layoutInflater.inflate(R.layout.item_news,parent,false); ViewHolder myViewHolder=new ViewHolder(view); return myViewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { TextView textView=holder.textView; ImageView imageView=holder.imageView; textView.setText(titleData.get(position)); Glide.with(context).load(bmpData.get(position)).into(imageView);//加载图片 } @Override public int getItemCount() { return titleData.size(); } public class ViewHolder extends RecyclerView.ViewHolder{ ImageView imageView; TextView textView; public ViewHolder(View itemView) { super(itemView); imageView= (ImageView) itemView.findViewById(R.id.item_img); textView= (TextView) itemView.findViewById(R.id.item_text); } } //item的监听器接口 public interface MyItemListener{ void onClickMyItem(View view,int position); } //设置监听 public void setMyItemListener(MyItemListener myItemListener){ this.myItemListener=myItemListener; } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp" android:layout_marginBottom="10dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical"> <ImageView android:id="@+id/item_img" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="5dp"/> <TextView android:id="@+id/item_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:layout_marginLeft="8dp"/> </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout>这里我们只需要显示的内容是新闻的title以及图片。
enqueue()
会发送一个异步请求,当响应结果返回的时候通过回调通知应用。因为是异步请求,所以 Retrofit 将在后台线程处理,这样就不会让 UI 主线程堵塞或者受到影响。
要使用 enqueue()
,你必须实现这两个回调方法:
onResponse()
onFailure()
只有在请求有响应结果的时候才会调用其中一个方法。
onResponse()
:接收到 HTTP 响应时调用。该方法会在响应结果能够被正确地处理的时候调用,即使服务器返回了一个- 错误信息。所以如果你收到了一个 404 或者 500 的状态码,这个方法还是会调用。为了拿到状态码以便后续的处理,你可以
- 使用
response.code()
方法。你也可以使用isSuccessful()
来确定返回的状态码是否在 200-300 范围内,该范围的 - 状态码也表示响应成功。
onFailure()
:在与服务器通信的时候发生网络异常或者在处理请求或响应的时候发生异常的时候调用。
如果要执行同步请求,你可以使用 execute()
方法。要注意同步请求在主线程会阻塞用户的任何操作。所以不要在主线程执行同步请求,要在后台线程执行。
下面附上实现代码:
//进行网络请求,获取首页数据 private void fillNewsContent() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(ApiUtils.BASE_URL) .addConverterFactory(GsonConverterFactory.create())//使用Gson解析 .build(); WebRequestService service = retrofit.create(WebRequestService.class); Call<NewsInfo> call = service.getRequest(); call.enqueue(new Callback<NewsInfo>() { @Override public void onResponse(Call<NewsInfo> call, Response<NewsInfo> response) { //成功 if (response.isSuccessful() && response.body() != null) { //解析成功 Log.d("Retrofit", "onResponse: 解析成功"); stories = response.body().getStories(); initDatas();//初始化数据 } else { //解析失败 Log.d("Retrofit", "onResponse: 解析失败"+response.message()+response.code()); } } @Override public void onFailure(Call<NewsInfo> call, Throwable t) { //失败 Log.d("TAG", "onFailure: 响应失败"); } }); }
private NewsAdapter newsAdapter; private RecyclerView recyclerView; List<Story> stories; List<String> titleData; List<String> bmpData;......
init(); fillNewsContent();//填充数据 RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); newsAdapter=new NewsAdapter(MainActivity.this,titleData,bmpData); recyclerView.setAdapter(newsAdapter);
//初始化数据,填充首页数据 private void initDatas() { Log.d("TAG", "initDatas: "); for (Story s:stories){ titleData.add(s.getTitle()); if(s.getImages()!=null) { bmpData.add(s.getImages().get(0)); }else { bmpData.add("http://pic2.zhimg.com/afecdc04983a8e261326386995150599_t.jpg"); } } newsAdapter.notifyDataSetChanged(); } private void init() { recyclerView= (RecyclerView) findViewById(R.id.my_recyclerview); titleData = new ArrayList<String>(); bmpData = new ArrayList<String>(); }