banner
jzman

jzman

Coding、思考、自觉。
github

Android組件化之Application

PS:不想看專業的知識就看點感興趣的知識,總之不要過於慵懶。

上一篇文章籠統的總結了一下組件化開發的一些基礎性問題,本篇文章繼續組件化的學習,主要分如下三個方面介紹組件化中的 Application 如下:

  1. Application 的作用
  2. 合併 Application
  3. 動態配置 Application

Application 的作用#

Androuid 應用的啟動的時候最先啟動的就是 Application,每個 App 運行時僅創建唯一一個 Application,其生命週期就是 App 的生命週期,Application 中常用的回調方法如下:

  • onCreate:創建應用程序時回調,回調時機早於任何 Activity。
  • onTerminate:終止應用程序時調用,不能保證一定會被調用。
  • onLowmemory:當後台應用程序終止,但前台用用程序內存還不夠時調用該方法,可在該方法中釋放一些不必要的資源來應對這種情況。
  • onConfigurationChanged:配置發生變化時回調該方法,如手機螢幕旋轉等
  • onTrimMemory:通知應用的不同內存情況,下面內存級別說明來自

其中附上一張來自Carson_Ho總結的 onTrimMemory 相關內存級別的說明如下:

image

Application 作為整個 App 的一個單例對象,其作用如下:

  1. 作為 App 的入口,可用來初始化基本配置,如第三方 SDK 的初始化。
  2. 可以在 Application 中定義供全局使用的變量,不過當應用被強殺之後有可能出現空指針的問題,導致再次打開應用的時候崩潰,如果確定要這樣使用,一定要處理好這種情況。
  3. 可以借助 Application 管理 Activity 的生命週期狀態以及判斷應用處於前台還是後台等,可根據內存優先級降低自身應用所占內存,減小自身應用被系統強殺的可能性。

合併 Application#

AndroidManifest 是每個 Module 的聲明配置文件,對應的在生成一個 App 的時候也應該對應一份 AndroidManifest 文件,那麼在多個 Module 彼此依賴的情況下就需要合併子 Module 的 AndroidManifest 文件內容到主 Module 的 AndroidManifest 文件中,最終會在 build 目錄下 生成最終的 AndroidManifest 文件,編譯生成的 AndroidManifest 文件的具體路徑參考如下:

app\build\intermediates\manifests\full\debug\AndroidManifest.xml

在合併子 Module 的 AndroidManifest 文件時,編譯器會補全 use-sdk 的信息以及一些未設置的屬性,在合併後如 Activity 等組件中的 name 屬性都以包名 + 文件名來指定。

其中在合併 AndroidManifest 文件要對 Application 進行合併, Application 合併規則如下:

  1. 如果子 Module 中有自定義的 Application,主 Module 中沒有自定義 Application,則會將子 Module 中的 Application 合併到最終的 AndroidManifest 文件中。
  2. 如果主 Module 有自定義 Application,子 Module 沒有自定義的 Application,則會在最終合併的 AndroidManifest 文件中使用主 Module 中的 Application。
  3. 如果多個子 Module 中都自定義了 Application,在解決衝突後則會在最終合併的 AndroidManifest 文件中使用最後編譯的 Module 中的 Application。
  4. 如果子 Module 中有自定義的 Application,子 Module 中也有自定義的 Application,此時也會提示要在主 Module 的 AndroidManifest 文件中添加 tools 屬性,編譯完成之後,合併後的 AndroidManifest 文件使用的是主 Module 中自定義的 Application。

在合併過程中如果不添加 tools 屬性,則會提示添加 tools 屬性,提示的錯誤信息如下:

Manifest merger failed : Attribute application@name value=(com.manu.module_one.OneApplication) from [:moduel_one] AndroidManifest.xml:13:9-58
	is also present at [:module_two] AndroidManifest.xml:13:9-58 value=(com.manu.module_two.TwoApplication).
	Suggestion: add 'tools:replace="android:name"' to <application> element at AndroidManifest.xml:6:5-21:19 to override.

比如這裡就要在子 Module 中的 AndroidManifest 文件的 application 標籤下添加 tools 屬性:

tools:replace="android:name"

動態配置 Application#

除了 Application 需要合併之外,在組件化過程中各個 Module 的初始化也非常重要,可以使用反射完成各個 Module 的初始化,就是在主 Module 中反射獲取子 Module 的初始化對象,然後調用其初始化方法,為了方便定義一個類管理子 Module 的初始化類,參考如下:

/**
 * Created by jzman
 * Powered by 2019/04/15 0022.
 */
public class ModuleConfig {
    private static final String moduleOneInit = "com.manu.module_one.ModuleOneAppInit";
    private static final String moduleTwoInit = "com.manu.module_two.ModuleTwoAppInit";
    public static String[] moduleInits = {
            moduleOneInit,
            moduleTwoInit
    };
}

創建一個初始化的基類接口如下:

/**
 * 統一App初始化接口
 * Created by jzman
 * Powered by 2019/04/15 0022.
 */

public interface BaseAppInit {
    /**
     * 高優先級被初始化
     * @param application
     * @return
     */
    boolean onInitHighPriority(Application application);

    /**
     * 低優先級被初始化
     * @param application
     * @return
     */
    boolean onInitLowPriority(Application application);
}

為了使得每個子 Module 都能方便使用該初始化基類,應將其放在基類 Module 中,因為基類被所有的 Module 所依賴,然後在每個子 Module 中繼承 BaseAppInit 實現自己 Module 的初始化類,參考如下:

/**
 * module_one初始化文件
 * Created by jzman
 * Powered by 2019/04/15 0022.
 */

public class ModuleOneAppInit implements BaseAppInit {
    private static final String TAG = ModuleOneAppInit.class.getSimpleName();

    @Override
    public boolean onInitHighPriority(Application application) {
        Log.i(TAG, "ModuleOneAppInit---onInitHighPriority");
        return true;
    }

    @Override
    public boolean onInitLowPriority(Application application) {
        Log.i(TAG, "ModuleOneAppInit---onInitLowPriority");
        return true;
    }
}

最後在主 Module 的自定義的 Application 中通過反射創建各個子 Module 的初始化類對象,並調用其初始化方法,參考如下:

/**
 * 高優先級初始化
 */
private void initModuleHighPriority(){
    for (String init: ModuleConfig.moduleInits){
        try {
            Class<?> clazz = Class.forName(init);
            BaseAppInit appInit = (BaseAppInit) clazz.newInstance();
            appInit.onInitHighPriority(this);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 低優先級初始化
 */
private void initModuleLowPriority(){
    for (String init: ModuleConfig.moduleInits){
        try {
            Class<?> clazz = Class.forName(init);
            BaseAppInit appInit = (BaseAppInit) clazz.newInstance();
            appInit.onInitLowPriority(this);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

運行日誌如下:

ModuleOneAppInit---onInitHighPriority 
ModuleTwoAppInit---onInitHighPriority
ModuleOneAppInit---onInitLowPriority
ModuleTwoAppInit---onInitLowPriority

此外,還可以在基類 Module 中創建初始化基類和 BaseApplication,然後在 BaseApplication 中反射調用具體的初始化方法,歸根結底還是使用反射,只是另一種實現方式,首先在基類 moddule 中創建 BaseAppInit 如下:

/**
 * Created by jzman
 * Powered by 2019/04/15 0022.
 */
public abstract class BaseAppInit {

    private Application mApplication;

    public BaseAppInit() {
    }

    public void setApplication(@NonNull Application application) {
        this.mApplication = application;
    }

    public void onCreate(){}

    public void OnTerminate(){}

    public void onLowMemory(){}

    public void configurationChanged(Configuration configuration){}
}

在基類 Module 中創建 BaseApplication 如下:

/**
 * Created by jzman
 * Powered by 2019/04/15 0023.
 */

public abstract class BaseApplication extends Application {

    private List<Class<? extends BaseAppInit>> classInitList = new ArrayList<>();
    private List<BaseAppInit> appInitList = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        appInit();
        initCreate();
    }

    protected abstract void appInit();

    protected void registerApplicationInit(Class<? extends BaseAppInit> classInit) {
        classInitList.add(classInit);
    }

    private void initCreate() {
        for (Class<? extends BaseAppInit> classInit : classInitList) {
            try {
                BaseAppInit appInit = classInit.newInstance();
                appInitList.add(appInit);
                appInit.onCreate();

            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        for (BaseAppInit appInit : appInitList) {
            appInit.OnTerminate();
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        for (BaseAppInit appInit : appInitList) {
            appInit.onLowMemory();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        for (BaseAppInit appInit : appInitList) {
            appInit.configurationChanged(newConfig);
        }
    }
}

然後在子 Module 中實現具體的初始化類,參考如下:

/**
 * Created by jzman
 * Powered by 2019/04/15 0023.
 */

public class ModuleThreeAppInit extends BaseAppInit {
    private static final String TAG = ModuleThreeAppInit.class.getSimpleName();

    @Override
    public void onCreate() {
        Log.i(TAG, "ModuleThreeAppInit---onCreate");
    }
}

最後,在主 Module 中繼承 BaseApplication 實現自定義的 Application,並註冊每個子 Module 的初始化文件,參考如下:

/**
 * Created by jzman
 * Powered by 2019/04/15 0023.
 */

public class MApplication extends BaseApplication{
    @Override
    protected void appInit() {
        registerApplicationInit(ModuleThreeAppInit.class);
        registerApplicationInit(ModuleForeAppInit.class);
    }
}

運行日誌如下:

ModuleThreeAppInit---onCreate
ModuleForeAppInit---onCreate

如上兩種方式都是使用了反射,發射在解耦的同時,也在一定程度上降低了應用的性能,當然組件化的目的就是要讓各個組件或各個 Module 之間儘可能的解耦,如果犧牲一點兒性能,能夠獲取解耦的最大化也是可以接受的。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。