大家都知道,安卓最大的特點就是開源化,這自然會產生很多十分好用的第三方API,而基本每一個APP都會與網絡操作和緩存處理機制打交道,當然,你可以自己通過HttpUrlConnection再通過返回數據進行解析解決,而我們自己學的東西大多數情況下都沒有針對網絡很糟糕的情況進行優化。下面就給大家帶來Square Inc這家有創新精神的公司留下的Retrofit網絡加載庫的使用!
項目已經同步至github:https://github.com/nanchen2251/retrofitDemo
Retrofit封裝了從Web API下載數據,解析成一個普通的java對象(POJO),這里我們就去天狗網使用他們的一個菜譜的API做簡單演示,供大家一起學習思考。在天狗網的API文檔網站http://www.tngou.net/doc/cook的菜譜API接口:http://www.tngou.net/api/cook/list
詳細使用:
1)首先得添加支持包
compile 'com.squareup.retrofit2:retrofit:2.1.0'
2)然后每一次使用都需要定義一個接口,用於下載網絡數據,注意其中的{category}是為了之后更好的擴展性,我們定義一個未知的子目錄,通過參數中指定可以訪問固定的子目錄,這個方式非常棒。
package com.example.nanchen.retrofitdemo;
import com.example.nanchen.retrofitdemo.json.Tngou;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
/**
* Created by 南塵 on 16-7-15.
*/
public interface Service {
@GET("/")//網址下面的子目錄
Call<String> getBaidu();
@GET("/api/{category}/list")//網址下面的子目錄 category表示分類,因為子目錄只有一點不一樣
Call<Tngou> getList(@Path("category") String path,@Query("id") int id, @Query("page") int page, @Query("rows") int rows);
}
3)由於我們返回的數據為Json數據,所以我們可以用它本身自帶的Gson解析方式進行返回數據的解析,同樣先導入支持包
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
4)我們寫一個DataBean用於存放返回的數據
package com.example.nanchen.retrofitdemo.json;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
* Created by 南塵 on 16-7-15.
*/
public class Tngou {
//加上注解
@SerializedName("status")
private boolean status;
@SerializedName("total")
private int total;
@SerializedName("tngou")
private List<Cook> list;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<Cook> getList() {
return list;
}
public void setList(List<Cook> list) {
this.list = list;
}
}
里面要放List, 是一個類,所以要新建一個類。
package com.example.nanchen.retrofitdemo.json;
import com.google.gson.annotations.SerializedName;
/**
* 菜譜
* Created by 南塵 on 16-7-15.
*/
public class Cook {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;//名稱
@SerializedName("food")
private String food;//食物
@SerializedName("img")
private String img;//圖片
@SerializedName("images")
private String images;//圖片,
@SerializedName("description")
private String description;//描述
@SerializedName("keywords")
private String keywords;//關鍵字
@SerializedName("message")
private String message;//資訊內容
@SerializedName("count")
private int count;//訪問次數
@SerializedName("fcount")
private int fcount;//收藏數
@SerializedName("rcount")
private int rcount;//評論讀數
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public String getImages() {
return images;
}
public void setImages(String images) {
this.images = images;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getKeywords() {
return keywords;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getFcount() {
return fcount;
}
public void setFcount(int fcount) {
this.fcount = fcount;
}
public int getRcount() {
return rcount;
}
public void setRcount(int rcount) {
this.rcount = rcount;
}
}
5)我們做一個簡單演示,把返回並解析的數據放在ListView里面顯示,所以自定義一個顯示Item的Xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@mipmap/ic_launcher"
android:id="@+id/item_iv"/>
<LinearLayout
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="標題"
android:textAppearance="@android:style/TextAppearance.Large"
android:id="@+id/item_title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="abcabcacbacbacbacbacbacacacacacasdadasd"
android:ellipsize="end"
android:id="@+id/item_info"/>
</LinearLayout>
</LinearLayout>
6)主布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.nanchen.retrofitdemo.RetrofitJsonActivity">
<ListView
android:id="@+id/json_lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</RelativeLayout>
7)自定義適配器,其中又用到了他們的圖片加載框架picasso,我們在一個項目中最好都用一個團隊的框架,這樣才會讓出錯的幾率大大降低。
當然別忘了添加支持包
compile 'com.squareup.picasso:picasso:2.3.2'
再是Adapter
1 package com.example.nanchen.retrofitdemo.json; 2 3 import android.content.Context; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 import android.widget.BaseAdapter; 8 import android.widget.ImageView; 9 import android.widget.TextView; 10 11 import com.example.nanchen.retrofitdemo.R; 12 import com.squareup.picasso.Picasso; 13 14 import java.util.Collection; 15 import java.util.List; 16 17 /** 18 * Created by 南塵 on 16-7-15. 19 */ 20 public class MyAdapter extends BaseAdapter { 21 22 private Context context; 23 private List<Cook> list; 24 25 public MyAdapter(Context context, List<Cook> list) { 26 this.context = context; 27 this.list = list; 28 } 29 30 @Override 31 public int getCount() { 32 if (list != null){ 33 return list.size(); 34 } 35 return 0; 36 } 37 38 @Override 39 public Object getItem(int position) { 40 return list.get(position); 41 } 42 43 @Override 44 public long getItemId(int position) { 45 return position; 46 } 47 48 @Override 49 public View getView(int position, View convertView, ViewGroup parent) { 50 if (convertView == null){ 51 convertView = LayoutInflater.from(context).inflate(R.layout.list_item,parent,false); 52 convertView.setTag(new ViewHolder(convertView)); 53 } 54 ViewHolder holder = (ViewHolder) convertView.getTag(); 55 Cook cook = list.get(position); 56 holder.tv_title.setText(cook.getName()); 57 holder.tv_info.setText(cook.getDescription()); 58 //使用同樣開發團隊的Picasso支持包進行圖片加載,由於接口中返回的img路徑不是全的,所以需要加上網站前綴 59 Picasso.with(context).load("http://tnfs.tngou.net/img"+cook.getImg()).into(holder.iv); 60 return convertView; 61 } 62 63 public void addAll(Collection<? extends Cook> collection){ 64 list.addAll(collection); 65 notifyDataSetChanged(); 66 } 67 68 public static class ViewHolder{ 69 private final ImageView iv; 70 private final TextView tv_title; 71 private final TextView tv_info; 72 73 public ViewHolder(View item){ 74 iv = ((ImageView) item.findViewById(R.id.item_iv)); 75 tv_title = ((TextView) item.findViewById(R.id.item_title)); 76 tv_info = ((TextView) item.findViewById(R.id.item_info)); 77 } 78 } 79 }
8)再看看Activity的代碼
1 package com.example.nanchen.retrofitdemo; 2 3 import android.os.Bundle; 4 import android.support.v7.app.AppCompatActivity; 5 import android.widget.ListView; 6 7 import com.example.nanchen.retrofitdemo.json.Cook; 8 import com.example.nanchen.retrofitdemo.json.MyAdapter; 9 import com.example.nanchen.retrofitdemo.json.Tngou; 10 11 import java.util.List; 12 13 import retrofit2.Call; 14 import retrofit2.Callback; 15 import retrofit2.Response; 16 import retrofit2.Retrofit; 17 import retrofit2.converter.gson.GsonConverterFactory; 18 19 public class RetrofitJsonActivity extends AppCompatActivity implements Callback<Tngou> { 20 21 22 private ListView lv; 23 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_retrofit_json); 28 29 Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.tngou.net") 30 .addConverterFactory(GsonConverterFactory.create()).build(); 31 Service service = retrofit.create(Service.class); 32 Call<Tngou> call = service.getList("cook",0, 1, 20); 33 call.enqueue(this); 34 lv = (ListView) findViewById(R.id.json_lv); 35 36 } 37 38 @Override 39 public void onResponse(Call<Tngou> call, Response<Tngou> response) { 40 List<Cook> list = response.body().getList(); 41 lv.setAdapter(new MyAdapter(this,list)); 42 } 43 44 @Override 45 public void onFailure(Call<Tngou> call, Throwable t) { 46 47 } 48 }
最后上一波運行圖

2016年8月1日補充
————————————————————————————————————————————————
下面帶來一些2.0版本后需要注意的問題。
1)Retrofit 2.0版本后趨向於在baseUrl中以“/”結尾,@Url不要以“/”開頭,這樣更專業。額,其實你還可以把整個地址放在接口中去,這樣baseUrl就忽略了。
1 Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.tngou.net/") 2 .addConverterFactory(GsonConverterFactory.create()).build();
2)Retrofit的事務可以在之后立即取消,只需要調用call.cancel()即可,好簡單哈哈。
3)官方提供了好幾種ConverterFactory,適合你的才是最好的,自己去選擇吧~
Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml
4)額,你還可以選擇實現Converter.Factory接口來自定義一個converter,啦啦啦。
5)oh,no,遇到服務器返回的時間格式很奇葩怎么辦?哈哈,其實你完全可以通過GsonBuilder().create()創建一個gson對象作為Converter的參數來實現自定義Gson對象呀。
1 //需要使用的時候放到GsonConverterFactory.create(gson)中作為參數即可。 2 Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); 3 4 5 6 Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.tngou.net/") 7 .addConverterFactory(GsonConverterFactory.create(gson)).build();
6)之前有的小伙伴會問為什么我們在Demo中用call.enqueue而沒有用execute,這是因為我們一般都選擇用enqueue,這樣會默認在異步線程中執行,少寫一個線程,何樂而不為呢?
7)額,之前Demo沒有對Retrofit 2.0 的注解方式進行完善的整理,這里給大家帶來一些更詳細的東西。
8)首先帶來http的請求方法:

我們一般常用的就是GET請求和POST請求,當然你或許會用到http的其他注解方式,值得注意的是HTTP注解方式可以涵蓋上面的任意一種注解。
而它有三個屬性:method,path,hasBody。
9)再帶來標記類

額,上面的這個表單請求,我相信在post請求中用的不少吧,你值得嘗試。
10)當然就少不了我們的參數類咯,圖片來源於網絡。

這里值得注意的是:
{占位符}和path都盡量出現在Url的path部分,url中的參數使用Query和QueryMap代替,保證接口定義的簡潔。
另外,Query,Field和Part都支持數組和實現了Iterable接口的類型,如List,Set等,方便向后台傳遞數組。
好了,這個強大的Retrofit就補充到這里了,大家可以自行補充。
