自己備忘,隨便寫
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涉及到的注解
- 網(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
- 標(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
- MultipartBody.Part.createFormData(String name, @Nullable String filename, RequestBody body):
- 網(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);
- @Body注解參數(shù),則不能同時(shí)使用@FormUrlEncoded顾患、@Multipart,否則會(huì)報(bào)錯(cuò):
- @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
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
- Retrofit支持多種數(shù)據(jù)解析方式,使用時(shí)需要在build.gradle添加依賴
- 步驟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ì)模式
- 模板模式
- 定義:定義一個(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) + 鉤子方法的重寫
- 父類:抽象類 public abstract class AbsParent
- 代碼實(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") } }
- 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í)例;
- 產(chǎn)品類
- 代碼實(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(); } }
- 外觀模式/門面模式
- 定義:定義一個(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();
- 代理模式
- 定義:通過訪問代理類的方式來間接訪問目標(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)代理對(duì)象P執(zhí)行方法調(diào)用順序:
- 策略模式:簡(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);