alvo de aprendizagem:
Proficiência no desenvolvimento de componentes, configuração de roteamento
Conteúdo de aprendizagem:
** Antes de usar o desenvolvimento com componentes, você deve primeiro esclarecer a estrutura geral do projeto, dividir os módulos e o negócio (foco) e um bom começo terá um bom resultado. ** Depois que o módulo estiver claramente dividido, comece a configurar o módulo.
Conforme mostrado na figura, temos que completar as seguintes funções:
1. Clique no shopping para entrar no ShoppingModule
2. Clique em Login para entrar no LoginModule
3. Clique na área vermelha da conta para exibir a lista de contas (Fragmento em outros Módulos)
(shareModule é um módulo público)
Criado da seguinte forma de acordo com os requisitos de negócios:
Adicione no arquivo gradle.properties do aplicativo para controlar se o módulo é executado de forma independente.
#配置某个组件是否可以独立运行
isShoppingRunAlone = true
isLoginRunALone = true
Em seguida, configure o App build.gradle.
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.moduledemo"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//导入公共模块
implementation project(':ShareModule')
// 根据gradle中的配置来决定是否引用module
if (!isLoginRunALone.toBoolean()){
implementation project(':LoginModule')
}
if (!isShoppingRunAlone.toBoolean()){
implementation project(':ShoppingModule')
}
}
Continue a configurar o arquivo build.gradle de outros Módulos.
if (isShoppingRunAlone.toBoolean()){
apply plugin: 'com.android.application'
}else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets{
main{
// 在独立运行或者作为Libarary调试时,使用不同的AndroidManifest.xml文件
if (isShoppingRunAlone.toBoolean()){
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//导入公共模块
implementation project(':ShareModule')
}
Use diferentes arquivos de manifesto em diferentes modos de operação.
Você precisa criar uma nova pasta de manifesto no diretório principal do módulo correspondente (caso contrário, você não encontrará o arquivo de manifesto se executá-lo sozinho).
O arquivo de manifesto executado separadamente é definido da seguinte maneira:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.loginmodule">
<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">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Incorpore as configurações principais do arquivo de manifesto de tempo de execução do Módulo da seguinte maneira:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.shoppingmodule">
<application>
<activity android:name=".ShoppingActivity"/>
</application>
</manifest>
Depois que toda a configuração for concluída, você pode modificar o valor da variável em gradle.properties, compilar para verificar se a configuração está correta e se o arquivo de manifesto foi substituído. Corra para ver se está normal.
Em seguida, comece a configurar o roteamento.
Muitas pessoas estão se perguntando por que precisam usar os saltos de roteamento se podem obter diretamente a atividade do submódulo depois de fazer referência ao Módulo. É porque o desenvolvimento do componente é para fazer um único módulo compilar independentemente.Se o módulo principal fizer referência ao nome da classe do submódulo, o módulo principal irá compilar de forma anormal quando o submódulo for executado sozinho.
Precisamos saber que um projeto não pode ter apenas um submódulo.Como usar o roteamento quando nossos outros submódulos querem saltar um para o outro? Portanto, precisamos configurar o roteamento no ShareModule.Na configuração anterior, importamos ShareModule em cada Módulo.
Primeiro passo
Criamos um modelo de salto correspondente ao Módulo
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public interface ILoginService {
void launch(Context ctx, String targetClass);
}
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public interface IShoppingService {
void launch(Context ctx, String string);
Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle);
}
Segundo passo
Realize a lógica de salto e as operações de transferência de valor no moudle correspondente
package com.example.loginmodule;
import android.content.Context;
import android.content.Intent;
import com.example.sharemodule.ILoginService;
public class LoginService implements ILoginService {
@Override
public void launch(Context ctx, String targetClass) {
Intent intent = new Intent(ctx, LoginActivity.class);
ctx.startActivity(intent);
}
}
package com.example.shoppingmodule;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.example.sharemodule.IShoppingService;
public class ShoppingService implements IShoppingService {
@Override
public void launch(Context ctx, String string) {
Intent intent = new Intent(ctx, ShoppingActivity.class);
ctx.startActivity(intent);
}
@Override
public Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
BillFragment fragment = new BillFragment();
fragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(viewId, fragment).commit();
return fragment;
}
}
terceiro passo
Em seguida, criamos uma ServiceFactory para nos fornecer instâncias de salto e lidar com exceções que podem ocorrer quando executado sozinho
package com.example.sharemodule;
public class ServiceFactory {
private static final ServiceFactory instance = new ServiceFactory();
private ILoginService mLoginService;
private IShoppingService mShoppingService;
private ServiceFactory(){}
public static ServiceFactory getInstance() {
return instance;
}
public ILoginService getLoginService() {
if (mLoginService == null){
mLoginService = new EmptyLoginService();
}
return mLoginService;
}
public void setLoginService(ILoginService mLoginService) {
this.mLoginService = mLoginService;
}
public IShoppingService getSignService() {
if (mShoppingService == null){
mShoppingService = new EmptyShoppingService();
}
return mShoppingService;
}
public void setSignService(IShoppingService mSignService) {
this.mShoppingService = mSignService;
}
}
package com.example.mylibrarySharedLibrary;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public class EmptyLoginService implements ILoginService {
@Override
public void launch(Context ctx, String targetClass) {
}
@Override
public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
return null;
}
}
package com.example.mylibrarySharedLibrary;
import android.content.Context;
public class EmptySignService implements ISignService {
@Override
public void launch(Context ctx, String userId) {
}
}
Dessa forma, nenhuma exceção ocorrerá, mesmo quando executarmos o Moudle principal sozinhos.
O código que pulamos acima está concluído. A próxima etapa é adicionar à serviceFactory
private ILoginService mLoginService;
private IShoppingService mSignService;
Tarefa
package com.example.sharemodule;
import android.app.Application;
public interface IComponentApplication {
void initialize(Application application);
}
Fornece interface de inicialização unificada
package com.example.moduledemo;
import android.app.Application;
import android.util.Log;
import com.example.sharemodule.AppConfig;
import com.example.sharemodule.IComponentApplication;
public class MainApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication(){
return application;
}
@Override
public void onCreate() {
super.onCreate();
initialize(this);
}
@Override
public void initialize(Application application) {
for (String cpnt : AppConfig.Components){
try{
Class<?> clz = Class.forName(cpnt);
Object obj = clz.newInstance();
if (obj instanceof IComponentApplication){
((IComponentApplication) obj).initialize(this);
}
}catch (Exception e){
Log.e("TAG", e.getMessage());
}
}
}
}
package com.example.loginmodule;
import android.app.Application;
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
public class LoginApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication(){
return application;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void initialize(Application app) {
application = app;
ServiceFactory.getInstance().setLoginService(new LoginService());
}
}
package com.example.shoppingmodule;
import android.app.Application;
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
public class ShoppingApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication() {
return application;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void initialize(Application app) {
application = app;
ServiceFactory.getInstance().setSignService(new SignService());
}
}
package com.example.sharemodule;
public class AppConfig {
public static final String[] Components = {
"com.example.shoppingmodule.ShoppingApplication",
"com.example.loginmodule.LoginApplication"
};
}
Ele é inicializado ao entrar no App, e a instância do Application do submódulo é obtida através de reflexão para inicialização.
Resultado final:
Este artigo está incluído no projeto de código aberto: https://github.com/Android-Alvin/Android-LearningNotes , que contém o projeto de código aberto mais totalmente aberto de componentes Android (Meituan App, Get App, Alipay App, WeChat App, Mogujie App, Youzan APP ...) etc. Os recursos estão sendo atualizados continuamente ...