Android组件化二【跨Module调用方法】

Android组件化二【跨Module调用方法】

经过前一篇的文章,我们已经可以将Module单独运行了,这一篇的话,我们继续探索下组件化(其实是模块化)的内容。
我们这次使用的是AppJoint的组件化方案,仍旧使用上篇文章的代码来做演示。

Appjoint的详细说明文档在 回归初心:极简 Android 组件化方案 — AppJoint !

一、目标

这次我们的目标是在app模块的MainActivity中启动food模块的FoodActivity,然后在food模块的FoodActivity中启动movie模块中的MovieActivity。其实但是启动Activity的话我们可以直接采取Arouter的路由方案来进行,但是这里的话我们不使用Arouter,而是使用AppJoint提供的方法来演示。

二、准备工作

在food模块中创建FoodActivity,在movie模块中创建MovieActivity,app模块中有默认的MainActivity。创建好必备的Activity后我们就需要准备跨模块来启动Activity了。

三、方案

其实当两个模块之间想要通讯的话,我们一般需要暴漏出来一个接口,然后给另一个模块调用,然而这样的话可能就会发生一个模块依赖另一个模块的问题了,所以我们需要将这个接口单独暴漏出来,这时有两种方案:

  • 添加一个基础路由模块例如叫router,统一声明所有对外暴漏的接口。所有需要实现或者调用其他模块的都要依赖该router模块。
  • 每个需要对外暴漏方法的模块添加一个专门对外暴漏接口的模块。例如movie模块想暴露出来一个方法,那么添加一个movieApi模块,该模块只声明对外暴漏的接口,movie模块需要依赖movieApi模块并实现相应的方法,假如food模块需要调用该方法,那么food模块也需要依赖movieApi模块。

方案就是这样,我们按照第二种来进行尝试:

3.1、新建movieApi模块

在该模块中,只创建对外暴漏的接口IMovieRouter,不包含任何业务逻辑,业务只交由movie模块来实现。该接口包含一个启动MovieActivity的方法,代码如下:

package com.cooloongwu.movieapi;

import android.content.Context;

public interface IMovieRouter {
    void startMovieActivity(Context context);
}

3.2、依赖movieApi模块

3.2.1、movie模块依赖

movie模块依赖movieApi模块后,需要实现IMovieRouter接口,代码如下:

package com.cooloongwu.movie;

import android.content.Context;

import com.cooloongwu.movieapi.IMovieRouter;

import io.github.prototypez.appjoint.core.ServiceProvider;

@ServiceProvider
public class MovieRouterImpl implements IMovieRouter {
    @Override
    public void startMovieActivity(Context context) {
		Intent intent = new Intent(context, MovieActivity.class);
        context.startActivity(intent);
    }
}

注意: MovieRouterImpl 类需要使用 ServiceProvider 注解!
注意: MovieRouterImpl 类需要使用 ServiceProvider 注解!
注意: MovieRouterImpl 类需要使用 ServiceProvider 注解!

3.2.2、food模块依赖

food模块依赖movieApi模块后,直接可以在FoodActivity中调用IMovieRouter 中的方法了。代码如下:

        IMovieRouter movieRouter = AppJoint.service(IMovieRouter.class);
        movieRouter.startMovieActivity(this);

3.3、其他流程

因为app模块直接依赖了food和movie模块,所以在app模块中可以直接启动food模块中的FoodActivity了,但是正常开发中不建议在app模块中做过多的操作,app只作为一个壳即可。然后在FoodActivity中可以通过AppJoint提供的**AppJoint.service()**来调用其他模块的方法。

四、单个模块运行的问题

以上所有步骤当你运行app模块的时候是没有问题的,但是当你单独运行food模块的时候,此时问题就来了,因为我们在food模块中调用了movie模块中的方法。而单独food运行的时候,movie模块是完全不参与的,所以就没了IMovieRouter 的实现,当我们调用AppJoint.service()就会获取到null,这时也会产生各种问题。

怎么解决呢?

还记得我们第一篇文章的壳模块么,我们可以在food模块的壳模块(比如我们新建可模块叫runfood模块)中做些操作,因为这个模块在正式打包的时候是不会参与打包的,所以我们可以随意在其中添加代码。既然没有IMovieRouter 的实现,那我们就在可模块中去实现,当然正常逻辑是走不通的,我们就在实现里面进行打印或者进行Toast操作吧。

4.1、food模块中的依赖修改

在原来的food模块中,可能我们直接依赖movieapi的方法是使用的implementation 。

dependencies {
	...
	implementation project(path: ':movieapi')
}

这样当runfood模块依赖food模块的时候,runfood模块就会访问不到movieapi模块,也就是无法访问到IMovieRouter 接口,此时我们需要把 implementation修改为api,这样将movieapi模块也暴漏给runfood模块。

4.2、runfood模块中实现IMovieRouter

我们给runfood模块添加IMovieRouter 的Mock类,只打印日志:

package com.cooloongwu.foodrun;

import android.content.Context;
import android.util.Log;

import com.cooloongwu.movieapi.IMovieRouter;

public class MovieRouterMockImpl implements IMovieRouter {
    @Override
    public void startMovieActivity(Context context) {
        Log.e("MovieRouterMockImpl", "startMovieActivity");
    }
}

4.3、调用IMovieRouter

其实在使用IMovieRouter的等其他暴漏的接口的时候可以直接将这些接口实例存放在类(例如Router类,演示用)中,代码如下:

package com.cooloongwu.food;

import com.cooloongwu.movieapi.IMovieRouter;

import io.github.prototypez.appjoint.AppJoint;

public class Router {
    public static IMovieRouter movieRouter = AppJoint.service(IMovieRouter.class);
}

使用的时候直接使用 Router.movieRouter.startMovieActivity(this);

这样的话当然也可以对其进行统一赋值了,也就是我们可以在runfood模块中添加Application,然后在Application的onCreate()方法中对Router.movieRouter进行赋值,如下:

Router.movieRouter = new MovieRouterMockImpl();

这样,当我们在单独运行的时候就不会出现AppJoint.service()获取的是null的问题了,而是我们Mock的这么一个实现。

你可能感兴趣的:(Android-组件化)