一項簡單的任務分析示例,Android組件化開發簡單示例

 2023-11-19 阅读 40 评论 0

摘要:Android組件化示例代碼github地址:https://github.com/respost/ModuleDemo 一、組件化初始模型 1、通過一個簡單的android項目初始架構圖來了解組件化,如下圖: 打個生動的比喻,把我們的APP當成一個電腦主機,那么app外殼就是主機外殼ÿ

Android組件化示例代碼github地址:https://github.com/respost/ModuleDemo

一、組件化初始模型

1、通過一個簡單的android項目初始架構圖來了解組件化,如下圖:

打個生動的比喻,把我們的APP當成一個電腦主機,那么app外殼就是主機外殼,main組件就是主板,其他各個組件就類似于硬盤、網卡、顯卡之類的東西,各個組件連接到主板上,然后再安裝到主機殼中,對外展示為一個完整的電腦主機。?

2、app外殼和main組件是我們app的必備組成部分,一起構成了可對外發布的完整app,其他組件可以集成進來,也可以不集成進來,只會增加或者減少我們app的功能,但不影響我們app的最終發布。

二、創建Module模塊(組件)

一項簡單的任務分析示例,在我們的實際項目中,組件展示出來的效果大概是這樣的:

1、我們開始創建Module模塊,項目的APP上右鍵?→ New??→ Module

2、選擇?Android Library? →? Next

android組件、

3、 輸入模塊名稱?? →? Finish

三、組件的build.gradle文件說明

1、通過以上Module模塊的創建,包括main組件在內,一共有5個組件,所以對應的有5個組件build.gradle文件,如下圖:

安卓組件化開發、2、?main組件的gradle文件中,apply plugin使用的是com.android.application

3、其他業務模塊(組件A、組件B、組件C、common組件等),apply plugin使用的是com.android.library?

四、組件集成

各個組件都建立完成之后,接下來可以把組件集成到main組件中,集成非常簡單,只需在main組件的gradle文件中添加dependencies{}配置,添加如下語句:

dependencies {...//集成組件Aimplementation project(':modulea')//集成組件Bimplementation project(':moduleb')//集成組件Cimplementation project(':modulec')
}

android組件化。如下圖:?

五、組件資源共享

1、在common組件的build.gradle文件中,添加android?配置,如下:

android {//省略前面的代碼...repositories {flatDir {dirs 'libs'}}
}

在各個需要調用公共common組件的組件build.gradle文件中,也添加android?配置,如下:

android {//省略前面的代碼..repositories {flatDir {dirs '../common/libs/', 'libs'}}}

2、common組件里引入各種類庫的時候必須用api,而不是用implementation,原因:

安卓組件化?implementation編譯的依賴只作用于當前的module,即common組件模塊中使用implementation編譯的三方庫只對common模塊起作用,main組件模塊中無法使用該三方庫。

?

3、關于組件資源共享,舉個簡單示例:例如圖片都是存放到公共的common組件的res里,那么如何在組件A、組件B、組件C里使用呢?

使用方法如下:

  • 打開各組件的build.gradle文件,在dependencies{}里添加如下代碼即可:
dependencies {...implementation project(':common')
}
  • 如此一來,就能在組件A里調用common組件的圖片資源了

Android。

4、同樣的道理, 組件A、 組件B、 組件C的顏色代碼也可以直接調用公共common組件里colors.xml的代碼

5、我們可以把其他第三方庫、自定義view、工具類、公用資源都放進公共common組件里,也就是說組件A、組件B、組件C里build.gradle所引入的類庫,都可以放到common組件里的dependencies{}里

android開發四大組件,6、所以各個業務組件里面的build.gradle文件的dependencies{}配置簡化后,就變成了下面這樣:

7、通過以上解說,大家應該都明白了吧,圖片、xml這些(value目錄下的各種xml文件),都可以放到公共common組件里,然后再被其他組件引用。對于全局共用的style.xml文件,我們更應該把它放在common組件中,例如我們的項目theme,本來是放在main組件的style里面,我們可以把它移到common中,這樣其他組件調試時,作為一個單獨的項目,也能和主項目有一樣的主題。總而言之,所有你認為可以被各個組件共享的資源,都可以放在common組件中。

六、往組件里添加Fragment

1、以組件A為例,在組件A里添加一個包fragment

android組件化開發?2、在fragment包右鍵?? →? New???→??Fragment???→Fragment(Blank)

3、 填寫Fragment碎片名稱,勾選創建xml文件,如下:

4、對應的fragment_module_a.xml文件代碼:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@drawable/a" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="5dp"android:text="組件A"android:background="@color/green"android:textColor="@android:color/white"android:textSize="24dp" /></FrameLayout>

android插件化框架。

5、其他組件也類似組件A一樣,創建一個Fragment碎片,然后添加不同的背景圖片即可。

6、main組件里添加導航和Fragment容器,main組件里activity_main.xml的代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/container"android:orientation="vertical"><FrameLayoutandroid:id="@+id/content"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></FrameLayout><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/navigation"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="bottom"android:background="?android:attr/windowBackground"app:menu="@menu/navigation" />
</LinearLayout>

7、res下創建一個menu目錄,里面添加一個navigation.xml文件,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/navigation_a"android:icon="@drawable/home"android:title="組件A" /><itemandroid:id="@+id/navigation_b"android:icon="@drawable/video"android:title="組件B" /><itemandroid:id="@+id/navigation_c"android:icon="@drawable/me"android:title="組件C" />
</menu>

?navigation.xml里調用的icon圖片分別放到drawable和drawable-24目錄里,最終主APP的MainActivity界面如下圖:

android快速開發框架?

8、MainActivity.java的代碼如下:

package net.zy13.module.demo;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;import android.os.Bundle;
import android.view.MenuItem;import com.google.android.material.bottomnavigation.BottomNavigationView;import net.zy13.module.modulea.fragment.ModuleAFragment;
import net.zy13.module.moduleb.fragment.ModuleBFragment;
import net.zy13.module.modulec.fragment.ModuleCFragment;public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {//定義碎片集合private Fragment[] fragments = new Fragment[3];//當前顯示的fragment的索引位置private int currentIndex = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFragment();BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);navigation.setOnNavigationItemSelectedListener(this);}/*** 初始化Fragment碎片*/private void initFragment() {if (fragments[0] == null) {fragments[0] = new ModuleAFragment();getSupportFragmentManager().beginTransaction().add(R.id.content, fragments[0], "moduleA").commit();} else {getSupportFragmentManager().beginTransaction().show(fragments[0]);}}/*** 導航選擇事件* @param item* @return*/@Overridepublic boolean onNavigationItemSelected(@NonNull MenuItem item) {switch (item.getItemId()) {case R.id.navigation_a:if (currentIndex == 0) return true;//如果已經是當前的fragment,不用切換FragmentTransaction transition0 = getSupportFragmentManager().beginTransaction();hideAndShow(0,transition0);return true;case R.id.navigation_b:if (currentIndex == 1) return true;//如果已經是當前的fragment,不用切換FragmentTransaction transition1 = getSupportFragmentManager().beginTransaction();if (fragments[1] == null) {fragments[1] = new ModuleBFragment();transition1.add(R.id.content, fragments[1], "moduleB");}hideAndShow(1,transition1);return true;case R.id.navigation_c:if (currentIndex == 2) return true;//如果已經是當前的fragment,不用切換FragmentTransaction transition2 = getSupportFragmentManager().beginTransaction();if (fragments[2] == null) {fragments[2] = new ModuleCFragment();transition2.add(R.id.content, fragments[2], "modulec");}hideAndShow(2,transition2);return true;}return false;}/*** 除了指定的fragment不hide,其他fragment全hide* @param expectIndex 指定的fragment在fragments中的位置* @param transition*/private void hideAndShow(int expectIndex,FragmentTransaction transition) {for (int i = 0; i < fragments.length; i++) {if (i != expectIndex && fragments[i] != null) {transition.hide(fragments[i]);}}transition.show(fragments[expectIndex]);transition.commit();currentIndex = expectIndex;}
}

9、Run運行項目,最終效果如下圖,點擊導航,可以切換到對應的組件上:

七、各個組件單獨開發(測試)

?前面我們把各個組件集成到main組件中,現在我們把組件拆分出來,單獨開發,開發測試完成后,再把組件集成到main組件中,最后發布。組件單獨出來開發的方法就是:在build.gradle文件中,把apply plugin: 'com.android.library',改成apply plugin: 'com.android.application',也就是把其library模式改為application模式,因為只有application才可以單獨運行,library必須依靠application才能運行。

android ui組件庫,那么問題來了?

組件單獨開發時,我們需要改build.gradle的apply plugin模式,等要集成到main組件時,又得改回來,如果這樣子手工去改,組件一多,修改起來比較麻煩,也不優雅。優雅的解決辦法就是設置一個開關,打開時,就是application模式,可以單獨開發;關閉時,就是library模式,可以集成到main組件中。現在按我下面的步驟來實現:

1、在項目根目錄下,有一個build.gradle文件,在這個文件最末尾添加一個ext {}配置,然后在ext配置里設定一個常量isDebug,值設為true

ext {/*** 組件調試模式* isDebug = true 是組件模塊,說明是單獨的App* isDebug = false是集成模式,說明是依賴Lib* 每次更改“isDebug”的值后,需要點擊 "Sync Project" 按鈕**/isDebug = true
}

?

2、build.gradle里設置了isDebug常量后,我們項目中的其他build.gradle文件都可以把這個常量讀取出來,所以我們可以在其他組件的build.gradle文件中,讀取該常量的值,動態設置apply plugin,代碼如下:

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

3、這樣子設置之后,當我們需要切換模式時,只需要修改項目根目錄下build.gradle文件中isDebug常量的值,修改完成之后,點擊Project sync按鈕同步一下即可。如果有報錯,那么還有個地方需要修改一下,就是main組件的build.gradle文件,我們把module的模式改成了application,main組件就不能引入application,引入的話就會報錯,所以當是debug調試模式時,這里就不引入該組件,以免報錯。所以在集成組件前,要先判斷是什么模式,如下圖:

4、接下來還得修改?AndroidManifest.xml,當把一個module設置為application時,AndroidManifest.xml需要包含一個app所需要的屬性,例如app的icon、theme、launch Activity這些屬性設置,而當module為library時,這些屬性就都不需要用到,所以當我們處于不同模式時,AndroidManifest.xml文件的配置也得不同。方法如下:

(1)、Android目錄模式切換到Project目錄模式

(2)、?在各個組件的src文件夾中新創建一個debug目錄,再把我們用于debug調試的AndroidManifest.xml文件放進去

(3)、調試用的AndroidManifest.xml文件可以直接復制manifests目錄里的,然后添加application的基本信息,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="net.zy13.module.modulea"><applicationandroid: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"></application>
</manifest>

以上內容會有很多錯誤提示,其實提示的無非就是資源找不到,既然前面我們已經創建了公共的common組件,那么我們只需要把main組件中相應的資源移動到common組件中就可以了。

5、接下來在各個組件的build.gradle文件中,指定不同模式下使用的AndroidManifest.xml文件,在android {}里添加如下代碼:

sourceSets {main {if (isDebug) {manifest.srcFile 'src/debug/AndroidManifest.xml'}else{manifest.srcFile 'src/main/AndroidManifest.xml'//集成開發模式下排除debug文件夾中的所有Java文件java {exclude 'debug/**'}}}
}

6、以上設置完成,并且sync project(同步項目)之后,各個組件會是這樣的目錄結構:

7、在各個組件里創建一個用于調試啟動的MainActivity活動,然后把fragment加載到activity_main.xml里,所對應的activity_main.xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="net.zy13.module.modulea.fragment.ModuleAFragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</fragment>

8、添加MainActivity活動后,我們還需要手動設置該活動為入口,打開src/debug/目錄下的AndroidManifest.xml文件,修改MainActivity的配置如下:

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

9、以上步驟完成之后,組件就可以單獨作為一個app來開發測試了,android studio的運行app里面,同時多了幾個可運行的項目,如下圖:

八、統一項目版本號

各個組件的build.gradle文件中,有很多版本號。為了避免每次修改都得同時修改多份build.gradle文件,也避免不同的組件使用的版本不一樣,導致沖突,所以我們可以把這些版本號統一管理起來,方法如下:

1、在項目根目錄下的build.gradle文件中,定義版本號常量

ext {/*** 組件調試模式* isDebug = true 是組件模塊,說明是單獨的App* isDebug = false是集成模式,說明是依賴Lib* 每次更改“isDebug”的值后,需要點擊 "Sync Project" 按鈕**/isDebug = true//版本號android = [compileSdkVersion: 30,buildToolsVersion: "30.0.0",minSdkVersion    : 15,targetSdkVersion : 30,versionCode      : 1,versionName      : "1.0"]
}

2、然后在各個組件的build.gradle文件中,做這樣的修改:

android {compileSdkVersion rootProject.ext.android.compileSdkVersionbuildToolsVersion rootProject.ext.android.buildToolsVersiondefaultConfig {minSdkVersion rootProject.ext.android.minSdkVersiontargetSdkVersion rootProject.ext.android.targetSdkVersionversionCode rootProject.ext.android.versionCodeversionName rootProject.ext.android.versionName}
}

九、使用路由實現組件之間的通信?

通過前面的學習,我們已經知道組件可以拆分了,但是當他們集成到main組件中時,還是需要一定的通信,例如業務A需要調用到業務B的一個頁面,甚至進行傳參。但是在“組件化”模式下,業務A和業務B是完全分開的,在業務A的認知里,根本就不存在業務B,也就沒辦法直接調用。當然,如果要業務A與業務B可以直接通信,互相引入就可以,但是這樣的話,項目耦合性太高,架構也混亂,會把“組件化”的所有優點都一一撇掉,所以我們應該用另外一種方式來處理。

這里需要引入一個概念:路由”,就如我們實際訪問網絡一樣,我們電腦發送的請求都經過路由器轉發,在“組件化”中,我們也可以設置這么一個中轉站,來統一處理不同組件之間的調用關系。關于路由的用法,后面會補充,這里先用最簡單的方法,來實現組件之間的調用(頁面跳轉),代碼如下:

try {Class c= Class.forName("net.zy13.module.modulea.MainActivity");Intent intent = new Intent(context,c);startActivity(intent);
} catch (ClassNotFoundException e) {Log.e("組件","組件未集成,無法跳轉");
}

以上代碼是通過完整的類名來進行跳轉,在debug模式下,調用其他組件時,找不到對應組件,不會直接報錯,只是提示“未集成,無法跳轉”。我們可以把這個方法寫成一個工具類,放在common組件中,方法如下:

1、在common組件里創建一個工具類PageUtils.java,代碼如下:

import android.content.Context;
import android.content.Intent;
import android.util.Log;/*** @author 安陽 QQ:15577969* @version 1.0* @team 美奇軟件開發工作室* @date 2020/11/12 11:55*/
public class PageUtils {/*** 頁面跳轉* @param context* @param className*/public static void jump(Context context, String className){try {Class c = Class.forName(className);Intent intent = new Intent(context,c);context.startActivity(intent);} catch (ClassNotFoundException e) {Log.e("組件","未集成,無法跳轉");}}/*** 頁面跳轉,可以傳參,參數放在intent中,所以需要傳入一個intent* @param context* @param className* @param intent*/public static void jump(Context context,String className,Intent intent){try {Class c = Class.forName(className);intent.setClass(context,c);context.startActivity(intent);} catch (ClassNotFoundException e) {Log.e("組件","未集成,無法跳轉");}}
}

2、在需要跳轉的地方,直接用以下方法調用:

PageUtils.jump(context,"net.zy13.module.modulea.MainActivity");

還有一種通信情況,就是其中某個組件的改變會影響到其他組件的改變。例如用戶的登陸情況(是否登錄),會影響到其他組件中一些控件的顯示情況,這個時候我們可以用android的廣播機制,或者用EventBus來解決。實際開發中,如果是像這種會影響全局的改變,應該放在公共的common組件之中。用戶的登陸情況,是影響全局的存在,那么就可以在common中,定義一個用戶單例,其他組件分別拿這個單例去控制其界面的顯示,特別是引入databingding之后,操作起來更為方便,具體的實現方式大家可以百度參考其他教程,這里我就不實現了。

十、android路由框架

關于路由框架的具體用法,我單獨整理成了一篇文章,示例代碼也是寫在了組件化項目里。

Android的路由框架用法:https://blog.csdn.net/qq15577969/article/details/109690111

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/181469.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息