1. 程式人生 > >阿里ARouter路由實現Android模組化開發

阿里ARouter路由實現Android模組化開發

概述

從 2016 年開始,模組化在 Android 社群越來越多的被提及。隨著移動平臺的不斷髮展,移動平臺上的軟體慢慢走向複雜化,體積也變得臃腫龐大,為了降低大型軟體複雜性和耦合度,同時也為了適應模組重用、多團隊並行開發測試等等需求,模組化在 Android 平臺上變得勢在必行。阿里 Android 團隊在年初開源了他們的容器化框架 Atlas 就很大程度說明了當前 Android 平臺開發大型商業專案所面臨的問題。

那麼什麼是模組化呢,和我們常說的元件化又有什麼聯絡和區別呢?根據《 Java 應用架構設計:模組化模式與 OSGi 》一書中對模組化的定義:模組化是一種處理複雜系統分解為更好的可管理模組的方式。對於這種概念性的解釋,太過生澀難懂,不夠直觀。

那麼究竟何為模組化呢?舉個例子,相信隨著業務的不斷迭代,APK專案已經無限大了,以我們公司的電商專案為例,在迭代了5年後,apk的體積已經40M+,如果使用傳統的ant打包大概差不多要近10分鐘,如果用增量打包時間也要3-5分鐘。但是可以發現,很多老的程式碼其實我們在最新的版本是不需要的,當然我們可以手動的將這些程式碼刪除,但是又還怕啥時候用到。此時,最好的方法就是將這些模組獨立成一個獨立的工程,當需要的時候再引入進來,這就是模組化的一個背景。

所以,此處,我們對模組化和元件化做一個簡單的定義:
模組化:指解決一個複雜問題時自頂向下逐層把系統劃分成若干模組的過程,如訂單模組(OrderModule)、特賣模組(SPecialModule)、即時通訊模組(InstantMessagingModule)等等。

元件化:元件是指通用的功能或者UI庫可以做成一個功能元件,如地圖元件(MapSDK)、支付元件(AnjukePay)、路由元件(Router)等等;

外掛化:和模組化差不多,只是它是可以把模組打包成獨立的apk,可以動態的載入,刪除,獨立的外掛apk可以線上下載安裝,有利於減少apk的體積和實現模組的熱修復。目前熱門的外掛化方案有:阿里的atlas,360公司的RePlugin,滴滴的VirtualAPK等等;

例如,下面是模組化之前和模組化之後的專案的目錄結構:
這裡寫圖片描述
模組化的示意圖可以用下面的模型表示:
這裡寫圖片描述

模組化要解決的問題

要使用模組化開發Android專案,有以下幾點需要注意:

  1. 模組間頁面跳轉(路由);
  2. 模組間事件通訊;
  3. 模組間服務呼叫;
  4. 模組的獨立執行;
  5. 其他注意事項;
    為了方便講解,我們以下面的專案為例:
    這裡寫圖片描述
    這是一個常見的首頁畫面,該頁面主要有首頁、微聊、推薦和我的組成。我們將該4個Tab單獨成4個獨立的模組。
    這裡寫圖片描述

其中:

  • app模組:主模組,主要進行搭載各個模組的功能;
  • lib_base:對ARouter進行初始化,和放置一些各個模組公用的封裝類;
  • module_home,module_caht,module_recom,module_me:分別對應“首頁”、“微聊”、“推薦”、“我的”模組。

ARouter模組化開發

ARouter各個模組的gradle配置

app模組是程式的容器,起到程式入口的作用,lib_base作為基礎模組,用來將一些公共的庫和對ARouter的初始化操作放在這一模組中。因此每個子模組都會用到它裡面的內容,所以我們在 lib_base中新增如下內容。

 compile 'com.alibaba:arouter-api:1.2.4'
    annotationProcessor "com.alibaba:arouter-compiler:1.1.4"
    compile 'com.android.support:design:27.1.1'
    compile 'org.simple:androideventbus:1.0.5.1'
    compile 'com.alibaba:fastjson:1.2.31'

因為我們把攔截器等公用類放在base註冊,在編譯期間生成路徑對映。所以還需要在build中加入如下配置:

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

由於每個子模組都會用到lib_base裡面的東西,所以需要在各子模組的build檔案中匯入(即module_home,module_caht,module_recom,module_me等模組中)如下配置:

//注意,此處也需要引入了com.alibaba:arouter-compiler:1.1.4
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
compile project(':lib_base')

同樣,也需要在各子模組的build中加入如下配置。

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

然後在app模組(也即是主模組)對各個子模組進行依賴。

compile project(':module_home')
compile project(':module_chat')
compile project(':module_recom')
compile project(':module_me')

子模組依賴規則配置

對於模組化專案,每個單獨的Module 都可以單獨編譯成 APK。在開發階段需要單獨打包編譯,專案釋出的時候又需要它作為專案的一個 Module 來整體編譯打包。簡單的說就是開發時是 Application,釋出時是 Library。所以,需要在子模組中做如下的配置:

if(isBuildModule.toBoolean()){
    apply plugin: 'com.android.application'
}else{
    apply plugin: 'com.android.library'
}

同理,Manifest.xml 也需要有兩套:

if (isBuildModule.toBoolean()) {
           manifest.srcFile 'src/main/debug/AndroidManifest.xml'
       } else {
           manifest.srcFile 'src/main/release/AndroidManifest.xml'
       }

同時,每個子模組的defaultConfig還需要增加如下配置:

defaultConfig {
        if (!isNeedMeModule.toBoolean()) {
            applicationId "com.xzh.module_me"
        }
 }

而上面的isBuildModule.toBoolean()判斷條件,讀取的是專案根目錄下的gradle.properties配置檔案。

# 是否需要單獨編譯 true表示需要,false表示不需要
isNeedHomeModule=false
#isNeedHomeModule=true
isNeedChatModule=false
#isNeedChatModule=false
isNeedRecomModule=false
#isNeedRecomModule=false
isNeedMeModule=false
#isNeedMeModule=false

然後根據上面的編譯配置在app模組中新增如下依賴:

if (!isNeedHomeModule.toBoolean()) {
        compile project(':module_home')
    }
    if (!isNeedChatModule.toBoolean()) {
        compile project(':module_chat')
    }
    if (!isNeedRecomModule.toBoolean()) {
        compile project(':module_recom')
    }
    if (!isNeedMeModule.toBoolean()) {
        compile project(':module_me')
    }

如果需要單獨執行某個模組時,只需要修改gradle.properties對應的配置即可。例如,需要單獨執行module_home模組時,只需要開啟對於的配置即可isNeedHomeModule=true。

配置注意

由於配置後項目只有一個入口和啟動檔案(即app模組的MainActvity),所以其他子模組的MainActivity的intent-filter攔截要去掉,不然會有多個桌面入口。

<activity android:name=".MainActivity">
            <!--<intent-filter>-->
                <!--<action android:name="android.intent.action.MAIN" />-->

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

ARouter使用

以上面的效果實現為例,在MainActivity中使用TabLayout+Adapter的形式搭建4個Tab頁面。程式碼如下:

public class MainActivity extends AppCompatActivity {

    private ViewPager mMViewPager;
    private TabLayout mToolbarTab;
    private int[] tabIcons = {
            R.drawable.tab_home,
            R.drawable.tab_weichat,
            R.drawable.tab_recommend,
            R.drawable.tab_user
    };
    private String[] tab_array;
    private DemandAdapter mDemandAdapter;
    private List<Fragment> fragments = new ArrayList<>();

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

    private void init() {
        initData();
        initView();
        setViewPagerAdapter();
        setTabBindViewPager();
        setItem();
    }

    private void initData() {
        tab_array = getResources().getStringArray(R.array.tab_main);
        fragments.clear();
        fragments.add(FragmentUtils.getHomeFragment());
        fragments.add(FragmentUtils.getChatFragment());
        fragments.add(FragmentUtils.getRecomFragment());
        fragments.add(FragmentUtils.getMeFragment());
    }

    private void initView() {
        mMViewPager = (ViewPager) findViewById(R.id.mViewPager);
        mToolbarTab = (TabLayout) findViewById(R.id.toolbar_tab);

    }

    private void setViewPagerAdapter() {
        mDemandAdapter = new DemandAdapter(getSupportFragmentManager(),fragments);
        mMViewPager.setAdapter(mDemandAdapter);
    }

    private void setTabBindViewPager() {
        mToolbarTab.setupWithViewPager(mMViewPager);
    }

    private void setItem() {

        for (int i = 0; i < mToolbarTab.getTabCount(); i++) {
            mToolbarTab.getTabAt(i).setCustomView(getTabView(i));
        }
    }

    public View getTabView(int position) {
        View view = LayoutInflater.from(this).inflate(R.layout.item_tab, null);
        ImageView tab_image = view.findViewById(R.id.tab_image);
        TextView tab_text = view.findViewById(R.id.tab_text);
        tab_image.setImageResource(tabIcons[position]);
        tab_text.setText(tab_array[position]);
        return view;
    }
}

然後,使用ARouter來獲取到各個模組的Fragment。

public class FragmentUtils {

    public static Fragment getHomeFragment() {
        Fragment fragment = (Fragment) ARouter.getInstance().build(RouteUtils.Home_Fragment_Main).navigation();
        return fragment;
    }

    public static Fragment getChatFragment() {
        Fragment fragment = (Fragment) ARouter.getInstance().build(RouteUtils.Chat_Fragment_Main).navigation();
        return fragment;
    }

    public static Fragment getRecomFragment() {
        Fragment fragment = (Fragment) ARouter.getInstance().build(RouteUtils.Recom_Fragment_Main).navigation();
        return fragment;
    }

    public static Fragment getMeFragment() {
        Fragment fragment = (Fragment) ARouter.getInstance().build(RouteUtils.Me_Fragment_Main).navigation();
        return fragment;
    }
}

而FragmentUtils使用了RouteUtils來定義具體的跳轉協議。

public class RouteUtils {

    public static final String Home_Fragment_Main = "/home/main";
    public static final String Chat_Fragment_Main = "/chat/main";
    public static final String Recom_Fragment_Main = "/recom/main";
    public static final String Me_Fragment_Main = "/me/main";

}

上面的子模組使用的是Fragment,所以,在子模組中要使用Route說明。例如:

@Route(path = RouteUtils.Chat_Fragment_Main)
public class MainFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
            savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_weichat, null);
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }
}

跨模組跳轉

假如要實現跨模組跳轉,首先在RouteUtils定義

public static final String Me_Login = "/me/main/login";

ARouter要跳轉Activity,就在這個Activity上加入註解。

@Route(path = RouteUtils.Me_Login)
public class LoginActivity extends AppCompatActivity{
}

然後在需要跳轉的地方新增如下程式碼:

ARouter.getInstance().build(RouteUtils.Me_Login).navigation();

實現ForResult返回資料

如果跨模組跳轉需要返回資料,即Activity的StartActivityForResult,則可以使用下面的方式。

ARouter.getInstance().build(RouteUtils.Chat_ForResult).navigation(this, 666); //666即為Code

接收資料資料:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 666:
                String name = data.getStringExtra("name");
                UIUtils.showToast(name + ",resultCode===>" + resultCode);
                break;
            default:
                break;
        }
    }

然後,接受返回資料:

Intent intent = new Intent();
            intent.putExtra("name", "ForResult返回的資料");
            setResult(999, intent);
            finish();

使用Eventbus跨模組通訊

使用Eventbus進行跨模組通訊,首先在需要接受的地方定義一個訂閱者。

@Subscriber(tag = EvenBusTag.GOTO_EVENTBUS)
    public void onEvent(String s) {
        UIUtils.showToast(s);
    }

然後在傳送方使用EventBus傳送訊息。例如:

@Route(path = RouteUtils.Me_EventBus)
public class EventBusActivity extends AppCompatActivity implements View.OnClickListener {

    /**
     * eventBus資料接收頁面
     */
    private TextView mTextView;
    /**
     * eventBus返回資料
     */
    private Button mBtnBackData;

    private String name;

    private long age;
    private EventBusBean eventbus;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus);
        ARouter.getInstance().inject(this);
        initData();
        initView();
    }

    private void initData() {
        name = getIntent().getStringExtra("name");
        age = getIntent().getLongExtra("age", 0);
        eventbus = getIntent().getParcelableExtra("eventbus");
    }

    private void initView() {
        mTextView = (TextView) findViewById(R.id.textView);
        mBtnBackData = (Button) findViewById(R.id.btn_back_data);
        mBtnBackData.setOnClickListener(this);
        mTextView.setText("name=" + name + ",\tage=" + age + ",\tproject=" + eventbus.getProject() +
                ",\tnum=" + eventbus.getNum());
    }

    @Override
    public void onClick(View v) {
        int i = v.getId();
        if (i == R.id.btn_back_data) {
            EventBus.getDefault().post(name, EvenBusTag.GOTO_EVENTBUS);
            finish();
        } else {
        }
    }
}

其實,ARouter的功能遠不止於此,後面將為大家一一講解,並最終自己實現一個模組間的路由。

相關推薦

阿里ARouter路由實現Android模組開發

概述 從 2016 年開始,模組化在 Android 社群越來越多的被提及。隨著移動平臺的不斷髮展,移動平臺上的軟體慢慢走向複雜化,體積也變得臃腫龐大,為了降低大型軟體複雜性和耦合度,同時也為了適應模組重用、多團隊並行開發測試等等需求,模組化在 Android

使用阿里ARouter路由實現元件模組開發流程

Android平臺中對頁面、服務提供路由功能的中介軟體,我的目標是 —— 簡單且夠用。 這是阿里對Arouter的定位,那麼我們一起來梳理一下Arouter使用流程,和使用中我所遇到的一些問題! 先來看看有哪些功能 模組化的要解決的問題

Android模組開發、元件開發

模組化開發:優點嘛,專案過大時便於管理; 1、在根目錄的gradle.properties檔案下新增 isBuildModule=false; 使用isBuildModule來控制這個是Library還是獨立的APP; 2、建立一個新的Module,在其build.gra

Android模組開發遇到的問題: 資源名衝突的問題

方法一: 保護某些 resources 不被外部訪問,可以建立res/values/public.xml,因為 public 是關鍵詞,所以需要用 new file 的方式建立。至少新增一行,未新增的視為 private。(經驗證,好像沒有效果,知道的大神幫忙留言,最好

一點一點學maven(10)——eclipse實現maven模組開發

1、新建父專案modules-container,選擇maven project,作為所有子模組的容器 2、新建子專案modules-demo01,選擇maven module,module name為子模組名,parent project選擇

模組開發框架實現原理

本文是螞蟻金服微貸事業部自主研發的模組化開發框架TITAN的實現原理,該框架後續可能會開源,敬請期待! 需求背景 應用拆分的多或少都有問題。多則維護成本高,每次釋出一堆應用。少則拆分成本高,無用功能很難下線。 故障不隔離。當一個系統由多人同時參與開發時,修改A功能,可能會影響B功能,引發故障

android模組app開發筆記-1環境搭建

由於專案做的越來越大,業務上就產生了要將app模組化的需求,所謂模組化就是將一個app分成不同功能的小模組(外掛),當安裝程式的時候並不需要將所有模組一次全部安裝,使用者可以在需要的時候視情況從伺服器上更新新增小外掛。    android上模組化一直都有人在摸索也出現

android模組app開發筆記-2外掛間佈局檔案共享

  android程式設計時佈局檔案,圖片資源等都是放在同一個資料夾下,這樣照成一個問題就是我們想重用UI佈局檔案和圖片時就還需要其分離這些資料,相信大部分android程式設計師都遇到過這樣的問題,其痛苦程度不亞於世紀末日趕不上諾亞方舟。    今天我用apkplug框

阿里再開源!模組開發框架JarsLink

JarsLink (原名Titan) 是一個基於JAVA的模組化開發框架,它提供在執行時動態載入模組(一個JAR包)、解除安裝模組和模組間呼叫的API。也是阿里巴巴的開源專案之一 https://github.com/alibaba/jarslink,目前在微貸事業群廣泛使用。 需求背景 應

通過Gradle自動實現Android元件模組構建

為什麼我們要用Gradle管理元件呢? 先來看看Android元件化需要實現的目標 按照業務邏輯劃分模組 專案模組能夠單獨啟動測試 能夠根據需求引入或刪除某些業務模組 通過不同模組的組合,組成不同的App 對於第一點:需要根據技術架構和業務架構來劃分模組,這裡

Android模組和元件開發簡單理解(一)

模組化和元件化可以理解為同一個概念: 將一個app分成多個模組,每個模組都是一個元件(module),開發過程中讓這些元件相互依賴或者單獨除錯某個元件。在釋出的時候將這些元件合併成一個apk。 Android元件化我的理解是 application與library之間相互

BaseAdapter的封裝來實現模組開發

引子 要在一個手掌大的手機上展示各種豐富的資訊,橫向滑動與上下滑動已經成為了手機應用不可缺少的一部分。對於上下滑動,有一個被小看的神器ListView配合Adapter。為什麼被小看了呢?對比ScrollView來說,ListView不僅能夠實現不同介面的

使用DroidPlugin實現模組開發(主要說呼叫)

github地址:https://github.com/DroidPluginTeam/DroidPlugin/blob/master/readme_cn.md 關於配置,跟著說明文件配置吧。 關於跳轉呼叫, 使用new Intent(String ActiviyUrl),你會發現,

Android 專案模組開發,提高開發效率。

把Demo放在第一!!! 隨著需求的增加,程式碼量隨之變得龐大、臃腫。於是產生了很多影響開發效率的問題。 例如: 1. 方法數超過65K。 2. 程式編譯執行一次至少1-2分鐘。 3. 程式碼變得難以管理,影響閱讀及修改效

js-模組開發總結

一.模組開發的概念 模組化開發是什麼:模組化開發是一種生產方式,這種方式生產效率高,維護成本低。從軟體開發的角度說,模組化開發是一種開發模式,寫程式碼的一種方式,開發效率高,維護成本低。 為什麼需要模組化開發:當一個專案開發的越來越複雜的時候,會遇到一些問題,比如命名衝突(重新命名),檔

模組開發(requireJS)

模組化 在前端使用模組化開發,可以將程式碼根據功能實施模組的劃分,每個模組功能(職責)單一,在需要更改對應的功能的時候,只需要對指定的模組進行修改,其他模組不受任何影響。 為什麼要進行前端模組化? 達到公共模組的複用 可以很好的解決全域性變數汙染的問題 可以很好的解決各個功能之間的依賴關係

javascript 模組開發

一、為什麼會有模組化 1. 當一個專案開發的越來越複雜的時候,會遇到一些問題,比如: 命名衝突:當專案由團隊進行協作開發的時候,不同開發人員的變數和函式命名可能相同;即使是一個開發,當開發週期比較長的時候,也有可能會忘記之前使用了什麼變數,從而導致重複命名,導致命

沒有前戲,簡明扼要的進入主題——什麼是模組開發

模組化開發,一個模組就是一個實現特定功能的檔案,有了模組我們就可以更方便的使用別人的程式碼,要用什麼功能就載入什麼模組。 模組化開發的4點好處:   1 避免變數汙染,命名衝突   2  提高程式碼複用率   3 提高維護性   4 依賴關係的管理

Android 模組 元件 外掛的關係

模組化:一個程式按照其功能做拆分,分成相互獨立的模組(例如:登陸,註冊)。模組化的具體實施方法分為外掛化和元件化。 元件化:開發模式下面module本來就是一個獨立app,只是釋出模式下變成library。 外掛化:就是不存在釋出模式開發模式,每個元件業務就是一個獨立

初識模組開發工具:

初識模組化開發工具:git 是分散式程式碼管理工具,管理程式碼的npm 是包管理工具,管理外掛、工具啊,是個轉換器,他是哪來的了,他是伴隨node下載下來的,版本也是伴隨node變化;node 是個後臺的環境;首先安裝node,然後用命令安裝browserify:npm install -g browseri