Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android的IoC(六)—— Hilt注入原理深入解析 #42

Open
soapgu opened this issue May 18, 2021 · 0 comments
Open

Android的IoC(六)—— Hilt注入原理深入解析 #42

soapgu opened this issue May 18, 2021 · 0 comments
Labels
IoC New feature or request 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented May 18, 2021

  • 前言

在完成了初步的学习和一些验证工作及可行性实验代码以后。正式开始在项目中尝试使用Hilt,以实践带动学习。最近在开发过程中遇到了些问题,在解决过程中对Hilt的注入原理又有了更深一点的理解。

首先我在Application中的代码是这样的

@HiltAndroidApp
public class App extends Application {

    @Inject
    ILauncherManager launcherManager;

    @Override
    public void onCreate() {
        FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(true)
                .tag("XXXXXX")
                .build();
        Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));
        launcherManager.lauch();
        super.onCreate();
    }
}

launcherManager通过外部注入获取,结果运行直接出错,launcherManager为null。注入失败了?
有一些奇怪,接下来尝试到MainActivity

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

    @Inject
    ShellFragment shellFragment;
    @Inject
    ILauncherManager launcherManager;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        launcherManager.lauch();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setFragment(shellFragment);
    }

    private void setFragment(Fragment fragment) {
        getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true)
                .replace(R.id.container, fragment, null)
                .commit();
    }
}

注入成功了。奇怪了,都是EntryPoint。话说总入口还比Activity的EntryPoint要高级啊,没道理啊

终于一个尝试改动修复了HiltAndroidApp注入失败的问题。(我承认运气一直不错。偶然的改动往往能撞到真相,算是瞎猫碰死耗子吧)

改动如下

@HiltAndroidApp
public class App extends Application {

    @Inject
    ILauncherManager launcherManager;

    @Override
    public void onCreate() {
        super.onCreate();
        FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(true)
                .tag("XXXXXX")
                .build();
        Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));
        launcherManager.lauch();
    }
}

就是说只要在super.onCreate();之后再去调用launcherManager就行,这时候launcherManager已经被注入了。

  • 打破沙锅问到底

  1. 显然是 super.onCreate();的时候launcherManager被执行了注入操作。但是Application类作为基类,也翻不到执行相关代码的地方啊。难道执行了什么魔法?

  2. 进一步探索,通过AS的Find Usage的菜单,翻到了Hilt自动生成的App_MembersInjector类,这里自动执行了laucherManager注入
    代码片段(带删减)

@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class App_MembersInjector implements MembersInjector<App> {
  @InjectedFieldSignature("com.space365.smartboard.App.laucherManager")
  public static void injectLaucherManager(App instance, ILauncherManager laucherManager) {
    instance.launcherManager = laucherManager;
  }
}
  1. 对injectLaucherManager进一步追踪追到Hilt_App这里生成类里面
    在onCreate方法中实现了App类型的字段注入
    代码片段
/**
 * A generated base class to be extended by the @dagger.hilt.android.HiltAndroidApp annotated class. If using the Gradle plugin, this is swapped as the base class via bytecode transformation.
 */
abstract class Hilt_App extends Application implements GeneratedComponentManagerHolder {
  private final ApplicationComponentManager componentManager = new ApplicationComponentManager(new ComponentSupplier() {
    @Override
    public Object get() {
      return DaggerApp_HiltComponents_SingletonC.builder()
          .applicationContextModule(new ApplicationContextModule(Hilt_App.this))
          .build();
    }
  });

  @Override
  public final ApplicationComponentManager componentManager() {
    return componentManager;
  }

  @Override
  public final Object generatedComponent() {
    return this.componentManager().generatedComponent();
  }

  @CallSuper
  @Override
  public void onCreate() {
    // This is a known unsafe cast, but is safe in the only correct use case:
    // App extends Hilt_App
    ((App_GeneratedInjector) generatedComponent()).injectApp(UnsafeCasts.<App>unsafeCast(this));
    super.onCreate();
  }
}

已经很接近真相了,通过debug来验证下

  1. 在injectApp这行代码加上断点。
    在断点命中后,查看堆栈,发现是在App.java的23line调用的就是super.onCreate();
    很神奇吧,就是说Hilt_App是App的真父类,而Application其实是爷爷
    狸猫换太子,Hilt_App就是玩了一把适配器模式,让大家都毫无知觉

  2. 幕后的App_HiltComponents
    通过前面的Hilt_App的代码我们可以看到componentManager是加载了App_HiltComponents的工厂类型。我们在Hilt的幕后组件原理藏在这里。所以说Hilt是Dragger的Plus版本

  3. 一网打尽
    图片

  • Hilt_MainActivity
    就是MainActivity的影子父类
    Hilt_MainActivity会通过Application找到activityComponent这层的组件完成注入工作。
Hilt_MainActivity() {
    super();
    _initHiltInternal();
  }

  Hilt_MainActivity(int contentLayoutId) {
    super(contentLayoutId);
    _initHiltInternal();
  }

  private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
      @Override
      public void onContextAvailable(Context context) {
        inject();
      }
    });
  }

可以看到是在onContextAvailable的回调中执行了注入操作

  • MainActivity_MembersInjector
    MainActivity具体的注入方法

  • 总结

  • @HiltAndroidApp和@androidentrypoint都有影子父类

  • 影子父类通过适配模式存在

  • 影子父类会加载componentManager,component就是Dragger的Component

  • Component根据安卓的组件分成很多层

  • 影子父类会执行对象注入的具体工作

  • 注入时机非常巧妙,其中Application是在执行onCreate时执行,而Activity是在ContextAvailable以后。反正绝对不是构造函数,大家在使用中一定要注意。

老规矩,以后补图,完

@soapgu soapgu added IoC New feature or request 安卓 安卓 labels May 18, 2021
@soapgu soapgu changed the title Android的IOC(六)—— Hilt注入原理深入解析 Android的IoC(六)—— Hilt注入原理深入解析 Jul 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
IoC New feature or request 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant