曲线救国之修复腾讯 X5 内核文件上传的兼容性问题

来说说可恶的腾讯 X5 内核,记录下今天问题的排查,定位,和修复过程。

背景

Android 项目中的 WebView 集成了腾讯的 X5 内核,由于 X5 在展示方面的兼容性问题深受前端们的喜爱(我能说,他们的 H5 页面的样式兼容问题,全部推锅到移动开发吗, 除了最新版本的 Chrome 内核,是否可以考虑下别的浏览器...)。

然而,就在今天,一个不幸的上午,捕获到了如下异常:

 UncaughtException detected: android.os.FileUriExposedException: file:///storage/emulated/0/DCIM/Camera/1536649175379.jpg exposed beyond app through ClipData.Item.getUri()
        at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958)
        at android.net.Uri.checkFileUriExposed(Uri.java:2356)
        at android.content.ClipData.prepareToLeaveProcess(ClipData.java:941)
        at android.content.Intent.prepareToLeaveProcess(Intent.java:9747)
        at android.content.Intent.prepareToLeaveProcess(Intent.java:9732)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1611)
        at android.app.Activity.startActivityForResult(Activity.java:4536)
        at android.support.v4.app.BaseFragmentActivityApi16.startActivityForResult(BaseFragmentActivityApi16.java:54)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:65)
        at android.app.Activity.startActivityForResult(Activity.java:4494)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:711)
        at android.app.Activity.startActivity(Activity.java:4855)
        at android.app.Activity.startActivity(Activity.java:4823)
        at android.content.ContextWrapper.startActivity(ContextWrapper.java:376)
        at org.chromium.android_webview.ResourcesContextWrapperFactory$WebViewContextWrapper.startActivity(Unknown Source:11)
        at com.tencent.tbs.core.partner.b.a$2.onClick(Unknown Source:406)
        at android.view.View.performClick(View.java:6266)
        at android.view.View$PerformClick.run(View.java:24730)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:171)
        at android.app.ActivityThread.main(ActivityThread.java:6672)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:246)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
复制代码

一回头

幸运的是找到了出现问题的调用: <input type="file" acctType="image/*"/>

上述标签,最终会调用到 WebChromeClient 的 onShowFileChooser 方法(不同 Android 版本有所差异,>= 5.0 是此方法),然而,经过测试,这次崩溃并没有调用到此处(一脸懵逼)。

乍一看是跨 APP 文件共享导致的问题,恰好最近项目刚把 targetSdkVersion 从 22 升级到 26,也就是必须要兼容 Android 6.0 的动态权限以及 7.0 的跨应用文件共享。

但是,仔细分析堆栈信息,没有发现项目主动调用的代码,可疑点锁定到这一行 at com.tencent.tbs.core.partner.b.a$2.onClick(Unknown Source:406)。但是下载下来的 腾讯内核 jar 文件中并没有包含 com.tencent.tbs.core.partner.**,由于 X5 内核只提供了轻量级的 jar 文件,实际内核的下载和更新是 APP 安装后动态进行的,于是乎去了 /data/data/{packageUd}/app_tbs, 并且把所有文件都导到电脑上,里面主要包含了包含资源文件的 apk, .so 文件以及一个 dex 文件,经过 dex2jar 这些操作后,仍然没有找到可疑的类文件。

二回头

上一条路被堵死。 很幸运,恰好今天帮测试解决 UI Automator 的问题,顺道也用了一下, 获得如下布局:

emmm, 出问题的关键就是点击拍照,项目中没有主动显示这种样式的弹窗,因此可能是系统做了拦截处理,或者就是 X5。

换了几台不同厂商的测试机实验,样式都是一样,基本排除是系统拦截处理的锅。最可疑的就是 X5 了。

中午还去了浜烧市场吃了顿饭,心好大....

三回头

基本锁定问题后,就开始各种预先申请权限,StrictMode 上折腾,试图解决权限问题,无果。

但每次 APP 崩溃几次后,再次调用,发现又会调用到 WebChromeClient 的 onShowFileChooser 方法,由于我们自己做过权限处理,一切又恢复正常。 (后来发现是 X5 发现崩溃后,降级逻辑)。

四回头

测试发现,出问题的点只是拍照这一个地方,可恶的腾讯 X5 内核并没有做兼容 7.0 的逻辑处理,并且恶意拦截 input file 标签,美美的弹出自己的文件选择框。 兼容都没有做好,有碧莲弹窗。。。。佩服!!!

五回头

不知怎么滴灵光一现,就想如果我们去掉拍照这个按钮,问题不就解决了吗?分析布局,目测是 X5 在 WebView 后面动态 add 了一个 android.widget.LinearLayout, 别问我怎么知道的...

于是乎诞生了如下代码:

项目中 WebView.java

    LinearLayout fuckTbsLayout;
    List<TextView> fuckTextViews;
    @Override
    public void onViewAdded(View child) {
        if (child.getClass().getName().startsWith("com.tencent.tbs.")
                &&
                child instanceof LinearLayout
                &&
                ((LinearLayout) child).getChildCount() > 0
                ) {
            fuckTbsLayout = (LinearLayout) child;
            TextView fuckItem;
            if (fuckTbsLayout.getChildAt(0) instanceof TextView) {
                fuckItem = (TextView) fuckTbsLayout.getChildAt(0);
                String fuckTitle = fuckItem.getText().toString();
                if (fuckTitle.contains("请选择上传方式") || fuckTitle.contains("相册") || fuckTitle.contains("拍照") || fuckTitle.contains("其它方式")) {
                    fuckTextViews = new ArrayList<>();
                    for (int i = 0; i < fuckTbsLayout.getChildCount(); i++) {
                        TextView fuckTextView = null;
                        if (fuckTbsLayout.getChildAt(i) instanceof TextView) {
                            fuckTextView = (TextView) fuckTbsLayout.getChildAt(i);
                        }
                        if (fuckTextView != null && fuckTextView.getText().toString().trim().contains("拍照")) {
                            fuckTextViews.add(fuckTextView);
                        }
                    }
                    if (fuckTextViews != null && fuckTextViews.size() > 0) {
                        for (TextView todoRemoveFuckTextView : fuckTextViews) {
                            fuckTbsLayout.removeView(todoRemoveFuckTextView);
                        }
                    }
                    fuckTextViews = null;
                }
            }
        }
        super.onViewAdded(child);
    }
//sorry for the F words.
复制代码

主要思想就是在 WebView 的 onViewAdded 方法中做手脚,此方法是作甚的呢?

    /**
     * Called when a new child is added to this ViewGroup. Overrides should always
     * call super.onViewAdded.
     *
     * @param child the added child view
     */
    public void onViewAdded(View child) {
    }
复制代码

非常通俗易懂,当 X5 偷偷的去动态 addView 的时候,所在的父组件此方法必定会被调用,只要过滤一下,记录下来拍照的所属的 View,然后从父组件上调用 removeView 移除掉就好了。

再回头脖子就要断了

上述问题,曲线救国得以解决。

方案的缺点

  • TextView 变化,文案变化,会再次失效

尾巴

  • 在问题排查以及解决过程中,腾讯的 X5 文档,以及论坛,QQ 群形同虚设,几乎没有什么有价值的线索,狂吐槽。

  • X5 估计自己都没有做兼容测试,就动态下发错误的逻辑代码,属实是狂傲。

  • X5 检测应用崩溃后,降级逻辑总算是有点良心,降低对用户的影响,但是我们的 crash 率飙升了...唉

转载于:https://juejin.im/post/5b97d665f265da0ae92a54b6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在uni-app中,使用uni.request()是无法直接上文件的,因为request的data参数无法接受FormData格式的数据。所以需要通过曲线救国的方式来上文件,可以使用uni.uploadFile()来实现。首先,你需要使用uni.chooseImage()方法选择要上文件,并将文件保存在file变量中。然后,将file作为参数递给uni.uploadFile()方法,同时指定上文件的URL、文件类型、文件名等信息。最后,在success回调函数中可以处理上成功后的响应。以下是一个示例代码: uni.chooseImage({ count: 1, // 默认为9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 从相册选择 success: (res) => { let file = res.tempFiles; // 获取file格式的文件 uni.uploadFile({ url: 'XXX', // 上文件的URL file: file, // 要上文件 fileType: 'image', // 文件类型 name: 'img_file', // 文件名 success: (res) => { console.log(res); } }); } }); 通过以上代码,你可以在uni-app中使用uni.request()来实现文件功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [uni-app 上图片 H5 微信小程序 APP 均可使用](https://blog.csdn.net/weixin_49230250/article/details/127262596)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [uniapp上文件(图片、视频)](https://blog.csdn.net/u011200562/article/details/113544671)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [uniapp前端单文件JAVA后台接收实现(亲测)](https://download.csdn.net/download/nhb8890/12936989)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值