筆記 android網(wǎng)絡(luò)框架源碼解析及對(duì)比(待續(xù))

自己備忘,隨便寫

android網(wǎng)絡(luò)框架源碼解析及對(duì)比

android常用網(wǎng)絡(luò)框架對(duì)比

Volley:

特點(diǎn)

  • 基于HttpUrlConnection
  • 封裝了UIL圖片加載框架,支持圖片加載
  • 緩存
  • Activity和生命周期的聯(lián)動(dòng),Activity結(jié)束時(shí)取消在此Activity中調(diào)用了所有網(wǎng)絡(luò)請(qǐng)求

應(yīng)用場(chǎng)景

  • 適合傳輸數(shù)據(jù)量小,網(wǎng)絡(luò)請(qǐng)求頻繁的場(chǎng)景
  • 不能進(jìn)行大數(shù)據(jù)量的網(wǎng)絡(luò)操作,比如下載及上傳文件,原因如下:
    • Volley的Request和Response都是把數(shù)據(jù)放到byte[]中,如果設(shè)計(jì)文件的上傳及下載,byte[]就會(huì)變得很大,嚴(yán)重的消耗內(nèi)存.比如下載一個(gè)大文件,不可能把整個(gè)文件一次性全部放到byte[]中再寫到本地文件.
    • 源碼為證:
      Request:
      com.android.volley.Request
          //Reqest中的數(shù)據(jù),最后是被轉(zhuǎn)換為byte[]數(shù)組
          //Returns the raw POST or PUT body to be sent.
          public byte[] getBody() throws AuthFailureError {
              Map<String, String> params = getParams();
              if (params != null && params.size() > 0) {
                  return encodeParameters(params, getParamsEncoding());
              }
              return null;
          }
          //Request實(shí)例解析當(dāng)前請(qǐng)求得到的網(wǎng)絡(luò)相應(yīng)數(shù)據(jù)
          protected abstract Response<T> parseNetworkResponse(NetworkResponse response);
      
      Response:
      com.android.volley.NetworkResponse
          //網(wǎng)絡(luò)響應(yīng)中的原始數(shù)據(jù),以byte[]形式存在
          //Raw data from this response.
          public final byte[] data;
      
      Request的具體繼承類,都是在parseNetworkResponse方法中,對(duì)NetworkResponse的data(byte[])進(jìn)行解析:
      StringRequest.parseNetworkResponse:
          parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
      ImageRequest.parseNetworkResponse:
          byte[] data = response.data;
          BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
          Bitmap bitmap = null;
          bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
      

OkHttp:

特點(diǎn)

  • 基于NIO和Okio,請(qǐng)求處理速度更快.
    IO:阻塞式;NIO:非阻塞式;Okio:Square基于IO和NIO做的更高效處理數(shù)據(jù)流的庫
    IO和NIO的區(qū)別:
    1.IO是面向流(Stream)的,而NIO是面向緩沖區(qū)(Buffer)的.
        1.1:面向流意味著IO讀取流中1個(gè)或多個(gè)字節(jié),他們沒有被緩存在任何地方,此外它不能前后移動(dòng)流中的數(shù)據(jù);
        1.2:面向緩沖區(qū)意味著先將數(shù)據(jù)讀取到一個(gè)稍后處理的緩沖區(qū),需要讀取時(shí)可以在緩沖區(qū)中前后移動(dòng);
    2:IO是阻塞的,NIO是非阻塞的
        2.1:IO的各種流是阻塞的,意味著一個(gè)線程執(zhí)行read()或write()時(shí),該線程被阻塞,直到數(shù)據(jù)被讀取或完全寫入,期間不能做任何別的事情;
        2.2:NIO,一個(gè)線程從1個(gè)通道讀取數(shù)據(jù),或者向1個(gè)通道寫入數(shù)據(jù),如果通道中暫時(shí)沒有數(shù)據(jù)可以讀取,或者寫入數(shù)據(jù)沒有完成,線程不會(huì)阻塞,可以去做別的事情.直到通道中出現(xiàn)了可以讀取的數(shù)據(jù)或者可以繼續(xù)寫入數(shù)據(jù),再繼續(xù)之前的工作.NIO情況下,一個(gè)線程可以處理多個(gè)通道的讀取和寫入,更充分的利用線程資源;
    3:IO和NIO的適用場(chǎng)景
        3.1:IO適合于鏈接數(shù)量不大,但是每個(gè)鏈接需要發(fā)送/接收的數(shù)據(jù)量很大,需要長(zhǎng)時(shí)間連續(xù)處理;
        3.2:NIO更適合于同時(shí)存在海量鏈接,但是每個(gè)鏈接單次發(fā)送/接收的數(shù)據(jù)量較小的情形.比如聊天服務(wù)器.海量鏈接但是單個(gè)鏈接單次數(shù)據(jù)較小
    
  • 無縫支持GZIP來減少數(shù)據(jù)流量
    • GZIP是網(wǎng)站壓縮加速的一種技術(shù),開啟后可以加快客戶端的打開速度.原理是響應(yīng)數(shù)據(jù)先經(jīng)過服務(wù)器壓縮,客戶端快速解壓呈現(xiàn)內(nèi)容,減少客戶端接收的數(shù)據(jù)量
    • android客戶端在Request頭加入"Accept-Encoding","gzip",告知服務(wù)器客戶端接受gzip的數(shù)據(jù);服務(wù)器支持的情況下幅虑,返回gzip后的response body煮甥,同時(shí)加入以下header:
      Content-Encoding: gzip:表明body是gzip過的數(shù)據(jù)
      Content-Length:117:表示body gzip壓縮后的數(shù)據(jù)大小灵巧,便于客戶端使用后裸。
      或
      Transfer-Encoding: chunked:分塊傳輸編碼
      
    • OkHttp3是支持Gzip解壓縮的:它支持我們?cè)诎l(fā)起請(qǐng)求的時(shí)候自動(dòng)加入header,Accept-Encoding:gzip,而我們的服務(wù)器返回的時(shí)候也需要header中有Content-Encoding:gzip
      開發(fā)者沒有在Header中添加Accept-Encoding時(shí),自動(dòng)添加Accept-Encoding: gzip
      自動(dòng)添加的request,response支持自動(dòng)解壓
      手動(dòng)添加不負(fù)責(zé)解壓縮
      自動(dòng)解壓時(shí)移除Content-Length豪娜,所以上層Java代碼想要contentLength時(shí)為-1
      自動(dòng)解壓時(shí)移除 Content-Encoding
      自動(dòng)解壓時(shí)的分塊編碼傳輸不受影響
      
      okhttp3.internal.http.BridgeInterceptor
      public final class BridgeInterceptor implements Interceptor {
          @Override 
          public Response intercept(Chain chain) throws IOException {
              ****
              //如果header中沒有Accept-Encoding,默認(rèn)自動(dòng)添加,且標(biāo)記變量transparentGzip為true
              boolean transparentGzip = false;
              if (userRequest.header("Accept-Encoding") == null) {
                transparentGzip = true;
                requestBuilder.header("Accept-Encoding", "gzip");
              }
              ****
              Response.Builder responseBuilder = networkResponse.newBuilder().request(userRequest);
              //符合條件時(shí)執(zhí)行g(shù)zip自動(dòng)解壓:
                  //header中手動(dòng)添加ccept-Encoding不負(fù)責(zé)gzip解壓
                  //自動(dòng)添加ccept-Encoding才負(fù)責(zé)gzip解壓
              if (transparentGzip&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))&&HttpHeaders.hasBody(networkResponse)) {
                  //gzip自動(dòng)解壓的前提條件
                  //1:transparentGzip = true,即用戶沒有主動(dòng)在request頭中加入"Accept-Encoding", "gzip"
                  //2:header中標(biāo)明了Content-Encoding為gzip
                  //3:networkResponse中有body
                  GzipSource responseBody = new GzipSource(networkResponse.body().source());
                  Headers strippedHeaders = networkResponse.headers().newBuilder()
                      //自動(dòng)解壓時(shí)移除Content-Encoding
                      .removeAll("Content-Encoding")
                      //自動(dòng)解壓時(shí)移除Content-Length嚷辅,所以上層Java代碼想要contentLength時(shí)為-1
                      .removeAll("Content-Length")
                      .build();
                  responseBuilder.headers(strippedHeaders);
                  responseBuilder.body(new RealResponseBody(strippedHeaders,Okio.buffer(responseBody)));
              }
              return responseBuilder.build();
          }
      }
      
    • 使用OkHttp3,我們?cè)谙蚍?wù)器提交大量數(shù)據(jù),希望對(duì)post的數(shù)據(jù)進(jìn)行g(shù)zip壓縮的實(shí)現(xiàn)方法:首先實(shí)現(xiàn)自定義攔截器,然后在構(gòu)建OkhttpClient的時(shí)候麻惶,添加攔截器
      實(shí)現(xiàn)自定義攔截器(官方實(shí)現(xiàn)):
      static class GzipRequestInterceptor implements Interceptor {
          @Override 
          public Response intercept(Chain chain) throws IOException {
              Request originalRequest = chain.request();
              if (originalRequest.body() == null ||             originalRequest.header("Content-Encoding") != null) {
                  return chain.proceed(originalRequest);
              }
              Request compressedRequest = originalRequest.newBuilder()
                  .header("Content-Encoding", "gzip")
                  .method(originalRequest.method(), gzip(originalRequest.body()))
                  .build();
              return chain.proceed(compressedRequest);
          }
          private RequestBody gzip(final RequestBody body) {
              return new RequestBody() {
                  @Override public MediaType contentType() {
                      return body.contentType();
                  }
                  @Override public long contentLength() {
                      //因?yàn)闊o法預(yù)知在經(jīng)過gzip壓縮后的長(zhǎng)度,設(shè)置為-1
                      return -1;
                  }
                  @Override 
                  public void writeTo(BufferedSink sink) throws IOException {
                      //通過GzipSink,將原始的BufferedSink進(jìn)行g(shù)zip壓縮
                      BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                      //將經(jīng)過gzip壓縮的內(nèi)容寫入RequestBody
                      body.writeTo(gzipSink);
                      gzipSink.close();
                  }
              };
          }
      }
      
      構(gòu)建OkhttpClient的時(shí)候,添加攔截器:
      OkHttpClient okHttpClient = new OkHttpClient.Builder()
          .addInterceptor(new GzipRequestInterceptor())//開啟Gzip壓縮
          ...
          .build();
      

應(yīng)用場(chǎng)景

  • 重量級(jí)網(wǎng)絡(luò)交互場(chǎng)景:網(wǎng)絡(luò)請(qǐng)求頻繁,傳輸數(shù)據(jù)量大

Retrofit:

特點(diǎn)

  • 基于OkHttp
  • 通過注解配置請(qǐng)求
  • 性能最好,處理最快
  • 解析數(shù)據(jù)需要使用統(tǒng)一的Converter
  • 易與其他框架RxJava聯(lián)合使用

應(yīng)用場(chǎng)景

  • 任何場(chǎng)景下都優(yōu)先使用,特別是項(xiàng)目中有使用RxJava或者后臺(tái)API遵循Restful風(fēng)格

Retrofit的使用

Retrofit涉及到的注解

  1. 網(wǎng)絡(luò)請(qǐng)求方法注解:
    @GET,@POST,@PUT,@OPTIONS,@PATCH,@DELETE,@HEAD,@HTTP
    @HTTP用于替換其余方法注解,通過method,path,hasBody進(jìn)行設(shè)置:
    public @interface HTTP {
        //網(wǎng)絡(luò)請(qǐng)求的方法(區(qū)分大小寫)
        String method();
        //網(wǎng)絡(luò)請(qǐng)求地址路徑
        String path() default "";
        //是否有請(qǐng)求體
        boolean hasBody() default false;
    }
    實(shí)例:
    @HTTP(method="GET",path="blog/{id}",hasBody=false)
    Call<ResponseBody> getCall(@Path("id") int id);
    
    網(wǎng)絡(luò)請(qǐng)求的完整Url=創(chuàng)建Retrofit實(shí)例時(shí)通過.baseUrl()+方法注解(path),
    通常使用:baseUrl目錄形式+path相對(duì)路徑 的方法組成完整Url:
        Url = "http:host:port/a/b/appath"
        baseUrl = "http:host:port/a/b/"
        path = appath
    
  2. 標(biāo)記注解
    @FormUrlEncoded,@Multipart,@Streaming
    
    @FormUrlEncoded:表示發(fā)送form-encoded的數(shù)據(jù)
        每個(gè)鍵值對(duì)需要用@Filed來注解鍵名,隨后的對(duì)象提供值
    @Multipart:表示發(fā)送form-encoded的數(shù)據(jù)(適用于有文件上傳的場(chǎng)景)
        1:每個(gè)鍵值對(duì)需要用@Part來注解鍵名,隨后的對(duì)象提供值.
        2:@Part后面支持3種數(shù)據(jù)類型:RequestBody,okhttp3.MultipartBody.Part,任意類型
    @Streaming:表示返回?cái)?shù)據(jù)以流的形式返回,適用于返回?cái)?shù)據(jù)較大的場(chǎng)景.如果沒有使用Streaming,默認(rèn)把數(shù)據(jù)全部載入內(nèi)存,之后讀取數(shù)據(jù)也是從內(nèi)存中獲取.
    
    實(shí)例:
    public interface GetRequest_Interface {
        /**
         *表明是一個(gè)表單格式的請(qǐng)求(Content-Type:application/x-www-form-urlencoded)
         * Field("username")表示將后面的String name中name的取值作為username 的值
         */
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
         
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
    }
    具體使用:
    GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
    // @FormUrlEncoded 
    Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
    
    // @Multipart
    // 1:Part修飾的參數(shù)類型:RequestBody
    MediaType textType = MediaType.parse("text/plain");
    RequestBody name = RequestBody.create(textType, "Carson");
    RequestBody age = RequestBody.create(textType, "24");
    // 2:Part修飾的參數(shù)類型:MultipartBody.Part
        //2.1:文件路徑
    String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()+ File.separator + "icon.jpg";
        //2.2:文件
    File file = new File(path);
        //2.3:文件所關(guān)聯(lián)的MediaType
    MediaType type = MediaType.parse("image/*");
        //2.4:通過MediaType和File創(chuàng)建RequestBody
    RequestBody body = RequestBody.create(type,file);
        //2.5:通過MultipartBody.Part.createFormData(String name, @Nullable String filename, RequestBody body)
        // 創(chuàng)建指定文件關(guān)聯(lián)的MultipartBody.Part實(shí)例
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("image", "icon.jpg", body);
    Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
    
    • MultipartBody.Part.createFormData(String name, @Nullable String filename, RequestBody body):
      • name是網(wǎng)絡(luò)請(qǐng)求中的名稱
      • fileName是文件名稱,用于服務(wù)端解析
      • body就是文件關(guān)聯(lián)的RequestBody實(shí)例
    • 每個(gè)RequestBody都要指定MediaType,常見的MediaType.parse(X)中X:
      text/plain(純文本)
      application/x-www-form-urlencoded(使用HTTP的POST方法提交的表單)
      multipart/form-data(同上梅惯,但主要用于表單提交時(shí)伴隨文件上傳的場(chǎng)合
      image/gif(GIF圖像)
      image/jpeg(JPEG圖像)【PHP中為:image/pjpeg】
      image/png(PNG圖像)【PHP中為:image/x-png】
      video/mpeg(MPEG動(dòng)畫)
      application/octet-stream(任意的二進(jìn)制數(shù)據(jù))
      application/pdf(PDF文檔)
      application/msword(Microsoft Word文件)
      
    • 詳情的文件擴(kuò)展名和X之間的對(duì)應(yīng)關(guān)系:MIME 參考手冊(cè)
    • 如果任意一個(gè)文件/File,不知道其對(duì)應(yīng)的MediaType type=MediaType.parse(X)中X填什么,通過以下代碼可以獲取X值:
      通過文件完整路徑,來獲取其對(duì)應(yīng)的X
      
      import java.net.FileNameMap;    
      import java.net.URLConnection;    
      public class FileUtils {    
        public static String getMimeType(String fileUrl) throws java.io.IOException    
          {    
              FileNameMap fileNameMap = URLConnection.getFileNameMap();    
              String type = fileNameMap.getContentTypeFor(fileUrl);
              if (contentType == null) {
                  //* exe,所有的可執(zhí)行程序
                  contentType = "application/octet-stream"; 
              }
              return type;    
          }
      }
      1:文件的完整路徑
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "test.png";
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "2.doc";
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "2.csv";
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "LiveUpdate.exe";
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "1.txt";
      String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "MyDirectoty" + File.separator + "demo.jpg";
      String mimeType = FileUtils.getMimeType(filePath);
      //image/png  
      //application/msword  
      //application/vnd.ms-excel  
      //application/x-msdownload  
      //text/plain  
      //image/jpeg 
      
  3. 網(wǎng)絡(luò)請(qǐng)求參數(shù)注解
    • @Headers:用于添加固定的請(qǐng)求頭,注解在方法上
    實(shí)例:
    @Headers({
        "Accept: application/vnd.github.v3.full+json",
        "User-Agent: Retrofit-Sample-App"
    })
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
    @Headers("Cache-Control: max-age=640000")
    @GET("widget/list")
    Call<List<Widget>> widgetList();
    
    • @Header:用于添加不固定的請(qǐng)求頭,注解在方法參數(shù)上
    實(shí)例:
    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    
    • @HeaderMap:用于添加請(qǐng)求頭集合,注解在方法參數(shù)上
    實(shí)例:
    Map<string,string> headers = new HashMap()<>;
    headers.put("Accept","text/plain");
    headers.put("Accept-Charset", "utf-8");
    
    @GET("/search")
    void list(@HeaderMap Map<string, string=""> headers);
    
    • @Body:以 Post方式 傳遞 自定義數(shù)據(jù)類型 給服務(wù)器
      • @Body注解參數(shù),則不能同時(shí)使用@FormUrlEncoded顾患、@Multipart,否則會(huì)報(bào)錯(cuò):
        @Body parameters cannot be used with form or multi-part encoding
        
      • @Body是以什么形式上傳的參數(shù):是上傳的@Body參數(shù)實(shí)體的Json字符串,所以內(nèi)部需要一個(gè)GsonCoverter來將實(shí)體轉(zhuǎn)換成json字符串,需要Retrofit里配置addConverterFactory(GsonConverterFactory.create()).否則會(huì)報(bào)錯(cuò):
        Unable to create @Body converter for ***
        
      • @Body使用正確姿勢(shì)
        //1:配置你的Gson
        Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd hh:mm:ss")
            .create();
        //2:Retrofit實(shí)例設(shè)置addConverterFactory(GsonConverterFactory.create())
        Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://localhost:4567/")
            //可以接收自定義的Gson,當(dāng)然也可以不傳
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();
        //3:@Body注解的參數(shù),所在方法去掉@FormUrlEncoded个唧、@Multipart
        public interface BlogService {
            @POST("blog")
            Call<Result<Blog>> createBlog(@Body Blog blog);
        }
        //4:調(diào)用
        BlogService service = retrofit.create(BlogService.class);
        Blog blog = new Blog();
        blog.content = "新建的Blog";
        blog.title = "測(cè)試";
        blog.author = "怪盜kidou";
        Call<Result<Blog>> call = service.createBlog(blog);
        
    • @Field,@FieldMap:發(fā)送 Post請(qǐng)求時(shí)提交請(qǐng)求的表單字段,需要和@FormUrlEncoded配合使用
    實(shí)例:
    public interface GetRequest_Interface {
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
        
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
    }
    // @Field
    Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
    // @FieldMap
    Map<String, Object> map = new HashMap<>();
    map.put("username", "Carson");
    map.put("age", 24);
    Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);。
    
    • @Part,@PartMap:發(fā)送 Post請(qǐng)求 時(shí)提交請(qǐng)求的表單字段,需要和@Multipart配合使用.適用于文件上傳場(chǎng)景.
      • @Part注解的參數(shù)類型:RequestBody,okhttp3.MultipartBody.Part,任意類型
      • @PartMap注解一個(gè)Map<String,RequestBody>
    實(shí)例:
    public interface GetRequest_Interface {
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);
    }
    
    // 具體使用
    MediaType textType = MediaType.parse("text/plain");
    RequestBody name = RequestBody.create(textType, "Carson");
    RequestBody age = RequestBody.create(textType, "24");
    RequestBody file = RequestBody.create(MediaType.parse("multipart/form-data"), File一個(gè)文件);
    // @Part
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
    Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
    // @PartMap
    // 實(shí)現(xiàn)和上面同樣的效果
    Map<String, RequestBody> fileUpload2Args = new HashMap<>();
    fileUpload2Args.put("name", name);
    fileUpload2Args.put("age", age);
    //這里并不會(huì)被當(dāng)成文件设预,因?yàn)闆]有文件名(包含在Content-Disposition請(qǐng)求頭中)徙歼,但上面的 filePart 有
    //fileUpload2Args.put("file", file);
    Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); 
    
    • @Query,@QueryMap:用于 @GET 方法的查詢參數(shù)(Query = Url 中 ‘?’ 后面的 key-value)
      • @Query:URL問號(hào)后面的參數(shù);
      • @QueryMap:相當(dāng)于多個(gè)@Query
      • @Query和@QueryMap注解的查詢參數(shù)的key和value默認(rèn)都會(huì)開啟URL編碼,使用如下encoded=true來關(guān)閉URL編碼.
        • @Query(value="group",encoded=true)
        • @QueryMap(encoded=true)
      • @Query注解的參數(shù),參數(shù)值可以為空,為空該參數(shù)會(huì)被忽略
      • @QueryMap注解的Map,其鍵和值都不能為空,否則拋出IllegalArgumentException異常
    實(shí)例:
    @Query:
    @GET("/list")
    Call<responsebody> list(@Query("category") String category);
    //傳入一個(gè)數(shù)組
    @GET("/list")
    Call<responsebody> list(@Query("category") String... categories);
    //不進(jìn)行URL編碼
    @GET("/search")
    Call<responsebody> llist(@Query(value="foo", encoded=true) String foo);
    @Query調(diào)用:
    X.list("1")     URL:/list?category=1
    X.list(null)    URL:/list
    X.list("a","b") URL:/list?category=a&category=b
    @Query(value="foo", encoded=true)下,生成的URL和不設(shè)置encoded=true情況無差別,只是關(guān)閉了key和value的URL編碼
    
    @QueryMap:
    @GET("/search")
    Call<responsebody> list(@QueryMap Map<string, string> filters);
    @GET("/search")
    Call<responsebody> list(@QueryMap(encoded=true) Map<string,string> filters);
    @QueryMap調(diào)用:
    X.list(ImmutableMap.of("group", "coworker", "age", "42"))
        URL:/search?roup=coworker&age=42
    X.list(ImmutableMap.of("group", "coworker"))
        URL:/search?roup=coworker
    
    • @Path:URL中"?"前面部分,Path注解用于替換url路徑中的參數(shù)
    實(shí)例:
    @GET("users/{user}/repos")
    Call<ResponseBody>  getBlog(@Path("user") String user );
    X.getBlog("bb")     URL:users/bb/repos
    
    • @Url:作用于方法參數(shù),直接設(shè)置請(qǐng)求的接口地址
      • 當(dāng)@GET,@POST等注解里面沒有url地址時(shí),必須在方法中使用@Url鳖枕,將地址以第1個(gè)參數(shù)的形式傳入
      • @Url注解的地址,不要以/開頭
      • @Url支持的類型有 okhttp3.HttpUrl, String, java.net.URI, android.net.Uri
      • @Path注解與@Url注解不能同時(shí)使用,否則會(huì)拋異常
        Path注解用于替換url路徑中的參數(shù),這就要求在使用path注解時(shí),
        必須已經(jīng)存在請(qǐng)求路徑,不然沒法替換路徑中指定的參數(shù)啊,
        而Url注解是在參數(shù)中指定的請(qǐng)求路徑的,這個(gè)時(shí)候指定請(qǐng)求路徑已經(jīng)晚了,
        path注解找不到請(qǐng)求路徑,更別提更換請(qǐng)求路徑中的參數(shù)了
        
    public interface BlogService {
        /**
         * 當(dāng)GET魄梯、POST...HTTP等方法中沒有設(shè)置Url時(shí),則必須使用 {@link Url}提供
         * 對(duì)于Query和QueryMap宾符,如果不是String(或Map的第二個(gè)泛型參數(shù)不是String)時(shí)
         * 會(huì)被默認(rèn)會(huì)調(diào)用toString轉(zhuǎn)換成String類型
         * Url支持的類型有 okhttp3.HttpUrl, String, java.net.URI, android.net.Uri
         * {@link retrofit2.http.QueryMap} 用法和{@link retrofit2.http.FieldMap} 用法一樣酿秸,不再說明
         */
        @GET //當(dāng)有URL注解時(shí)豆胸,這里的URL就省略了
        Call<ResponseBody> testUrlAndQuery(@Url String url,@Query("showAll") boolean showAll);
    }
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://localhost:4567/")
        .build();
    BlogService service = retrofit.create(BlogService.class);
    Call<ResponseBody> call1 = service.testUrlAndQuery("headers",false);
    //http://localhost:4567/headers?showAll=false
    
image

Retrofit使用流程

  • 步驟1:添加Retrofit庫的依賴
    • Retrofit支持多種數(shù)據(jù)解析方式,使用時(shí)需要在build.gradle添加依賴
      build.gradle添加依賴:
      compile 'com.squareup.retrofit2:retrofit:2.0.2'
      
      Gson        com.squareup.retrofit2:converter-gson:2.0.2
      Jackson     com.squareup.retrofit2:converter-jackson:2.0.2
      Simple XML  com.squareup.retrofit2:converter-simplexml:2.0.2
      Protobuf    com.squareup.retrofit2:converter-protobuf:2.0.2
      Moshi       com.squareup.retrofit2:converter-moshi:2.0.2
      Wire        com.squareup.retrofit2:converter-wire:2.0.2
      Scalars     com.squareup.retrofit2:converter-scalars:2.0.2
      
    • Retrofit支持多種網(wǎng)絡(luò)請(qǐng)求適配器方式:guava瘾英、Java8和rxjava
      build.gradle添加依賴:
      guava       com.squareup.retrofit2:adapter-guava:2.0.2
      Java8       com.squareup.retrofit2:adapter-java8:2.0.2
      rxjava      com.squareup.retrofit2:adapter-rxjava:2.0.2
      
  • 步驟2:創(chuàng)建 接收服務(wù)器返回?cái)?shù)據(jù) 的類
  • 步驟3:創(chuàng)建 用于描述網(wǎng)絡(luò)請(qǐng)求 的接口
  • 步驟4:創(chuàng)建 Retrofit 實(shí)例
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://fanyi.youdao.com/") // 設(shè)置網(wǎng)絡(luò)請(qǐng)求的Url地址
        .addConverterFactory(GsonConverterFactory.create())//設(shè)置數(shù)據(jù)解析器
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//支持RxJava平臺(tái)
        .build();
    
  • 步驟5:創(chuàng)建 網(wǎng)絡(luò)請(qǐng)求接口實(shí)例 并 配置網(wǎng)絡(luò)請(qǐng)求參數(shù)
    // 創(chuàng)建 網(wǎng)絡(luò)請(qǐng)求接口 的實(shí)例
    GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
    //對(duì) 發(fā)送請(qǐng)求 進(jìn)行封裝
    Call<Reception> call = request.getCall();
    
  • 步驟6:發(fā)送網(wǎng)絡(luò)請(qǐng)求(異步 / 同步)
    //發(fā)送網(wǎng)絡(luò)請(qǐng)求(異步)
    call.enqueue(new Callback<Translation>() {
        //請(qǐng)求成功時(shí)回調(diào)
        @Override
        public void onResponse(Call<Translation> call,Response<Translation> response) {
            // 對(duì)返回?cái)?shù)據(jù)進(jìn)行處理
            response.body().show();
        }
        //請(qǐng)求失敗時(shí)候的回調(diào)
        @Override
        public void onFailure(Call<Translation> call, Throwable throwable) {
            System.out.println("連接失敗");
        }
    });
    // 發(fā)送網(wǎng)絡(luò)請(qǐng)求(同步)
    Response<Reception> response = call.execute();
    // 對(duì)返回?cái)?shù)據(jù)進(jìn)行處理
    response.body().show();
    

Retrofit源碼分析

Retrofit涉及到的設(shè)計(jì)模式

  1. 模板模式
    • 定義:定義一個(gè)操作的算法框架,將一些步驟延遲到子類中,使子類不改變算法的結(jié)構(gòu)即可重新定義該算法的某些特定步驟
    • 使用場(chǎng)景:多個(gè)子類有公有方法渴杆,且子類公有方法的調(diào)度邏輯基本相同
    • 模板模式包含2個(gè)角色:
      • 父類:抽象類 public abstract class AbsParent
        • 父類中包含:基本方法 + 模板方法 + 鉤子方法
        • 基本方法:父類提取公共代碼:protected abstract 方法,由子類具體實(shí)現(xiàn)
        • 模板方法:可以有1個(gè)或幾個(gè),完成對(duì)基本方法的調(diào)度,實(shí)現(xiàn)具體邏輯:public final方法,防止子類復(fù)寫
        • 鉤子方法:protected方法,注意不是抽象方法.在父類的模板方法中調(diào)用,對(duì)模板方法的執(zhí)行進(jìn)行約束
      • 子類:父類的實(shí)現(xiàn)類 public class Child1 extends AbsParent
        • 子類中包含:基本方法的具體實(shí)現(xiàn) + 鉤子方法的重寫
    • 代碼實(shí)例
       //父類
        public abstract class AbsParent{
          //鉤子方法
          protected boolean executeStep1(){
            return false;
          }
          //基本方法
          protected abstract void step1();
          protected abstract void step2();
          protected abstract void step3();
          //模板方法
          public final void execute(){
            if(this.executeStep1()){
              this.step1();
            }
            this.step2();
            this.step3();
          }
        }
        //子類
        public class Child1 extends AbsParent{
          //子類鉤子方法返回值
          private boolean executeFlag = true;
          //子類中可以對(duì)鉤子方法返回值進(jìn)行設(shè)置,從而對(duì)父類的模板方法進(jìn)行約束
          public void setExecuteFlag(boolean flag){
            this.executeFlag = flag;
          }
          @Override
          protected boolean executeStep1(){
            return this.executeFlag;
          }
          @Override
          protected void step1(){
            System.out.println("Child1:step1")
          }
          @Override
          protected void step2(){
            System.out.println("Child1:step2")
          }
          @Override
          protected void step3(){
            System.out.println("Child1:step3")
          }
        }
      
  2. Builder模式/建造者模式
    • 定義:將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示
    • 作用:用戶不需要知道復(fù)雜對(duì)象的建造過程細(xì)節(jié),只需要指定對(duì)象的具體類型,即可創(chuàng)建復(fù)雜對(duì)象;具體建造者根據(jù)用戶指定的對(duì)象具體類型A,按照指定順序創(chuàng)建A的實(shí)例;
    • 建造者模式包含4個(gè)角色:
      • 產(chǎn)品類
        • 產(chǎn)品類實(shí)現(xiàn)了模板模式.抽象產(chǎn)品父類包含基本方法和模板方法,具體產(chǎn)品子類實(shí)現(xiàn)了基本方法.
      • 抽象建造者
        • public abstract class:規(guī)范了產(chǎn)品的組建,全是抽象方法,是具體建造者的父類
      • 具體建造者
        • 實(shí)現(xiàn)抽象建造者所有方法,并返回一個(gè)建造好的具體產(chǎn)品子類實(shí)例
      • 導(dǎo)演類
        • 持有多個(gè)具體建造者,包含多個(gè)方法,用來生產(chǎn)多個(gè)具體產(chǎn)品類實(shí)例;
    • 代碼實(shí)例
      抽象產(chǎn)品父類:包含基本方法和模板方法
      public abstract class AbsProduct{
        //這個(gè)參數(shù)定義了各基本方法的執(zhí)行順序
        private ArrayList<String> sequence = new ArrayList<String>();
        //基本方法
        protected abstract void step1();
        protected abstract void step2();
        //設(shè)置參數(shù)
        public final void setSequence(ArrayList<String> sequence){
          this.sequence = sequence;
        }
        //模板方法
        public final void do(){
          for(String item:sequence){
            if(item.equalsIgnoreCase("step1")){
              this.step1();
            }else if(item.equalsIgnoreCase("step2")){
              this.step2();
            }
          }
        }
      }
      具體產(chǎn)品子類:實(shí)現(xiàn)了基本方法
      public class Product1 extends AbsProduct{
        @Override
        protected void step1(){
          System.out.println("Product1:step1");
        }
        @Override
        protected void step2(){
          System.out.println("Product1:step2");
        }
      }
      public class Product2 extends AbsProduct{
        @Override
        protected void step1(){
          System.out.println("Product2:step1");
        }
        @Override
        protected void step2(){
          System.out.println("Product2:step2");
        }
      }
      抽象建造者:規(guī)范產(chǎn)品的組建
      public abstract class ProductBuilder{
        //設(shè)置產(chǎn)品參數(shù)
        public abstract void setSequence(ArrayList<String> sequence);
        //獲取產(chǎn)品實(shí)例
        public abstract AbsProduct getProduct();
      }
      具體建造者:實(shí)現(xiàn)抽象建造者所有方法,并返回一個(gè)建造好的具體產(chǎn)品子類實(shí)例
      public class Product1Builder extends ProductBuilder{
        //私有變量就是將要產(chǎn)生的具體產(chǎn)品類實(shí)例
        private Product1 p = new Product1();
        @Override
        public void setSequence(ArrayList<String> sequence){
          this.p.setSequence(sequence);
        }
        @Override
        public AbsProduct getProduct(){
          return this.p;
        }
      }
      public class Product2Builder extends ProductBuilder{
        //私有變量就是將要產(chǎn)生的具體產(chǎn)品類實(shí)例
        private Product2 p = new Product2();
        @Override
        public void setSequence(ArrayList<String> sequence){
          this.p.setSequence(sequence);
        }
        @Override
        public AbsProduct getProduct(){
          return this.p;
        }
      }
      導(dǎo)演類:持有多個(gè)具體建造者,包含多個(gè)方法,用來生產(chǎn)多個(gè)具體產(chǎn)品類實(shí)例
      public class Director{
        //影響產(chǎn)品流程順序的參數(shù)
        private ArrayList<String> sequence = new ArrayList<String>();
        //具體建造者
        private Product1Builder builder1 = new Product1Builder();
        private Product2Builder builder2 = new Product2Builder();
        //根據(jù)需求可自行擴(kuò)展
        //1:生產(chǎn)不同類型的具體產(chǎn)品
          //(gainProduct1A,gainProduct1B) 和 gainProduct2 就是生產(chǎn)不同類型的具體產(chǎn)品
        //2:相同類型的產(chǎn)品,其產(chǎn)品流程的數(shù)量及順序也可以任意變化:
          //gainProduct1A和gainProduct1B 就是同類產(chǎn)品的流程數(shù)量及順序變化
        public Product1 gainProduct1A(){
          this.sequence.clear();
          this.sequence.add("step1");
          this.sequence.add("step2");
          this.builder1.setSequence(this.sequence);
          return (Product1)this.builder1.getProduct();
        }
        public Product1 gainProduct1B(){
          this.sequence.clear();
          this.sequence.add("step2");
          this.builder1.setSequence(this.sequence);
          return (Product1)this.builder1.getProduct();
        }
        public Product2 gainProduct2(){
          this.sequence.clear();
          this.sequence.add("step1");
          this.sequence.add("step2");
          this.builder2.setSequence(this.sequence);
          return (Product2)this.builder2.getProduct();
        }
      }
      
  3. 外觀模式/門面模式
    • 定義:定義一個(gè)統(tǒng)一接口屿聋,外部與通過該統(tǒng)一的接口對(duì)子系統(tǒng)里的其他接口進(jìn)行訪問:如Retrofit調(diào)用某個(gè)具體接口,都是通過Retrofit.create創(chuàng)建的統(tǒng)一Service接口實(shí)例I,通過I調(diào)用具體方法;
    • 實(shí)現(xiàn):在復(fù)雜系統(tǒng)S和客戶端C之間再加一層"接待員"R,在R中實(shí)現(xiàn)對(duì)S復(fù)雜功能的訪問封裝;C直接和R交互即可.
    • 作用:隱藏了S的復(fù)雜性;R對(duì)S中復(fù)雜功能進(jìn)行了封裝,C調(diào)用R封裝過的方法,可避免低水平錯(cuò)誤
    • 場(chǎng)景:去醫(yī)院看病擅编,要 掛號(hào),問診,繳費(fèi),取藥,讓患者或患者家屬覺得很復(fù)雜藏否,如果有提供接待人員期吓,只讓接待人員來處理苛白,就很方便
    • 外觀模式包含:接口+實(shí)現(xiàn)類+外觀類(R)
    • 代碼實(shí)例
      //創(chuàng)建1個(gè)接口,代表醫(yī)院每個(gè)流程
      public interface Step{
        void execute();
      }
      //創(chuàng)建實(shí)現(xiàn)類,代表不同類型的具體流程
      public class GuaHao implements Step{
        @Override
        public void execute(){
          System.out.println("老子正在掛號(hào)");
        }
      }
      public class WenZhen implements Step{
        @Override
        public void execute(){
          System.out.println("老子正在問診");
        }
      }
      public class JiaoFei implements Step{
        @Override
        public void execute(){
          System.out.println("老子正在繳費(fèi)");
        }
      }
      public class QuYao implements Step{
        @Override
        public void execute(){
          System.out.println("老子正在取藥");
        }
      }
      //創(chuàng)建外觀類"接待員R"
      public class Reception{
        //"接待員"持有S中復(fù)雜功能的引用
        private Step guahao;
        private Step wenzhen;
        private Step jiaofei;
        private Step quyao;
        public Reception(){
          this.guahao = new GuaHao();
          this.wenzhen = new WenZhen();
          this.jiaofei = new JiaoFei();
          this.quyao = new QuYao();
        }
        //定義一個(gè)供客戶端調(diào)用的方法,完整實(shí)現(xiàn)一串流程
        public void executeAll(){
          this.guahao.execute();
          this.wenzhen.execute();
          this.jiaofei.execute();
          this.quyao.execute();
        }
        //也可以封裝單個(gè)流程
        public void guaHao(){
          this.guahao.execute();
        }****
      }
      //客戶端直接調(diào)用Reception
      Reception r = new Reception();
      r.executeAll();  r.guaHao();
      
  4. 代理模式
    • 定義:通過訪問代理類的方式來間接訪問目標(biāo)類
    • 優(yōu)點(diǎn):隱藏目標(biāo)類實(shí)現(xiàn)細(xì)節(jié);不改變目標(biāo)類情況下,對(duì)指定操作前后執(zhí)行擴(kuò)展,比如進(jìn)行校驗(yàn)和其他操作
    • 分類:
      • 靜態(tài)代理:代理類在程序運(yùn)行前已經(jīng)存在
      • 動(dòng)態(tài)代理:代理類在程序運(yùn)行前不存在呐赡、運(yùn)行時(shí)由程序動(dòng)態(tài)生成的代理方式稱為動(dòng)態(tài)代理
    • 靜態(tài)代理包含:
      • 目標(biāo)類和代理類共同實(shí)現(xiàn)的接口
      • 目標(biāo)類,代理類(代理類中持有目標(biāo)類實(shí)例)
    • 靜態(tài)代理實(shí)例:
      //創(chuàng)建一個(gè)接口
      public interface MyOpt{
        void opt();
      }
      //創(chuàng)建目標(biāo)類
      public class TargetOpt implements MyOpt{
        @Override
        public void opt(){
          System.out.println("TargetOpt:opt");
        }
      }
      //創(chuàng)建代理類
      public class ProxyOpt implements MyOpt{
        //代理類中持有 目標(biāo)類實(shí)例
        private TargetOpt target;
        public ProxyOpt(){
          this.target = new TargetOpt();
        }
        @Override
        public void opt(){
          this.target.opt();
        }
      }
      //客戶端和代理類直接進(jìn)行交互:
      ProxyOpt proxy= new ProxyOpt();
      proxy.opt();
      
    • 動(dòng)態(tài)代理
      • 動(dòng)態(tài)代理對(duì)象P執(zhí)行方法調(diào)用順序:
        • P.func==>InvocationHandler.invoke==>目標(biāo)類實(shí)例.func
      • 動(dòng)態(tài)代理實(shí)現(xiàn)需要3步:
        • 1 創(chuàng)建目標(biāo)類接口 及 目標(biāo)類
        • 2 實(shí)現(xiàn)InvocationHandler接口
          • 調(diào)用代理對(duì)象的每個(gè)函數(shù)實(shí)際最終都是調(diào)用了InvocationHandler的invoke函數(shù)
        • 3 通過Proxy類新建代理類對(duì)象:Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
      • 動(dòng)態(tài)代理實(shí)例:
        //創(chuàng)建接口
        public interface Step{
          void execute();
        }
        //創(chuàng)建目標(biāo)類
        public class MyStep implements Step{
          @Override
          public void execute(){
            System.out.println("MyStep:execute");
          }
        }
        //實(shí)現(xiàn)InvocationHandler接口
        public StepHandler implements InvocationHandler{
          //target:目標(biāo)類實(shí)例
          private Object target;
          public StepHandler(){}
          public StepHandler(Object obj){
            this.target = obj;
          }
          //proxy:通過 Proxy.newProxyInstance() 生成的代理對(duì)象
          //method:表示proxy被調(diào)用的方法
          //args:表示proxy被調(diào)用的方法的參數(shù)
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object obj = method.invoke(this.target, args);
            return obj;
          }
        }
        
        //通過Proxy類新建代理對(duì)象,直接調(diào)用代理對(duì)象的方法
        //1:創(chuàng)建InvocationHandler的實(shí)現(xiàn)類實(shí)例,將目標(biāo)類實(shí)例作為構(gòu)造參數(shù)傳入
        StepHandler h = new StepHandler(new MyStep());
        //2:創(chuàng)建代理對(duì)象
        Proxy: Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
            loader:目標(biāo)類繼承的接口所屬的類加載器
            interfaces:目標(biāo)類繼承的接口的Class
            h:InvocationHandler的實(shí)現(xiàn)類實(shí)例
        Step step = (Step)(Proxy.newProxyInstance(Step.class.getClassLoader(),new Class[]{Step.class},h));
        //3:直接調(diào)用代理對(duì)象的方法
        step.execute();    ==> "MyStep:execute"
        
        step.execute()實(shí)質(zhì)是調(diào)用了生成的代理對(duì)象P中的execute方法
        ==>
        而P中的execute方法,是調(diào)用了剛剛創(chuàng)建的h.invoke方法
        ==>
        h.invoke,則調(diào)用了目標(biāo)類MyStep實(shí)例中的execute方法
        
      • Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)源碼分析
        僅貼出關(guān)鍵代碼
        
        Proxy:
        package java.lang.reflect
        public class Proxy implements java.io.Serializable {
          private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
                proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
          private static final Class<?>[] constructorParams = { InvocationHandler.class };
          protected InvocationHandler h;
          protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
          }
          @CallerSensitive
          public static Object newProxyInstance(
            ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h                             )
                throws IllegalArgumentException
          {
            //獲取目標(biāo)類繼承的接口的Class副本
            final Class<?>[] intfs = interfaces.clone();
            //1:通過接口的Class副本,獲取代理類的Class
            Class<?> cl = getProxyClass0(loader, intfs);
            //2:Invoke its constructor with the designated invocation handler.
            //使用指定的InvocationHandler實(shí)例作為參數(shù),執(zhí)行代理類的構(gòu)造函數(shù),獲取代理類實(shí)例
            try {
              //2.1:Class.getConstructor
              //2.2:constructorParams
              //代理類extends Proxy,  
              //cons實(shí)質(zhì)上是Proxy的構(gòu)造函數(shù):protected Proxy(InvocationHandler h)
              //所以生成的代理類實(shí)例,內(nèi)部的 h 就是 newProxyInstance方法傳入的h
              final Constructor<?> cons = cl.getConstructor(constructorParams);
              ****
              return cons.newInstance(new Object[]{h});
            }
          }
          //1:如果代理類已經(jīng)存在則返回副本;不存在則通過ProxyClassFactory創(chuàng)建并返回:見1.1
          private static Class<?> getProxyClass0(ClassLoader loader,
                                                   Class<?>... interfaces) {
                // If the proxy class defined by the given loader implementing
                // the given interfaces exists, this will simply return the cached copy;
                // otherwise, it will create the proxy class via the ProxyClassFactory
            return proxyClassCache.get(loader, interfaces);
          }
          //1.1:此處直接看ProxyClassFactory的apply方法即可
          proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
          private static final class ProxyClassFactory 
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
            //所有要生成的代理類名稱前綴
            // prefix for all proxy class names
            private static final String proxyClassNamePrefix = "$Proxy";
            //為了生成的代理類不重名采取的名稱后綴:后面代碼會(huì)看到
            // next number to use for generation of unique proxy class names
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
            //生成代理類的Class
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
              //接口數(shù)組生成Map
              Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
              //遍歷代理類要實(shí)現(xiàn)的每個(gè)接口,注意必須是Interface,否則會(huì)拋異常
              for (Class<?> intf : interfaces) {
                        Class<?> interfaceClass = null;
                        try {
                            interfaceClass = Class.forName(intf.getName(), false, loader);
                        } catch (ClassNotFoundException e) {
                        }
                        //如果newProxyInstance中傳入的ClassLoader并不是接口所屬的ClassLoader,會(huì)拋異常
                        if (interfaceClass != intf) {
                            throw new IllegalArgumentException(intf + " is not visible from class loader");
                        }
                       //如果newProxyInstance中傳入的Class數(shù)組,數(shù)組項(xiàng)不屬于Interface,拋異常
                        if (!interfaceClass.isInterface()) {
                            throw new IllegalArgumentException(
                                interfaceClass.getName() + " is not an interface");
                        }
                        //通過Set,防止同一個(gè)接口重復(fù)處理
                        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                            throw new IllegalArgumentException(
                                "repeated interface: " + interfaceClass.getName());
                        }
              }
              String proxyPkg = null;     //要生成的代理類所在package
              int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
              //遍歷每個(gè)接口,獲取代理類所在package
              for (Class<?> intf : interfaces) {
                        int flags = intf.getModifiers();
                        if (!Modifier.isPublic(flags)) {
                            accessFlags = Modifier.FINAL;
                            //獲取接口名稱
                            //如:接口IStep,接口所在package為a.b.c,
                            //則(new IStep()).getClass().getName為:  "a.b.c.IStep"
                            String name = intf.getName();
                            int n = name.lastIndexOf('.');
                            //獲取接口所在package:對(duì)應(yīng)IStep則為"a.b.c."
                            String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                            if (proxyPkg == null) {
                                //proxyPkg不存在則賦值
                                proxyPkg = pkg;
                            } else if (!pkg.equals(proxyPkg)) {
                                //注意:如果proxyPkg已經(jīng)存在,說明傳入的接口Class數(shù)組不止一個(gè)Clas項(xiàng)
                                //為了如果數(shù)組中的接口包名不同,就會(huì)拋異常
                                //所以  
                                  //1:保證所有Class關(guān)聯(lián)的Interface包名相同
                                  //2:只傳1個(gè)Interface的Class不就行了,有需要傳多個(gè)的場(chǎng)景嗎?
                                throw new IllegalArgumentException(
                                    "non-public interfaces from different packages");
                            }
                        }
              }
              ****
              //這里用到了AtomicLong,用來拼接代理類的名稱
              long num = nextUniqueNumber.getAndIncrement();
              //以接口"a.b.c.IStep"為例,其代理類名稱為:"a.b.c.$Proxy0"
              String proxyName = proxyPkg + proxyClassNamePrefix + num;
              //1.2:生成代理類class,以byte[]形式返回
              byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
              try {
                //1.3:native方法生成代理類的Class,并返回
                return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
              } catch (ClassFormatError e) {
                ****
              }
            }
          }
          //1.3:defineClass0是一個(gè)native方法
          private static native Class<?> defineClass0(
            ClassLoader loader,
            String name,byte[] b, int off, int len);
          //2.2:parameter types of a proxy class constructor
          //生成Proxy實(shí)例所需的InvocationHandler類
          private static final Class<?>[] constructorParams =
                { InvocationHandler.class };
        }
        
        //2.1:Class.getConstructor
        Class
        //返回值一個(gè)類符合指定參數(shù)數(shù)組的構(gòu)造器
        public Constructor<T> getConstructor(Class<?>... parameterTypes)
        
        //1.2:ProxyGenerator.generateProxyClass
        ProxyGenerator
        
        package sun.misc
        public class ProxyGenerator {
          //代理類名稱
          private String className;
          //代理類繼承的接口數(shù)組
          private Class<?>[] interfaces;
          //訪問修飾符:Modifier.PUBLIC|Modifier.FINAL
          private int accessFlags;
        
          private ProxyGenerator(String var1, Class<?>[] var2, int var3) {
                this.className = var1;
                this.interfaces = var2;
                this.accessFlags = var3;
          }
          //1.2
          public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
            //設(shè)置代理類名稱className
            //代理類繼承的接口數(shù)組interfaces
            //訪問修飾符accessFlags
            ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
            //1.2.1:generateClassFile生成了代理類Class的byte[]
            final byte[] var4 = var3.generateClassFile();
            ***
            return var4;
          }
          //1.2.1:生成代理類Class的byte[]
          //Object中三個(gè)方法:hashCode,equals,toString
          private static Method hashCodeMethod;
          private static Method equalsMethod;
          private static Method toStringMethod;
          static {
                try {
                    hashCodeMethod = Object.class.getMethod("hashCode");
                    equalsMethod = Object.class.getMethod("equals", Object.class);
                    toStringMethod = Object.class.getMethod("toString");
                } catch (NoSuchMethodException var1) {
                    throw new NoSuchMethodError(var1.getMessage());
                }
          }
          private List<ProxyGenerator.FieldInfo> fields = new ArrayList();
          private List<ProxyGenerator.MethodInfo> methods = new ArrayList();
          private byte[] generateClassFile() {
            //addProxyMethod:1.2.1.1
            //將Object中3個(gè)方法對(duì)應(yīng)的ProxyGenerator.ProxyMethod實(shí)例添加到proxyMethods中
            this.addProxyMethod(hashCodeMethod, Object.class);
            this.addProxyMethod(equalsMethod, Object.class);
            this.addProxyMethod(toStringMethod, Object.class);
            //代理類繼承的接口數(shù)組,只考慮一個(gè)接口的場(chǎng)景
            Class[] var1 = this.interfaces;
            //var2 = 1
            int var2 = var1.length;
            int var3;
            Class var4;
            //只考慮一個(gè)接口的場(chǎng)景
            for(var3 = 0; var3 < var2; ++var3) {
                    var4 = var1[var3];
                    //獲取接口中的public方法;
                    //接口中的方法,默認(rèn)就是public,所有獲取到的是接口中所有方法
                    Method[] var5 = var4.getMethods();
                    int var6 = var5.length;
                    //遍歷接口中所有方法,
                    //將其包裝為ProxyGenerator.ProxyMethod實(shí)例添加到proxyMethods中
                    for(int var7 = 0; var7 < var6; ++var7) {
                        Method var8 = var5[var7];
                        this.addProxyMethod(var8, var4);
                    }
            }
            //proxyMethods的value的迭代器
            Iterator var11 = this.proxyMethods.values().iterator();
            ****
            Iterator var15;
            try {
                    //methods中添加(?沒看懂)
                    this.methods.add(this.generateConstructor());
                    var11 = this.proxyMethods.values().iterator();
                    while(var11.hasNext()) {
                        var12 = (List)var11.next();
                        var15 = var12.iterator();
                        while(var15.hasNext()) {
                            //遍歷proxyMethods中所有的ProxyGenerator.ProxyMethod
                              //生成FieldInfo添加到fields
                              //生成MethodInfo添加到methods
                            ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                            this.fields.add(new ProxyGenerator.FieldInfo(
                              var16.methodFieldName, 
                              "Ljava/lang/reflect/Method;", 10));
                            this.methods.add(var16.generateMethod());
                        }
                    }
                    //methods中添加(?沒看懂)
                    this.methods.add(this.generateStaticInitializer());
            }catch (IOException var10) {****}
            ****下面這段關(guān)鍵代碼沒看懂,只能猜一下****
            //設(shè)置代理類訪問修飾符 public final
            var14.writeShort(this.accessFlags);
            //設(shè)置代理類名稱
            var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
            //設(shè)置代理類extends Proxy
            var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
            ****
            for(int var19 = 0; var19 < var18; ++var19) {
              Class var22 = var17[var19];
              //設(shè)置代理類implements接口數(shù)組中所有接口
              var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
            }
            var14.writeShort(this.fields.size());
            var15 = this.fields.iterator();
            while(var15.hasNext()) {
              ProxyGenerator.FieldInfo var20 =(ProxyGenerator.FieldInfo)var15.next();
              //遍歷fields,在代理類中生成對(duì)應(yīng)的變量.
                          //變量數(shù)量X=繼承的接口中方法數(shù)量+3(hashCode,equals,toString 3個(gè))
              var20.write(var14);
            }
            var14.writeShort(this.methods.size());
            var15 = this.methods.iterator();
            while(var15.hasNext()) {
              ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
              //遍歷methods,在代理類中生成對(duì)應(yīng)的方法.
              //方法數(shù)量Y=
                //3+繼承的接口中的方法
                //+this.generateConstructor()對(duì)應(yīng)的構(gòu)造方法
                //+this.generateStaticInitializer()對(duì)應(yīng)的static初始化方法快
              //方法數(shù)量Y=X+2個(gè)(將靜態(tài)方法快也看作1個(gè)方法)
              var21.write(var14);
            }
            var14.writeShort(0);
            //返回代理類Class生成的byte[]
            return var13.toByteArray();
          }
          //1.2.1.1:
          private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();
          private void addProxyMethod(Method var1, Class<?> var2) {
            String var3 = var1.getName();  //方法名稱
            Class[] var4 = var1.getParameterTypes();  //方法參數(shù)類型數(shù)組
            Class var5 = var1.getReturnType();  //方法的正式返回類型
            Class[] var6 = var1.getExceptionTypes();  //方法拋出的異常類型數(shù)組
            //方法名稱+拼接的方法參數(shù)字符串K,類似:"funcName(int a,int b,String c)"
            //1.2.1.1.1:獲取 拼接的方法參數(shù)字符串
            String var7 = var3 + getParameterDescriptors(var4);
            Object var8 = (List)this.proxyMethods.get(var7);
            if (var8 != null) {
              ****
            }else{
              //創(chuàng)建ArrayList,以K為鍵,將ArrayList存儲(chǔ)到proxyMethods中
              var8 = new ArrayList(3);
              this.proxyMethods.put(var7, var8);
            }
            //向ArrayList中添加ProxyGenerator.ProxyMethod實(shí)例
            //ProxyGenerator.ProxyMethod:一個(gè)和Method類似的包含代理類方法各屬性的類
            ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
          }
          //1.2.1.1.1:根據(jù)方法參數(shù)類型數(shù)組,獲取 拼接的方法參數(shù)字符串,
          //類似:  "(int a,int b,String c)"
          private static String getParameterDescriptors(Class<?>[] var0) {
                StringBuilder var1 = new StringBuilder("(");
                for(int var2 = 0; var2 < var0.length; ++var2) {
                    var1.append(getFieldType(var0[var2]));
                }
                var1.append(')');
                return var1.toString();
          }
          
        }
        
      • 動(dòng)態(tài)代理生成的Class圖示:
        圖片來自于公共技術(shù)點(diǎn)之 Java 動(dòng)態(tài)代理
        動(dòng)態(tài)代理生成的Class結(jié)構(gòu).png
  5. 策略模式:簡(jiǎn)單講就是替代if else if,根據(jù)參數(shù)選擇對(duì)應(yīng)算法處理
    • 定義:定義一組算法,將它們封裝起來,彼此可以替換;是if else if的優(yōu)化
    • 場(chǎng)景:在一個(gè)處理過程中,涉及很多if else判斷,對(duì)同樣的數(shù)據(jù)進(jìn)行不同邏輯的處理;可以將每個(gè)具體的邏輯處理進(jìn)行封裝.根據(jù)具體情況選擇合適的封裝過的實(shí)例處理.
    • 作用:解決復(fù)雜邏輯下if else太復(fù)雜難以維護(hù).
    • 成員:策略接口+策略實(shí)現(xiàn)類+環(huán)境角色+客戶端
    • 代碼實(shí)例
      場(chǎng)景:比如去洗頭房,去過一次和去過100次,
      洗頭房給提供的技師水平是不同的(新手,老手,大師),
      不同級(jí)別的技師,洗頭的時(shí)間和手法都不同.
      
      //1:創(chuàng)建策略接口
      public interface JiShi{
        public void service();
      }
      //2:創(chuàng)建具體策略角色
      public class XinShou implements JiShi{
        @Override
        public void service(){
          System.out.println("洗頭時(shí)間30分鐘");
          System.out.println("手法一般!");
        }
      }
      public class LaoShou implements JiShi{
        @Override
        public void service(){
          System.out.println("洗頭時(shí)間60分鐘");
          System.out.println("手法很好!");
        }
      }
      public class DaShi implements JiShi{
        @Override
        public void service(){
          System.out.println("洗頭時(shí)間90分鐘");
          System.out.println("手法牛逼!");
        }
      }
      //3:創(chuàng)建環(huán)境角色
      public class JiShiContext{
        //環(huán)境角色中持有策略角色
        private JiShi ji;
        //times:客人第幾次來
        public void xitou(int times){
          if(times > 60){
            ji = new DaShi();
          }else if(times > 30){
            ji = new LaoShou();
          }else{
            ji = new XinShou();
          }
          ji.service();
        }
      }
      //4:客戶端調(diào)用
      JiShiContext context = new JiShiContext();
      context.xitou(70);
      context.xitou(40);
      context.xitou(10);
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末退客,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子链嘀,更是在濱河造成了極大的恐慌萌狂,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀泊,死亡現(xiàn)場(chǎng)離奇詭異茫藏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)包个,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門刷允,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碧囊,你說我怎么就攤上這事树灶。” “怎么了糯而?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵天通,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我熄驼,道長(zhǎng)像寒,這世上最難降的妖魔是什么烘豹? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮诺祸,結(jié)果婚禮上携悯,老公的妹妹穿的比我還像新娘。我一直安慰自己筷笨,他們只是感情好憔鬼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胃夏,像睡著了一般轴或。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仰禀,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天照雁,我揣著相機(jī)與錄音,去河邊找鬼答恶。 笑死饺蚊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悬嗓。 我是一名探鬼主播卸勺,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烫扼!你這毒婦竟也來了曙求?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤映企,失蹤者是張志新(化名)和其女友劉穎悟狱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堰氓,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挤渐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了双絮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浴麻。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖囤攀,靈堂內(nèi)的尸體忽然破棺而出软免,到底是詐尸還是另有隱情,我是刑警寧澤焚挠,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布膏萧,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏榛泛。R本人自食惡果不足惜蝌蹂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望曹锨。 院中可真熱鬧孤个,春花似錦、人聲如沸沛简。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽覆享。三九已至,卻和暖如春营袜,著一層夾襖步出監(jiān)牢的瞬間撒顿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工荚板, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凤壁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓跪另,卻偏偏與公主長(zhǎng)得像拧抖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子免绿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345