Architecture e e-commerce APP Componentization Exploration, Shock

Keywords: Android Design Pattern

When isModuleRun is false, both Application and AndroidManifest participate in the compilation as Libraries, which do not require Activation to be started and custom Applications to be inverted.

isModuleRun=false

Unordered modification

<application
    android:allowBackup="true"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
 </application> 

isModuleRun=false

Create a new AndroidManifest.xml file in the main/debug directory

<application
    android:name=".debug.GoodsApplication"
    android:allowBackup="true"
    android:label="@string/goods_name"
    android:supportsRtl="true"
    tools:replace="android:label"
    android:theme="@style/AppTheme">
    <activity android:name=".GoodsActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

Reference Method

References in the gradle directory of Modele

Modify Plugin

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

New applicationId

if (isModuleRun.toBoolean()) {
    applicationId "com.wustor.cartmoudle"
} 

Toggle AndroidManifest Files

sourceSets {
    main {
        if (isModuleRun.toBoolean()) {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java {
                //Remove debug directory when compiling all modules together
                exclude '**/debug/**'
            }
        }
    }
} 

Resource Conflict

Suppose we defined an Application in CartModule and then defined app_in strings.xml in the current ModuleName, which is also defined in strings.xml in OrderModule Name, there will be a conflict when you merge, we can only change the above fields to cart_name and order_name solves this problem, which can be solved by this kind of differential naming under strict development specifications, because different Modules basically have different names for resource files, and instant conflicts are small and easy to resolve.

Of course, in addition to this, you can prefix the resource file name in build.gradle

resourcePrefix "cart_" 

It can be checked forcefully and price prefix is required for naming, which violates the original intention of componentization and makes the operation more difficult. However, it does not feel necessary to do this. Of course, sometimes pictures with the same name may appear. This can actually be restored to the analysis in the project before componentization, which is not possible.So after all, there is still no good development specification and development habits, there is no need to make some modifications for this kind of, after all, the agreement is larger than the configuration.

Dependent Configuration

From the initial overall architecture diagram, it can be seen that any Module that can switch between Library and Application undoubtedly relies on two of our Base's modules, which can be merged into one. I have two, one is BaseModule and the other is LibraryModule.Here's a look at their dependencies through the configuration in build.gradle:

MainModule
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile project(':routermodule')
} 

Components are isolated during compilation, so MainModule only depends on RouterModule, and what you just said is on-demand dependency at runtime, which is controlled by gradle's script

//Compile-time component isolation, runtime components depend on demand
//mainModule needs to interact with cartModule,goodsModule,usersModule, so dependencies are added at runtime
def tasks = project.gradle.startParameter.taskNames
for (String task : tasks) {
    def upperName = task.toUpperCase()
    if (upperName.contains("ASSEMBLE") || upperName.contains("INSTALL")) {
        dependencies.add("compile", project.project(':' + 'cartmodule'))
        dependencies.add("compile", project.project(':' + 'goodsmodule'))
        dependencies.add("compile", project.project(':' + 'usermodule'))
        dependencies.add("compile", project.project(':' + 'ordermodule'))
    }
} 
BusinessModule

This refers to Goods/Cart/User/OrderModule, which is actually parallel

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile project(':routermodule')
} 

Business Module depends on RouterModule

RouterModule
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile project(':modulelib')
    compile 'com.alibaba:arouter-api:1.2.1.1'
} 

RouterModule depends on LibraryModule

BaseModule
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile project(':librarymodule')
} 

BaseModule, as a base library, relies on LibraryModule

LibraryModule

As the lowest working people, it actually provides a dependency, so there is no dependency and they can only play with themselves.

So here, the basic dependencies are already clear, the entire architecture is known, and it is easy to proceed with the construction.

Component Communication

In fact, when we initially divided the modules, it came from business, so when we entered a Module, most of the logic should still be handled in this Module, but occasionally we would deal with other modules to see an interface

[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-DD1Ri1On-1630513155378). (https://user-gold-cdn.xitu.io/2018/1/21/161189f8e008ae32?ImageView2/0/w/1280/h/960/ignore-error/1)]

In the case of GoodsModule and CartModule, these two modules can jump to each other. Clicking on the shopping cart icon on the list page of GoodsModule will lead you to the shopping cart list of CartModule, and clicking on the goods will also lead you to the commodity details page of GoodsModule.In addition to this jump, there are actually variables to get, such as HomeFragment, SortFragment in GoodsModule, CartFragment in CartModule, and MineFragment in UserModule on the first page.I am directly dependent on four business modules in the MainModule, but I can actually do otherwise. We can also use Arouter to get instances of Fragments.

Get Instances

In fact, the examples here mostly refer to Fragments, so let's take Fragments as an example. Others can be processed just as they are.

  • Reflection Acquisition

Because modules are isolated, we can't create instances of Fragment s directly. It's easy to think of reflection at this time. Launching is omnipotent. Paste the code below.

//Get Fragment Instance
public static Fragment getFragment(String className) {
    Fragment fragment;
    try {
        Class fragmentClass = Class.forName(className);
        fragment = (Fragment) fragmentClass.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return fragment;
} 
  • Arouter

Arouter It is a routing framework that Alibaba exits. It is convenient to use routing tables in components, as illustrated below.

Add annotations to target fragments

@Route(path = "cart/fragment")
public class CartFragement extends BaseFragment{
} 

Get instances anywhere

Fragmetn fragment = (Fragment) ARouter.getInstance().build("/cart/fragment").navigation(); 
Method Call

There are method calls between different Modules, and we can define an interface within each Module, implement it, get it where we need to call, and make a method call.For unified management, we define the interface of each module inside the RouterModule, and since each business module relies on the RouteModule, we simply need to get the interface through reflection and make a method call.

[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-6Nghm5lH-1630513155380). (https://user-gold-cdn.xitu.io/2018/1/21/161189f8e03411d1?ImageView2/0/w/1280/h/960/ignore-error/1)]

ModuleCall

Interface Callback Between Module s

public interface ModuleCall {
   //Initit method is called to pass Context parameter
    void initContext(Context context);
} 

Service Interface inherits from ModuleCall to define some callback methods for Modules other than itself to invoke

public interface AppService extends ModuleCall {
    //TODO Call Method Customization
    void showHome();
    void finish();

} 

Impl implementation classes are specific callbacks corresponding to each Module and are direct subclasses that implement the Service interface

public class AppServiceImpl implements AppService {
    @Override
    public void showHome() {
    }
    @Override
    public void finish() {
    }
    @Override
    public void initContext(Context context) {
    }
} 

The following is also illustrated by reflection and Arouter

  • Reflection Call

    public static Object getModuleCall(String name) {
        T t;
        try {
            Class aClass = Class.forName(name);
            t = (T) aClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return t;
    } 
    

Get Interface

AppService appService = (AppService) ReflectUtils.getModuleCall(Path.APP_SERVICE); 

In fact, just like getting an instance of a Fragment, it is OK to get the corresponding interface by its class name and then call the corresponding method. One thing to note is that if the method invoked after the acquired interface needs to pass in the Context parameter, the initContext method must be called before calling the interface method to use the incoming Context, otherwise a null pointer exception will be reported.

  • Arouter

There is an IProvider interface in Arouter, as follows

public interface IProvider {
    void init(Context var1);
} 

IProvider is the same as the ModuleCall above, except that once he gets an interface instance, he calls the initContext method, where the Context comes from the parameter passed in from ARouter.init(this), and we don't need to call the initContext manually anymore.

Injection Path in Target Class

@Route(path = Path.APP_SERVICE)
public class AppServiceImpl implements AppService {
    private Context mContext;
    @Override
    public void showHome() {
        Log.d("go--->", "home--->");
    }

    @Override
    public void finish() {
    }

    @Override
    public void init(Context context) {
        mContext = context;
    }
} 

Get the target class anywhere

AppService appService = (AppService) RouterUtils.navigation(Path.APP_SERVICE); 

Then call the method

UI Jump

Jump basically refers to a jump between activities, not to mention a lot, but still an Arouter and a reflex

  • reflex

    //Convert class name to target class
    public static void startActivityWithName(Context context, String name) {
        try {
            Class clazz = Class.forName(name);
            startActivity(context, clazz);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    //Get Intent
    public static Intent getIntent(Context context, Class clazz) {
        return new Intent(context, clazz);
    }
    //Start Activity
    public static void startActivity(Context context, Class clazz) {
        context.startActivity(getIntent(context, clazz));
    } 
    
  • Arouter

    Register the target Activity with Arouter

    @Route(path = Path.CART_MOUDLE_CART)
    public class CartActivity extends BaseActivity<UselessPresenter, UselessBean> {
    } 
    

    Start Target Activity

     ARouter.getInstance().build(Path.CART_MOUDLE_CART).navigation() 
    

summary

Finally, in order to help you deeply understand the principles of Android-related knowledge points and interview-related knowledge, here are the 14 sets of Tencent, byte bouncing, Ali, Baidu and other 2021 interviews that I collected and sorted out, I sorted out the technical points into videos and PDF (actually spend a lot more effort than expected), including the knowledge context + many details.

CodeChina Open Source Project: "Android Learning Notes Summary + Mobile Architecture Video + Factory Interview True Topics + Project Actual Source"



Guan's collection of 14 sets of Tencent, byte bouncing, Ali, Baidu and so on 2021 latest interview true topic analysis, I organized the technical points into videos and PDF (actually spent a lot more energy than expected), including knowledge context + many details.

CodeChina Open Source Project: "Android Learning Notes Summary + Mobile Architecture Video + Factory Interview True Topics + Project Actual Source"

[img-hsqQq8Kw-1630513155381)]
[img-oKuaDbwm-1630513155383)]

[img-2XFAvnXk-1630513155384)]

[img-4u3mGwIp-1630513155385)]
There is a lot of information about learning Android online, but if the knowledge is not systematic, you just have a taste when you encounter problems and don't go into further research, it will be difficult to achieve real technology improvement.

Posted by Mark on Wed, 01 Sep 2021 20:45:55 -0700