Networking là một trong những phần quan trọng nhất của các ứng dụng Android. Ban đầu, chúng ta sử dụng HTTP class để xử lý networking. Theo Thời gian mọi thứ trở nên dễ dàng hơn khiến chúng ta phụ thuộc vào các thư viện. Chúng ta sử dụng nhiều thư viễ để thực hiện công việc nhanh hơn nhưng hầu hết chúng ta không phân thích những hạn chế và nhược điểm của nó. Trươc sử dụng bất kỳ thư viện nào chúng ta cần phân tích ba điều What, Why và How. Một thư viện rất phổ biến để xử lý Networking là Retrofit. Trong bài viết này, hãy phân tích những điều trên và hiểu cách mà Retrofit xử lý các request bên trong nó.
What
Retrofit là một type-safe HTTP client cho Android và Java.
Why
Sử dụng Retrofit làm các tác vụ networking trở nên dêx dàng hơn trong các ứng dụng Android. Vì nó có nhiều tính năng như dễ dàng thêm custom heawder và request types, file uploads, mocking response, v.v giúp chúng ta giảm code thừa trong các ứng dụng và xử lý web service dễ dàng.
How
Để làm việc với Retrofit về cơ bản chúng ta cần 3 class sau:
- Một model class được sử dụng làm Json model.
- Một interface mà định nghĩa HTTP operastion cần phải xử lý.
- Retrofit.Builder clas: Instance sử dụng interface được định nghĩa ở trên và Builder API để cho phép định nghĩa URL endpoin cho HTTP operations. Nó cũng nhận vào converter chúng ta cung cấp để format Response.
Về cơ bản
Để implement retrofit trong các ứng dụng của chúng ta, chúng ta cần theo các bước cơ bản sau:
- Add các dependencies cần thiết trong build.gradle module level
1 2 3 | implementation ‘com<span class="token punctuation">.</span>squareup<span class="token punctuation">.</span>retrofit2<span class="token operator">:</span>retrofit<span class="token operator">:</span><span class="token number">2.3</span><span class="token punctuation">.</span><span class="token number">0</span>’ implementation ‘com<span class="token punctuation">.</span>squareup<span class="token punctuation">.</span>retrofit2<span class="token operator">:</span>converter<span class="token operator">-</span>gson<span class="token operator">:</span><span class="token number">2.3</span><span class="token punctuation">.</span><span class="token number">0</span>’ |
- Tạo POJO class hoặc data model tương ứng với response
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>retrofit <span class="token keyword">import</span> com<span class="token punctuation">.</span>google<span class="token punctuation">.</span>gson<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span>SerializedName <span class="token keyword">import</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>Serializable <span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">SampleSharingModel</span><span class="token punctuation">(</span> <span class="token annotation builtin">@SerializedName</span><span class="token punctuation">(</span><span class="token string">"source"</span><span class="token punctuation">)</span> <span class="token keyword">val</span> source<span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token annotation builtin">@SerializedName</span><span class="token punctuation">(</span><span class="token string">"url"</span><span class="token punctuation">)</span> <span class="token keyword">val</span> url<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> <span class="token annotation builtin">@SerializedName</span><span class="token punctuation">(</span><span class="token string">"message"</span><span class="token punctuation">)</span> <span class="token keyword">val</span> message<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">)</span> |
- Tạo API interface có các phương thức thực hiện các HTTP request. Retrofit cung cấp một list các anotation cho mỗi HTTP method như @GET, @POST, @PUT, @DELETE,@PATCH, v.v.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>retrofit <span class="token keyword">import</span> retrofit2<span class="token punctuation">.</span>http<span class="token punctuation">.</span><span class="token operator">*</span> <span class="token keyword">interface</span> ApiService <span class="token punctuation">{</span> <span class="token annotation builtin">@GET</span><span class="token punctuation">(</span><span class="token string">"api/share-data/"</span><span class="token punctuation">)</span> <span class="token keyword">fun</span> <span class="token function">getShareData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Call<span class="token operator"><</span>SampleSharingModel<span class="token operator">></span> <span class="token punctuation">}</span> |
- Tạo một Retrofit Builder
1 2 3 4 5 6 | <span class="token comment">// Create a very simple REST adapter which points the API.</span> <span class="token keyword">val</span> retrofit <span class="token operator">=</span> Retrofit<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">baseUrl</span><span class="token punctuation">(</span>BASE_URL<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">addConverterFactory</span><span class="token punctuation">(</span>GsonConverterFactory<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
- Tạo một instance của service và gọi các phương thức bên trong nó. Bạn có thể làm điều này trong presenter hoặc ViewModel để lấy được các dữ cần thiết.
1 2 3 4 | // Create an instance of our ApiService interface. val shareService = retrofit.create(ApiService.class); val shareData = shareService.getShareData().execute() |
Việc này sẽ lấy dữ liệu từ server và sau đó chúng ta có thể thực hiện các actions mà cần dùng đến chúng. Liệu chúng ta đã hoàn thành hay chưa?. Với quan điểm của một developer bình thường, như thế này đã ổn để áp thực hiện như kì vọng nhưng chúng ta hãy cùng đi sâu hơn và sẽ thấy được nhiều điều khác. Bây giờ chúng ta hãy cùng nhau phân tích điều gì sảy ra sâu bên trong thư viện.
Behind the scenes
Khi chúng ta tạo một instance của service từ dòng này:
1 2 | <span class="token keyword">val</span> shareService <span class="token operator">=</span> retrofit<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>ApiService<span class="token punctuation">.</span>class<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Có một vài ma thuật xảy ra ở đây. Hãy xem nó. Click vào phương thức create chúng ta sẽ đi tới class Retrofit.java và phương thức create sẽ trong như dưới đây
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">public</span> <span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> T <span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">final</span> Class<span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> service<span class="token punctuation">)</span> <span class="token punctuation">{</span> Utils<span class="token punctuation">.</span><span class="token function">validateServiceInterface</span><span class="token punctuation">(</span>service<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>validateEagerly<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">eagerlyValidateMethods</span><span class="token punctuation">(</span>service<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">(</span>T<span class="token punctuation">)</span> Proxy<span class="token punctuation">.</span><span class="token function">newProxyInstance</span><span class="token punctuation">(</span>service<span class="token punctuation">.</span><span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Class</span><span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> service <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">InvocationHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">final</span> Platform platform <span class="token operator">=</span> Platform<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> emptyArgs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token annotation punctuation">@Nullable</span> Object <span class="token function">invoke</span><span class="token punctuation">(</span>Object proxy<span class="token punctuation">,</span> Method method<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> Throwable <span class="token punctuation">{</span> <span class="token comment">// If the method is a method from Object then defer to normal invocation.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> Object<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> method<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>platform<span class="token punctuation">.</span><span class="token function">isDefaultMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> platform<span class="token punctuation">.</span><span class="token function">invokeDefaultMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> service<span class="token punctuation">,</span> proxy<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">loadServiceMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>args <span class="token operator">!=</span> null <span class="token operator">?</span> args <span class="token operator">:</span> emptyArgs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Việc kiểm tra đầu tiên là interface được cungc ấp có phải alf một interface hợp lệ hay không bằng cách gọi phương thức validateServiceInterface. Nếu không hợp lệ sẽ ném ra ** IllegalArgumentException**
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">static</span> <span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> <span class="token keyword">void</span> <span class="token function">validateServiceInterface</span><span class="token punctuation">(</span>Class<span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> service<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>service<span class="token punctuation">.</span><span class="token function">isInterface</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"API declarations must be interfaces."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Prevent API interfaces from extending other interfaces. This not only avoids a bug in</span> <span class="token comment">// Android (http://b.android.com/58753) but it forces composition of API declarations which is</span> <span class="token comment">// the recommended pattern.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>service<span class="token punctuation">.</span><span class="token function">getInterfaces</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"API interfaces must not extend other interfaces."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bước thứ 2 giống như một chuỗi các phương bắt đầu từ lệnh gọi phương thức eagerlyValidateMethods
1 2 3 4 5 6 7 8 9 | <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">eagerlyValidateMethods</span><span class="token punctuation">(</span>Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> service<span class="token punctuation">)</span> <span class="token punctuation">{</span> Platform platform <span class="token operator">=</span> Platform<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Method method <span class="token operator">:</span> service<span class="token punctuation">.</span><span class="token function">getDeclaredMethods</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>platform<span class="token punctuation">.</span><span class="token function">isDefaultMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token operator">!</span>Modifier<span class="token punctuation">.</span><span class="token function">isStatic</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">getModifiers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">loadServiceMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Trong phương thức trên Platform.get() có implementation dưới đây để return platform type. Bạn có thể check full code trong IDE của bạn nếu bạn muốn biết cụ thể hơn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <span class="token keyword">class</span> <span class="token class-name">Platform</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Platform PLATFORM <span class="token operator">=</span> <span class="token function">findPlatform</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">static</span> Platform <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> PLATFORM<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">static</span> Platform <span class="token function">findPlatform</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> Class<span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span><span class="token string">"android.os.Build"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Build<span class="token punctuation">.</span>VERSION<span class="token punctuation">.</span>SDK_INT <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Android</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> ignored<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> Class<span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span><span class="token string">"java.util.Optional"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Java8</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> ignored<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Platform</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* More methods of this class ........ ........ ......... */</span> <span class="token punctuation">}</span> |
Sau khi lấy được platform type nó gọi service.getDeclaredMethods() bên trong eagerlyValidateMethods() trả ra một array chứa các Method object phản ánh tất cả các phương thức được khia báo của class hoặc interface đại diện bởi object này, bao gồm cả public, protected, default, access, và private method nhưng không bao gồm các phương thức được kế thừa.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">public</span> Method<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">getDeclaredMethods</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> SecurityException <span class="token punctuation">{</span> Method<span class="token punctuation">[</span><span class="token punctuation">]</span> result <span class="token operator">=</span> <span class="token function">getDeclaredMethodsUnchecked</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Method m <span class="token operator">:</span> result<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Throw NoClassDefFoundError if types cannot be resolved.</span> m<span class="token punctuation">.</span><span class="token function">getReturnType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> m<span class="token punctuation">.</span><span class="token function">getParameterTypes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Duyệt qua tất cả các phần tử chứa trong các method array để xác định request type, method name, Annotations và arguments của mỗi cái được lưu trữ trong map serviceMethodCache như trong code bên dưới:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">private</span> <span class="token keyword">final</span> Map<span class="token operator"><</span>Method<span class="token punctuation">,</span> ServiceMethod<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">>></span> serviceMethodCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentHashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ServiceMethod<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> <span class="token function">loadServiceMethod</span><span class="token punctuation">(</span>Method method<span class="token punctuation">)</span> <span class="token punctuation">{</span> ServiceMethod<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> result <span class="token operator">=</span> serviceMethodCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>result <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span>serviceMethodCache<span class="token punctuation">)</span> <span class="token punctuation">{</span> result <span class="token operator">=</span> serviceMethodCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>result <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span> result <span class="token operator">=</span> ServiceMethod<span class="token punctuation">.</span><span class="token function">parseAnnotations</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> method<span class="token punctuation">)</span><span class="token punctuation">;</span> serviceMethodCache<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
ServiceMethod là một abstract class như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <span class="token keyword">package</span> retrofit2<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span>Method<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span>Type<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Nullable<span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token keyword">static</span> retrofit2<span class="token punctuation">.</span>Utils<span class="token punctuation">.</span>methodError<span class="token punctuation">;</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">ServiceMethod</span><span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> ServiceMethod<span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> <span class="token function">parseAnnotations</span><span class="token punctuation">(</span>Retrofit retrofit<span class="token punctuation">,</span> Method method<span class="token punctuation">)</span> <span class="token punctuation">{</span> RequestFactory requestFactory <span class="token operator">=</span> RequestFactory<span class="token punctuation">.</span><span class="token function">parseAnnotations</span><span class="token punctuation">(</span>retrofit<span class="token punctuation">,</span> method<span class="token punctuation">)</span><span class="token punctuation">;</span> Type returnType <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getGenericReturnType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Utils<span class="token punctuation">.</span><span class="token function">hasUnresolvableType</span><span class="token punctuation">(</span>returnType<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">methodError</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> <span class="token string">"Method return type must not include a type variable or wildcard: %s"</span><span class="token punctuation">,</span> returnType<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>returnType <span class="token operator">==</span> <span class="token keyword">void</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">methodError</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> <span class="token string">"Service methods cannot return void."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> HttpServiceMethod<span class="token punctuation">.</span><span class="token function">parseAnnotations</span><span class="token punctuation">(</span>retrofit<span class="token punctuation">,</span> method<span class="token punctuation">,</span> requestFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">abstract</span> <span class="token annotation punctuation">@Nullable</span> T <span class="token function">invoke</span><span class="token punctuation">(</span>Object<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Đến lượt nó gọi final class RequestFactory trong đó Builder method có code như sau:
1 2 3 4 5 6 7 8 | <span class="token function">Builder</span><span class="token punctuation">(</span>Retrofit retrofit<span class="token punctuation">,</span> Method method<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>retrofit <span class="token operator">=</span> retrofit<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>method <span class="token operator">=</span> method<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>methodAnnotations <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getAnnotations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>parameterTypes <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getGenericParameterTypes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>parameterAnnotationsArray <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getParameterAnnotations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Vì chúng ta không thể đi qua toàn bộ code của framework ở đây, vui lòng check trong IDE của bạn di chuyển qua các phương thức của chúng.
Cuối chúng ta sẽ tới java.lang.reflect để fetch annotation từ AccessibleObject class, getGenericParameterTypes() và getGenericParameterTypes() từ Method class.
Data Flow
Hãy xem data Flow của retrofit:
Trong hình trên, chúng ta có thể thấy rằng OkHttp bên dưới Retrofit. OkHttp kết hợp với OkHttp Soket tạo rat HTTP request. Và nó có các types của Okio library được gọi là BufferSink và BufferedSource
chúng ta có thể nghĩ chúng như input stream và output stream. Các types đặt ở giữa Retrofit và OkHttp là RequestBody và ResponseBody. Và chúng ta có các converter cho conversions, ở đây chúng ta thấy Moshi converter. Vì Moshi và Okio đều xuất phát từ Square nên 2 thư viện này có thể tương tác được với nhau. Trong Moshi Converter sectiont ở trong ảnh trên, chúng ta cosd thể thấy nó, và nó rất quan trọng bởi vì chúng ta có thể tránh nhiều absstractions ở giữa các layer kahcs nhau. Converter liên lạc với RequestBody và ResponseBody với Retrofit.
Khi chúng ta cọi endpoint Retrofit lấy Object chúng ta đưa vào và đưa nó vào converter. Khi converter nhận các đối tượng đó khi họ muốn gửi nó tới server. Moshi sẽ lấy Object đó và viết nó vào BufferSink, cái mà sẽ được wrap trọng RequestBody và nó cũng chính là type mà Retrofit đưa cho OkHttp và một thứ tương tự cũng được chuyển tới soket bằng BufferSink.
Và tương tự là các đọc từ sockSocketet, OkHttp sẽ wrap nó trong Okio types và cuối cũng thành ResponseBody. Và các converter được đưa trực tiếp cho ResponseBody bới vì Moshi biết cách để giao tiếp với Okio type. Nó lấy dữ liệu từ BuferSource sau đó converter chuyển đổi chúng thành Data Model mà ứng dụng cần.
Kết
Trên đây là bài viết của mình về Retrofit, nếu bài viết có sai sót mong mọi người đã theo dõi.
Tham khảo
https://medium.com/mindorks/understand-how-does-retrofit-work-c9e264131f4a