Android-模塊化妻味、組件化、插件化欣福、熱修復(fù)-組件化工程構(gòu)建+頁面路由多種方式實(shí)踐

上一篇MonkeyLei:Android-模塊化责球、組件化、插件化拓劝、熱修復(fù)-組件化一下試試 我們大概嘗試了下一個(gè)Module的library與application切換雏逾。這也是為為后續(xù)的繼續(xù)做準(zhǔn)備...今天我們就嘗試自己搭建一個(gè)組件化工程,并初步做一個(gè)頁面的跳轉(zhuǎn)(組件間的通信其中一個(gè)點(diǎn))郑临,后續(xù)還會完善組件間相互通信的方式栖博。

工程實(shí)踐完后,我會設(shè)置下Module的狀態(tài)(Library和Module直接做切換)厢洞,然后單獨(dú)運(yùn)行App或者M(jìn)odule笛匙,如果都沒問題,這才能保證組件之間無耦合O洹!秋柄!

工程走起!

image

1. 公共庫Module一般作為Library使用获枝。而組件Module我們可以來回切換,但是我們新建Module的時(shí)候還是如下方式(保證有啟動的manifest配置):

image
image

2. 然后按照上一篇學(xué)到的知識骇笔,代碼配置下Module的Library和Application切換省店;然而為了方便統(tǒng)一管理App基礎(chǔ)信息,各個(gè)Module的版本信息笨触,以及第三方依賴庫的版本信息等懦傍,我們項(xiàng)目根目錄下新建一個(gè)config.gradle,專門用來配置:

config.gradle

  /**
 * 全局統(tǒng)一配置
 */
ext {
    /**
     * module開關(guān)統(tǒng)一聲明在此處
     * true:module作為application芦劣,可單獨(dú)打包為apk
     * false:module作為library粗俱,可作為宿主application的組件
     */
    isLoginModule = false
    isPersonalModule = false

    /**
     * 版本統(tǒng)一管理
     */
    versions = [
            applicationId          : "com.skl.zujianhua",        // 應(yīng)用ID
            versionCode            : 1,                         // 版本號
            versionName            : "1.0.0",                    // 版本名稱

            compileSdkVersion      : 28,
            minSdkVersion          : 15,
            targetSdkVersion       : 28,
            buildToolsVersion      : "29.0.0",

            constraintlayoutVersion: "1.1.3",
            runnerVersion          : "1.0.2",
            espressoVersion        : "3.0.2",
            junitVersion           : "4.12",
            appcompatVersion       : "28.0.0",

            arouterApiVersion      : "1.5.0",
            arouterCompilerVersion : "1.2.2",
    ]

    dependencies = [
            "appcompat"       : "com.android.support:appcompat-v7:${versions["appcompatVersion"]}",
            "constraintlayout": "com.android.support.constraint:constraint-layout:${versions["constraintlayoutVersion"]}",
            "runner"          : "com.android.support.test:runner:${versions["runnerVersion"]}",
            "espresso_core"   : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
            "junit"           : "junit:junit:${versions["junitVersion"]}",
            // TODO MD等新增支持庫
            //            //注釋處理器
            //            "support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
            //            "design"              : "com.google.android.material:material:${versions["designVersion"]}",

            // TODO 其他公共的依賴都可以配置到這里
            //               //方法數(shù)超過65535解決方法64K MultiDex分包方法
            //            "multidex"            : "androidx.multidex:multidex:2.0.0",

            //阿里路由
            "arouter_api"     : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
            "arouter_compiler": "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
            //            "arouter_annotation"  : "com.alibaba:arouter-annotation:${versions["arouterAnnotationVersion"]}",
            //
            //            //黃油刀
            //            "butterknife"         : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
            //            "butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}"
    ]
}

2.1 根目錄下的build.gradle下添加config配置:

  apply from: "config.gradle" 

3. 此時(shí)我們不想每個(gè)Library/Application用到第三方庫(比如Butterknife、Arouter虚吟、MultiDex等)都去依賴寸认,因此我們新建一個(gè)基礎(chǔ)模塊basemodule: - (Arouter實(shí)踐過了签财,所以就一并貼出來算了。不過我們的學(xué)習(xí)過程一般都是漸進(jìn)式的偏塞,先實(shí)現(xiàn)基礎(chǔ)工程唱蒸,模塊的構(gòu)建,完事三方依賴都配置好灸叼,切換都測試成功了神汹,然后再開始搞路由!)

重點(diǎn)看下build.gradle配置:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 把implementation 用api代替,它是對外部公開的, 所有其他的module就不需要添加該依賴
    api rootProject.ext.dependencies["appcompat"]
    api rootProject.ext.dependencies["constraintlayout"]
    api rootProject.ext.dependencies["junit"]
    api rootProject.ext.dependencies["runner"]
    api rootProject.ext.dependencies["espresso_core"]

    //    //注釋處理器古今,butterknife所必需
    //    api rootProject.ext.dependencies["support_annotations"]
    //
    //    //MultiDex分包方法
    //    api rootProject.ext.dependencies["multidex"]
    //
    //    //Material design
    //    api rootProject.ext.dependencies["design"]
    //
    //    //黃油刀
    //    api rootProject.ext.dependencies["butterknife"]
    //    annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]

    //Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
    api rootProject.ext.dependencies["arouter_api"]
    //    api rootProject.ext.dependencies["arouter_annotation"]
}

4. 然后接著我們創(chuàng)建Appliaction模塊Login和Personal屁魏,這樣創(chuàng)建出來就直接配置上了,然后單獨(dú)可以運(yùn)行啦沧卢,記得都依賴上basemodule - 后續(xù)的公共模塊都可以放到basemodule中去蚁堤!

image

4.1 然后根據(jù)之前的知識配置下和Library的切換: - 參數(shù)都是根目錄/congig.gradle配置的,別忘記了

login/build.gradle

 if (Boolean.valueOf(rootProject.ext.isLoginModule)) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

// TODO 雖然依賴了公共庫但狭,但是有些配置還是要模塊內(nèi)部自己配置
// apply plugin: 'com.jakewharton.butterknife'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"

    defaultConfig {
        // Application模式下設(shè)置applicationId
        if (Boolean.valueOf(rootProject.ext.isLoginModule)) {
            applicationId "com.skl.login"
        }
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            if (Boolean.valueOf(rootProject.ext.isLoginModule)) {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                // Library模式可以不用配置manifest文件
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 公共依賴庫
    implementation project(path: ':basemodule')

    // TODO 雖然依賴了公共庫翩剪,但是有些配置還是要模塊內(nèi)部自己配置
    //    //黃油刀
    //    annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
    //Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
}

personal/build.gradle

if (Boolean.valueOf(rootProject.ext.isPersonalModule)) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

// TODO 雖然依賴了公共庫,但是有些配置還是要模塊內(nèi)部自己配置
// apply plugin: 'com.jakewharton.butterknife'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"

    defaultConfig {
        // Application模式下設(shè)置applicationId
        if (Boolean.valueOf(rootProject.ext.isLoginModule)) {
            applicationId "com.skl.personal"
        }
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            if (Boolean.valueOf(rootProject.ext.isPersonalModule)) { // apk
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                // Library模式可以不用配置manifest文件
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 公共依賴庫
    implementation project(path: ':basemodule')

    // TODO 雖然依賴了公共庫痊剖,但是有些配置還是要模塊內(nèi)部自己配置
    //    //黃油刀
    //    annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
    //Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
}

5. 修改下app主模塊下的布局晃洒,做一個(gè)點(diǎn)擊按鈕事件,分別用來跳轉(zhuǎn)到login和personal頁面:

app/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="GoLogin"
        android:text="跳轉(zhuǎn)到Login界面"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="GoPersonal"
        android:text="跳轉(zhuǎn)到個(gè)人信息頁面"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java

package com.skl.zujianhua;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

import com.alibaba.android.arouter.launcher.ARouter;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 跳轉(zhuǎn)到登錄頁面
     *
     * @param view
     */
    public void GoLogin(View view) {
        // 跳轉(zhuǎn)方式1: 通過反射
//        try {
//            Class clazz = Class.forName("com.skl.login.LoginActivity");
//            Intent intent = new Intent(this, clazz);
//            startActivity(intent);
//            // startActivityForResult(intent, 110);
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }

        // 跳轉(zhuǎn)方式2:每個(gè)頁面都注冊到base模塊唱歧,由base模塊提供跳轉(zhuǎn)服務(wù)
//        CompomentsService.getiAppComponentHashMap(AppConfig.PAGE_TYPE.LOGIN).launch(this, null);

        // 跳轉(zhuǎn)方式3:采用URI方式
//        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("activity://login")));

        // 跳轉(zhuǎn)方式4:采用 Arouter
        ARouter.getInstance().build("/hl/login")
                // .withString("nothing", "Gone with the Wind")
                // .withObject("author", Object)
                .navigation(this, 110);
    }

    /**
     * 跳轉(zhuǎn)到個(gè)人中心
     *
     * @param view
     */
    public void GoPersonal(View view) {
        // 跳轉(zhuǎn)方式4:采用 Arouter
        ARouter.getInstance().build("/ppx/personal")
                // .withString("nothing", "Gone with the Wind")
                // .withObject("author", Object)
                .navigation(this, 110);
    }
}

5.1 上面分別列出了四種跳轉(zhuǎn)方式宪摧,首先反射的方式,這個(gè)要試試哈颅崩。記得頁面是需要配置到AndroidManifest.xml的喲几于。

5.2 先說跳轉(zhuǎn)方式3, URI的方式沿后,這個(gè)需要配置待跳轉(zhuǎn)頁面的scheme沿彭、host、action尖滚、category信息喉刘,然后跳轉(zhuǎn)時(shí)根據(jù)scheme和host做路由:

login/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.skl.login">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LoginActivity">
            <!--跳轉(zhuǎn)方式3:提供URI方式-->
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="activity" />
                <data android:host="login" />
            </intent-filter>
        </activity>
    </application>

</manifest>

然后 startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("activity://login")));就可以跳轉(zhuǎn)了啦.....

5.3 接著說跳轉(zhuǎn)方式4 ,ARouter的方式漆弄,github地址睦裳,上面配置方式都有啦:alibaba/ARouter

照著配置就行,不過要注意我們有basemodule撼唾,多個(gè)library廉邑,so,注意下面這個(gè)說明: - javaCompileOptions比較關(guān)鍵,不然你的路由就會報(bào)="http://www.baidu.com/link?url=SFoQYEc0VH3o8IZkDe1H0v2W30NKqGHpsDU24PfR5pLn_c7GdXgkmO8moxxq7YGDO8VFzM4u0FFIwpTv25xIgKeKXK3YTN6DS8bTFKQuthy">ARouter there's no route matched

 雖然我們basemodule采用api的方式引入了arouter:
basemodule/build.gradle
    //Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
    api rootProject.ext.dependencies["arouter_api"]

但是其他的Module需要使用的地方同樣需要添加如下配置:
login/build.gradle
personal/build.gradle

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"

    defaultConfig {
        ......
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
    .....
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 公共依賴庫
    implementation project(path: ':basemodule')

    // TODO 雖然依賴了公共庫鬓催,但是有些配置還是要模塊內(nèi)部自己配置
    //    //黃油刀
    //    annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
    // Arouter路由
    annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
}

5.3.1 然后我們的login肺素,personal都配置上路由信息,方便路由跳轉(zhuǎn)(其他用法待深入) - 路由配置注意:不同module的路由路徑的一級命名不能相同宇驾。

image

login/ LoginActivity.java

  package com.skl.login;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

import com.alibaba.android.arouter.facade.annotation.Route;

@Route(path = "/hl/login")
public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    /**
     * 登錄 + 可以保存數(shù)據(jù)到本地(采用sharedpreferences的方式存儲)
     * TODO 注意:這里為了模擬組件間的接口調(diào)用倍靡,采用其他方式
     * @param view
     */
    public void logining(View view) {

    }
}

personal/PersonalActivity.java

 package com.skl.personal;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

import com.alibaba.android.arouter.facade.annotation.Route;

@Route(path = "/ppx/personal")
public class PersonalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_personal);
    }

    /**
     * 獲取登錄頁面?zhèn)€人信息并展示
     * @param view
     */
    public void getLoginInfo(View view) {

    }
}

然后開始路由前,主模塊app下的MainApplication需要初始化路由組件 - 直接文檔添加過來稍加處理就行

        // ARouter路由初始化
        if (BuildConfig.DEBUG) {           // These two lines must be written before init, otherwise these configurations will be invalid in the init process
            ARouter.openLog();     // Print log
            ARouter.openDebug();   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
        }
        ARouter.init(this); // As early as possible, it is recommended to initialize in the Application

5.3.2 此時(shí)就可以進(jìn)行路由跳轉(zhuǎn)了呀

        // 跳轉(zhuǎn)方式4:采用 Arouter
        ARouter.getInstance().build("/hl/login")
                // .withString("nothing", "Gone with the Wind")
                // .withObject("author", Object)
                .navigation(this, 110);

6. 最后跳轉(zhuǎn)方式2课舍,這里采用下沉到base模塊的方式塌西,各個(gè)組件注冊到base模塊的服務(wù)頁面,然后由各個(gè)模塊提供lunch方法筝尾。其他模塊使用的時(shí)候只需要調(diào)用base模塊的注冊的組件對應(yīng)的lunch方法既可捡需!這個(gè)就主要涉及到接口的使用了。

處理方式:6.1 定義IAppComponent接口

  package com.skl.basemodule.common_interface;

import android.app.Application;
import android.content.Context;

/**
 * 組件Application初始化時(shí)需要實(shí)現(xiàn)的接口
 */
public interface IAppComponent {
    void initialize(Application app);
    void launch(Context context, String extra);
}

6.2 此時(shí)就需要各個(gè)模塊添加Application類(basemodule純粹是一個(gè)library筹淫,不參與)站辉,然后統(tǒng)一由主Application去調(diào)用各個(gè)模塊的initialize方法(這個(gè)時(shí)候模塊Library要注意,如果不涉及到單獨(dú)運(yùn)行损姜,不用去配置application饰剥,不然運(yùn)行報(bào)錯(cuò)啦),如下:

image
image

6.3 主模塊此時(shí)由于不能直接與其他模塊產(chǎn)生耦合,所以采用反射的方式去初始化各個(gè)組件的Application

實(shí)現(xiàn)之前先定義一個(gè)basemodule/AppConfig.java用來存儲組件信息: - 我采用的接口和其他人略有不同摧阅,主要是方便到時(shí)候根據(jù)枚舉進(jìn)行頁面注冊汰蓉,同時(shí)根據(jù)枚舉進(jìn)行頁面跳轉(zhuǎn),后面就知道了

package com.skl.basemodule;

import java.util.HashMap;

public class AppConfig {
    public enum PAGE_TYPE {
        LOGIN, PERSONAL
    }

    /**
     * 組件集合
     */
    public static HashMap<PAGE_TYPE, String> COMPONENTS = new HashMap<PAGE_TYPE, String>() {
        {
            // 登錄頁面
            put(PAGE_TYPE.LOGIN, "com.skl.login.LoginApplication");
            // 登錄頁面
            put(PAGE_TYPE.PERSONAL, "com.skl.login.PersonalApplication");
        }
    };
}

app/src/main/xxxxx/MainApplication.java - MainApplication也實(shí)現(xiàn)組件通信的接口IAppComponent棒卷,其他頁面有可能也需要跳轉(zhuǎn)到主頁面

  package com.skl.zujianhua;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

import com.alibaba.android.arouter.launcher.ARouter;
import com.skl.basemodule.AppConfig;
import com.skl.basemodule.common_interface.IAppComponent;

import java.util.Map;

public class MainApplication extends Application implements IAppComponent {
    @Override
    public void onCreate() {
        super.onCreate();
        initialize(this);
    }

    @Override
    public void initialize(Application app) {
        // 遍歷所有的組件的Application類顾孽,依次用反射的方式實(shí)現(xiàn)組件初始化和注冊
        for(Map.Entry<AppConfig.PAGE_TYPE, String> entry: AppConfig.COMPONENTS.entrySet())
        {
            try {
                Class classz = Class.forName(entry.getValue());
                Object object = classz.newInstance();
                // 實(shí)例化后,調(diào)用各個(gè)組件的initialize方法(init會實(shí)現(xiàn)組件的注冊)
                if (object instanceof IAppComponent) {
                    ((IAppComponent) object).initialize(app);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        // ARouter路由初始化
        if (BuildConfig.DEBUG) {           // These two lines must be written before init, otherwise these configurations will be invalid in the init process
            ARouter.openLog();     // Print log
            ARouter.openDebug();   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
        }
        ARouter.init(this); // As early as possible, it is recommended to initialize in the Application
    }

    @Override
    public void launch(Context context, String extra) {
        Intent intent = new Intent(context, MainActivity.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            Bundle bundle = new Bundle();
            if (null != extra && !extra.equals(""))
                bundle.putString("extra_data", extra);
            context.startActivity(intent, bundle);
        } else {
            if (null != extra && !extra.equals(""))
                intent.putExtra("extra_data", extra);
            context.startActivity(intent);
        }
    }
}

6.4 為了實(shí)現(xiàn)組件Application初始化比规,并且能全局存儲組件的信息若厚,我們定一個(gè)組件服務(wù)類CompomentsService.java - 注冊組件時(shí)根據(jù)枚舉存儲組件信息,同時(shí)提供了獲取組件(類型就是通信接口IAppComponent )

 package com.skl.basemodule;

import com.skl.basemodule.common_interface.IAppComponent;

import java.util.HashMap;

/**
 * 組件初始化時(shí)都注冊到這個(gè)服務(wù)里面
 */
public class CompomentsService {
    private static HashMap<AppConfig.PAGE_TYPE, IAppComponent> iAppComponentHashMap = new HashMap<>();

    public static void setiAppComponentHashMap(AppConfig.PAGE_TYPE componentName, IAppComponent iAppComponent){
        iAppComponentHashMap.put(componentName, iAppComponent);
    }

    public static IAppComponent getiAppComponentHashMap(AppConfig.PAGE_TYPE componentName){
        if (iAppComponentHashMap.containsKey(componentName)){
            return iAppComponentHashMap.get(componentName);
        }
        return null;
    }
}

6.5 然后此時(shí)我們就可以把login蜒什、personal模塊初始化并注冊了:

login/ LoginApplication.java - 里面有注釋盹沈,相對好理解

  package com.skl.login;

/**
 * 單獨(dú)運(yùn)行時(shí)需要的話可以配置到AndroidManifest,不需要就不要配置吃谣,不然運(yùn)行報(bào)錯(cuò)
 */

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

import com.skl.basemodule.AppConfig;
import com.skl.basemodule.CompomentsService;
import com.skl.basemodule.common_interface.IAppComponent;

public class LoginApplication extends Application implements IAppComponent {
    /**
     * 單獨(dú)作為Application時(shí)會走該方法
     */
    @Override
    public void onCreate() {
        super.onCreate();
        // TODO 單獨(dú)運(yùn)行時(shí)沒有需要可以不用調(diào)用
        initialize(this);
    }

    /**
     * App的Application注冊組件時(shí)會調(diào)用initialize方法!
     *
     * @param app
     */
    @Override
    public void initialize(Application app) {
        // 注冊自己到組件服務(wù)
        CompomentsService.setiAppComponentHashMap(AppConfig.PAGE_TYPE.LOGIN, this);
    }

    /**
     * 啟動自己
     *
     * @param context
     * @param extra
     */
    @Override
    public void launch(Context context, String extra) {
        Intent intent = new Intent(context, LoginActivity.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            Bundle bundle = new Bundle();
            if (null != extra && !extra.equals(""))
                bundle.putString("extra_data", extra);
            context.startActivity(intent, bundle);
        } else {
            if (null != extra && !extra.equals(""))
                intent.putExtra("extra_data", extra);
            context.startActivity(intent);
        }
    }
}

personal/PersonalApplication.java

package com.skl.personal;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

import com.skl.basemodule.AppConfig;
import com.skl.basemodule.CompomentsService;
import com.skl.basemodule.common_interface.IAppComponent;

/**
 * 單獨(dú)運(yùn)行時(shí)需要的話可以配置到AndroidManifest,不需要就不要配置做裙,不然運(yùn)行報(bào)錯(cuò)
 */
public class PersonalApplication extends Application implements IAppComponent {
    /**
     * 單獨(dú)作為Application時(shí)會走該方法
     */
    @Override
    public void onCreate() {
        super.onCreate();
        // TODO 單獨(dú)運(yùn)行時(shí)沒有需要可以不用調(diào)用
        initialize(this);
    }

    /**
     * App的Application注冊組件時(shí)會調(diào)用initialize方法!
     *
     * @param app
     */
    @Override
    public void initialize(Application app) {
        // 注冊自己到組件服務(wù)
        CompomentsService.setiAppComponentHashMap(AppConfig.PAGE_TYPE.PERSONAL, this);
    }

    /**
     * 啟動自己
     *
     * @param context
     * @param extra
     */
    @Override
    public void launch(Context context, String extra) {
        Intent intent = new Intent(context, PersonalActivity.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            Bundle bundle = new Bundle();
            if (null != extra && !extra.equals(""))
                bundle.putString("extra_data", extra);
            context.startActivity(intent, bundle);
        } else {
            if (null != extra && !extra.equals(""))
                intent.putExtra("extra_data", extra);
            context.startActivity(intent);
        }
    }
}

7. 完事了我們就可以采用下沉到公共模塊的方式來實(shí)現(xiàn)頁面跳轉(zhuǎn) - 枚舉方便我們清晰知道要跳轉(zhuǎn)到哪里岗憋。相比Arouter,還是不安逸锚贱。但是了解一下也好鴨.....

          // 跳轉(zhuǎn)方式2:每個(gè)頁面都注冊到base模塊仔戈,由base模塊提供跳轉(zhuǎn)服務(wù)
         CompomentsService.getiAppComponentHashMap(AppConfig.PAGE_TYPE.LOGIN).launch(this, null);

8. 大概一個(gè)組件化工程目前一天兩天的學(xué)習(xí)過程和實(shí)踐就是醬紫。。监徘。還有很多我們可以去聯(lián)想和實(shí)踐的晋修,比如頁面之間的相互通信,主動回調(diào)通知刷新等凰盔。以及Eventbus相關(guān)集成墓卦,如何集成的更好,其他的路由通信方式等等一堆問題户敬!落剪。。尿庐。忠怖。

組件化如果產(chǎn)生了耦合就尷尬了,這就是設(shè)計(jì)的魅力抄瑟!

附上工程地址吧: https://gitee.com/heyclock/doc/tree/master/組件化

一些個(gè)參考:可以多像網(wǎng)友學(xué)習(xí)凡泣,但是還是要有自己的想法,哪怕改成自己順手都是可以的皮假,可能會實(shí)踐的不好也沒關(guān)系鞋拟,至少大的方向應(yīng)該是對的了。钞翔。

https://blog.csdn.net/jiyidehao/article/details/85390370

Android組件化

https://blog.csdn.net/sziitjin/article/details/97761949

Android組件化框架搭建

https://blog.csdn.net/hailong0529/article/details/89392064

https://blog.csdn.net/gaolei1201/article/details/77601027 - 如何跳轉(zhuǎn)頁面的方式

alibaba/ARouter

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末严卖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子布轿,更是在濱河造成了極大的恐慌哮笆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汰扭,死亡現(xiàn)場離奇詭異稠肘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萝毛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門项阴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笆包,你說我怎么就攤上這事环揽。” “怎么了庵佣?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵歉胶,是天一觀的道長巴粪。 經(jīng)常有香客問我粥谬,道長,這世上最難降的妖魔是什么漏策? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮掺喻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巢寡。我一直安慰自己,他們只是感情好抑月,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布舆蝴。 她就那樣靜靜地躺著,像睡著了一般洁仗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赠潦,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機(jī)與錄音她奥,去河邊找鬼。 笑死哩俭,一個(gè)胖子當(dāng)著我的面吹牛绷跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凡资,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼砸捏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了隙赁?” 一聲冷哼從身側(cè)響起垦藏,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伞访,沒想到半個(gè)月后膝藕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咐扭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝗肪。...
    茶點(diǎn)故事閱讀 40,769評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袜爪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薛闪,到底是詐尸還是另有隱情辛馆,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布豁延,位于F島的核電站昙篙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诱咏。R本人自食惡果不足惜苔可,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袋狞。 院中可真熱鬧焚辅,春花似錦、人聲如沸苟鸯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽早处。三九已至湾蔓,卻和暖如春默责,著一層夾襖步出監(jiān)牢的瞬間么库,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工葡缰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忱反,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓怜校,卻偏偏與公主長得像茄茁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子裙顽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容