有证教练新手陪驾 發表於 2019-9-26 20:35:00

Android开发 retrofit入门讲解

<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p><span style="color: rgba(0, 128, 128, 1)">  <span style="color: rgba(0, 0, 0, 1)">retrofit基于okhttp封装的网络请求框架,网络请求的工作本质上是 OkHttp 完成,而 retrofit 仅负责网络请求接口的封装.如果你不了解OKhttp建议你还是先了解它在来学习使用retrofit,传送门:</span></span>Android 开发 框架系列 OkHttp使用详解</p>
<p><span style="color: rgba(0, 0, 0, 1)">  Retrofit优势,就是简洁易用,解耦,扩展性强,可搭配多种Json解析框架(例如Gson),另外还支持RxJava.<strong>但是,这篇博客不讲解RxJava配合使用的部分,与RxJava的配合使用将在另外一篇博客中讲解.</strong></span></p>
<p><span style="color: rgba(0, 0, 0, 1)">  另外<span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">retrofit</span></span>已经是封装的非常好了,作者的想的很完整,它的封装的思想十分准确而且恰到好处(堪称标准),所以不建议多此一举的再次<span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">封装<span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">retrofit</span></span>. 再次封装不会让你很牛逼. 只会让你看起来更蠢。<strong>你会想封装只能说明你压根没理解或者阅读过<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">retrofit</span></span></span></strong>。过度封装以后出现一个任何问题都可能出现重构灾难,那就整个项目的接口代码都要增加代码。减少重复工作只需要给2个东西做一个工具类(<span style="color: rgba(0, 0, 0, 1)">OkHttpClient与<span style="color: rgba(0, 0, 0, 1)">Retrofit.Builder())</span></span>,在一些简单场景只需要将配置好的网络接口列表服务类保存好(保存到Application或者单例保存)。</span></span></span></p>
<h1><span style="color: rgba(0, 128, 128, 1)"><strong>Github地址</strong></span></h1>
<p><span style="color: rgba(0, 128, 128, 1)"><strong>  https://github.com/square/retrofit</strong></span></p>
<h1><span style="color: rgba(0, 128, 128, 1)">依赖</span></h1>
<p><span style="color: rgba(0, 128, 128, 1)">  <span style="color: rgba(0, 0, 0, 1)">如果你不需要使用RxJava模式,那么你只需要依赖下面2个:</span></span></p>
<div class="cnblogs_code">
<pre>    implementation <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">com.squareup.retrofit2:retrofit:2.6.2</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    implementation </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">com.squareup.retrofit2:converter-gson:2.4.0</span><span style="color: rgba(128, 0, 0, 1)">'</span></pre>
</div>
<p>  gson是用来解析的Json数据使用的(个人偏爱Gson),<span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">retrofit也支持其他解析工具比如fastJson</span></span></p>
<h1><span style="color: rgba(0, 128, 128, 1)">简单的Demo(异步请求)</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">  老规矩按思维顺序讲解demo</span></p>
<h3><span style="color: rgba(0, 0, 0, 1)">1.创建Retrofit请求基础配置</span></h3>
<p>  <span style="color: rgba(0, 0, 0, 1)">Retrofit</span>配置好后,你可以将它单例保存,也不可以保存。<span style="color: rgba(0, 0, 0, 1)">Retrofit.Builder()</span>就是希望你根据不同的业务创建出不同的<span style="color: rgba(0, 0, 0, 1)">Retrofit来搭配接口服务使用。</span></p>
<div class="cnblogs_code">
<pre>  <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Retrofit mRetrofit;
  </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> initHttpBase(){
      mRetrofit </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Retrofit.Builder()
                .baseUrl(</span>"http://doclever.cn:8090/mock/5c3c6da33dce46264b24452b/")<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">base的网络地址baseUrl不能为空,且强制要求必需以 / 斜杠结尾</span>
                .addConverterFactory(GsonConverterFactory.create())<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用Gson解析</span>
          .callbackExecutor(Executors.newSingleThreadExecutor())<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用单独的线程处理 (这很重要,一般网络请求如果不设置可能不会报错,但是如果是下载文件就会报错)</span>
<span style="color: rgba(0, 0, 0, 1)">                .build();
    }</span> </pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"><strong>注意! </strong></span>base的网络地址 baseUrl不能为空,且强制要求必需以 / 斜杠结尾</span></p>
<h3>2.创建数据返回后的Bean类</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> LoginBean {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> code;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String message;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getCode() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> code;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setCode(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> code) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.code =<span style="color: rgba(0, 0, 0, 1)"> code;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getMessage() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> message;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setMessage(String message) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.message =<span style="color: rgba(0, 0, 0, 1)"> message;
    }
}</span></pre>
</div>
<h3>2.创建一个网络请求接口</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> HttpList {

    @FormUrlEncoded </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注解表示from表单还有@Multipart 表单可供使用 当然你也可以不添加</span>
    @POST("test/login_test") <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">网络请求路径</span>
    Call&lt;LoginBean&gt; login(@Field("number") String number, @Field("password"<span style="color: rgba(0, 0, 0, 1)">) String password); <span style="color: rgba(0, 128, 0, 1)">//@Field("number") 为post值的的key</span>

}</span></pre>
</div>
<p>注意,这是一个接口类. LoginBean则是数据返回后的Bean类(<span style="color: rgba(0, 0, 0, 1)">Retrofit</span>会自动使用导入的Gson解析)</p>
<p><span style="color: rgba(255, 0, 0, 1)"><strong>注意! <span style="color: rgba(0, 0, 0, 1)">@POST("test/login_test") 这路径最前面不能加斜杠 /&nbsp; ,否则它会自动裁剪路径,这样会导致你的路径错误</span></strong></span></p>
<h3><span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">3.请求网络</span></span></h3>
<p><span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">这里的mRetrofit.create创建的接口服务,如果无改变<span style="color: rgba(0, 0, 0, 1)">Retrofit</span>,也可以用一个单例类保存在<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 128, 128, 1)"><span style="color: rgba(0, 0, 0, 1)">Application</span></span></span>起来全局使用。</span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> postHttp(){
      HttpList httpList </span>= mRetrofit.create(HttpList.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
      Call</span>&lt;LoginBean&gt; call = httpList.login("181234123", "123456"<span style="color: rgba(0, 0, 0, 1)">);
      call.enqueue(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Callback&lt;LoginBean&gt;<span style="color: rgba(0, 0, 0, 1)">() {
            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onResponse(Call&lt;LoginBean&gt; call, Response&lt;LoginBean&gt;<span style="color: rgba(0, 0, 0, 1)"> response) {
                LoginBean bean </span>=<span style="color: rgba(0, 0, 0, 1)"> response.body();
                Log.e(TAG, </span>"onResponse: code="+<span style="color: rgba(0, 0, 0, 1)">bean.getCode());
                Log.e(TAG, </span>"onResponse: message="+<span style="color: rgba(0, 0, 0, 1)">bean.getMessage());
            }

            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onFailure(Call&lt;LoginBean&gt;<span style="color: rgba(0, 0, 0, 1)"> call, Throwable t) {
                Log.e(TAG, </span>"onFailure: 网络请求失败="+<span style="color: rgba(0, 0, 0, 1)">t.getMessage());

            }
      });
    }</span></pre>
</div>
<p>&nbsp;这样,我们就完成了一个网络请求.是不是特别简单</p>
<h1><span style="color: rgba(0, 128, 128, 1)">同步请求</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> postHttp2() {
      HttpList httpList </span>= mRetrofit.create(HttpList.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">final</span> Call&lt;LoginBean&gt; call = httpList.login("181234123", "123456"<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span> Runnable() { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Android主线程不能操作网络请求,所以new一个线程来操作</span>
<span style="color: rgba(0, 0, 0, 1)">            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() {
                </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
                  Response</span>&lt;LoginBean&gt; response = call.execute();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">同步请求网络</span>
                  LoginBean bean =<span style="color: rgba(0, 0, 0, 1)"> response.body();
                  Log.e(TAG, </span>"onResponse: code=" +<span style="color: rgba(0, 0, 0, 1)"> bean.getCode());
                  Log.e(TAG, </span>"onResponse: message=" +<span style="color: rgba(0, 0, 0, 1)"> bean.getMessage());
                } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
                  e.printStackTrace();
                }

            }
      }).start();

    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">取消网络请求</span></h1>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> cancelHttp(){
      HttpList httpList </span>= mRetrofit.create(HttpList.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里贴这部分代码是告诉call是哪里来的,关键点就是这个call,当然你也可以从回调里获取</span>
      mCall = httpList.login("181234123", "123456"<span style="color: rgba(0, 0, 0, 1)">);

      mCall.cancel(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">取消请求</span>
    }</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">如何添加Header头</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">以固定数据的形式添加头信息</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> HttpList {

    @Headers({</span>"content1:one","content2:two"<span style="color: rgba(0, 0, 0, 1)">})
    @POST(</span>"test/logout_test"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;LoginBean&gt;<span style="color: rgba(0, 0, 0, 1)"> logout1();

}</span></pre>
</div>
<p>以非固定数据的形式添加头信息</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> HttpList {

    @POST(</span>"test/logout_test"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;LoginBean&gt; logout2(@Header("content"<span style="color: rgba(0, 0, 0, 1)">) String content);

}</span></pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)">批量设置head头</span></p>
<div>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 获取帮助列表数据 AppGetQuestAndHelp
*/
@POST("jiaiot/api/app/smarthome/token")
suspend fun helpList(
    @HeaderMap headers: Map&lt;String, String&gt;,
    @Body requestBody: RequestBody?
): Response&lt;Result&lt;List&lt;HelpBean&gt;&gt;&gt;</code></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">Body配置</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">Body一般有4个种类</span></p>
<ul>
<li><span style="color: rgba(0, 0, 0, 1)"><strong>application/x-www-form-urlencoded 表单数据 </strong></span></li>
<li><span style="color: rgba(0, 0, 0, 1)"><strong>multipart/form-data 表单文件上传 </strong></span></li>
<li><span style="color: rgba(0, 0, 0, 1)"><strong>application/json 序列化JSON数据 </strong></span></li>
<li><span style="color: rgba(0, 0, 0, 1)"><strong>text/xml XML数据</strong></span></li>
</ul>
<p><span style="color: rgba(0, 0, 0, 1)">框架直接提供的2个Body</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> HttpList {

    @FormUrlEncoded </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">application/x-www-form-urlencoded表单body</span>
    @POST("test/login_test"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;LoginBean&gt; login2(@Field("number") String number, @Field("password"<span style="color: rgba(0, 0, 0, 1)">) String password);


    @Multipart </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">multipart/form-data此body支持文件上传与下载</span>
    @POST("test/login_test"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;LoginBean&gt; login3(@Field("number") String number, @Field("password"<span style="color: rgba(0, 0, 0, 1)">) String password);


}</span></pre>
</div>
<h2><span style="color: rgba(0, 0, 0, 1)">自定义Body</span></h2>
<p><span style="color: rgba(0, 0, 0, 1)">其他2个就需要自定义创建了,下面举例Json Body的创建:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">  /**</span><span style="color: rgba(0, 128, 0, 1)">
   *
   * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> string直接导入需要发送给服务器的JSON的String值
   * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
   <span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> RequestBody getRequestBody(String string) {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> RequestBody.create(MediaType.parse("application/json; charset=utf-8"<span style="color: rgba(0, 0, 0, 1)">), string);
    }</span></pre>
</div>
<p>在接口类参数需要设置为 @Body RequestBody requestBody</p>
<div class="cnblogs_code">
<pre>    @POST("app/system/demo"<span style="color: rgba(0, 0, 0, 1)">)
    Observable</span>&lt;UpdateInfo&gt; demo(@Body RequestBody requestBody);</pre>
</div>
<h2><span style="color: rgba(0, 0, 0, 1)">表单带参上传文件的Body</span></h2>
<p><span style="color: rgba(0, 0, 0, 1)">http</span></p>
<div>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 上传日志文件
* @return
*/
@Multipart
@POST("smarthome/upload_token")
suspend fun updateLogFile(
    @HeaderMap headers: Map&lt;String, String&gt;,
    @Part part: List&lt;MultipartBody.Part&gt;
): Response&lt;Result&lt;Any&gt;&gt;</code></pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)">请求部分</span></p>
<div>
<div>
<pre class="highlighter-hljs" data-dark-theme="true"><code>val http = Http.getInstance().tokenUploadFile&lt;HttpList&gt;(HttpList::class.java)
val fileBody = zipFile.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val multipartBody = MultipartBody.Builder()
    .addFormDataPart("biz_id", "app-log")
    .addFormDataPart("file_name", zipFile.getName())
    .addFormDataPart("file", zipFile.getName(),fileBody)
    .build().parts

http.updateLogFile(heads, multipartBody).apply {
    "上传日志结果 = $isSuccessful".logE()
    if (isSuccessful) {
      "${body().toString()}".logE()
    } else {

    }
}</code></pre>
</div>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">手动解析Response请求返回</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">有时候我们不需要使用GsonConverterFactory帮我们解析body数据,我们希望自己能操作数据并且解析。比如获取下载数据的流,我们需要使用ResponseBody类(这个类是okhttp返回body类)。代码如下</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> HttpList {

    @GET(</span>"aaa/bbb/ccc/"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;ResponseBody&gt; post(@Query("token"<span style="color: rgba(0, 0, 0, 1)">) String token);
}</span></pre>
</div>
<p>&nbsp;</p>
<p><span style="color: rgba(0, 0, 0, 1)">请求后返回的ResponseBody使用方式请参考okhttp的博客 https://www.cnblogs.com/guanxinjing/p/9708575.html</span></p>
<h1><span style="color: rgba(0, 128, 128, 1)">添加配置的OkHttpClient(主要使用请求超时/拦截器等功能)</span></h1>
<p>上面说了retrofit是基于Okhttp开发的网络请求框架,所以它有一部分的功能依然需要使用Okhttp的方式来配置比如请求超时时间/设置拦截器等等,下面就展示一下如何添加</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> initHttpBase2() {
      OkHttpClient okHttpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> OkHttpClient.Builder()
                .retryOnConnectionFailure(</span><span style="color: rgba(0, 0, 255, 1)">false</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在连接失败时重试</span>
                .callTimeout(30, TimeUnit.SECONDS) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">呼叫超时,设置此参数为整体流程请求的超时时间</span>
                .connectTimeout(20,TimeUnit.SECONDS)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">连接超时</span>
                .readTimeout(20,TimeUnit.SECONDS)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读取超时</span>
                .writeTimeout(20,TimeUnit.SECONDS)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">写入超时
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">                .callTimeout()</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">呼叫超时,设置此参数为整体流程请求的超时时间
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">                .addInterceptor() </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置拦截器
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">                .authenticator() </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置认证器
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">                .proxy()</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置代理</span>
<span style="color: rgba(0, 0, 0, 1)">                .build();

      mRetrofit </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(</span>"http://doclever.cn:8090/mock/5c3c6da33dce46264b24452b/")<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">base的网络地址</span>
                .addConverterFactory(GsonConverterFactory.create())<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用Gson解析</span>
<span style="color: rgba(0, 0, 0, 1)">                .callbackExecutor(Executors.newSingleThreadExecutor())
                .build();
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">部分路径动态的BaseUrl</span></h1>
<div class="cnblogs_code">
<pre>    @POST("/article/query/{page}/json"<span style="color: rgba(0, 0, 0, 1)">)
    @FormUrlEncoded
    Observable</span>&lt;DataResponse&lt;Article&gt;&gt; getSearchArticles(@Path("page") <span style="color: rgba(0, 0, 255, 1)">int</span> page, @Field("k") String k);</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">实现Url路径传参数</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">@Query 有值的查询名称</span></p>
<div class="cnblogs_code">
<pre>    @GET("app/data"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;Result&gt; getData(@Query("id") String id);</pre>
</div>
<div class="cnblogs_code">
<pre>    @GET("app/data"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;Result&gt; getData(@Query("id") String... id);</pre>
</div>
<p>@QueryName 只有值没有key的传参</p>
<div class="cnblogs_code">
<pre>    @GET("app/data"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;Resul&gt; getData(@QueryName String id);</pre>
</div>
<div class="cnblogs_code">
<pre>    @GET("app/data"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;Resul&gt; getData(@QueryName String... id);</pre>
</div>
<p>@QueryMap 用哈希集合传值</p>
<div class="cnblogs_code">
<pre>    @GET("app/data"<span style="color: rgba(0, 0, 0, 1)">)
    Call</span>&lt;Result&gt; getData(@QueryMap Map&lt;String,String&gt; map);</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">自定义GsonConverterFactory</span></h1>
<p>下面这个自定义用于检查了token是否失效</p>
<p>bean</p>
<div class="cnblogs_code">
<pre>data <span style="color: rgba(0, 0, 255, 1)">class</span> BaseResult&lt;T&gt;(var code: Int, var msg: String?, var data: T?)</pre>
</div>
<p>GsonConverterFactory</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> CheckTokenGsonConverterFactory <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Converter.Factory {
    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * Create an instance using a default {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> Gson} instance for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> CheckTokenGsonConverterFactory create() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> create(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Gson());
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * Create an instance using {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> gson} for conversion. Encoding to JSON and decoding from JSON
   * (when no charset is specified by a header) will use UTF-8.
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    @SuppressWarnings(</span>"ConstantConditions") <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Guarding public API nullability.</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> CheckTokenGsonConverterFactory create(Gson gson) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (gson == <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> NullPointerException("gson == null"<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> CheckTokenGsonConverterFactory(gson);
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Gson gson;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> CheckTokenGsonConverterFactory(Gson gson) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.gson =<span style="color: rgba(0, 0, 0, 1)"> gson;
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> Converter&lt;ResponseBody, ?&gt;<span style="color: rgba(0, 0, 0, 1)"> responseBodyConverter(
            Type type, Annotation[] annotations, Retrofit retrofit) {
      TypeAdapter</span>&lt;?&gt; adapter =<span style="color: rgba(0, 0, 0, 1)"> gson.getAdapter(TypeToken.get(type));
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> MyGsonResponseBodyConverter&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(gson, adapter);
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> Converter&lt;?, RequestBody&gt;<span style="color: rgba(0, 0, 0, 1)"> requestBodyConverter(
            Type type,
            Annotation[] parameterAnnotations,
            Annotation[] methodAnnotations,
            Retrofit retrofit) {
      TypeAdapter</span>&lt;?&gt; adapter =<span style="color: rgba(0, 0, 0, 1)"> gson.getAdapter(TypeToken.get(type));
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> MyGsonRequestBodyConverter&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(gson, adapter);
    }


    </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">class</span> MyGsonRequestBodyConverter&lt;T&gt; <span style="color: rgba(0, 0, 255, 1)">implements</span> Converter&lt;T, RequestBody&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8"<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Charset UTF_8 = Charset.forName("UTF-8"<span style="color: rgba(0, 0, 0, 1)">);

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Gson gson;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> TypeAdapter&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> adapter;

      MyGsonRequestBodyConverter(Gson gson, TypeAdapter</span>&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> adapter) {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.gson =<span style="color: rgba(0, 0, 0, 1)"> gson;
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.adapter =<span style="color: rgba(0, 0, 0, 1)"> adapter;
      }

      @Override
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> RequestBody convert(T value) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IOException {
            Buffer buffer </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Buffer();
            Writer writer </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter </span>=<span style="color: rgba(0, 0, 0, 1)"> gson.newJsonWriter(writer);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> RequestBody.create(MEDIA_TYPE, buffer.readByteString());
      }
    }


    </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">class</span> MyGsonResponseBodyConverter &lt;T&gt; <span style="color: rgba(0, 0, 255, 1)">implements</span> Converter&lt;ResponseBody, T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Gson gson;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> TypeAdapter&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> adapter;

      MyGsonResponseBodyConverter(Gson gson, TypeAdapter</span>&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> adapter) {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.gson =<span style="color: rgba(0, 0, 0, 1)"> gson;
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.adapter =<span style="color: rgba(0, 0, 0, 1)"> adapter;
      }

      @Override
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> T convert(ResponseBody value) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IOException {
            String response </span>=<span style="color: rgba(0, 0, 0, 1)"> value.string();
            BaseResult baseResult </span>= gson.fromJson(response, BaseResult.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (baseResult.getCode() ==<span style="color: rgba(0, 0, 0, 1)"> NetCode.REQUEST_SUCCESS){
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">token失效,请在这里处理token失效后返回登入页面的代码。</span>
<span style="color: rgba(0, 0, 0, 1)">            
            }

            InputStream inputStream </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ByteArrayInputStream(response.getBytes());
            MediaType contentType </span>=<span style="color: rgba(0, 0, 0, 1)"> value.contentType();
            Charset charset </span>= contentType != <span style="color: rgba(0, 0, 255, 1)">null</span> ?<span style="color: rgba(0, 0, 0, 1)"> contentType.charset(UTF_8) : UTF_8;
            Reader reader </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> InputStreamReader(inputStream, charset);
            JsonReader jsonReader </span>=<span style="color: rgba(0, 0, 0, 1)"> gson.newJsonReader(reader);
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
                T result </span>=<span style="color: rgba(0, 0, 0, 1)"> adapter.read(jsonReader);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (jsonReader.peek() !=<span style="color: rgba(0, 0, 0, 1)"> JsonToken.END_DOCUMENT) {
                  </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> JsonIOException("JSON document was not fully consumed."<span style="color: rgba(0, 0, 0, 1)">);
                }
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
            } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> {
                value.close();
            }
      }
    }
}</span></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>end</p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>Response</code></pre>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/11594249.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/11594249.html
頁: [1]
查看完整版本: Android开发 retrofit入门讲解