Flutter 静态挂载腾讯X5WebView(Tbs)浏览器内核


背景

项目中(Flutter项目)有一个需求是通过webview加载界面,来显示实时视频(webrtc),那么问题是,不同Android手机浏览器对于webrtc视频解码的支持性有差异导致部分手机无法正确显示视频,部分手机只有声音没有视频。那么我决定通过端侧来解决这个问题(我猜测修改web端代码进行适配也能解决),由此引申出了这篇文章,留作参考记录。涉及到的主要知识点如下:

  1. Android 平台下替换浏览器内核方案。
  2. Android Native 静态集成腾讯X5(tbs)浏览器内核。(需要原生静态集成tbs浏览器内核的同学同样适用)
  3. Flutter plugin 插件开发。
  4. Flutter 打包机制。

一、寻找Android WebView 内核替换方案(在native层面验证)

1.CrossWalk

经过测试,最新版本的CrossWalk并不能解决问题,仍然存在视频无法解码的问题。集成方式不再多说,相关教程很多。要注意的是crosswalk webview 想要略过ssl验证,需要修改源码中的SslUtil文件,修改为如下,直接忽略掉ssl异常。

 public static boolean shouldDenyRequest(int error) {
        if (error >= -214 && error <= -200) {
            switch(error) {
            case -213:
            case -212:
            case -211:
            case -208:
            case -207:
            case -206:
            case -203:
            case -202:
            case -201:
            case -150:
            case -129:
                return false;
            default:
                return false;
            }
        } else {
            throw new AssertionError();
        }
    }

然后将这个java文件编译成".class"文件后,替换到crosswalk的aar包中。我在验证过程中也做了一些工作,把用到的资源分享一下,以便有同学用得到,下面的下载链接是编译成.class的SslUtil文件(其实这个是比较难搞的),将内核的aar sdk解压替换即可,可以到crosswalk官网自行下载32or64位的sdk。
下载链接
提取码:8cy5

2.腾讯X5浏览器

另外一个方案是腾讯的X5浏览器内核(https://x5.tencent.com/),经测试,可以解决我的问题,兼容性良好,有两种集成方式说明一下

1)动态集成

动态集成具体方式官网上&其他帖子有很多说明,自行查找。经过测试,动态集成在Native项目中,内核挂载还算相对稳定(也有反复挂载不成功的情况),但是已Flutter 插件(Flutter 插件为静态集成)的方式,集成到Flutter项目后,体验非常之差,内核挂载成功率很低。所以动态集成方案pass,无法使用。(说句题外话,虽然这里只写了一段,但是验证过程确实还是用了一些时间的)

2)静态集成

那么目前就明确了两点,要解决两个问题,首先是采用x5Webview替换内核方案,其次是需要静态集成。

参考文章 原生项目需要静态集成的同学对照这篇文章集成即可。

我这里补充一下我提取到到的64位内核资源(实际提取不难但我觉得麻烦,其实用32位的so文件足够),下载链接如下:
下载链接
提取码:fmy5

到此,这两个问题圆满解决。

二、Flutter 静态集成的X5WebView 的插件开发

明确了方案,并且tbs静态集成也ok,那么下一步就是要创建一个静态集成的X5WebView的Flutter插件给Flutter项目使用,搜索了一下目前只有x5webview动态集成的flutter插件,那么这里就需要自己去搞一个插件来用,这里我借鉴了pub上已有的动态继承tbs的库。
插件提供了两种使用方式,一种是直接从Flutter界面跳转到Native方式使用了Tbs浏览器的Activity,一种是直接使用实现了TbsWebView的Flutter Widget

1.StaticTBS Flutter Plugin(插件) 开发

1)创建Flutter Plugin

as中,点击File->New->New Flutter Project ,弹出选择项目类型界面,选择Flutter Plugin,点击Next,之后填写相应信息,最后finish,完成创建。创建的项目中内含一个example目录(是一个flutter 项目),这个是用来调试plugin功能的,可作为临时宿主使用。

2)编写Android Native代码

这点比较关键的,我想通过android studio 开发原生部分的代码,关键点是,先要构建一下生成的eample项目,cd 到 example目录,执行“flutter build apk ”即可,构建完成(是否成功不重要),之后就可以通过android studio 打开项目进行native开发,要注意的是,要打开example里面的android项目。

plugin project结构
将提取到的armeabi架构的tbs so库文件以及,静态加载用到的jar包导入项目。

3)编写Flutter插件代码

这里涉及到的代码以及知识点较多,主要是插件开发相关,后面我也许会单独写文章记录,就不在这单独讲解了,可以搜索Flutter Plugin插件开发相关文章,目前项目已上传到git,请自行查看
插件项目地址

三、集成插件项目的编译

使用静态tbs插件后,直接打包还是会发现内核挂载失败,核心问题就是我目前还没有提取到不同架构下的tbs so库,只提取到了armeabi架构的,受限于tbs挂载规则或者是flutter so加载机制,tbs的armeabi so库并不能向上兼容,放在armeabi-v7下不不好用。只能放到armeabi文件加下才可以保证加载成功,所以还需要关注一下下面的说明

1. 针对“Shrink”做的修改

当项目集成了静态内核,会发现还是无法挂载,是因为flutter的打包apk机制 ,进行了“shrink”操作,将“com.tencent.cmtt” 包下的“utils”文件夹给“优化”掉了,误伤友军,所以为了能正常挂载静态内核,那么需要对flutter gradle文件进行修改。

Flutter 本身gradle的文件路径 :
D:\你的flutter本地源码文件夹\flutter_windows_v1.9.1+hotfix.6-stable\flutter\packages\flutter_tools\gradle

打开这个路径下的“flutter.gradle” 文件进行修改,修改这个部分代码,将注释部分屏蔽掉,这样flutter工程在打包anroid项目的时候,就不会去shrink,对apk进行瘦身了,这里要注意,如果修改了flutter源码下的代码,后面想要通过“flutter upgrade”升级版本的时候会出错,需要先revert掉,才可以正常升级。

     private static Boolean shouldShrinkResources(Project project) {
     	//将这个函数屏蔽掉
        if (project.hasProperty("shrink")) {
           return project.property("shrink").toBoolean()
        }
         return false
     }

2.so库打包机制

接下来要讲一下到Flutter so 库打包机制了,这里会延伸出一个flutter常见问题“couldn’t find “libflutter.so””,特别是你通过命令行进行打包的时候更容易出现这个问题,原因稍后讲解。
当解压正常的Flutter apk包,我们可以看到lib下的armeabi,arm64-v8a,x86等文件夹下会有“libflutter.so”以及“libapp.so”这两个文件,可以这样理解,libapp.so他就是我们用flutter写的业务逻辑所打包的产物,libflutter.so实际上可以理解为libapp.so执行所必备的环境之一,正常运行的时候根据手机架构加载对应的so库,达到native层面优化的效果。
那么回到上面的问题上来,“couldn’t find "libflutter.so”导致崩溃,极大概率就是你使用了一些plugin,或者是引用了一些第三方库文件,打包时导致slibflutter.so库没有被打到指定的文件夹下,导致应用崩溃,如果是libapp.so没有被打进去,应用只是会白屏,因为找不到UI以及逻辑代码。
解决这个问题需要你理一下你项目中用到了哪些第三方so库,特别关注plugin,然后可以通过修改android目录下的ndk打包模式来进行调整。
修改的位置如下图:
Android gradle 配置修改
有两种情况
如果你只打算适配arm架构,那么就修改为只打包armeabi-v7即可,你需要保证所有的so库,都有这个架构的so文件。
这里有一个坑,flutter现在打包默认不支持将libflutter.so以及libapp.so打到armeabi下,如果你想向上兼容,只对armeabi做适配那是行不通,这里有个骚操作,那就是先生成armeabi-v7 的apk,解压后拿到这两个so库,手动复制到armeabi目录下,在进行只有armeabi架构的打包,就可以了,是有些麻烦,注意,目前如果使用这个插件,需要按照这个步骤操作。
其实也可以修改flutter gradle文件,让它可以将这两个so文件打包到armeabi目录下,不是很好搞。

如果你打算适配不同架构,核心就是要不把不同架构so库找全了,全部统一到对应so目录下,在进行打包。

如果还是有问题,有个排查技巧,拿到启动会崩溃的apk,丢到as里面,查看lib文件,可以看到哪些so文件出了问题,这样比较直观
不同架构so库分析
上图所示,armeabi有41MB,是因为我把tbs浏览器内核放到了armeabi文件夹下,打包的是全平台,所以其他三个下面没有这些so文件,所以如果运行在arm64,x86设备上,内核是会挂载失败的。

总结

总结一下心得:
首先这边博客还只是算是给出了解决思路,实际看起来有些杂乱,杂乱的原因是目前我这个方案肯定不是最优解,需要后续完善,这里做一下记录,如果能吸引到牛人来一起探讨那更好了,我下一步的思路就是继续尝试修改flutter gradle,让flutter支持打包armeabi架构的apk,因为时间不是很多,之前只是简单试过没有成功。如果哪位有思路欢迎交流。

近期的一些思考
对于技术,每一个点,往细了深究都会牵扯到好多东西,需要静下心来去研究,试错,尽可能的使点连成片。以这篇文章来讲,我还有好多地方做的不过好,需要后续改进。
对于项目,不同业务情况不同的分析方式,面对不同Bug也要有不同的解决思路,具体问题具体分析,不要局限于一小块,已万变应对不变。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要在Flutter中实现在`flutter_inappwebview`中更改应用程序栏标题,您需要使用`InAppWebViewController`的`shouldOverrideUrlLoading`方法。该方法在Web视图中的任何导航期间调用,在此期间您可以获取导航的URL并相应地更新应用程序栏标题。 下面是一个简单的例子: ``` import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; class MyInAppWebView extends StatefulWidget { @override _MyInAppWebViewState createState() => _MyInAppWebViewState(); } class _MyInAppWebViewState extends State<MyInAppWebView> { InAppWebViewController _webViewController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('My App'), ), body: InAppWebView( initialUrl: 'https://www.example.com', onWebViewCreated: (InAppWebViewController controller) { _webViewController = controller; }, shouldOverrideUrlLoading: (controller, navigationAction) async { var url = navigationAction.request.url; if (url.contains('example.com')) { setState(() { // 更新应用程序栏标题 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Loading ' + url), duration: Duration(seconds: 1), ), ); AppBar( title: Text('New Title'), ); }); return NavigationActionPolicy.ALLOW; } return NavigationActionPolicy.ALLOW; }, ), ); } } ``` 在上面的代码中,我们使用一个名为`shouldOverrideUrlLoading`的回调来捕获Web视图中的任何导航,并检查URL是否包含`example.com`。如果是,则我们更新应用程序栏标题。我们使用`setState`方法来更新标题,并使用`ScaffoldMessenger`来显示一个短暂的消息,以通知用户正在加载新页面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值