主頁 >  其他 > 安卓一步一步搭建組件化

安卓一步一步搭建組件化

2020-11-12 16:59:53 其他

安卓組件化的搭建和基本功能的實作

  • 1.組件化是什么?
    • 1.1 了解組件化:
    • 1.2 組件化的基本結構:
    • 1.3 組件化的優點:
  • 2.組件化框架的搭建:
    • 2.1 第一步:搭建基礎層
      • 2.1.1 創建config.gradle
      • 2.1.2 建立一個library模塊作為基礎層
      • 2.1.3 所有模塊都要中都要添加基礎層模塊的依賴
    • 2.2 第二步:搭建組件層,
      • 2.2.1 為組件層模塊創建不同的Manifest表單
    • 2.3 第三步:搭建app層
    • 2.4 檢驗搭建成果
  • 3. 組件間的activity等界面跳轉
    • 3.1 ARouter介紹
    • 3.2 使用ARouter的準備
    • 3.3 ARouter的使用
  • 4. 組件之間的資料傳遞之(介面+實作)
    • 4.1 (介面+實作)的大概思路
    • 4.2 (介面+實作)的實操
  • 5. 組件之間的資料傳遞之(EventBus)
    • 5.1 什么是EventBus?
    • 5.2 使用EventBus的準備作業
    • 5.3 EventBus的基本使用
      • 5.3.1 訂閱事件
      • 5.3.2 發布事件
    • 5.4 EventBus的進階使用
      • 5.4.1 EventBus的執行緒模式
      • 5.4.2 EventBus的黏性事件
  • 6.Applicaion的動態配置
    • 6.1 建立一個BaseApplication
    • 6.2 修改主Module的application類
    • 6.3 修改Login組件的application類
    • 6.4 運用反射獲取其他組件的application進行初始化
  • 7.主專案使用各個組件的類的方法
    • 7.1 反射的方法
    • 7.2 介面+實作的方法
    • 7.3 路由的實作方法

1.組件化是什么?

1.1 了解組件化:

  • 在沒有接觸組件化之前,我們寫安卓專案的程序中,所有布局,邏輯功能的都是在app模塊下實作,這種方式寫一些小專案是很方便的,但是有些真正有用處的手機軟體都是由團隊來協作完成的,由于功能的繁雜,如果多個開發人員都在app一個模塊中進行功能開發,代碼就會顯得很臃腫,所以為了更好的更有效率的分工,組件化功能的實作必不可少,

1.2 組件化的基本結構:

  • 組件化大致分為3個結構:基礎層組件層應用層,下面我們具體說一說著三個結構,
  1. 基礎層:基礎層,就是由我們手動在這里去包含所有專案中想用到的基本庫的依賴,然后在其他所有組件中依賴于這個Base基礎層,就可以在專案的任何地方應用到我們添加的依賴,這樣不僅只需要開發一套依賴庫的代碼,還解耦了基礎功能和業務功能的耦合,在基礎庫變更時更加容易操作,
  2. 組件層:組件層可以理解為業務層,比如一個聊天軟體專案,其中分為登錄,聊天,查詢等功能,組件層就是把這些功能進行一個一個的分離出來,每一個功能對應一個特定的組件,
  3. 應用層:應用層就是app,在未接觸組件化之前,我們所有的邏輯,布局都在app里面寫,而組件化中,app可以形容為一個空殼,它其中依賴包含了我們創建的其他組件,它會根據設定按照需要參考不同的模塊,

1.3 組件化的優點:

  • 經過剛剛的介紹,我們對組件化有了大致的認識,想必他的有點我們也清楚了,就是方便多人開發,互不影響,自己開發的組件可以單獨運行,不受外界限制,不至于因為其他模塊出現問題而導致自己模塊不能運行的情況,另外,在一個很龐大的專案需要修改時,也可以很方便的找到要修改的模塊,不必要去在龐大的代碼中尋找,

2.組件化框架的搭建:

  • 在說搭建組件化之前,先介紹一下AndroidStudio開發Android專案時常用到的兩種插件,
  • application插件:如果一個模塊被宣告為aoolication,那么它會成為一個apk檔案,是可以直接安裝運行的專案,
  • library插件:被宣告為library的模塊,它會成為一個aar檔案,不可以單獨運行,

2.1 第一步:搭建基礎層

2.1.1 創建config.gradle

在這里插入圖片描述

  • 在這里面,我們寫上所有依賴庫,以及專案中sdk等等的版本號,然后直接讓buil.gradle去apply它,之后有什么更改就可以直接在這里面改,比如版本號升級等問題,以下是我在其中添加的代碼:
ext{

    android = [
            compileSdkVersion :30,
            buildToolsVersion: "30.0.2",
            applicationId :"activitytest.com.example.moduletest",
            minSdkVersion: 29,
            targetSdkVersion :30,
            versionCode :1,
            versionName :"1.0",
    ]

    androidxDeps = [
            "appcompat": 'androidx.appcompat:appcompat:1.1.0',
            "material": 'com.google.android.material:material:1.1.0',
            "constaraintlayout": 'androidx.constraintlayout:constraintlayout:1.1.3',
    ]

    commonDeps = [
            "arouter_api"          : 'com.alibaba:arouter-api:1.5.1',
            "glide"                : 'com.github.bumptech.glide:glide:4.11.0'

    ]

    annotationDeps = [
            "arouter_compiler" : 'com.alibaba:arouter-compiler:1.5.1'
    ]

    retrofitDeps = [
            "retrofit"  : 'com.squareup.retrofit2:retrofit:2.9.0',
            "converter" : 'com.squareup.retrofit2:converter-gson:2.9.0',
            "rxjava"    : 'io.reactivex.rxjava2:rxjava:2.2.20',
            "rxandroid" : 'io.reactivex.rxjava2:rxandroid:2.1.1',
            "adapter"   : 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
    ]

    androidxLibs = androidxDeps.values()
    commonLibs = commonDeps.values()
    annotationLibs = annotationDeps.values()
    retrofitLibs = retrofitDeps.values()
}
  • 在這里我寫了一些版本號,support庫,路由,圖片加載等常用的基礎庫,最后面的這4行,是對前面的一個封裝,在其他地方呼叫這4行,相當于呼叫了我在這里寫的所有代碼,這里的具體知識請看使用config.gradle統一管理專案的依賴庫
  • 之后在專案目錄下的build.gradle中apply操作,
    在這里插入圖片描述

2.1.2 建立一個library模塊作為基礎層

  1. 點擊file->new->new module,選擇library module模塊,我在這里命名為Baselibs,
  2. 在模塊的build.gradle中,添加我們剛剛寫的依賴,注意這里運用關鍵字api來添加,因為這樣做,別的模塊繼承當前模塊時,不必再單獨寫依賴了,
dependencies {

    api rootProject.ext.androidxLibs
    api rootProject.ext.commonLibs
    api rootProject.ext.annotationLibs
    testImplementation 'junit:junit:4.+'
    api 'org.greenrobot:eventbus:3.1.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

2.1.3 所有模塊都要中都要添加基礎層模塊的依賴

  • 這里以app層為例:在app模塊中的build.gradle中添加如下,
dependencies {
    implementation project(":Baselibs")
}

就這樣,基礎層就建立完成了,

2.2 第二步:搭建組件層,

  • 由于組件層根據集成開發和組件開發兩種不同的情況,要在application和library之間來回切換,所以我們還是先創建一個模塊,這里我建立一個login模塊,就在login的build.gradle中,我們要動態的規定是application還是library,
  • 由于專案目錄下的gradle.properties中的變數,可以在build.gradle中參考,所以在gradle.preperties中設定一個isModule變數:
# true時為組件化模式開發,false時為集成模式開發
isModule = false
  • 然后在組件中的build.gradle中根據isModule的值來進行設定:
if(isModule.toBoolean()){
    apply plugin: 'com.android.application'
}else{
    apply plugin: 'com.android.library'
}
  • 不要忘記添加基礎庫的依賴:
dependencies {
    implementation project(":Baselibs")
 }

2.2.1 為組件層模塊創建不同的Manifest表單

  • 因為一個專案中只能有一個application,所以我們要在組件開發時,令login模塊應用存在application的表單,在集成開發時,令login模塊應用不存在application的Manifest表單,
  1. 在login/src/main目錄下創建module檔案,在這里面創建一個Manifest表單,書寫集成開發情況下的表單,
  • login/src/main/module目錄下的:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="activitytest.com.example.login">

    <application
        android:allowBackup="true">
        <activity android:name=".Login"></activity>
    </application>

</manifest>
  • login/src/main目錄下的:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="activitytest.com.example.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/Theme.ModuleTest">
        <activity android:name=".Login">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  • 這里的區別就不說了,
  1. 一個專案里只有一個application,那么也就是說一個專案中也只有一個applicationId,applicationId和表單的選擇都是在build.gradle中設定的,所以我們還要根據isModule的值,來設定組件中是否存在applicationId,以及應用哪一個Maniest.xml,
defaultConfig {

    if(isModule.toBoolean()){
        applicationId "activitytest.com.example.login"
    }
 }
sourceSets{
    main{
        if(isModule.toBoolean()){
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }else{
            manifest.srcFile 'src/main/module/AndroidManifest.xml'
        }
    }
}
  • 按照如上所述方法,我又創建了一個share組件和一個mine組件,之后的內容會用到,

2.3 第三步:搭建app層

  • 這一步就比較簡單,只需要在app模塊的build.gradle中根據isModule的值來決定是否添加其他組件層的模塊就好了,
dependencies {
    implementation project(":Baselibs")

    if(!isModule.toBoolean()){
        implementation project(":login")
        implementation project(":share")
        implementation project(":mine")
    }
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

2.4 檢驗搭建成果

  • 當isModule的值設定為false時,代表此時為集成開發,只有app組件可以運行,其他子業務組件都不可以單獨運行,
    在這里插入圖片描述

  • 當isModule的值為true時,代表此時為組件開發,所有組件都可以單獨運行,這時當你運行每一個組件的時候,主頁面會顯示你運行組件的頁面,
    在這里插入圖片描述
    注:每次修改isModule的值和修改build.gradle時,都要點擊一下sync,

  • 這樣一個簡單的組件化框架就搭建完了,但是組件之間也要有通信交流的功能,這些知識請往下看,

3. 組件間的activity等界面跳轉

  • 在我們沒有應用組件化開發之前,界面跳轉我們主要用顯式Intent和隱式Intent兩種方法,但是組件化開發中,是不允許組件層的模塊橫向依賴的,所以不可以直接訪問彼此的類,就不能用顯式Intent來跳轉,而用隱式Intent跳轉還需要通過 AndroidManifest 集中管理,在協作開發就比較麻煩,所以在這里介紹一個界面跳轉的新方法,使用 Alibaba 開源的 ARouter 來實作,

3.1 ARouter介紹

  • ARouter是一個用于幫助 Android App 進行組件化改造的框架 —— 支持模塊間的路由、通信、解耦,它可以實作組件間的路由功能,路由是指從一個介面上收到資料包,根據資料路由包的目的地址進行定向并轉發到另一個介面的程序,這里可以體現出路由跳轉的特點,非常適合組件化解耦,

3.2 使用ARouter的準備

  1. 要使用 ARouter 進行界面跳轉,需要我們的組件對 Arouter 添加依賴,因為所有的組件都依賴了基礎層Baselibs模塊,Baselibs模塊又依賴了我們寫的config.gradle,所以我們在config.gradle模塊中添加 ARouter 的依賴即可,
    在這里插入圖片描述
  2. 除了添加ARouter的依賴,我們還要添加注釋處理器依賴,這個不同于ARouter依賴,這個需要在每一個應用的組件中都添加一次,不僅添加這個,還要在每個應用的組件的build.gradle的defaultConfig下配置ARouter,
dependencies {
    implementation project(":Baselibs")
    annotationProcessor rootProject.ext.annotationLibs		//注釋處理器

    if(!isModule.toBoolean()){
        implementation project(":login")
        implementation project(":share")
        implementation project(":mine")
    }
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
defaultConfig {
    javaCompileOptions{				//配置ARouter
        annotationProcessorOptions{
            includeCompileClasspath false
            arguments = [AROUTER_MODULE_NAME:project.getName()]
        }
    }
}
  1. 在使用之前,我們還要在app層的application中初始化ARouter,為了讓專案剛剛啟動就初始化,所以我們在application中的oncreate方法中初始化,
public class MainApplication extends BaseApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        //ARouter后臺有ILogger介面,定義了一些輸出日志
        if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init程序中將無效
            ARouter.openLog();     // 列印日志
            ARouter.openDebug();   // 開啟除錯模式(如果在InstantRun模式下運行,必須開啟除錯模式!線上版本需要關閉,否則有安全風險)
        }
        ARouter.init(this); // 盡可能早,推薦在Application中初始化ARouter

        init(this);
        initover(this);
    }

    private boolean isDebug() {
        return BuildConfig.DEBUG;
    }
 }
  • 這樣我們就準備好了ARouter的配置,接下來就可以開始用了,

3.3 ARouter的使用

  1. 在我們即將要跳轉的activity,service或者fragment等等上面宣告添加注解@Route(path = “/xx/xx”),這里path是一個跳轉的路徑,/xx/xx是一個二級目錄,注意:不可以出現路徑相同的情況,不同的組件第一級目錄不可以相同,同一組件的一級目錄可以相同,
  2. 接著在想要跳轉的時候,寫下面這一行代碼就可以跳轉對應路徑的活動ARouter.getInstance().build("/xx/xx").navigation();build里面填的是path地址,后面還可以添加后綴withInteger,withString等等傳遞資料,然后再navigation就可以跳轉了,
  • 這里我們在app層設定兩個跳轉按鈕,在login和share組件中宣告注解,接受跳轉,
login.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ARouter.getInstance().build("/login/login1").navigation();
    }
});
share.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ARouter.getInstance().build("/share/share1").navigation();
    }
});
@Route(path = "/login/login1")
public class Login extends AppCompatActivity {

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

    }
}


@Route(path = "/share/share1")
public class Share extends AppCompatActivity {

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

運行結果:
在這里插入圖片描述

  • 點擊跳轉到登陸
    在這里插入圖片描述

  • 點擊跳轉到分享
    在這里插入圖片描述

4. 組件之間的資料傳遞之(介面+實作)

  • 由于主專案與組件,組件與組件之間都是不可以直接使用類的相互參考來進行資料傳遞的,組件間資料互動還有很多其他的方式,比如 EventBus,廣播,資料持久化等方式,但是往往這些方式的互動會不那么直觀,所以對通過 Service 這種形式可以實作的互動,我們最好通過這介面+實作這種方式進行,

4.1 (介面+實作)的大概思路

  • 比如這里我們在share組件中要獲取到登錄的相關資訊:由于所有組件都依賴Baselibs組件,所以在Baselibs組件中創建一個介面和ServiceFactroy,然后在login組件中創建類實作這個介面,然后把這個類上傳到ServiceFactroy,然后在share組件中從ServiceFactroy中獲取這個實作類物件,

4.2 (介面+實作)的實操

  1. 創建介面供login組件實作:
public interface LoginService {
     boolean isLogin();
     String getPassword();
}
  1. 創建ServiceFactroy來管理login組件傳遞上來的介面實作類,并設定get,set方法:
public class ServiceFactory {
    private LoginService loginService;
    private ServiceFactory(){

    }
    public static ServiceFactory getInstance(){
        return Inner.serviceFactory;
    }
    private static class Inner{
        private static ServiceFactory serviceFactory = new ServiceFactory();
    }

    public void setLoginService(LoginService loginService){
        this.loginService = loginService;
    }
    public LoginService getLoginService(){
        if(loginService == null){
            return new EmptyService();
        }else{
            return loginService;
        }
    }
}

這里通過靜態內部類方式實作 ServiceFactory 的單例,由于ServiceFactroy中包含著登錄資訊這種重要的唯一的資訊,所以全域只可以有一個ServiceFactroy物件,所以要實作它的單例創建,

  1. 由于login組件可能并沒傳遞過來一個實作類,share就呼叫get方法,為了防止例外,我們還要創建一個服務的空實作,當login并未上傳實作類時,get回傳這個空實作,
public class EmptyService implements LoginService{
    @Override
    public boolean isLogin() {
        return false;
    }

    @Override
    public String getPassword() {
        return null;
    }
}
  1. 接下來就是在login組件中,實作這個LoginService介面,并在activity中點擊登錄按鈕后,會存盤登陸狀態和用戶資訊,并上傳到這個介面實作類,再將這個介面實作類上傳到ServiceFactroy,這里我們還創建了一個LoginUtil類作為一個傳遞橋梁來存盤這些資料,
  • 首先是實作介面:
public class AccountService implements LoginService {

    private boolean login;
    private String password;

    public AccountService(boolean login, String password) {
        this.login = login;
        this.password = password;
    }

    @Override
    public boolean isLogin() {
        return login;
    }

    @Override
    public String getPassword() {
        return password;
    }
}
  • 存盤資料的工具類:
public class LoginUtil {
    static boolean isLogin = false;
    static String password = null;
}
  • 在activity中點擊登錄按鈕的實作操作:
login = (Button)findViewById(R.id.login_text);
login.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        LoginUtil.isLogin = true;
        LoginUtil.password = "admin";
        ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
    }
});
  • 因為我們并不知道什么時候其他的組件就會在ServiceFactroy中獲得這個類,所以我們上傳這個介面實作類要在專案剛剛開始的時候,所以就在login的application中實作上傳,
public class LoginApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
     }
}
  1. 最后一步就是在share組件中獲取登錄資訊了,這里我們在分享組件中添加一個分享按鈕,點擊后,會根據登錄資訊的是否登錄來提示你分享是否成功,
share = (Button)findViewById(R.id.share_text);
share.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(ServiceFactory.getInstance().getLoginService().isLogin()){
            Toast.makeText(Share.this,"分享成功!",Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(Share.this,"分享失敗,請先登錄!",Toast.LENGTH_SHORT).show();
        }
    }
});

注意: 在這里出現了一個問題,就是我們想在專案最開始運行的時候,就要在login登陸組件中上傳一個實作類到ServiceFactroy中,并且將這個命令寫到了login組件的application中,但是一個專案只可以有一個application,也就是說Login組件中的application不會初始化,所以我們就涉及到了一個Application的動態配置,下面目錄6中會說如何操作,

5. 組件之間的資料傳遞之(EventBus)

5.1 什么是EventBus?

  • EventBus是一種用于Android的發布/訂閱事件總線,它有很多優點:簡化應用組件間的通信;解耦事件的發送者和接收者;避免復雜和容易出錯的依賴和生命周期的問題;很快,專門為高性能優化過等等,
  • EventBus采取的是訂閱者/發布者的模式,發布者通過EventBus發布事件,訂閱者通過EventBus訂閱事件,當發布者發布事件時,訂閱該事件的訂閱者的事件處理方法將被呼叫,

5.2 使用EventBus的準備作業

  1. 要使用EventBus,第一項準備作業就是添加EventBus的依賴,我們這里直接在Baselibs中添加,
dependencies {
    api 'org.greenrobot:eventbus:3.1.1'
}
  1. 剛才說過,EventBus采用的是訂閱者/發布者模式,也就是說,我們要先建立一個事件,然后再想發送相關資訊的組件創建發布者,然后在要接收資訊的組件創建訂閱者并且獲取資訊,所以第二步就是創建事件,隨便創建一個類,在其中寫出要傳遞的資訊,在加入get/set方法即可,
public class EventMessage {

    String account;

    public EventMessage(String account) {
        this.account = account;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }
}

由于這個事件可能在每個組件都會發布或接收,為了讓其他所有組件都可以獲取到這個類的實體,所以就直接在Baselibs組件中main/java下創建一個檔案EventBus,在其中寫定義的事件類,
在這里插入圖片描述

  • 準備作業就這些就可以了,

5.3 EventBus的基本使用

5.3.1 訂閱事件

  1. 首先我們要在準備訂閱事件的組件中注冊訂閱者,具體就是在activity的onCreate中注冊,在onDestroy中注銷,呼叫EventBus.getDefault().register()和EventBus.getDefault().unregister()方法,
  • 這里我又建立了一個Mine模塊,和分享模塊類似,只不過這里展示用戶的個人資訊,為了區分EventBus和介面+實作的兩種之間的區別,所以我們在Mine模塊中注冊,
@Route(path = "/mine/mine1")
public class Mine extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mine);
        textView = (TextView)findViewById(R.id.message);
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}
  1. 接下來訂閱者需要定義事件處理方法(也稱為訂閱者方法),當發布對應型別的事件時,該方法將被呼叫,EventBus使用 @Subscribe 注解來定義訂閱者方法,方法名可以是任意合法的方法名,引數型別為訂閱事件的型別,
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
public void showEventMessage(EventMessage message){
    textView.setText(message.getAccount());
}

至于threadMode和sticky我們一會單獨說,

5.3.2 發布事件

  1. 在需要的地方發布事件,所有訂閱了該型別事件并已注冊的訂閱者將在任何時候收到該事件,這里在login組件中運用了EventBus.getDefault().post()方法來發布一個事件,
login.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        LoginUtil.isLogin = true;
        LoginUtil.password = "admin";
        EventBus.getDefault().post(new EventMessage(LoginUtil.password));           //發送EventBus
    }
});
  • 到這里EventBus的簡單使用就說完了,接下來是Eventbus的幾個進階用法,

5.4 EventBus的進階使用

5.4.1 EventBus的執行緒模式

  • 在剛才創建訂閱方法時,注釋中出現了threadMode,這就是執行緒模式,EventBus支持訂閱者方法在不同于發布事件所在執行緒的執行緒中被呼叫,你可以使用執行緒模式來指定呼叫訂閱者方法的執行緒,EventBus總共支持5種執行緒模式:
  1. ThreadMode.POSTING 訂閱者方法將在發布事件所在的執行緒中被呼叫,這是 默認的執行緒模式,事件的傳遞是同步的,一旦發布事件,所有該模式的訂閱者方法都將被呼叫,這種執行緒模式意味著最少的性能開銷,因為它避免了執行緒的切換,因此,對于不要求是主執行緒并且耗時很短的簡單任務推薦使用該模式,使用該模式的訂閱者方法應該快速回傳,以避免阻塞發布事件的執行緒,這可能是主執行緒,
  2. ThreadMode.MAIN訂閱者方法將在主執行緒(UI執行緒)中被呼叫,因此,可以在該模式的訂閱者方法中直接更新UI界面,如果發布事件的執行緒是主執行緒,那么該模式的訂閱者方法將被直接呼叫,使用該模式的訂閱者方法必須快速回傳,以避免阻塞主執行緒,
  3. ThreadMode.MAIN_ORDERED 訂閱者方法將在主執行緒(UI執行緒)中被呼叫,因此,可以在該模式的訂閱者方法中直接更新UI界面,事件將先進入佇列然后才發送給訂閱者,所以發布事件的呼叫將立即回傳,這使得事件的處理保持嚴格的串行順序,使用該模式的訂閱者方法必須快速回傳,以避免阻塞主執行緒,
  4. ThreadMode.BACKGROUND 訂閱者方法將在后臺執行緒中被呼叫,如果發布事件的執行緒不是主執行緒,那么訂閱者方法將直接在該執行緒中被呼叫,如果發布事件的執行緒是主執行緒,那么將使用一個單獨的后臺執行緒,該執行緒將按順序發送所有的事件,使用該模式的訂閱者方法應該快速回傳,以避免阻塞后臺執行緒,
  5. ThreadMode.ASYNC 訂閱者方法將在一個單獨的執行緒中被呼叫,因此,發布事件的呼叫將立即回傳,如果訂閱者方法的執行需要一些時間,例如網路訪問,那么就應該使用該模式,避免觸發大量的長時間運行的訂閱者方法,以限制并發執行緒的數量,EventBus使用了一個執行緒池來有效地重用已經完成呼叫訂閱者方法的執行緒,

5.4.2 EventBus的黏性事件

  • 在EventBus中,如果先發布了事件,然后有訂閱者訂閱了該事件,那么除非再次發布該事件,否則訂閱者將永遠接收不到該事件,此時,可以使用粘性事件,發布一個粘性事件之后,EventBus將在記憶體中快取該粘性事件,當有訂閱者訂閱了該粘性事件,訂閱者將接收到該事件,
  1. 黏性事件的訂閱方法的注釋上要寫上sticky = true,然后發布黏性事件也有所不同,發布黏性事件用的不是EventBus.getDefault().post()方法,而是EventBus.getDefault().postSticky()方法,
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)		//訂閱
public void showEventMessage(EventMessage message){
}

EventBus.getDefault().postSticky(new EventMessage(LoginUtil.password));           //發送黏性EventBus
  • 正好我們這個專案要符合最開始就獲取到登錄資訊,所以要發布一個黏性事件才合適,

6.Applicaion的動態配置

  • 在上面我們說的組件之間資訊相互傳遞的問題中,采用介面+實作的方法傳遞時,我們想讓專案初始化時,在Login組件中發送出介面實作類,但是在一個專案中只有一個application,Login組件的application是不會初始化的,所以這時候login組件就不會發送介面實作類,
  • 解決方法:將組件的類強參考到主 Module 的 Application 中進行初始化,這就必須要求主模塊可以直接訪問組件中的類,而我們又不想在開發程序中主模塊能訪問組件中的類,這里可以通過反射來實作組件 Application 的初始化,也就是Application的動態配置,
  • ==思路:==建立一個BaseApplication繼承Application,然后令其他的application類都繼承自BaseApplication,在BaseApplication中寫兩個方法,一個是初始化,一個是初始化之后呼叫的函式,然后在app主模塊里面利用反射,得到Login中的application類,然后呼叫其初始化方法即可,

6.1 建立一個BaseApplication

  • 由于Bselibs組件是其他組件都依賴的,并且要求繼承BaseApplication的類必須重寫方法,所以我們在Baselibs中創建抽象類BaseApplication繼承Application,如下:
public abstract class BaseApplication extends Application {
    public abstract void init(Application application);         //初始當前組件呼叫的方法
    public abstract void initover(Application application);               //其他需要呼叫的方法
}

6.2 修改主Module的application類

  • 直接上代碼
public class MainApplication extends BaseApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        //ARouter后臺有ILogger介面,定義了一些輸出日志
        if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init程序中將無效
            ARouter.openLog();     // 列印日志
            ARouter.openDebug();   // 開啟除錯模式(如果在InstantRun模式下運行,必須開啟除錯模式!線上版本需要關閉,否則有安全風險)
        }
        ARouter.init(this); // 盡可能早,推薦在Application中初始化ARouter

        init(this);
        initover(this);
    }

    private boolean isDebug() {
        return BuildConfig.DEBUG;
    }


    @Override
    public void init(Application application) {...}

    @Override
    public void initover(Application application) {...}
}

6.3 修改Login組件的application類

  • 直接代碼
public class LoginApplication extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        init(this);
        initover(this);
    }


    @Override
    public void init(Application application) {
        ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
    }

    @Override
    public void initover(Application application) {

    }
}
  • 在這里我們完成了application的配置,接下來我們就可以用反射的方式在app主Module的application類中獲取Login組件的application類,執行其方法,

6.4 運用反射獲取其他組件的application進行初始化

  • 在app模塊中的application中重寫的兩個方法,獲取并進行初始化,
@Override
public void init(Application application) {
    for(String moduleApp : AppConfig.moduleApps){
        try{
            Class clazz = Class.forName(moduleApp);
            BaseApplication baseApplication = null;
            baseApplication = (BaseApplication) clazz.newInstance();
            baseApplication.init(this);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

@Override
public void initover(Application application) {
    for(String moduleApp : AppConfig.moduleApps){
        try{
            Class clazz = Class.forName(moduleApp);
            BaseApplication baseApplication = null;
            baseApplication = (BaseApplication) clazz.newInstance();
            baseApplication.initover(this);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 這樣操作之后,我們在專案開始的時候,Login組件就會發送出一個介面實作類到ServiceFactroy了,

7.主專案使用各個組件的類的方法

  • 這里介紹三個方法,一個是反射,一個是通過介面+實作的方法,另外一個就是路由的方法,

7.1 反射的方法

  • 和剛剛說的獲取Login組件的application類是一樣的,只不過這次我們利用反射獲取碎片實體,將碎片加載到activity中,然后activity再將它放到合適的位置,

7.2 介面+實作的方法

  • 和之前的也一樣,在ServiceFactroy中在創建一個碎片物件,然后添加一個建構式,引數是碎片,在提供一個get和set方法就可以了,
  • 不要忘記,在Baselibs中在添加一個空實作,以免get方法呼叫時出現錯誤,

7.3 路由的實作方法

  • 簡單明了一行代碼,
mineFragment = (Fragment) ARouter.getInstance().build("/mine/fragment").navigation();
  • 就這樣輕輕松松的獲取到了組件碎片的實體,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/212202.html

標籤:其他

上一篇:Android WebView

下一篇:行程間的實時通訊方案: local socket(解決擴展和容器應用的實時通訊問題)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more