Android事件動機模式

和你一起終身學習,這裏是程序員 Android

經典好文推薦,通過閱讀本文,您將收穫以下知識點:

一、事件動機模式簡介
二、事件動機模式的性質
三、事件動機模式的Java桌面程序版
四、事件動機模式的Android版
五、事件動機模式的原理
5.1 對方法進行拆解封裝重構
5.2 事件是程序執行的動機
5.3 子模塊的內部結構與外部關係
5.4 事件動機模式Android版實現的技巧
5.5 純計算
5.6 事件動機模式的數據流圖
六、項目舉例

一、事件動機模式簡介

事件動機模式是一種架構模式。
事件動機模式的程序包含多個子模塊、一個外部關係模塊和一個純計算工具類。
常見的子模塊有 Activity、Dialog、Toast、Fragment、SharedPreferences、FileManager、SqliteManager、HttpUtil、LocationManager等。

二、事件動機模式的性質

性質一:事件動機模式中,只存在外部關係模塊調用子模塊,不存在子模塊調用外部關係模塊,也不存在子模塊調用其他子模塊。
性質二:事件動機模式中,子模塊只存在這六類供外部關係模塊調用的方法:

  1. new XxxManager();子模塊的構造器。例如new FileManager();
  2. void setListener();子模塊設置監聽器。例如Activity中登錄按鈕設置OnClickListener;
  3. Data get();從子模塊獲取數據。例如Activity中獲取編輯框內編輯的文本,再例如FileManager從文件讀取文本;
  4. void set(Data);將數據刷新到子模塊。例如Activity將String刷新到TextView文本框,再例如FileManager將文本保存到文件;
  5. void request(Data, OnResponseListener);子模塊執行耗時任務。例如網絡模塊執行Http請求;
  6. 生命週期控制方法;控制子模塊的生命週期。例如Activity中startXxxActivity()、mActivity.finish(),再例如mFileManager.open(filename)、mFileManager.close()。

三、事件動機模式的Java桌面程序版

任何Java桌面應用程序,都可以通過重構得到例子A這種形式的外部關係模塊:
例子A:

public class XxxExternalRelations {
    ViewManager mViewManager;
    FileManager mFileManager;
    GpsManager mGpsManager;
    GeocoderManager mGeocoderManager;
    public XxxExternalRelations(Object param) {
        mViewManager = new ViewManager();
        mFileManager = new FileManager();
        mGpsManager = new GpsManager();
        mGeocoderManager = new GeocoderManager();
        mViewManager.setOnVvvListener((vparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
        mFileManager.setOnFffListener((fparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
        mGpsManager.setOnGggListener((gparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
    }
}

也就是說程序是這樣執行的:先創建子模塊並且給子模塊設置監聽器,然後等待事件的發生來執行其他代碼。

四、事件動機模式的Android版

任何Android程序,都可以通過重構得到下面這種形式(下面的代碼都可在“項目舉例”的ProgramStructureGPS_20210630.zip項目文件中閱讀):
ActivityLifecycleListener.java

package org.ourmap.programstructuregps.event_motive_mode;
public class ActivityLifecycleListener {
    public void onModulesCreated() {
    }
    public void onResume() {
    }
    public void onPause() {
    }
    public void onDestroy() {
    }
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    }
}

BaseActivity.java

/**
 * 事件動機模式的View模塊
 */
public abstract class BaseActivity extends Activity {
    private ActivityLifecycleListener mLifecycleListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResourceID());
        onCreateViewModule();
        newExternalRelations(); // new ExternalRelations(this) and  setLifecycleListener(), create modules, and set listeners for modules.
        if (mLifecycleListener != null) {
            mLifecycleListener.onModulesCreated();
        }
    }
    protected abstract int getLayoutResourceID();
    protected abstract void onCreateViewModule();
    protected abstract void newExternalRelations();
    protected void setLifecycleListener(ActivityLifecycleListener activityLifecycleListener) {
        mLifecycleListener = activityLifecycleListener;
    }
    @Override
    protected void onResume() {
        super.onResume();
        if (mLifecycleListener != null) {
            mLifecycleListener.onResume();
        }
    }
    ... // onPause(), onDestroy(), onRequestPermissionsResult()等類似於onResume()一樣
}

BaseExternalRelations.java

package org.ourmap.programstructuregps.event_motive_mode;
/**
 * 事件動機模式的外部關係模塊
 */
public class BaseExternalRelations<Activity extends BaseActivity> {
    protected Activity mActivity;
    public BaseExternalRelations(Activity activity) {
        mActivity = activity;
        mActivity.setLifecycleListener(newActivityLifecycleListener());
    }
    protected ActivityLifecycleListener newActivityLifecycleListener() {
        return new ActivityLifecycleListener(){
        };
    }
}

五、事件動機模式的原理

5.1 對方法進行拆解封裝重構

對方法進行拆解封裝重構的例子:

void functionX() {
    sentenceA();
    functionB();
    functionC();
}
private void functionB() {
    sentenceD();
    functionE();
}
private void functionE() {
    sentenceF();
}
private void functionC() {
    sentenceG();
    sentenceH();
}

對方法functionX()拆解封裝重構之後得到:

void functionX() {
    sentenceA();
    sentenceD();
    sentenceF();
    sentenceG();
    sentenceH();
}

重構之前,functionX()方法調用語句sentenceF()形成的棧是:

functionX() > functionB() > functionE() > sentenceF();

重構之後,functionX()方法調用語句sentenceF()形成的棧是:

functionX() > sentenceF();

5.2 事件是程序執行的動機

例如,點擊登錄按鈕執行登錄這個過程,是點擊事件導致了登錄請求的執行。
再例如,點擊桌面圖標啓動某個App這個過程,是點擊事件導致了某個Activity的創建。
再例如,應用收到一個透傳的推送消息而彈出某個提示這個過程,是網絡消息事件導致程序的執行。
於是得到命題一,命題一:事件是程序執行的動機。
事件是程序執行的動機,意味着程序的方法棧的棧底是一個事件方法。
例如,執行登錄網絡請求時,程序的方法棧是:

onClick() > requestLogin() > HttpUtil.login();

其中onClick()就是登錄按鈕的OnClickListener監聽器的事件方法。
對onClick()方法進行拆解封裝重構之後,requestLogin()就消除了,執行登錄網絡請求的方法棧變爲:

onClick() > HttpUtil.login();

於是得到結論A,結論A:對事件方法進行拆解封裝重構之後,程序調用各個子模塊的方法都是事件方法。

5.3 子模塊的內部結構與外部關係

對於程序,爲了讓每個子模塊內部都不調用其他子模塊(包括Android的Activity),那必然需要構建一個“中間模塊”來創建和調用各個子模塊。
又由5.2節中的“結論A:程序調用各個子模塊的方法都是事件方法”,那麼只要把事件方法都遷移到“中間模塊”,就可以讓程序只存在“中間模塊”調用子模塊。於是程序滿足了性質一“事件動機模式中只存在‘中間模塊’調用子模塊”。
至於怎樣把事件方法遷移到“中間模塊”,只要將監聽器的設置放在“中間模塊”即可。例如將mActivity.setLoginListener()、mGpsManager.setLocationListener()放在“中間模塊”中調用。
如果子模塊的某個監聽器只調用子模塊本身,那麼這個監聽器只需要在子模塊內部創建,不需要放在“中間模塊”中。

命題二:任何一個本體都具有內部結構與外部關係,一內一外構成其整體。
本體的內部結構與外部關係:


子模塊的內部結構就是子模塊內部的變量和方法等。由於只有“中間模塊”的方法中調用了多個子模塊,所以子模塊的外部關係就是“中間模塊”,於是將“中間模塊”命名爲外部關係模塊。

5.4 事件動機模式Android版實現的技巧

Activity模塊創建完成之後再創建外部關係模塊,並把Activity傳入外部關係模塊的構造器中。然後在外部關係模塊的構造器中優先執行mActivity.setLifecycleListener()。對於ActivityLifecycleListener,由於點擊返回按鈕會導致onDestroy()執行,所以將onDestroy()視爲事件方法,onResume()、onRequestPermissionsResult()等也是這樣,於是將這幾個事件方法放在ActivityLifecycleListener監聽器中。
BaseActivity.java

public abstract class BaseActivity extends Activity {
    private ActivityLifecycleListener mLifecycleListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResourceID());
        onCreateViewModule();
        newExternalRelations();
        if (mLifecycleListener != null) {
            mLifecycleListener.onModulesCreated();
        }
    }
    protected abstract int getLayoutResourceID();
    protected abstract void onCreateViewModule();
    protected abstract void newExternalRelations();
    protected void setLifecycleListener(ActivityLifecycleListener activityLifecycleListener) {
        mLifecycleListener = activityLifecycleListener;
    }
    @Override
    protected void onDestroy() {
        if (mLifecycleListener != null) {
            mLifecycleListener.onDestroy();
        }
        super.onDestroy();
    }
    ... // onResume(), onPause(), onRequestPermissionsResult()等類似於onDestroy()一樣
}

MainActivity.java

    @Override
    protected void newExternalRelations() {
        new MainRelations(this);
    }

BaseExternalRelations.java

public class BaseExternalRelations<Activity extends BaseActivity> {
    protected Activity mActivity;
    public BaseExternalRelations(Activity activity) {
        mActivity = activity;
        mActivity.setLifecycleListener(newActivityLifecycleListener());
    }
    protected ActivityLifecycleListener newActivityLifecycleListener() {
        return new ActivityLifecycleListener(){
        };
    }
}

MainRelations.java

    public MainRelations(MainActivity mainActivity) {
        super(mainActivity);
        mGpsManager = new GpsManager(mainActivity.getApplicationContext());
        ...
        mGpsManager.setLocationListener(newLocationListener());
    }
    @Override
    protected ActivityLifecycleListener newActivityLifecycleListener() {
        return new ActivityLifecycleListener() {
            @Override
            public void onModulesCreated() { // 當各個模塊都創建完成、設置監聽器完成後,所執行的
            ... // do something
            }
            @Override
            public void onDestroy() {
            ... // do something
            }
        };
    }
    ...

相當於Java桌面程序版這樣的流程:

ViewManager mViewManager = new ViewManager();
new XxxExternalRelations(mViewManager);
    public XxxExternalRelations(mViewManager) {
        mViewManager.setOnVvvListener((vparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
        mFileManager = new FileManager();
        mGpsManager = new GpsManager();
        mGeocoderManager = new GeocoderManager();
        mFileManager.setOnFffListener((fparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
        mGpsManager.setOnGggListener((gparam) -> {
            // 調用mViewManager, mFileManager, HttpUtil, mGpsManager, mGeocoderManager, PureCalculation
        });
    }

對於攜帶參數數據的情況,參數視爲業務數據(業務數據的概念在“5.6 事件動機模式的數據流圖”),所以參數數據的變量放在外部關係模塊中:
MainRelations.java

    public MainRelations(MainActivity mainActivity) {
        super(mainActivity);
        data = mActivity.getIntent().getData();
        mActivity.initViewWithData(data);
        ...
    }

對於Fragment,類似的方法如下:
XxxRelations.java

    public XxxRelations(XxxFragment xxxFragment) {
        super(xxxFragment);
        data = mFragment.getArguments().getData();
        mFragment.initViewWithData(data);
        ...
    }

5.5 純計算

數據體:例如Java Bean、字符串、數字、byte數組、Bitmap等稱之爲數據體。View對象、File對象等不是數據體。
純計算:以一組數據體作爲輸入、一組數據體作爲輸出的函數,稱之爲純計算。

[dataD, dataE] = function(dataA, dataB, dataC)

在java語言中,純計算封裝爲public static修飾的方法。第3節中的PureCalculation類就是一個純計算工具類。
純計算的性質:純計算函數執行時,數據只在內存和CPU流動,不會涉及顯示屏、文件等硬件模塊和View、File等軟件模塊。

5.6 事件動機模式的數據流圖

事件動機模式的數據流圖:


“業務數據”通過“純計算”得到“網絡模塊數據”的舉例:
登錄時,帶有手機號和驗證碼的Bean對象通過new Gson().toJson(bean)轉化爲json字符串,json字符串可直接用於網絡請求。其中Bean對象是業務數據,new Gson().toJson(bean)是純計算,json字符串是網絡模塊數據。
“業務數據”通過“純計算”得到“視圖模塊數據”的舉例:
例子一,業務數據“int count”需要顯示到TextView中,中間需要進行純計算“stringCount = String.valueOf(count)”,得到視圖模塊數據stringCount,stringCount可以直接傳入TextView中顯示出來;
例子二,有6個標籤([tag0, tag1, tag2, tag3, tag4, tag5])和它們的選中狀態(其中第{0, 1, 5}個是選中的)需要顯示出來,由於顯示的方式是列表,所以要將標籤名和{0, 1, 5}轉化爲[{tag0, true}, {tag1, true}, {tag2, false}, {tag3, false}, {tag4, false}, {tag5, true}]這種列表形式才能直接被列表的Adapter使用,這一步轉化就是純計算。
“業務數據”通過“純計算”得到“文件模塊數據”的舉例:
對View截圖獲取的Bitmap,需要轉碼爲jpg格式得到byte[]比特數組,然後byte[]可以直接被FileManager寫入圖片文件,轉碼這一步是純計算。

數據流圖說明了命題三,命題三:程序執行的過程是內部通信與計算的過程。內部通信是指內存到其他硬件之間的通信。

六、項目舉例

使用事件動機模式構建的項目:ProgramStructureGPS_20210630.zip。
其中包含了事件動機模式的Activity型基類、兩種Fragment型基類、MainActivity實現類、MainRelations實現類、FileManager、PureCalculation等等,以及依據事件動機模式的思想構建的OkHttp封裝類。
項目文件下載地址:
鏈接:https://pan.baidu.com/s/18cCiAzBQtz4xlkkqFdsX1g
提取碼:wkl7

原文鏈接:https://blog.csdn.net/definedone/article/details/119034635

至此,本篇已結束。轉載網絡的文章,小編覺得很優秀,歡迎點擊閱讀原文,支持原創作者,如有侵權,懇請聯繫小編刪除,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

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