Android實用技巧-動態代理

應用場景

在Android的代碼維護當中,經常會涉及到邏輯變更.但是又並不是整個邏輯變更了,往往是類似在之前的操作前面追加邏輯,或者是在之後追加邏輯.對於這樣的邏輯,往往是每個類型的操作裏面都要變更.
比較笨的方法當然就是挨個去寫啦.但是這種體力勞動太低效,太浪費了.在JAVA中,針對這樣的應用場景,比較常規的是有兩種處理方案:
1.繼承,重寫方法;
2.裝飾模式;
3.動態代理;

1.繼承

對於這種方式,是最簡單的方式.對於這個方式,大部分開發者都會很簡單的就想到.但是這個方式會造成很大部分的垃圾代碼,且靈活性並不高,而且還必須知道具體的類才能完成.示例如下:

1.1 寫一個普通的類實現一個普通的方法

由於這裏寫在外面類比較麻煩,所以就直接在一個類中用靜態內部類實現.

static class Man{
    public void run(){
        //一個正常人 跑步的方法
        System.out.println("the man is running !");
    }
}

1.2 寫一個類繼承這個類,進行增強

static class SuperMan extends Man{
    @Override
    public void run() {
        //super.run();
        System.out.println("the superMan is flying !");
    }
}

1.3 運行測試

       Man man = new SuperMan();
       man.run();

       //輸出結果  the superMan is flying !

2.裝飾模式

在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
裝飾者模式,在java中的實現是通過接口來定義,包裝類和被包裝類實現同一個接口,包裝類持有被包裝類對象,包裝類就可以對被包裝類實現增強了.

2.1 接口類定義

interface Waiter{
    void server();
}

2.2 被包裝類實現

static class Waitress implements Waiter{

    @Override
    public void server() {
        System.out.println("Waitress server !");
    }
}

2.3 包裝類實現

static class WaitessWrapper implements Waiter{

    public Waiter waiter;

    public WaitessWrapper(Waiter waiter) {
        //爲了通用  持有接口類
        this.waiter = waiter;
    }

    @Override
    public void server() {
        System.out.println("smile");
        waiter.server();
        System.out.println("smile end !");
    }
}

2.4 調用函數

    Waiter waiter1 = new Waitress();
    waiter1.server();
    Waiter waiter2 = new WaitessWrapper(waiter1);
    waiter2.server();
    //結果顯示如下

Waitress server !

smile
Waitress server !
smile end !

3.動態代理

代理機制及特點
1.通過實現 InvocationHandler 接口創建自己的調用處理器;
2.通過爲 Proxy 類指定 ClassLoader 對象和一組 interface 來創建動態代理類;
3.通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;
4.通過構造函數創建動態代理類實例,構造時調用處理器對象作爲參數被傳入。

由於主要目的是實現Android中的應用,因此我就直接附上項目代碼進行說明:

 private void initListener() {


    Object object = Proxy.newProxyInstance(this.getClass()
                                               .getClassLoader(),
                                           this.getClass()
                                               .getInterfaces(),
                                           (Object proxy, Method method, Object[] args) -> {
                                               switch (method.getName()) {
                                                   case "onCategory":
                                                   case "onName":
                                                   case "onShare":
                                                   case "onPic":
                                                   case "onItemClick":
                                                   case "onRefresh":
                                                   case "onClick":
                                                   case "onOptionsItemSelected":
                                                   case "onNavigationItemSelected":
                                                       if (isEnvent) {
                                                           return null;
                                                       }
                                                       return method.invoke(NavActivity.this,
                                                                            args);

                                                   case "onOption":
                                                       if (isEnvent) {
                                                           return null;
                                                       }
                                                       if (!TDevice.hasInternet()) {
                                                           showErrorTip(getString(R.string.footer_type_net_error));
                                                           return null;
                                                       }
                                                       if (WXShareManager.sWXAPI.getWXAppSupportAPI() <= WXShareManager.TIMELINE_SUPPORTED_VERSION) {
                                                           AppContext.showToast(R.string.weixin_update);
                                                           return null;
                                                       }
                                                       return method.invoke(NavActivity.this,
                                                                            args);

                                               }
                                               return method.invoke(NavActivity.this, args);
                                           });

    mNavView.setNavigationItemSelectedListener((NavigationView.OnNavigationItemSelectedListener) object);
    mFgtMainSrl.setOnRefreshListener((SwipeRefreshLayout.OnRefreshListener) object);
    mAdapter.setListener((FileItemAdapter.OnFileItemListener) object);
    mCreate.setOnClickListener((View.OnClickListener) object);
    mScrollListener = new ScrollListener(null);
    mFgtMainRv.addOnScrollListener(mScrollListener);
    mCiv.setOnClickListener((View.OnClickListener) object);
    mTvUser.setOnClickListener((View.OnClickListener) object);
    mFusion.setOnClickListener((View.OnClickListener) object);
    mDelete.setOnClickListener((View.OnClickListener) object);
    mSeletedAll.setOnClickListener((View.OnClickListener) object);
}

如上,是我項目中的代碼.簡單說明一下:
1.標記位 isEnvent 是項目中當前是否有操作,在有操作的情況下應該避免別的操作.
2.Proxy.newProxyInstance 返回值 Object 是返回的當前操作執行返回的接口對象.
3.Proxy.newProxyInstance 中參數說明:如下是方法聲明

  public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException{}

參數一:ClassLoader loader 類加載器
參數二:Class<?>[] interfaces 代理接口
參數三:InvocationHandler h 對於代理接口的處理對象,主要代碼邏輯在這個類中實現,處理邏輯詳細看上述例子.

這樣實現可以最大限度的簡化代碼,解耦.而且這樣改起來也很方便.在後續代碼邏輯的變化中,主要邏輯寫在接口方法實現中,追加的邏輯都寫在代理對象裏面.可以方便隨時更改,而且比較統一,找起來也不復雜.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章