}
// 检查是否需要拷贝main.zip包。
MXBundleZipManager bundleManager = MXBundleZipManager(jsAppPath: jsAppPath);
MXBundleZipCheckResult result = await bundleManager.checkNeedCopyMainZip();
if (!result.success) {
MXJSLog.log(
‘MXJSFlutter.runJSApp: checkAppBundleZip failed: ${result.errorMessage}’);
// 引擎初始化的success回调
MXJSFlutter.getInstance().jsEngineInitCompletionHandler(
{‘success’: result.success, ‘errorMessage’: result.errorMessage});
return;
}
// 调试状态下,debugJSBundlePath不为空,则运行此目录下的js文件。
String realJSAppPath = jsAppPath;
if (debugJSBundlePath != null &&
debugJSBundlePath.isNotEmpty &&
await canDefineDebugJSBundlePath()) {
realJSAppPath = debugJSBundlePath;
}
_localJSAppPath = realJSAppPath;
// 加载main.js。
_callNativeRunJSApp(
jsAppPath: realJSAppPath, jsExceptionHandler: jsExceptionHandler);
}
/// 默认的 JSBundle 的热更新目录,用于放置下载的JS Bundle文件
static Future defaultJSAppUpdatePath() async {
// 如果业务没有指定目录,则使用默认目录
return await Utils.findLocalPath() +
Platform.pathSeparator +
mxJSAPPDefaultAssetsKey;
}
/// 是否允许定义debugJSBundlePath
static Future canDefineDebugJSBundlePath() async {
// 目前只支持场景:1)调试环境的iOS模拟器
if (kDebugMode && Platform.isIOS) {
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
IosDeviceInfo deviceData = await deviceInfoPlugin.iosInfo;
return !deviceData.isPhysicalDevice;
} else {
return false;
}
}
static _callNativeRunJSApp(
{String jsAppPath = “”, MXJSExceptionHandler jsExceptionHandler}) {
Map<String, dynamic> args = {“jsAppPath”: jsAppPath};
// 设置JS Exception Handler。
MXPlatformChannel.getInstance().setJSExceptionHandler((arguments) {
// 如果是main.js的错误,arguments[‘jsFileType’] 为 0 则执行js引擎的success回调。
if (arguments is Map && arguments[‘jsFileType’] == 0) {
MXJSFlutter.getInstance().jsEngineInitCompletionHandler(
{‘success’: false, ‘errorMessage’: arguments[‘errorMsg’]});
}
// 回调给业务侧。
if (jsExceptionHandler != null) {
jsExceptionHandler(arguments);
}
});
// 初始化MXFFICallbackManager。
MXFFICallbackManager.getInstance();
args[“flutterAppEnvironmentInfo”] = flutterAppEnvironmentInfo;
MXPlatformChannel.getInstance().invokeMethod(“callNativeRunJSApp”, args);
}
///从Flutter Push一个 JS写的页面
///@param widgetName: “widgetName”,在main.js MyApp::createJSWidgetWithName 函数中使用
///MyApp::createJSWidgetWithName 通过 widgetName 来创建对应的JSWidget
/// 通常你应该使用更高层的API MXJSPageWidget 包装类来显示JS Widget 请参考 MXJSPageWidget 的用法
dynamic navigatorPushWithName(
String widgetName, Key widgetKey, Map flutterPushParams,
{String bizPath});
/// 设置处理器,当JS页面加载时,定制Loading widget。
void setJSWidgetLoadingHandler(MXWidgetBuildHandler handler);
/// 设置处理器,当JS页面加载错误时,定制Error widget。
void setJSWidgetBuildErrorHandler(MXWidgetBuildHandler handler);
/// JS引擎初始化结束回调。
void jsEngineInitCompletionHandler(dynamic arguments);
/// JS引擎是否已初始化。
bool isJSEngineInit();
/// 设置JS引擎已初始化。
void setJSEngineInit();
/// JS引擎初始化结果。
Map<String, dynamic> jsEngineInitResult();
/// 重新创建MXJSFlutter,包括通道,属性。
void resetup();
/// 当前flutterApp。
MXJSFlutterApp get currentApp;
}
Kraken
Kraken是阿里开源的一款基于W3C标准的高性能渲染引擎。也是目前几个大厂框架内维护力度最高的库,详见GitHub。Kraken的优势在于其能够基于W3C进行开发,而且引入npm生态,支持使用Vue、React框架开发,一般前端人员都能进行开发,学习成本很低。
使用介绍
pubspec引入,然后直接使用Widget Kraken传入脚本的url就可以了。
kraken: ^0.9.0
Widget build(BuildContext context) {
// 我们只需要维护js脚本就可以了
Kraken kraken = Kraken(
bundleURL:
‘http://kraken.oss-cn-hangzhou.aliyuncs.com/demo/guide-styles.js’);
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(40),
child: AppBar(
centerTitle: true,
title: new Text(
‘商品详情’,
style: Theme.of(context).textTheme.headline6,
),
),
),
body: kraken,
);
}
可以看到,重点在于我们如何去维护带有动态运营内容的js文件,这是Kraken相对于其他框架最有竞争力的点,官方api写的非常详细,基于W3C标准,能够使用Rax、Vue、React这些主流框架进行开发。
/// Vue代码
demo
Kraken的缺点是不支持css样式,使Vue开发的体验也相对一般。但总体而言已经很不错了,官方维护力度大,满足前端生态,使用方便,是动态化技术很不错的选择。
Webview增强优化
几乎所有的移动应用中,都会用到webview来作为h5的容器。通过运营平台配置生成h5,app直接显示即可。但很遗憾,webview的体验性、稳定性/兼容性有很多的问题。
体验上加载过程白屏,加载中、出错状态没法定义等;兼容性上iOS还好,浏览器内核都是WKWebView,但是Android的设备多种多样,浏览器内核也参差不齐,所以在兼容性上经常存在问题。 为了解决以上问题,我们基于官方插件webview_flutter,做了以下方案:
-
体验上修改webview插件为可配置透明背景,去除加载条;Flutter层开发webview的增强容器,实现可定义加载中、加载失败的视图,达到基本符合app的加载效果
-
稳定性上,我们采取统一植入X5内核的方法
为何采取X5内核?
目前开源的浏览器内核sdk不多,主要有以下几个:ChromeView、Crosswalk、TbsX5。
-
基于Chromium内核的开源ChromeView 目前基本没有维护,另一个问题是编译出来的动态库太大,ARM-29M,x86-38M,这无疑对app体积来说是个大难题。因此放弃采用基于Chromium的ChromeView。
-
Crosswalk同样是基于Chromium内核,同样存在上述app体积问题,因此也放弃。
-
TbsX5 基于谷歌Blink内核,生态在国内是很成熟的,只要装有微信的手机,都支持X5。X5 提供两种集成方案:
-
只共享微信手Q空间的x5内核(for share)
-
独立下载x5内核(with download)
优化体验
- 修改webview_flutter为可配置透明色背景,具体做法请查看我上一篇文章 # 我该如何给Flutter webview添加透明背景?
最终业务层代码:
WebView(
initialUrl: ‘https://www.baidu.com’,
transparentBackground: true
)
- 构建webview容器。webview背景处理为透明后,通过Stack布局,以及监听
onProgress
回调,赋予webview容器加载中、加载失败的效果,让用户的体验达到与原生应用类似。
/// 我们用的是flutter_bloc进行状态管理
Stack(
alignment: Alignment.center,
children: [
WebView(
transparentBackground: widget.transparentBackground,
onProgress: (int progress) {
if (progress >= 100) {
context.read().loadSuccess(progress);
}
},
onWebResourceError: (error) {
context.read().loadError();
},
),
if (state is WebViewLoading)
Center(
child: widget.loadingView ?? const LoadingView(),
),
],
)
再看看bloc层,非常简单的状态切换。
/// Cubit
class WebViewContainerCubit extends Cubit {
WebViewContainerCubit() : super(WebViewLoading());
loadSuccess(int progress) {
if (state != WebViewLoadSuccess()) {
emit(WebViewLoadSuccess());
}
}
loadError() {
emit(WebViewLoadError());
}
}
/// State
abstract class WebViewContainerState {}
class WebViewLoading extends WebViewContainerState {}
class WebViewLoadSuccess extends WebViewContainerState {}
class WebViewLoadError extends WebViewContainerState {}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
新的开始
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
《系列学习视频》
《系列学习文档》
《我的大厂面试之旅》
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-0135cbpO-1712720145343)]
新的开始
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
《系列学习视频》
[外链图片转存中…(img-HO0YhqFv-1712720145344)]
《系列学习文档》
[外链图片转存中…(img-keR6Eo23-1712720145344)]
《我的大厂面试之旅》
[外链图片转存中…(img-fziLPTQz-1712720145345)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-ONI12Pp0-1712720145345)]