众所周知,flutter官方的webview存在这样那样的bug,一个个去维护又很累,还有一些是国内特有机型会产生的bug,特有输入法产生的bug。像这种不会产生crash的bug,在isssues里提了,多半就没有消息了。而且flutter官方也说webview不建议投入使用,键盘的坑也格外的多,无法复制粘贴之类的。对这一类做了一些适配,到最后百度输入法的一个bug切切实实的打败了我。也没有别的方案可以解决。
而且flutter webview的bug其实是来自于androidView,甚至可能是flutter engine的问题,国内的大公司类似咸鱼都是自己定制了flutter engine。所以,这块选择是用原生的webview来实现吧。
废话有点多,简单的介绍一下,我这里的业务场景,我现在的软件是提供webview给第三方,有点类似小程序的样子,然后也定制了一些功能。
实现类似小程序的样子
像微信,支付宝之类的小程序打开后,后台会多出一个任务来,可以同时打开主任务和小程序的任务进行交互。不是很清楚的话,可以自己打开下手机看一下。
想要实现两个任务其实很简单,更改AndroidManifest里taskAffinity属性,如下图,
然后在启动的时候设置intent属性
intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
这两段结合起来,在startActivity的时候,他会新开一个栈,而不是和主任务在一个栈里。这样就初步实现了后台中两个任务的需求了。但是,现在问题来了,那如果他开了第二个小程序了怎么办呢?对吧,看一下微信发现他会再创建一个。
好,那么问题来了,微信的小程序是不是开几个创建几个呢?开多少个后台就有多少个呢。我觉得不是,微信应该设置了数量,有个上限 ,但是这个上限是多少可以自己去设置一下。我在观察微信小程序的activity的时候发现,不同的小程序特定情况下会占用同一个的activity。
竟然如此,那么就是说,微信小程序和activity的按需分配的,也就是说小程序是无限的,但是activity是有限的。当要打开一个小程序前,他去看看有哪些activity是空着的,空着就把他塞进去。
所以这边,我的解决方法是创建多个webviewActivity,他们分属不同的栈,定了一下上限为5。也就是创建了五个一样的activity,当然存在一些小区别。实现的逻辑大概如下:
我在application申明了一个数组,List[0]即表示activity0,当list[0]中有数据的时候 ,即表示这个activity在使用中。list在application启动的时候会自动清零,然后依次存进去,一边存一边启动这个activity。5个存满之后,加个标识,从头开始覆盖。因为主项目是flutter写的,所以很多功能无法剥离开,必须要启动主项目,所以在application里再去申明一个int类型的activity,当webview里的页面需要调用主项目的方法时,把当前activity记录下来,比如activity0,就记录0,然后等主项目这边结束了,就启动0这个activity。
webview实现input type=file
flutter的webview是没有这个功能的,因为原生的webview也没有这个功能,需要重写onShowFileChooser这个方法,这一块我之前写过一篇比较详细的,可以看下这篇https://www.cnblogs.com/afei123/p/13053712.html。
简单说一下,那边的逻辑是在onShowFileChooser跳转一个透明activity,然后从这个activity去选择,如下图。然后打开相机和选择文件这块可以去那篇文章的github链接里去看,具体不赘述了。
当初为了下面这个dialog,新建了一个ativity,其实现在自定义webview的时候完全可以放到自己的webviewactivity里去。这里讲点高级的东西,需求来了,希望打开文件管理器可以根据前端代码进行选择。变成下图这样。
input type=file的时候还可以设置accept属性,用来选择打开照片还是别的什么东西,打开照片的情况下,文件什么都是不可看的状态。然后accept这个属性可以通过下面的方法获取,所以怎么搞明白了吧。
final String[] acceptTypes = getSafeAcceptedTypes(fileChooserParams);
实现文档预览
文档预览这块实现需要接入TBS服务,也就是腾讯的x5内核。这个有一个缺陷就是需要把文件下载到本地,而且如果不是用腾讯浏览器打开只支持9种格式,具体的可以在TBS的官网去看一下。
我这边将原生的webview也全部替换成了x5,也为了方便点。
所以实现文档预览需要走两步,一步下载,一步打开。下载文档自己就根据url去设置就好了,开一个线程,下载完了执行打开文档的方法。
我这里使用的是TbsReaderView这个方法,这个方法官网上没讲。使用方法如下
tbsReaderView = new TbsReaderView(this,readerCallback); RelativeLayout mRelativeLayout = findViewById(R.id.tbsView); mRelativeLayout.addView(tbsReaderView,new RelativeLayout.LayoutParams(-1,-1));
我这边是创建了一个activity来显示他,然后自己定义了下toolbar。
图片预览
然后还要实现的就是图片预览,我这边是默认拿到了图片地址list。然后用photoView+viewPager实现他。不过很奇怪,当photoView放入viewPager之后,就无法缩放了。这块mark一下。
和flutter的交互
如下图,当反馈需要打开flutter页面怎么办。
包括js事件需要传递给flutter怎么办,在flutter端注册一个MethodChannel,然后在flutter这边设置一个接收端。具体实现操作就是这样了。
如果对具体细节代码实现有问题的话可以私信我。q980160988