【安卓三分钟】升级到android 11后,ToastUtil突然导致页面崩溃了

一、背景

最近应用商店开始开始逐步清理安卓SDK版本低于30的软件,为了保证应用可以继续上架任务商店,我们升级了应用的sdk,在解决了一系列升级导致的空异常后,项目摇摇晃晃的 跑了起来,然而来不及高兴,应用就崩溃了,排查后定位到了项目中的工具类——ToastUtil,这个工具类由于空异常导致了项目的崩溃,接下来就是熟悉的改bug环节了。

二、问题分析

先来看下工具类的关键代码:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            mToast = Toast.makeText(mContext, null, Toast.LENGTH_SHORT)
            mToast?.setText(message)
        } else {
            if (mToast == null) {
                mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT)
                mToast?.setText(message)
            } else {
                mToast?.setText(message)
            }
        }

        val textview_id = Resources.getSystem().getIdentifier("message", "id", "android")
        val textView = mToast?.view?.findViewById<View>(textview_id) as TextView
        textView.gravity = Gravity.CENTER
        textView.setLineSpacing(ConvertUtils.dp2px(2f).toFloat(), 1.0f)

根据日志显示,问题就出现在这一行,日志还告诉我们,这行代码把null尝试转换为 TextView:

        val textView = mToast?.view?.findViewById<View>(textview_id) as TextView

这里肉眼肯定是看不出来问题了,所以我直接,debug,启动
在这里插入图片描述

在经过紧张刺激的调试后,我发现是mToast这个对象的view为空了,接着debug,发现问题出在Toast的makeText()方法中:

    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        return makeText(context, null, text, duration);
    }


    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
            Toast result = new Toast(context, looper);
            View v = ToastPresenter.getTextToastView(context, text);
            result.mNextView = v;
            result.mDuration = duration;

            return result;
        }
    }

经过反复的调试,发现在我的安卓13的手机上,断点始终会进入下面的判断:

if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        }

很明显这里和else的代码相比,是始终没有设置mNextView的,而这个mNextView,就是Toast的view(kotlin写法,view相当于getView和setView两个方法):

    @Nullable public View getView() {
        return mNextView;
    }

没有设置,当然会报空了,我们对比下Android SDK为28的时候的Toast的makeText方法:

public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        return makeText(context, null, text, duration);
    }


    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        Toast result = new Toast(context, looper);

        LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        tv.setText(text);

        result.mNextView = v;
        result.mDuration = duration;

        return result;
    }

可以看到这个时候是没有那个判断的,所以之前是一直没有问题的,随后在网上找到的文章也应证了我们的猜测:

Android 11适配指南之Toast解析

虽然不知道这个判断的逻辑是什么,但是可以确定的是,我们不能这么用了,只能使用标准的写法,或者按文章里的其他方案:

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()

三、用别人造好的轮子,而不是重复造轮子的工作

这时候按照常理,我们只能去自定义一个View了,但是我们的项目中,其实一直有blankj这个工具类框架:

    implementation 'com.blankj:utilcodex:1.31.1'

这里我们可以直接使用blankj的ToastUtils类:

   	ToastUtils.make().setGravity(Gravity.CENTER, 0, 70)
                    .setTextColor(ColorUtils.getColor(R.color.black_333333)).show(message)

再根据自己的需要调下样式,至此问题解决。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的倒计时HTML、CSS、JS代码: HTML代码: ```html <div class="countdown-container"> <div class="countdown-timer">03:00</div> <div class="countdown-message">游戏失败</div> </div> ``` CSS代码: ```css .countdown-container { position: relative; width: 100px; height: 100px; margin: 0 auto; } .countdown-timer { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; width: 80%; height: 80%; font-size: 24px; text-align: center; border-radius: 50%; background-color: #ccc; color: #fff; line-height: 100px; } .countdown-message { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; width: 80%; height: 80%; font-size: 24px; text-align: center; border-radius: 50%; background-color: #f00; color: #fff; line-height: 100px; display: none; } ``` JS代码: ```javascript const timerElement = document.querySelector('.countdown-timer'); const messageElement = document.querySelector('.countdown-message'); let seconds = 180; // 3分钟,即180秒 let timerIntervalId; // 更新倒计时时间 function updateTimer() { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; timerElement.innerHTML = `${minutes < 10 ? '0' : ''}${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`; if (seconds === 0) { clearInterval(timerIntervalId); timerElement.style.display = 'none'; messageElement.style.display = 'block'; } seconds--; } // 开始倒计时 function startCountdown() { timerIntervalId = setInterval(updateTimer, 1000); } startCountdown(); ``` 这个倒计时会在页面加载后就开始倒计时,每隔一秒钟会更新一次倒计时时间,并且当时间到达0时,会显示"游戏失败"的提示信息。这个倒计时的时间是通过`seconds`变量来控制的,您可以根据需要修改这个变量的值来设置倒计时的时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值