因为flutter和原声Android启动流程不一样所以如果使用android热修复框架比如tinker会有一些区别,所以先要弄明白flutter在android端的启动流程,然后我们才可以进行针对性的处理和热修复。
flutter在android端启动流程
首先flutter程序的入口和android一样都是application类,这个在android代码中的manifast文件中可以找到,默认都是flutterappllication,因为flutter在android端运行其实也就是和原生用了不一样的引擎,大概的架构思路都是一样的,默认的flutterappllication也比较简单,其中在oncreat中有比较重要的一行代码
FlutterInjector.instance().flutterLoader().startInitialization(this);
这行代码是调用了flutterLoader的startInitialization(this)方法来完成对flutter的初始化,这是flutterLoader的官方介绍
Finds Flutter resources in an application APK and also loads Flutter's native library.
说明这个类就是为了找到apk中的各种资源文件,而且会加载flutter的本地库,相当于对配置了flutter在android本地的环境和资源。
接下来我们继续看startInitialization(this)方法,这里截取一些重要的代码并进行说明
/*首先还是来看方法的解读,这个方法主要是加载flutter引擎来支持本地的JNI响应,
找到和解读我们写在app这个APK里面的dart代码,而且这个方法多次调用是没有效果的
关于这点,因为flutterLoader本身被设计成一个单例,其里面有一个settings变量,会在这个方法中
被赋值,下次来如果已经初始化来就不会执行方法
*/
* Starts initialization of the native system.
*
* This loads the Flutter engine's native library to enable subsequent JNI calls. This also
* starts locating and unpacking Dart resources packaged in the app's APK.
*
*
Calling this method multiple times has no effect.
//首先会判断程序是否运行在主线程,这里用android 的Looper是否是MainLooper()来判断。
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("startInitialization must be called on the main thread");
}
//其余的主要逻辑都是执行了一个callable的回调,在后台线程中执行一些初始化的逻辑
Callable initTask =new Callable()
//这里选取了一些主要逻辑
if (FlutterInjector.instance().shouldLoadNative()) {
//加载flutter核心库
System.loadLibrary("flutter");
}
// Prefetch the default font manager as soon as possible on a background thread.
// It helps to reduce time cost of engine setup that blocks the platform thread.
//大概就是对引擎的预先加载,防止引擎启动的时候太耗费时间
Executors.newSingleThreadExecutor()
.execute(
new Runnable() {
@Override
public void run() {
FlutterJNI.nativePrefetchDefaultFontManager();
}
});
这些就是startInitialization(this)主要做的事情,此时application初始化完毕,接下来就会执行我们指定的mainactivity了,flutter默认的mainactivity是继承自FlutterActivity,而打开flutteractivity我们不难发现,它其实本身也没有很多逻辑代码,主要主要是执行了FlutterActivityAndFragmentDelegate这个代理类的方法,在每一个activity的生命周期都是调用了代理的方法,比如他的oncreate如下。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
//获取代理类
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
//调用代理类的方法执行oncreate逻辑
delegate.onActivityCreated(savedInstanceState);
//activity基础操作
configureWindowForTransparency();
setContentView(createFlutterView());
configureStatusBarForFullscreenFlutterExperience();
}
所以我们要跟进其代理类FlutterActivityAndFragmentDelegate
//这里我们主要看和初始化有关的代码,在这个代理类中开始如果发现flutterengine没有初始化,
//就会调用setupFlutterengine方法,其中主要初始化逻辑为
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
isFlutterEngineFromHost = false;
接着我们跟进flutterengine的初始化,可以看到,初始化的核心最后都归到了这里,在引擎初始化的时候进行,抽取出初始化执行的核心代码
if (flutterLoader == null) {
flutterLoader = FlutterInjector.instance().flutterLoader();
}
flutterLoader.startInitialization(context.getApplicationContext());
flutterLoader.ensureInitializationComplete(context, dartVmArgs);
可以发现在engine其实是调用我们之前说到的 flutterLoader的start和ensure方法来初始化和加载APK资源,我们接着看之前没有看的ensureInitializationComplete方法
//开头会判断另一个参数initialized,保证不会重复调用
if (initialized) {
return;
}
//这个shellArgs 是整个方法的核心,这里的初始化主要就是新建一个shell壳,
//然后将之前初始化和获取的各种flutter资源添加到壳中,最后将壳中的数据
//传入FlutterJNI进行加载,我们看一些重要的资源
List shellArgs = new ArrayList<>();
//这里的DEFAULT_LIBRARY,就是"libflutter.so"
shellArgs.add(
"--icu-native-lib-path="
+ flutterApplicationInfo.nativeLibraryDir
+ File.separator
+ DEFAULT_LIBRARY);
//这里的flutterApplicationInfo.aotSharedLibraryName就是我们写的dart代码
//打包成的libapp.so
shellArgs.add(
"--" + AOT_SHARED_LIBRARY_NAME + "=" + flutterApplicationInfo.aotSharedLibraryName);
//还有一些其他的资源和文件,当全部加入壳中后,就会初始化 FlutterJNI,并将initialized 置为true
//
if (FlutterInjector.instance().shouldLoadNative()) {
FlutterJNI.nativeInit(
applicationContext,
shellArgs.toArray(new String[0]),
kernelPath,
result.appStoragePath,
result.engineCachesPath,
initTimeMillis);
}
initialized = true;
程序执行到这里,基本已经完成了大部分资源和代码的初始化工作,接下来就是FlutterView等搭建flutter渲染的环境以及形成平台通道,将平台设置信息等传回给dart,以及设置android一些生命周期的监听和操作等,这里就不细细研究了。
flutter如何实现热修复
这里就可以开始对我们android端的flutter项目进行改动,让热修复可以在flutter上面实现。
因为我们平时开发主要代码都是写在libapp.so中,所以我们要找到和这个so资源有关的方法,已经思考如何进行改动。
跟随初始化的流程,我们知道我们的代码是在ensureInitializationComplete中进行加载,同时其地址是存储在flutterApplicationInfo中,所以我们首先就会想到对flutterApplicationInfo进行更改来实现加载修复后的so库,但是因为这个类是开始就配置好的,所以我们要利用反射获取,然后动态的更改其中储存资源地址,将libapp地址改成修复后的libapp的地址,而且要保证这个更改是在ensureInitializationComplete调用之前完成。
解决了资源地址问题我们就要考虑如何将自己的反射方法加入到ensure之前,通过查找源码,可以在flutterActivity(不是代理)中找到一个方法
@Nullable
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
// No-op. Hook for subclasses.
return null;
}
这个方法其实继承自FlutterActivityAndFragmentDelegate.Host,因为flutterActivity本身也继承自这个Host类,Host类本身是一个接口类,其中实现了一下可以对代理类进行管理的方法
/**
* Returns the {@link FlutterEngine} that should be rendered to a {@link FlutterView}.
*
* If {@code null} is returned, a new {@link FlutterEngine} will be created automatically.
*/
@Nullable
FlutterEngine provideFlutterEngine(@NonNull Context context);
看到host里面这个方法的说明我们可以知道这个方法可以为代理类提供flutterengine,如果不实现它的话FlutterEngine会被自动创建。所以我们的目的就可以通过这个方法来自己创建FlutterEngine,利用startInitialization和ensureInitialization方法不会被多次调用,在创建FlutterEngine之前调用这两个方法,在中间加入我们的反射方法来修改libapp的路径就可以了。
public FlutterEngine provideFlutterEngine(@NonNull Context context){
FlutterInjector.instance().flutterLoader().startInitialization(getApplication());
//这里加上我们自己的反射方法
hotfixPath();
FlutterInjector.instance().flutterLoader().ensureInitializationComplete(getApplication(), getFlutterShellArgs().toArray());
//返回flutterEngine,跳过其自动创建。
return new FlutterEngine(context, getFlutterShellArgs().toArray(),
false, shouldRestoreAndSaveState());
}
总结
整个流程就到这里了,具体的实现要取决于项目的细节和实现,也可以用一些其他的android热修复框架,只要理解flutter加载的原理,对其中的细节进行调整,也可以实现热修复。