Home - grandsun/myNote GitHub Wiki


retrofit的基本使用

一、引入依赖 compile 'com.google.code.gson:gson:2.6.2' compile 'com.squareup.retrofit2:retrofit:2.0.2' compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.okhttp3:okhttp:3.2.0' 来源: http://www.2cto.com/kf/201605/506107.html

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

工具类 public class RetrofitUtil { private RetrofitUtil () { } @WorkerThread public static String getDataWithMethod (String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.getSearch(method, map); Response execute = call.execute(); ResponseBody body = execute.body(); if (body != null) { String jsonRes = body.string(); return jsonRes; } return null; } @WorkerThread public static String postDataWithMethod (String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder().baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.postSearch(method, map); Response execute = call.execute();//execute代表的是同步请求 String string = execute.body().string(); return string; } @WorkerThread public static String postTweets (String method, Map<String, RequestBody> data) throws IOException { Retrofit retrofit = new Retrofit.Builder().baseUrl(Const.BASE_URL).build(); MultiPartResult result = retrofit.create(MultiPartResult.class); Call call = result.uploadImage(method, data); Response execute = call.execute(); ResponseBody body = execute.body(); if (body != null) { String string = body.string(); return string; } return ""; } //第一步,先定义一个接口 public interface ResultByMap { @GET("{method}") Call getSearch (@Path("method") String method, @QueryMap Map<String, String> map); @FormUrlEncoded @POST("{method}") Call postSearch (@Path("method") String method, @FieldMap Map<String, String> map); } public interface MultiPartResult { @Multipart @POST("{method}") Call uploadImage (@Path("method") String method, @PartMap Map<String, RequestBody> body); } }

/**

  • RetrofitUtil访问网络
  • 方式:同步
  • 请求参数: Map集合
  • Created by dell on 2016/6/14. / public class RetrofitUtil { /*
    • get方式,无请求参数形式 / public static String getData (String method) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); //Call call=result.getData(); 方式1.ok // Call call=result.getSearch(method); Call call = result.getSearch(method); String jsonRes; Response execute = call.execute(); jsonRes = execute.body().string(); return jsonRes; } /*
    • get方式,有请求参数形式
    • 创建Map集合,来存放请求参数 / public static String getDataWithMethod (String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.getSearch(method, map); Response execute = call.execute(); String jsonRes = execute.body().string(); return jsonRes; } /*
    • post方式,有请求参数形式
    • method: 拼接url / public static String postData (String baseUrl, String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(baseUrl).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.postSearch(method, map); Response execute = call.execute(); String jsonRes = execute.body().string(); return jsonRes; } public static String postData (String method, Map<String, String> map) throws IOException { return postData(Const.BASE_URL, method, map); } public static String postDataMultiPart (String method1, String method2, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); MultiPartRequest multiPartRequest = retrofit.create(MultiPartRequest.class); Call data = multiPartRequest.getDataPost(method1, method2, map); System.out.println("开始请求数据"); Response execute = data.execute(); return execute.body().string(); } /*
    • 遇到类似
    • @param method1
    • @param method2
    • @return
    • @throws IOException / public static String getData (String method1, String method2) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); MultiPartRequest multiPartRequest = retrofit.create(MultiPartRequest.class); Call data = multiPartRequest.getData(method1, method2); Response execute = data.execute(); return execute.body().string(); } /*
    • 传递map集合来表达键值对信息 / public interface ResultByMap { @GET("home") Call getData (); @GET("{method}") Call getSearch (@Path("method") String method); @GET("{method}") Call getSearch (@Path("method") String method, @QueryMap Map<String, String> map); //有请求参数 @FormUrlEncoded @POST("{method}") Call postSearch (@Path("method") String method, @FieldMap Map<String, String> map); } /*
    */ public interface MultiPartRequest { @GET("{method1}/{method2}") Call getData (@Path("method1") String method1, @Path("method2") String method2); @FormUrlEncoded @POST("{method1}/{method2}") Call getDataPost (@Path("method1") String method1, @Path("method2") String method2, @FieldMap Map<String, String> map); @GET("{method1}/{method2}") Call getData (@Path("method1") String method1, @Path("method2") String method2, @QueryMap Map<String, String> map); } }

一:retrofit

1.异步和同步的区别:
                                异步在请求数据的时候,会开一个子线程;同步在请求网络数据的时候会一直卡在哪儿,直到拿到数据。需要特别注意,是要拿到数据后再继续操作(此时如果异步请求,可能会报空指针个异常。在要先请求到id,token一类的值,然后才能加载网页数据的情况下,优先对id和token使用同步请求。请求之后,再加载网页数据。);还是可以先操作,数据不直接影响下一步操作。

2.推荐阅读:D:\android书籍\网页资料\Retrofit分析-漂亮的解耦套路 - 简书.htm;D:\android书籍\网页资料\给 Android 开发者的 RxJava 详解.htm

注意点:
            1.网络权限,在清单文件中注册:<uses-permission android:name="android.permission.INTERNET"/>
            2.导入依赖库:compile('libs/convert-gson-2.0.0-beta2.jar')
                                    compile('libs/okhttp-2.7.5.jar')
                                    compile('libs/okio-1.6.0.jar')
                                    compile('libs/retrofit-2.1.0-beta2.jar')
                                   compile('libs/json.jar')<--如果在解析jsonbean的时候报错,就添加这个依赖-->

自定义接口中使用的retrofit的注解方法解释:

retrofit原理的介绍:
        1.retrofit的最大特点就是解耦,要解耦就需要大量的设计模式:外观模式,动态代理,策略模式,观察者模式。当然还有Builder模式,工厂等这些简单的设计模式。



    1.异步请求

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Toast; import com.squareup.okhttp.ResponseBody; import java.io.IOException; import java.util.HashMap; import java.util.Map; import retrofit.Call; import retrofit.Callback; import retrofit.Response; import retrofit.Retrofit; import retrofit.http.Field; import retrofit.http.FormUrlEncoded; import retrofit.http.GET; import retrofit.http.HTTP; import retrofit.http.POST; import retrofit.http.Path; import retrofit.http.Query; import retrofit.http.QueryMap; public class MainActivity extends AppCompatActivity { private final String baseUrl = "http://169.254.176.124:8080/market/home/";//用于拼接网络请求地址url @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

}
 publc void getNetData1(View view){
 
  Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).addConvertFactory(GsonConverterFactory.create()).build();
  Result result = retrofit.create(Result.class);
  Call<XXXJsinBean> callBean = result.getHome();
  call.enqueue(new Callback< XXXJsinBean>() {
        //接口的返回数据
        @Override
        public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {

            try {
                Toast.makeText(MainActivity.this,response.body().getResponse(),Toast.LENGTH_SHORT).show();//getResponse()是XXXJsonBean中的方法
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Throwable throwable) {

        }
    });
    }
 

//布局文件里面id为“getNetData”的Button的点击事件
public void getNetData(View view){
    //使用retrofit请求拿数据
    //第二步,创建出retrofit对象
    Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).build();
    Result result = retrofit.create(Result.class);//创建接口对象

// Call call = result.getData();;//调用接口对象的接口方法返回一个call对象,用于业务操作 // Call call = result.getRecommend();;//调用接口对象的接口方法返回一个call对象,用于业务操作 // Call call = result.getSearch("a",0,10,"saleDown");;//调用接口对象的接口方法返回一个call对象,用于业务操作 // Call call = result.getSearch1("search","a",0,10,"saleDown");;//调用接口对象的接口方法返回一个call对象,用于业务操作 Map<String,String> params = new HashMap<>(); params.put("keyword", "a"); params.put("page","0"); params.put("pageNum","10"); params.put("orderby","saleDown"); //Call call = result.getSearch2("search",params);//调用接口对象的接口方法返回一个call对象,用于业务操作 Call call = result.getSearchByPost("a",0,10,"saleDown"); // call.enqueue();//异步请求 // call.execute();//同步请求 call.enqueue(new Callback() { //接口的返回数据 @Override public void onResponse(Response response, Retrofit retrofit) { try { Toast.makeText(MainActivity.this,response.body().string(),Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Throwable throwable) { } }); } //第一步,定义接口 public interface Result { @GET("home")//“home”是要拼接在baseUrl后面的请求体。具体的值根据接口文档提供的网页链接相关信息为准 Call getData(); @GET("search/recommend") Call getRecommend(); //http://169.254.176.124:8080/market/search?keyword=a&page=0&pageNum=10&orderby=saleDown @GET("search") Call getSearch(@Query("keyword") String keyword, @Query("page") int page, @Query("pageNum") int pageNum, @Query("orderby") String orderby); @GET("{method}")//用于动态的设置接口后缀。具体的值,在调用getSearch1()的时候传入。 Call getSearch1(@Path("method") String method ,@Query("keyword") String keyword, @Query("page") int page, @Query("pageNum") int pageNum, @Query("orderby") String orderby); @GET("{method}")//@QueryMap Map<String, String> map 用于设置多个参数 Call getSearch2(@Path("method") String method , @QueryMap Map<String, String> map); @FormUrlEncoded @POST("search")//在使用post请求时,必须使用@FormUrlEncoded进行编码,否则出错,post进行拼接时,要用@File来进行url的拼接 Call getSearchByPost(@Field("keyword") String keyword,@Field("page") int page, @Field("pageNum") int pageNum, @Field("orderby") String orderby); @GET("home") Call getHome(); //先建立一个XXX的JsonBean的类,然后可以直接获得解析后的数据XXXJsinBean对象类型的数据 这行是后来添加的 } }

    2.同步请求

import android.support.annotation.WorkerThread; import java.io.IOException; import java.util.Map; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.PartMap; import retrofit2.http.Path; import retrofit2.http.QueryMap; /**

  • Created by MaxW on 2016/7/30. */ public class RetrofitUtil { private RetrofitUtil () { } @WorkerThread public static String getDataWithMethod (String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder(). baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.getSearch(method, map); Response execute = call.execute(); ResponseBody body = execute.body(); if (body != null) { String jsonRes = body.string(); return jsonRes; } return null; } @WorkerThread public static String postDataWithMethod (String method, Map<String, String> map) throws IOException { Retrofit retrofit = new Retrofit.Builder().baseUrl(Const.BASE_URL).build(); ResultByMap result = retrofit.create(ResultByMap.class); Call call = result.postSearch(method, map); Response execute = call.execute(); String string = execute.body().string(); return string; } @WorkerThread public static String postTweets (String method, Map<String, RequestBody> data) throws IOException { Retrofit retrofit = new Retrofit.Builder().baseUrl(Const.BASE_URL).build(); MultiPartResult result = retrofit.create(MultiPartResult.class); Call call = result.uploadImage(method, data); Response execute = call.execute(); ResponseBody body = execute.body(); if (body != null) { String string = body.string(); return string; } return ""; }

    // public static final String BASE_URL = "http://www.oschina.net/action/openapi/"; public interface ResultByMap { @GET("{method}") Call getSearch (@Path("method") String method, @QueryMap Map<String, String> map); @FormUrlEncoded @POST("{method}") Call postSearch (@Path("method") String method, @FieldMap Map<String, String> map); } public interface MultiPartResult { @Multipart @POST("{method}") Call uploadImage (@Path("method") String method, @PartMap Map<String, RequestBody> body); } }

3.使用retrofit工具类的时候使用方法回掉,取得要获得的数据

3.1 自定义的retrofit接口 import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.GET; /**

  • Created by CC on 2016/6/15.

  • Hello wolrd / public interface HomeApi { @GET("home") Call getHome(); } 3.2retrofit工具类 import com.itheima.retrofitsample2.api.HomeApi; import java.io.IOException; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; /*

  • Created by CC on 2016/6/15.

  • Hello wolrd */ public class RetrofitUtils { static String baseUrl = "http://10.0.2.2:8080/market/"; public static void getHome(final OnRetrofitResponse onRetrofitResponse) { Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).build(); retrofit .create(HomeApi.class) .getHome() .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { String reulst=null;。 try { reulst= response.body().string(); } catch (IOException e) { e.printStackTrace(); } if (onRetrofitResponse!=null) {//注意这个if不能写在try..catch里面。否则如果遇到异常。在这段代码在try..catch里面就不会被执行了 onRetrofitResponse.onResponse(reulst); } } @Override public void onFailure(Call call, Throwable throwable) { } }); } public interface OnRetrofitResponse{ void onResponse(String result); } } 3.3 mainActivity import android.os.Bundle; import android.widget.TextView; import com.itheima.retrofitsample2.api.HomeApi; import com.itheima.retrofitsample2.util.RetrofitUtils; import java.io.IOException; import butterknife.Bind; import butterknife.ButterKnife; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends BaseActivity { String reulst = null; @Bind(R.id.tv_result) TextView tvResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this);

    //callbackway(); //第一种,没使用工具类,作为内部类使用。。但是继承了一个基类BaseActivity..这样代码也是很简洁的 mRetrofit .create(HomeApi.class) .getHome() .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { try { String result = response.body().string(); updateView(result); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call call, Throwable throwable) { } }); }

    //第二种。写一个单独的工具类,然后使用接口回掉 private void callbackway() { RetrofitUtils.getHome(new RetrofitUtils.OnRetrofitResponse() { @Override public void onResponse(String result) { updateView(result); } }); } private void updateView(String reulst) { tvResult.setText(reulst); } } 3.4 不单独写一个retrofit的工具类,,但是使用自定义的BaseActivity。。这个基类主要是建立一个retrofit的对象。 import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import retrofit2.Retrofit; public class BaseActivity extends AppCompatActivity { public Retrofit mRetrofit; public String baseUrl="http://10.0.2.2/market/"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRetrofit = new Retrofit.Builder().baseUrl(baseUrl).build(); } }

2 Retrofit的原理http://www.jianshu.com/p/c1a3a881a144

从上面Retrofit的使用来看,Retrofit就是充当了一个适配器(Adapter)的角色:将一个Java接口翻译成一个Http请求,然后用OkHttp去发送这个请求

Volley描述一个HTTP请求是需要创建一个Request对象,而执行这个请求呢,就是把这个请求对象放到一个队列中,在网络线程中用HttpUrlConnection去请求

问题来了:

Retrofit是怎么做的呢?

答案很简单,就是:Java的动态代理

动态代理

我刚开始看Retrofit的代码,我对下面这句代码感到很困惑:

ZhuanLanApi api = retrofit.create(ZhuanLanApi.class); 我给Retrofit对象传了一个ZhuanLanApi接口的Class对象,怎么又返回一个ZhuanLanApi对象呢? 进入create方法一看,没几行代码,但是我觉得这几行代码就是Retrofit的精妙的地方: /** Create an implementation of the API defined by the {@code service} interface. */ public T create(final Class service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); create方法就是返回了一个Proxy.newProxyInstance动态代理对象。那么问题来了...

动态代理是个什么东西?

看Retrofit代码之前我知道Java动态代理是一个很重要的东西,比如在Spring框架里大量的用到,但是它有什么用呢?

Java动态代理就是给了程序员一种可能:当你要调用某个Class的方法前或后,插入你想要执行的代码

比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。

为什么要使用动态代理

你看上面代码,获取数据的代码就是这句:

Call call = api.getAuthor("qinchao"); 上面api对象其实是一个动态代理对象,并不是一个真正的ZhuanLanApi接口的implements产生的对象,当api对象调用getAuthor方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象,它的invoke方法会传入3个参数:

Object proxy: 代理对象,不关心这个 Method method:调用的方法,就是getAuthor方法 Object... args:方法的参数,就是"qinchao" 而Retrofit关心的就是method和它的参数args,接下去Retrofit就会用Java反射获取到getAuthor方法的注解信息,配合args参数,创建一个ServiceMethod对象

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送

总结:

注解

Retrofit使用注解+java接口来定义后台服务API接口

注解主要分为: 方法注解 和 参数注解

生成动态代理实例

Retrofit使用的关键一步就是Retrofit.create函数创建接口动态代理的示例,代码如下

可以看到是为接口的每个method创建了一个对应的ServiceMethod,并使用这个ServiceMethod对象创建OkHttpCall,并使用ServiceMethod实例的callAdapter来调用okhttpCall并返回结果。

调用流程

通过上面代码可以看到调用关键的就是三步:

1 加载对应method的ServiceMethod实例 2 使用ServiceMethod实例和方法调用参数创建OkHttpCall 3 调用serviceMethod.callAdapter.adapt(okHttpCall)来产生method所定义的返回(Call或者其他自定义CallAdapter支持的返回)