一般我们都把Toast当做一个UI控件在主线程显示。但是有时候非想在子线程中显示Toast,就会使用Handler切换到主线程显示。

但是子线程中真的不能直接显示Toast吗?

答案是:当然可以。

那应该怎么操作呢?在当前线程中先初始化一个Looper即可!

Looper.prepare();
Toast.makeText(getBaseContext(), "text", Toast.LENGTH_LONG).show();
Looper.loop();

为什么在子线程中使用Toast需要初始一个Looper呢? 我们看看源代码:

  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);
        ...
        return result;
    }

以上是我们使用Toast时调用的静态方法,可以看到第二个方法有个参数Looper,虽然我们平时用的时候都传入的是null,那这个Looper究竟有什么用呢?我们看看Toast的构造函数:

  public Toast(@NonNull Context context, @Nullable Looper looper) {
        mContext = context;
        mTN = new TN(context.getPackageName(), looper);
    }

可以看出这个Looper其实是TN在用,我们看看它的构造函数:

     TN(String packageName, @Nullable Looper looper) {
            if (looper == null) {
                // Use Looper.myLooper() if looper is not specified.
                looper = Looper.myLooper();
                if (looper == null) {
                    throw new RuntimeException(
                            "Can't toast on a thread that has not called Looper.prepare()");
                }
            }

        }

以上代码有简化。可以看出当Looper为null的时候,会通过Looper.myLooper获取一个当前的Looper。我们知道在主线程中系统已经为我们初始化了一个mainLooper,所以我们一般不用管。但是当我们子线程中如果没有初始化Looper,这里调用Looper.myLooper就获取不到一个Looper,则会抛出异常。所以当我们在子线程中使用Toast,使用Looper.prepare()方法初始化一个Looper并用Looper.loop()让它启动起来即可。

所以我们可以封装一个可以在任何线程使用的Toast。

 private static Toast toast = null;
    public static void showToast(Context context, String text) {
        Looper myLooper = Looper.myLooper();
        if (myLooper == null) {
            Looper.prepare();
            myLooper = Looper.myLooper();
        }

        if (toast == null) {
            toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
        }
        toast.show();
        if ( myLooper != null) {
            Looper.loop();
            myLooper.quit();
        }
    }

我们初始化Toast之前先判断当前线程的looper是否为空,为空则初始化一个新的myLooper,然后在调用Toast的show方法之后让looper启动起来即可。因为Looper的loop()方法是无限循环的,为了防止Looper阻塞线程,导致内存泄漏应该及时退出Looper。

最新文章

  1. [转]Worksheet.Change Event (Excel)
  2. mysql更新一个表里的字段等于另一个表某字段的值
  3. Android__Context
  4. python 缩进问题
  5. 第三方框架FMDB
  6. 5个让DBA爱上你的SQL技巧
  7. HTML+CSS学习笔记(1) - Html介绍
  8. PHP 14:类的实例
  9. 团队作业1 团队展示&选题
  10. jquery的动画学习--jquery权威指南
  11. colormap中的内嵌彩色模块和调用方式
  12. javascript函数闭包(closure)
  13. [No0000108]Git1/9-Git简介与入门
  14. ES6之数组操作
  15. HMM模型和Viterbi算法
  16. oracle不小心更新了数据库中的所有内容后的恢复
  17. 使用OpenSSL(Windows x64版)将pem格式证书转换为p12格式
  18. Android(java)学习笔记38:Android 短信发送
  19. matlplotlib 为折线图填充渐变颜色
  20. 如何解决GBK的编码的文件中的中文转换成为UTF-8编码的文件而且不乱码

热门文章

  1. 阅读阿里文档总结————Mysql数据库篇
  2. PHP代码审计之XSS操作
  3. 后端视角下的前端框架之Vue.js初探
  4. 双目立体匹配——归一化互相关(NCC)
  5. 在NOARCHIVELOG和ARCHIVELOG模式之间选择
  6. codeforces 1151 D
  7. PyCharm 项目删除
  8. day17——其他内置函数
  9. virtualenv安装及使用
  10. Linux下执行自定义的可执行命令无效原因