編輯:關於android開發
繼續更新設計模式系列,寫這個模式的主要原因是最近看到了動態代理的代碼。
先來回顧一下前5個模式:
- Android開發中無處不在的設計模式——單例模式
- Android開發中無處不在的設計模式——Builder模式
- Android開發中無處不在的設計模式——觀察者模式
- Android開發中無處不在的設計模式——原型模式
- Android開發中無處不在的設計模式——策略模式
動態代理模式在Java WEB中的應用簡直是隨處可見,尤其在Spring框架中大量的用到了動態代理;算是最重要的一個設計模式,也是最難理解的設計模式之一。
那麼什麼叫動態代理呢
代理類在程序運行前不存在、運行時由程序動態生成的代理方式稱為動態代理。
當前的網絡請求庫多種多樣,其中Square公司的OkHttp簡直是完美的一個網絡請求庫,而在其上又封裝了一層的Retrofit庫,為方便快捷的調用Restful Api提供了一種捷徑。如果你用過Retrofit,一定不會忘記有會有這麼一個過程:
首先定義一個接口,接口中定義網絡請求的具體方法,在方法上通過注解配置host,header,params等信息。
然後新建一個Retrofit對象,通過該對象產生一個你定義的接口對象。
通過接口對象調用具體的方法完成請求。
就像這樣子:
> listRepos(@Path("user") String user);
}
" data-snippet-id="ext.fadc3883ecfd2cd1a1ca67e15e7b1971" data-snippet-saved="false" data-csrftoken="5VgX1Wh4-W-l4cPYta7C6PsnrxDn_HUux6Fk">
public interface GitHubService {
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);
> repos = service.listRepos("octocat");
" data-snippet-id="ext.ee6b799b6e05337f344653d3f6028237" data-snippet-saved="false" data-csrftoken="zatspdc2-6xr9Qf-RbSgSei_XQsqIqeqXvQQ">
Call> repos = service.listRepos("octocat");
那麼你有沒有想過一個問題,接口是不可以直接new出來的,GitHubService接口的實例是如何產生的呢,retrofit.create方法內部到底做了什麼呢。沒錯,答案就是動態代理。該對象是程序運行期生成的代理對象。
動態代理雖然在Java WEB中大量的用到,但是在客戶端,由於考慮到性能的問題,所以用動態代理都會慎重考慮,但是,一旦動態代理用的好,就會產生不一樣的效果,就比如這個Retrofit庫。下面,我們實現一個Retrofit的最最簡易的版本。過一下動態代理的原理。由於是簡易版,所以很多東西和Retrofit還是有差距的,自然也沒有Retrofit那麼方便,這點無視就好了。我們就以實現上面那個例子為例:
首先說明一點,我們的請求是異步的,所以返回值我們使用void,增加一個回調的參數,約定最後一個參數是回調。
{
void onSuccess(Object t);
void onFailed(Exception e);
}
" data-snippet-id="ext.a886f274abbdc51fa06b7d1abed39036" data-snippet-saved="false" data-csrftoken="notDIIJF-5EKuT6X6LWWipbRR2DHvKJcZYxM">
public interface Callback {
void onSuccess(Object t);
void onFailed(Exception e);
}
最終的接口定義會是這個樣子。
> callback);
/**
* 約定最後一個參數是callback
*/
}
" data-snippet-id="ext.f115dcec337f47ddaaf65522ab40a2e0" data-snippet-saved="false" data-csrftoken="hOCzSeOZ-2RNrlcsbRh9aW4trRlUk_l6C1D4">
public interface GithubService {
@GET("users/{user}/repos")
void listRepos(@Path("user") String user,Callback> callback);
/**
* 約定最後一個參數是callback
*/
}
用到了兩個注解,一個是方法注解,一個是參數注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface GET {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Path {
String value();
}
Repo實體類是使用GsonFormat根據json自動生成的。
然後我們編寫Retrofit類,這個類應該是一個builder模式,裡面可以設置baseUrl,姑且忽略其他所有參數。還有一個create方法,則原型如下:
public class Retrofit {
private String baseUrl;
private Retrofit(Builder builder) {
this.baseUrl = builder.baseUrl;
}
public T create(Class clazz) {
return null
}
static class Builder {
private String baseUrl;
Builder baseUrl(String host) {
this.baseUrl = host;
return this;
}
Retrofit build() {
return new Retrofit(this);
}
}
}
最最關鍵的內容就是create方法的實現了。原理就是先拿到最後一個參數,也就是回調,再拿到方法上的注解,獲得具體的值,然後拿到除了回調之外的其他參數,獲得參數上的注解,然後根據注解取得對應的值,還有原來的參數值,將方法上的注解的值中進行替換。使用OkHttp構造請求,請求完成後根據將結果解析為回調中的類型。整個過程如下
public T create(Class clazz) {
/**
* 緩存中去
*/
Object o = serviceMap.get(clazz);
/**
* 取不到則取構造代理對象
*/
if (o == null) {
o = (T) Proxy.newProxyInstance(Retrofit.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final Callback callback = (Callback) args[args.length - 1];
final GET get = method.getAnnotation(GET.class);
if (get != null) {
/**
* 獲得GET注解的值
*/
String getValue = get.value();
System.out.println(getValue);
/**
* 獲得所有參數上的注解
*/
Annotation[][] methodParameterAnnotationArrays = method.getParameterAnnotations();
if (methodParameterAnnotationArrays != null) {
int count = methodParameterAnnotationArrays.length;
for (int i = 0; i < count; i++) {
/**
* 獲得單個參數上的注解
*/
Annotation[] methodParameterAnnotations = methodParameterAnnotationArrays[i];
if (methodParameterAnnotations != null) {
for (Annotation methodParameterAnnotation : methodParameterAnnotations) {
/**
* 如果是Path注解
*/
if (methodParameterAnnotation instanceof Path) {
/**
* 取得path注解上的值
*/
Path path = (Path) methodParameterAnnotation;
String pathValue = path.value();
System.out.println(pathValue);
/**
* 這是對應的參數的值
*/
System.out.println(args[i]);
Request.Builder builder = new Request.Builder();
/**
* 使用path注解替換get注解中的值為參數值
*/
String result = getValue.replaceAll("\\{" + pathValue + "\\}", (String) args[i]);
System.out.println(result);
/**
* 開始構造請求
*/
Request request = builder.get()
.url(baseUrl + "/" + result)
.build();
okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
/**
* 失敗則回調失敗的方法
*/
callback.onFailed(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
/**
* 請求成功
*/
String body = response.body().string();
/**
* 使用fastjson進行zhuan轉換
*/
Type type = callback.getClass().getGenericInterfaces()[0];
Object o1 = JSON.parse(body);
/**
* 回調成功
*/
callback.onSuccess(o1);
}
}
});
}
}
}
}
}
}
return null;
}
});
/**
* 扔到緩存中
*/
serviceMap.put(clazz, o);
}
return (T) o;
}
然後我們就可以根據Retrofit那樣進行調用了
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GithubService githubService = retrofit.create(GithubService.class);
githubService.listRepos("lizhangqu", new Callback>() {
@Override
public void onSuccess(Object t) {
System.out.println(t);
}
@Override
public void onFailed(Exception e) {
}
});
這只是Retrofit中最簡單的一個模塊實現,如果對其他內容感興趣,可以閱讀retrofit的源碼。
popupwindow展示,popupwindow 樣式: layout: popup_appinfo.xml 1 <?xml versi
Android中的LinearLayout布局,linearlayout垂直布局LinearLayout : 線性布局
Android開發學習之路--Content Provider之初體驗 天氣說變就變,馬上又變冷了,還好空氣不錯,陽光也不錯,早起上班的車上的人也不多,公司來的同事和
Android特效專輯(八)——實現心型起泡飛舞的特效,讓你的APP瞬間暖心 Android特效專輯(八)——實現心型起泡飛舞的特效,讓你的A