你曾遇到的某大厂奇葩问题:Android组件化开发,组件间的Activity页面跳转

/**

  • 安装用户组件对外暴露的接口

*/

public interface IUserInstallService {

// 跳转到安装用户页面,其中extra是要传递的数据

void launch(Context context, String extra);

}

然后在A组件里,实现此接口(当然A组件的gradle文件得添加对common组件的依赖):

public class UserInstallService implements IUserInstallService {

@Override

public void launch(Context context, String extra) {

Intent intent = new Intent(context, UserInstallActivity.class);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

intent.putExtra(“extra_data”, extra); // 传递数据

context.startActivity(intent);

}

}

这样,跳转方法就已经暴露出来可以供其他组件调用了,那在B组件里,我们怎么调用呢?得想办法拿到UserInstallService 的实例,然后就能调用它的launch方法来跳转了。

因为common组件还要管理其它组件的跳转,所以这些跳转得统一管理起来:

2.对组件间的跳转进行统一管理

我们在common组件里写一个工厂类,用于分配这些跳转:

public class ServiceFactory {

private static ServiceFactory instance;//单例模式

private ServiceFactory() {

}

public static ServiceFactory getInstance() {

if (instance == null) {

synchronized (ServiceFactory.class) {

if (instance == null)

instance = new ServiceFactory();

}

}

return instance;

}

// 安装用户组件的跳转服务的注册和获取

private IUserInstallService mIUserInstallService;

public IUserInstallService getIUserInstallService() {

return mIUserInstallService;

}

public void setIUserInstallService(IUserInstallService mIUserInstallService) {

this.mIUserInstallService = mIUserInstallService;

}

// 其他组件的跳转服务的注册和获取,与A组件的一样

}

那么很显然,在B组件里,我们就要想办法通过common组件的ServiceFactory 的getLoginService()方法来获取A组件的UserInstallService 的实例。要get,就首先要set,否则拿到的是一个null对象。那么这个set应该放在哪里实现呢?

3.利用Java反射将跳转服务进行实例化

set UserInstallService 对象的操作肯定得放在跳转之前,即get之前。最好在B组件初始化的时候就完成set,而且这个set 操作得放在A组件里,要不然又产生依赖了。

有了这个思路,就可以实现如下:

在common组件里,再增加一个接口:

public interface IAppComponent {

public void initialize(Application app);

}

这个接口用来表示各个组件的初始化。各个组件都要重写自己的Application,实现这个接口。比如A组件重写的Application类如下:

public class OrgApp extends Application implements IAppComponent{

@Override

public void onCreate() {

super.onCreate();

initialize(this);

}

@Override

public void initialize(Application app) {

ServiceFactory.getInstance().setIUserInstallService(new UserInstallService());

}

}

如果想让A组件的用于组件单独运行时,需要在A组件的AndroidManifest.xml里,指定这个类为组件的application:(注:作为module运行时记得删除android:name=“.OrgApp”,不然程序会报错。)

<application

android:name=“.OrgApp”

android:allowBackup=“true”

android:label=“@string/app_name”

android:theme=“@style/AppTheme”>

但是别忘了,当组件单独运行时,Application类的onCreate()可以走到;而A组件作为module集成之后,Application类是不会被加载的。

怎么办呢?因为app壳组件的Application是肯定会被加载的,所以可以在这里,用反射来加载其他组件的Application类。这也是为什么要在common组件里新建IAppComponent 接口类的原因。app壳组件的Application类也要重写并在manifest里指定。在app壳组件的Application初始化时,可以对其他的组件进行挨个加载,这样,上面我们想要的set 操作就可以在这里完成了。

为了管理要加载的组件,我们在common组件里新建一个AppConfig类,如下;

public class AppConfig {

public static String[] COMPONENTS = {

“mod.activity.com.orginfo.application.OrgApp”,//这个是A组件的Application类

// 还有其他的组件的Application类的全名,也都放这里

“mod.activity.com.login.application.LoginApp”

};

}

上面这个AppConfig类用来记录所有的组件的Application类的全名。

下面是app壳组件的新Application类:

public class AppApplication extends Application implements IAppComponent {

@Override

public void onCreate() {

super.onCreate();

initialize(this);

}

@Override

public void initialize(Application app) {

// 遍历所有的组件的Application类,依次用反射的方式实例化

for (String component : AppConfig.COMPONENTS) {

try {

Class<?> clazz = Class.forName(component);

Object object = clazz.newInstance();

// 实例化后,调用各个组件的 set 方法

if (object instanceof IAppComponent) {

((IAppComponent) object).initialize(app);

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

经过这样的处理,上面第二步最后“该在哪里set 跳转服务”的问题就解决了。总结本篇实现的组件间跳转原理如下:

在应用启动的时候,它的APP壳组件的Application类会进行初始化,在这里我们通过反射的方式初始化了其他各组件的Application类,而各组件的Application类在初始化时,又会通过set操作把自己的跳转服务注册到common组件。这样的话,通过common组件get对应的服务,即可实现跳转。

我们来试一下,在B组件里,跳转到A组件,就可以这么写了;

// 安装用户

ServiceFactory.getInstance().getIUserInstallService().launch(mContext, “”);

这样B组件就不依赖A组件也可以进行跳转,实现了我们的期望。

还有一点,要是A组件没有被集成到app里,那么ServiceFactory.getInstance().getIUserInstallService()就是null,会报空指针异常。我们需要把getIUserInstallService()补充下:

public IUserInstallService getIUserInstallService() {

if (mIUserInstallService == null)

mIUserInstallService = new EmptyUserInstallService();

return mIUserInstallService;

}

这里EmptyUserInstallService是对IUserInstallService 接口的一个空实现,目的只是为了避免这个空指针异常,就不贴代码了。

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-KDdPlG5u-1715004943151)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值