squareup 推出 retrofit2 已经有一段时间了,现在的版本比较稳定,没有什么大坑了。网络上的教程要么太简单,只是个Demo;要么有些落时,要么复用性比较差,所以自己写个教程,供大家参考。代码已上传至 https://github.com/Alex9Xu/RetrofitDemo
1. 首先在build.gradle引入依赖
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
注意,这里的 logging 用于输出网络交互的Log,对于开发调试极其有用。之前retrofit2因为不能输出Log被人嫌弃了很久,各高手实现了几种打印Log的方式,现在总算有官方的了。
2. 这是工具类
package com.alex9xu.hello.net; import android.app.Dialog; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; import com.alex9xu.hello.config.AppConfigInterface; import com.alex9xu.hello.utils.LogHelper; import java.io.IOException; import java.lang.ref.WeakReference; /** * Created by Alex9Xu@hotmail.com on 2016/7/13 */ public class RetrofitBase { private static final String TAG = "RetrofitBase"; private static Retrofit mRetrofit; // private static Dialog mLoadingDialog; private static WeakReference<Context> mContextRef; public static Retrofit retrofit() { if (mRetrofit == null) { OkHttpClient client; // Notice: The only differ of debug is: HttpLoggingInterceptor // Common Params: "version" and "server_call_version" will in every request if(!AppConfigInterface.isDebug) { client = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); HttpUrl originalHttpUrl = original.url(); HttpUrl url = originalHttpUrl.newBuilder() .addQueryParameter("call_version", AppConfigInterface.TO_SERVER_VERSION) .addQueryParameter("deviceType", AppConfigInterface.DEVICE_TYPE) .build(); // Request customization: add request headers Request.Builder requestBuilder = original.newBuilder() .addHeader("user-agent", "android") .url(url); Request request = requestBuilder.build(); return chain.proceed(request); } }) .build(); } else { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); client = new OkHttpClient.Builder() .addInterceptor(logging) .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); HttpUrl originalHttpUrl = original.url(); HttpUrl url = originalHttpUrl.newBuilder() .addQueryParameter("version", AppConfigInterface.TO_SERVER_VERSION) .addQueryParameter("deviceType", AppConfigInterface.DEVICE_TYPE) .build(); // Request customization: add request headers Request.Builder requestBuilder = original.newBuilder() .addHeader("user-agent", "android") .url(url); Request request = requestBuilder.build(); return chain.proceed(request); } }) .build(); } mRetrofit = new Retrofit.Builder() .baseUrl(AppConfigInterface.BASE_COM_URL) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build(); } return mRetrofit; } // Encapsulation Request and Response public static <T> void AddToEnqueue(Call<T> baseCall, Context context, boolean isShowDlg, final NetRequestListener listener) { mContextRef = new WeakReference<>(context); // if(isShowDlg && null == mLoadingDialog && null != mContextRef.get()) { // mLoadingDialog = DialogUtil.showLoginDialog(mContextRef.get()); // mLoadingDialog.getWindow().setBackgroundDrawable(new BitmapDrawable()); // } // if(isShowDlg) { // mLoadingDialog.show(); // } baseCall.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, retrofit2.Response<T> response) { LogHelper.d(TAG, "toEnqueue, onResponse:"); if (null != response.body()) { if(response.code() == 200) { LogHelper.d(TAG, "toEnqueue, onResponse Suc"); // if(null != mLoadingDialog) { // mLoadingDialog.dismiss(); // } listener.onRequestSuc(response.code(), response); } else { LogHelper.d(TAG, "toEnqueue, onResponse Fail:" + response.code()); // if(null != mLoadingDialog) { // mLoadingDialog.dismiss(); // } listener.onRequestFail(response.code(), response.message()); } } else { LogHelper.d(TAG, "toEnqueue, onResponse Fail"); // if(null != mLoadingDialog) { // mLoadingDialog.dismiss(); // } listener.onRequestFail(response.code(), response.message()); } } @Override public void onFailure(Call<T> call, Throwable t) { LogHelper.d(TAG, "toEnqueue, onFailure Fail"); // if(null != mLoadingDialog) { // mLoadingDialog.dismiss(); // } listener.onRequestFail(AppConfigInterface.RESULT_FAIL_UNKNOW, null); } }); } public static void stopLoadingDlg(Context context) { // if(null != mContextRef && context == mContextRef.get() && null != mLoadingDialog) { // mLoadingDialog.dismiss(); // mLoadingDialog = null; // } } }
讲解一下:
(1) 通过 addInterceptor 实现的打印日志及加入多个公共参数功能。
(2) 除了含有 HttpLoggingInterceptor 外,测试的和正式的,没有任何区别。通过全局变量控制是否为正式环境,如果是正式环境则不输出网络交互相关的Log。
(3) 可以通过 addQueryParameter("deviceType", "0") 的形式加入多个公共参数,这样所有的请求都会带该参数。
(4) 这里 BASE_COM_URL 是 http://test.hello.com/ 的形式。
3. 使用方式:
(1) 先写接口
package com.alex9xu.hello.net.apis; import android.support.v4.util.ArrayMap; import com.alex9xu.hello.config.AppConfigInterface; import com.alex9xu.hello.model.WeatherResult; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.QueryMap; /** * Created by Alex9Xu@hotmail.com on 2016/7/14 */ public interface CityWeatherApi { @GET(AppConfigInterface.GET_WEATHER) Call<WeatherResult> getClassify(@QueryMap ArrayMap<String,String> paramMap); }
这里通过Post提交参数,参数存储在Map里,可以添加多组参数。注意,我使用了ArrayMap,这是Android里特有的一种形式,内存占用只有HashMap的十分之一左右。
String CLASSIFYLIST = "query/classify.html";
(2) 再写返回值结构
package com.alex9xu.hello.model.Entity; /** * Created by Alex9Xu@hotmail.com on 2016/7/18 */ public class Weatherinfo { private String city; private String cityid; private String temp; private String WD; private String WS; private String SD; private String WSE; private String time; private String isRadar; private String Radar; private String njd; private String qy; public String getCity() { return city; } public String getTemp() { return temp; } }
package com.alex9xu.hello.model; import com.alex9xu.hello.model.Entity.Weatherinfo; /** * Created by Alex9Xu@hotmail.com on 2016/7/18 */ public class WeatherResult { private Weatherinfo weatherinfo; public Weatherinfo getWeatherinfo() { return weatherinfo; } }
返回的数据写成如上形式,以利于复用。
(3) 调用
import com.alex9xu.test.model.ClassifyListResult; import com.alex9xu.test.model.entity.ClassfiyBean; import com.alex9xu.test.net.ClassifyApi; import com.alex9xu.test.net.RetrofitBase;
/** * Created by Alex9Xu@hotmail.com on 2016/7/14 */ public class MainActivity extends BaseActivity {
// Note: make all the Activities extends BaseActivity to manage private static final String TAG = "MainActivity"; private TextView mTvwShowInfo; private Call<WeatherResult> mWeatherCall;private void getData() { // Notice: ArrayMap requires less memory in Android compare with HashMap (about 10%) CityWeatherApi classifyApi = RetrofitBase.retrofit().create(CityWeatherApi.class); ArrayMap<String,String> paramMap = new ArrayMap<>(); paramMap.put("sortType", "1"); paramMap.put("uid", "654321"); mWeatherCall = classifyApi.getClassify(paramMap); RetrofitBase.AddToEnqueue(mWeatherCall, MainActivity.this, true, new NetRequestListener() { @Override public void onRequestSuc(int code, Response response) { LogHelper.d(TAG, "onRequestSuc"); Response<WeatherResult> resultResponse = response; if(null != resultResponse.body().getWeatherinfo()) { Weatherinfo info = resultResponse.body().getWeatherinfo(); StringBuilder strBld = new StringBuilder(); strBld.append(info.getCity()); strBld.append(getString(R.string.temperature)); strBld.append(getString(R.string.colon)); strBld.append(info.getTemp()); mTvwShowInfo.setText(strBld.toString()); } } @Override public void onRequestFail(int code, String reason) { LogHelper.d(TAG, "onRequestFail: " + code + ", " + reason); } }); } @Override protected void onStop() { super.onStop(); // Notice: If the web operate is to update UI, you can cancel it when onStop mWeatherCall.cancel(); }...
讲解:会拼接成 https://test.hello.com/query/classify.html?uid=654321&version=1.0&Id=123456&deviceType=0 ,注意,其中两项是公共参数。
好了,这样就可以正常运行了。