Flutter动态化(基于Android)
动态化方式
DSL语言动态化,服务端动态下发dom(暂不分析)
动态替换打包后的产物
动态化效果
修改前
修改后:
flutter打包后结构
源码结构
打包后结构
.
├── AndroidManifest.xml
├── assets
│ └── flutter_assets
│ ├── AssetManifest.json
│ ├── assets
│ │ └── images
│ │ ├── test.jpeg
│ │ └── test_test.png
│ ├── FontManifest.json
│ ├── fonts
│ │ └── MaterialIcons-Regular.ttf
│ ├── NOTICES
│ └── packages
│ └── cupertino_icons
│ └── assets
│ └── CupertinoIcons.ttf
├── classes.dex
├── lib
│ ├── arm64-v8a
│ │ ├── libapp100.so
│ │ └── libflutter.so
│ ├── armeabi-v7a
│ │ ├── libapp.so
│ │ └── libflutter.so
│ └── x86_64
│ ├── libapp.so
│ └── libflutter.so
├── res
│ ├── drawable
│ │ ├── launch_background.xml
│ │ ├── notification_bg_low.xml
│ │ ├── notification_bg.xml
│ │ ├── notification_icon_background.xml
│ │ ├── notification_tile_bg.xml
│ │ └── toast_bg.xml
│ ├── mipmap-hdpi-v4
│ │ └── ic_launcher.png
│ ├── mipmap-mdpi-v4
│ │ └── ic_launcher.png
│ ├── mipmap-xhdpi-v4
│ │ └── ic_launcher.png
│ ├── mipmap-xxhdpi-v4
│ │ └── ic_launcher.png
│ └── mipmap-xxxhdpi-v4
│ └── ic_launcher.png
└── resources.arsc
flutter加载原理
FlutterActivity 壳中创建delegate 分发;
在onCreate中创建引擎,引擎中初始化flutterLoader;注册插件,插件是通过反射方式注册
FlutterLoader 配置加载打包dart后的产物;
FlutterJNI 根据路径加载对应的so;
FlutterLoader 启动初始化方法;调用FlutterJNI 加载flutter release中的so(debug版本加载assets下的产物,分为JIT模式和release模式)
FlutterActivity oncreate方法中 配置window背景,
FlutterActivity oncreate方法中 创建flutterview,设置splashview,执行setContentView
动态化方案
- 修改FlutterLoader 对象,使其指向自定义的FlutterLoader对象
如何修改:
在Application 中通过反射修改对象指针
public class BaseApplication extends FlutterApplication {
public static Application instance;
@Override
public void onCreate() {
instance=this;
try {
//替换成自定义的Flutter加载类 干预Flutter的加载
FlutterLoader flutterLoader = new CustomFlutterLoader();
Field field = FlutterLoader.class.getDeclaredField("instance");
field.setAccessible(true);//
field.set(null, flutterLoader);
} catch (Exception e) {
e.printStackTrace();
}
super.onCreate();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
instance=this;
}
}
- CustomFlutterLoader修改
public void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
……
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY);
if (args != null) {
Collections.addAll(shellArgs, args);
}
loadNewFlutterSo(applicationContext);
String kernelPath = null;
…………
String appStoragePath = PathUtils.getFilesDir(applicationContext);
String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
FlutterJNI.nativeInit(applicationContext, shellArgs.toArray(new String[0]),
kernelPath, appStoragePath, engineCachesPath,System.currentTimeMillis());
initialized = true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
- 加载so路径
private String[] loadNewFlutterSo(Context context) throws IOException {
String[] result=null;
final File dir = context.getDir("jniLibs", Context.MODE_PRIVATE);
List<File> files = FileUtils.listFilesInDirWithFilter(dir, pathname -> pathname.getName().startsWith(FileUtils.TIMESTAMP_PREFIX), (o1, o2) -> {
if (o1.isDirectory() && o2.isFile())
return -1;
if (o1.isFile() && o2.isDirectory())
return 1;
return o2.getName().compareTo(o1.getName());
});
if (!files.isEmpty()) {
//获取最新的Flutter so
File file = files.get(0);
FlutterSoEntity entity = DynamicUpdateExtractor.readSoInfo(file);
String flutterSoPath = FileUtils.generateSoFileName(entity);
File soFile = new File(dir,flutterSoPath);
if (soFile.exists() && Utils.getFileMD5(soFile).equals(entity.fileMd5)) {
result=new String[2];
result[0]=soFile.getAbsolutePath();
result[1]=soFile.getName();
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
DynamicUpdateExtractor.deleteOtherFlutterSoFile(dir.getAbsolutePath(),entity);
}
});
}
}
return result;
}
动态化缺陷
本地图片暂无法替换
替换so后需要重启
Q&A
Android native动态化方案
ReactNative动态化方案
参考源码:
https://github.com/heshiqi/flutter_dynamic_update