最近突然收到安全部门的消息,说有些页面在获取剪切板内容。我当时很确性的说,我压根没去获取剪切板内容啊。
但是他给出了具体的调用堆栈,这一看果然是真的。
at android.content.ClipboardManager.getPrimaryClip(Native Method) at io.flutter.plugin.platform.PlatformPlugin.getClipboardData(PlatformPlugin.java:332) at io.flutter.plugin.platform.PlatformPlugin.access$700(PlatformPlugin.java:28) at io.flutter.plugin.platform.PlatformPlugin$1.getClipboardData(PlatformPlugin.java:107) at io.flutter.embedding.engine.systemchannels.PlatformChannel$1.onMethodCall(PlatformChannel.java:141) at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233) at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85) at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818) at android.os.MessageQueue.nativePollonce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:325) at android.os.Looper.loop(Looper.java:142) at android.app.ActivityThread.main(ActivityThread.java:6549) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)找出问题
1.确定是什么原因导致的
根据截图的几个页面,当然他当时截图的页面是不对的,但是这个页面的后一个页面。
我是根据断点原生代码去确定范围的。
发现出现在了地址编辑页,根据经验判断问题出现在了输入框上。
然后我在flutter项目中搜索 Clipboard.getData
为什么搜索这个也是根据上面的堆栈那确定的
io.flutter.embedding.engine.systemchannels.PlatformChannel$1.onMethodCall(PlatformChannel.java:141) at
通过搜索发现
有这几个地方调用获取剪切板内容。
下面一个一个分析
发现editable是Edittext的Render child对象
且是剪切文字时触发的
这中操作会引起获取剪切板,但我进入页面时根本没有任何操作就出现了,只能说明这个地方会,但不是根本问题的地方。
切图上出现的问题的case有很多我就不一一分析了,我就分析真正出现的问题的地方。
ClipboardStatusNotifier这个类的update时会去获取剪切板
然后ClipboardStatusNotifier这个类的addListener会调用upData方法
为什么看它,前面已经说了是输入框导致的。Flutter中输入框就是text_field,ios样式的输入框也一样,发现它的子控件EditableText中调用了ClipboardStatusNotifier
然后EditableText的State类中也就是EditableTextState
里初始化了ClipboardStatusNotifier片 还记得上面说过,这个类的upData方法被调用时会去获取剪切板内容,然后这个类的addListener类也会触发upData方法
@override
void initState() {
super.initState();
_clipboardStatus?.addListener(_onChangedClipboardStatus);
问题明了了,initState的调用时机就是 Element创建时就会被调用。
再贴下flutter获取剪切板内容的地方
@override
void addListener(VoidCallback listener) {
if (!hasListeners) {
WidgetsBinding.instance!.addObserver(this);
}
if (value == ClipboardStatus.unknown) {
update();
}
super.addListener(listener);
}
只要
value == ClipboardStatus.unknown这个条件成立就会触发,那它成立吗,下面来看看
初始化的地方
默认值
那百分百会被调用了,这也是为什么我页面中有输入框时会被自动调用获取剪切板内容的原因了。
问题定位到了,且出现的原因也知道了。那解决办法也就出来了。
- 修改系统源码,拷贝TextField类,修改关键点
- 重原生入手,hook Channel交互方法,当由flutter获取剪切板内容时 阻止其获取方法。
我给出第二种解决办法吧,第二种的好处是不用修改多处Flutter代码,对双端的影响较小。
github地址



