Retrofit封装了从Web API下载数据,解析成一个普通的java对象(POJO),这里我们就去天狗网使用他们的一个菜谱的API做简单演示,供大家一起学习思考。在天狗网的API文档网站http://www.tngou.net/doc/cook的菜谱API接口:http://www.tngou.net/api/cook/list
1)首先得添加支持包
1
|
compile 'com.squareup.retrofit2:retrofit:2.1.0' |
2)然后每一次使用都需要定义一个接口,用于下载网络数据,注意其中的{category}是为了之后更好的扩展性,我们定义一个未知的子目录,通过参数中指定可以访问固定的子目录,这个方式非常棒。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 ban */ 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); } |
//get请求与post请求 在retrofit中,只有请求不一样,其它都一样,具体如下 //get请求 @GET("api/{category}/list") Call<Tngou> getList(@Path("category") String category, @Query("id") int id, @Query("page") int page, @Query("rows") int rows); //post请求 @POST("api/{category}/list") @FormUrlEncoded Call<Tngou> getList1(@Path("category") String category, @Field("id") int id, @Field("page") int page, @Field("rows") int rows);
@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); @POST("/api/{category}/list")//使用Post实现 @FormUrlEncoded //使用Field属性必须添加这个 Call<Tngou> getListForPost(@Path("category") String path, @Field("id") int id, @Field("page") int page, @Field("rows") int rows);
3)由于我们返回的数据为Json数据,所以我们可以用它本身自带的Gson解析方式进行返回数据的解析,同样先导入支持包
1
|
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
|
4)我们写一个DataBean用于存放返回的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package com.example.nanchen.retrofitdemo.json; import com.google.gson.annotations.SerializedName; import java.util.List; /** *
Created by ban */ 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, 是一个类,所以要新建一个类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
package com.example.nanchen.retrofitdemo.json; import com.google.gson.annotations.SerializedName; /** *
菜谱 *
Created by ban */ 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文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<?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)主布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?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,我们在一个项目中最好都用一个团队的框架,这样才会让出错的几率大大降低。
当然别忘了添加支持包
1
|
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 ban 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 }
1、Retrofit的事务可以在之后立即取消,只需要调用call.cancel()即可。
2、Retrofit必须定义一个接口。
3、xml解析的jar Simple XML: com.squareup.retrofit:converter-simplexml
这里值得注意的是:
{占位符}和path都尽量出现在Url的path部分,url中的参数使用Query和QueryMap代替,保证接口定义的简洁。
另外,Query,Field和Part都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。