Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android開發中無處不在的設計模式——動態代理模式

Android開發中無處不在的設計模式——動態代理模式

編輯:關於android開發

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的源碼。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved