Android元件化:stitch框架

爾·發表於2018-03-17

之前的文章【Android元件化開發框架】從整體上分析了要搭建一個元件化框架的技術原理。概括性的對元件化進行了簡單的分析。

stitch 是在專案實踐過程中結合之前的理論進行完善後組織起來的框架。它完成了元件生命週期、頁面路由、資料路由的基本功能,框架原始碼裡也包含了搭建元件化可能需要的指令碼示例。

框架包含3個部分:元件生命週期管理、頁面互動、資料互動。我們依次對其進行解析。

依賴

//AS 3.0之前使用
compile 'bamboo.component:stitcher:1.0'
//AS 3.0之後建議使用
implementation 'bamboo.component:stitcher:1.0'
複製程式碼

元件生命週期管理

每個元件相當於1個Module,許多元件的業務是需要在App啟動時進行初始化的,比如運營商支付sdk基本都需要在Application的onCreate方法中進行初始化。

stitch 框架採用手動配置的方式,元件將自己注入到stitch 框架中,主工程再通過stitch框架對元件生命週期進行統一管理。

具體使用方法:

1. 繼承ComponentApplication
public abstract class ComponentApplication {
    //Application物件注入
    public void setApplication(Application application)//控制元件的初始化順序,參看ComponentPriority
    public int level();

    //代理Application的OnCreate方法
    public void onCreate();

    //延遲初始化生命週期,用來注入頁面以及資料互動介面
    public void onCreateDelay(ComponentRouterRegistry routerRegistry, ActivityRouterRegistry activityRouterRegistry)//代理Application的attachBaseContext方法
    public void attachBaseContext(Context baseContext) ;

    //代理Application的onTrimMemory方法
    public void onTrimMemory(int level) ;

    //代理Application的onConfigurationChanged方法
    public void onConfigurationChanged(Configuration newConfig);

    //代理Application的onLowMemory方法
    public void onLowMemory();
}
複製程式碼

ComponentApplication是元件生命週期的代理類,代理了Application的常用關鍵方法。如果元件在App啟動時進行某些初始化或需要監聽生命週期,通過ComponentApplication即可實現。

2. 在Module的AndroidManifest.xml中進行配置
    <application>
        ...
        <meta-data
            android:name="bamboo.sample.account.component.AccountComponentApp"
            android:value="ComponentApplication" />
    </application>
複製程式碼

特別要注意:meta-data的value是ComponentApplication,name才是我們module的代理Application類。不要搞反了。

3. 主工程裡面的自定義Application修改

在主工程Application裡面我們需要主動呼叫元件的代理Application的方法,stitcher提供了兩種方式:

1. 直接繼承StitcherApplication

public class MainApplication extends StitcherApplication {
}
複製程式碼

2. 通過StitcherHelper呼叫元件的生命週期。 參考StitcherApplication。

public class StitcherApplication{
    public void onCreate() {
        super.onCreate();
        StitcherHelper.onCreate();
    }

    public void onCreateDelay() {
        StitcherHelper.onCreateDelay();
    }

    public void attachBaseContext(Context baseContext) {
        super.attachBaseContext(baseContext);
        StitcherHelper.init(this);
        StitcherHelper.attachBaseContext(baseContext);
    }

    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        StitcherHelper. onTrimMemory(level);
    }

    public void onLowMemory() {
        super.onLowMemory();
        StitcherHelper.onLowMemory();
    }

    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        StitcherHelper.onConfigurationChanged(newConfig);
    }
}
複製程式碼

通過上面幾步,就能對元件的生命週期進行管理。

在使用頁面互動以及資料互動之前我們先思考一個問題,這個問題是:

元件之間不能互相依賴,那如果要獲取資料,我們就必須要藉助於第三方Module進行中轉。我們要怎麼設計這個Module更方便呢?

沒錯,我們確實需要一個額外的Module來做互動中轉,也就是路由Module,如果以開發人員的角度來看,這個Module應該要具備以下幾個因素: 1.方便Module管理自己的介面。 2.其他Module最好能實時看到我們的修改。

所以在介紹互動功能之前,我們需要先來實現這個路由Module的配置。

公用路由Module配置

1. 建立一個Module:sampleouter
2. 修改sampleouter的build.gradle檔案,在裡面加入以下程式碼:

這一步的目的是為了把所有Module的projectDir的router資料夾都加入到sampleouter的原始碼資料夾中,這樣一來,我們就能在自己的Module的router資料夾內管理自己對外公開的介面以及頁面。

android {
    ...
    sourceSets {
        main {
            //將所有module裡的router資料夾都作為路由module的原始碼資料夾,方便Module開發時的方便
            ArrayList<String> strings = new ArrayList<String>()
            File[] modules = rootDir.listFiles(new FileFilter() {
                boolean accept(File pathname) {
                    return (pathname.isDirectory()
                            && pathname.name != "gradle"
                            && pathname.name != "build"
                            && !pathname.name.startsWith("."))
                }
            })
            for (File f : modules) {
                strings.add(f.absolutePath + File.separator + "router")
            }
            //不要忘了把原始的原始碼目錄新增進來
            strings.addAll(java.srcDirs)
            java.srcDirs = strings
        }
    }
}
複製程式碼

配置完後,點一下refresh按鈕,在Module裡面建立router資料夾你會看到這樣的效果。

image.png

3.在Module中配置samplerouter的依賴
    implementation 'bamboo.component:stitcher:1.0'
    implementation project(":samplerouter")
複製程式碼

OK,sampleouter Module就配置好了,現在我們繼續看頁面互動要怎麼實現。

頁面互動

我們在講述元件的生命週期管理時,可能你已經看到了元件生命週期代理類內有一個方法

    //延遲初始化生命週期,用來注入頁面以及資料互動介面
    public void onCreateDelay(ComponentRouterRegistry routerRegistry, ActivityRouterRegistry activityRouterRegistry);
複製程式碼

其中ActivityRouterRegistry就是我們進行頁面互動的登錄檔,我們只需要將我們需要公開的頁面注入到這裡面就可以進行互動了。具體的實現步驟:

1. 在router資料夾裡建立一個TaskInfoPage.class並繼承ActivityPage
package bamboo.sample.tasksrouter;

//每個對外公開的頁面都對應一個ActivityPage的子類
public class TaskInfoPage extends ActivityPage {
    public final String taskId;
    public TaskInfoPage(Context context, String taskId) {
        super(context);
        this.taskId = taskId;
    }
}
複製程式碼
2. 建立一個TasksPageConsumer.class
public class TasksPageConsumer {
    //這個方法將會與TaskInfoPage進行連線。
    //所有的TaskInfoPage的頁面互動請求都會最終呼叫到該方法中。
    public void consume(TaskInfoPage page) {
        Intent intent = new Intent(page.context, TaskInfoActivity.class);
        intent.putExtra("TaskInfoPage", page);
        page.context.startActivity(intent);
    }
}
複製程式碼
3. 在Module的ComponentApplication實現類中註冊
public class TasksComponentApp extends ComponentApplication {

    public void onCreateDelay(ComponentRouterRegistry routerRegistry, ActivityRouterRegistry activityRouterRegistry) {
        //將TasksPageConsumer注入到頁面路由登錄檔中
        activityRouterRegistry.regiest(new TasksPageConsumer());
    }
複製程式碼
4. 互動呼叫
//在需要呼叫TaskInfoPage的地方通過StitcherHelper使用
StitcherHelper.start(new TaskInfoPage(this, "taskId"));
StitcherHelper.start(new TaskInfoPage(this, "taskId"),1000/*requestCode*/);
複製程式碼
5. 更簡單的使用方式(PageConsumer註解)

實際上我們還有簡單的方式進行頁面注入,為什麼我要先說常規模式呢? 因為如果萬一簡單的方式沒辦法滿足你的需求的時候,你依然需要使用常規方式進行開發。

我們在繼承實現ActivityPage時,還可以通過PageConsumer直接配置Activity或Action來進行Activity<->ActivityPage的連線。

@PageConsumer(clasz = "bamboo.sample.tasks.ui.TaskCountActivity")
public class TaskListPage extends ActivityPage {
    public TaskListPage(Context context) {
        super(context);
    }
}
複製程式碼

這種方式是不需要在TaskPageConsumer中註冊的,框架會自動搜尋。

6. 特殊Intent引數配置

在頁面互動時我們有時會需要對Intent設定Flag,或需要通過Action或Data方式進行互動,這個時候我們可以通過ActivityPage的targetIntent進行傳遞,並暫時關閉ActivityPage的連線,stitch會在這種情況下放棄PageConsumer註解中class的引數連結,直接嘗試啟動Activity。

    public void onActionTest(View view) {
        ActionTestPage page = new ActionTestPage(this);
        Intent targetIntent = new Intent();
        targetIntent.setAction("bamboo.sample.actiontest");
        targetIntent.addCategory(Intent.CATEGORY_DEFAULT);
        page.setTargetIntent(targetIntent);
        page.setAutoLink(false);
        StitcherHelper.start(page);
    }
複製程式碼

優先順序: TaskPageConsumer > unAutolink > PageConsumer

資料互動

元件之間的資料互動與頁面互動原理是相似的。在onCreateDelay方法中的ComponentRouterRegistry就是資料互動的路由登錄檔,我們通過它來進行註冊。 具體使用參看以下步驟:

1.在router資料夾定義Module的ComponentOutput
package bamboo.sample.tasksrouter;
public interface ITaskComponent extends ComponentOutput{
    int getTaskCount();
}
複製程式碼
2. 在Module 實現該介面
public class TasksComponentOutput implements ITaskComponent {
    public int getTaskCount() {
        return 1000;
    }
複製程式碼
3. 在onCreateDelay方法中進行註冊
public class TasksComponentApp extends ComponentApplication {

    public void onCreateDelay(ComponentRouterRegistry routerRegistry, ActivityRouterRegistry activityRouterRegistry) {
        //將TasksComponentOutput注入到資料路由登錄檔中
        routerRegistry.regiest(registerComponentOutput,new TasksPageConsumer());
    }
}
複製程式碼
4.使用
public class ComponentInput {
    public int getTaskCount() {
        ITaskComponent taskComponent = StitcherHelper.searchComponentOutput(ITaskComponent.class);
        return taskComponent == null ? -1 : taskComponent.getTaskCount();
    }
}

複製程式碼

具體的資料互動流程入下圖:

資料互動流程

到這裡,stitcher的使用方式就介紹完了。

元件化指令碼配置請看:Android元件化:build.gradle配置

元件化基本概念請看:Android元件化開發框架

詳細示例請移步: stitch 原始碼及示例

歡迎大家轉發及使用。

相關文章