记录 | Flutter剪切板-刨根问底
项目背景
起因是因为某一天晚上梦魇哥哥告诉我速享重构多端共享剪切板,体验了一波发现只能前台去共享,那我复制一次打开一次速享,作为一个体验过苹果AirPlay的极客党感觉有点难受啊,于是逛逛某安发现了一个模块可以解除Android10以上的针对指定软件剪切板的限制。
那么原本就的速享是不是也可以实现像苹果生态那样的多端共享剪切板呢?别问我为什么不用Iphone(价格昂贵
马上掏出我的小米手机是不是打开Magisk安装模块,勾选速享,心想和我的Mac电脑也可以和Android同步了,美滋滋啊!然而结果令人意外,速享在后台依旧不能同步剪切板,(难道失效了?于是换了另外一个原生软件测试了一下,发现模块是正常的)这就忍不了,经过于梦魇哥哥的交流后,发现速享获取剪切板是调用的Flutter引擎的指令,于是决定拉取速享代码一探究竟。
初探速享
首先找到速享获取剪切板的位置,使用的是Flutter官方的Clipboard.getData来获取数据
来到源码出:
发现他是使用的系统消息SystemChannels去使用CLipboard.getData方法,
来到引擎
得知这个代码是在Flutter的engine中clone到本地进行搜索"CLipboard.getData"
在PlatformChannel.java文件中发现通过平台消息platformMessageHandler去调用getcLipboardData
来到getcLipboardData源码处可以看到这是一个常规的原生剪切板读取
(标记处为原生代码)
走进原生
这时候就需要一点原生的知识了,我们通过谷歌官网指南可以了解到 剪贴板框架图
如果我们需要读取剪切板需要去使用ClipboardManager获取ClipData.Item对象中的数据
其中ClipData.Item对象包含 Text、URI 或 Intent 数据
ClipData.Item | 官方解释 |
---|---|
Text | 一个 CharSequence 。 |
URI | 一个 Uri 。尽管允许使用任何 URI,但它通常包含 Content Provider URI。提供数据的应用将 URI 放到剪贴板中。需要粘贴数据的应用从剪贴板获取 URI,并使用它来访问 Content Provider(或其他数据源)以及检索数据。 |
Intent | 一个 Intent 。通过此数据类型,您可以将应用快捷方式复制到剪贴板。然后,用户可以将快捷方式粘贴到自己的应用中以供日后使用。 |
那么他们有什么区别呢?个人理解
Text 是纯文本,便于我们直接读取
URI 包含了更多的信息,系统会向粘贴应用提供此信息。粘贴应用可以检查该信息,以了解自己能否处理该数据。
Intent 官方的解释是可以将应用快捷方式复制到剪贴板,然后,用户可以将快捷方式粘贴到自己的应用中以供日后使用。
分析开始
Flutter由于在2016之前-2020年输入框TextField中直接粘贴内容的时候都是使用的getText()
没有进行判空的时候强行调用会抛出空指针异常,
而且flutter也没有做异常捕获。
所以Flutter在2020年解决了这个issue
但是并不完美,因为在低版本的android后台中,可能存在无URI信息,但是存在Text的情况。
通过以下代码可以看出
通过获取URI的信息
来判断是否自己能够处理
小插曲,在没开始DEBUG之前,我的一顿瞎猜,导致了浪费了一晚上
关键问题来了:
正式开工 做了几次小测试(过程就不详细描述了甚至翻了FlutterActivity的启动流程
通过编写简单的测试代码可以发现,我们的hook模块是直接把消息置入到了text中,没有封装URI信息。
解决方案
直接通过判断Item的长度是否大于0进行处理,而不是判断URI的长度,
在加上异常捕获即可。(截图没有更新)
所以这时候的text虽然不为空,但是Flutter无法识别来源,所以就当成不能处理的文本给过滤了
所以我们只需要自定义一个channel,把这个消息传递给我们Flutter侧即可
个人体会
通过这次的经历,虽然代码量很少,但是经过层层分析才最终得以实现,
中途逃不过各位大佬们的督促以及疑问,
所以当我们遇到问题的时候需要先理清楚需求,
从中分析出关键点,并抱着坚持不懈的心态去探索,
不要凭感觉去分析,而是找到证据
不要想着源码层的代码就会很复杂(那毕竟是几十K大佬层层review过的或者亲自写的)
最后肯定会收获满满!
最后的最后感谢所有大佬的帮助!!!!!
参考文章
转载自:https://juejin.cn/post/7143627359009112077