Toast在子线程报错

刚开始学习android的同学们,都应该知道Log,和Toast这两个类,因为这两个类经常用来,给我查看代码的运行情况,所以有些时候,就会导致用起来混淆的情况,Log的话,没什么限制,在任何的方法下都可以使用,当然使用Log的时候,记得标记TAG,不要直接写“TAG”,一般情况下,直接下类名的,这样的话,就方便将来去找相应的Log,当然,你也可以给Log加一个开关,当你的项目完成,log开关一关,那么所有的Log就消失的无影无踪了。 
Toast,一般在主线程使用的时候,没有多大的问题,可是一旦到子线程中,就有可能报错。那么这是什么原因呢? 
那么咱们就来看看Toast的源码

  public Toast(Context context) {
        mContext = context;
        mTN = new TN();
        mTN.mY = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.toast_y_offset);
        mTN.mGravity = context.getResources().getInteger(
                com.android.internal.R.integer.config_toastDefaultGravity);
    }

咋一看,什么玩意,就弄俩对象,第一个没啥可讲的,第二TN类,什么破类,咱们接着点。。。

  private static class TN extends ITransientNotification.Stub {
....
        看这里   ***final Handler mHandler = new Handler();***    
....

        }

Handler!眼前一亮,我们都知道Handler的使用,需要和Loop配合,假如在主线程中,那么Handler是直接可以new的,因为主线程中自带的有Loop,而普通的线程是没有Loop的(如果你不知道,那么你现在应该知道了),所以Toast在主线程使用的时候,就不会报错,那么为啥子线程中就会报错呢?

咱么继续点Handler

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

有木有,大家看到了这个没有

    if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

如果Looper为null的时候,就会抛出这个异常,前面咱们也说了,普通线程是不会创建Loop的(除了主线程),所有Loop自然就是null的,所以子线程中使用Toast就会报错!

既然找到了原因,那么就有了解决的方法: 
1、既然在主线程中使用,不会报错,那么咱们就在主线程使用呗!

public class MainActivity extends Activity {
    private static final int TAG_TOAST = 0;
    private Handler mHandler = new Handler(){

        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case TAG_TOAST:
                Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                break;

            default:
                break;
            }

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

public void onClick(View view){
            new Thread(){
            public void run() {
                Message _Message = mHandler.obtainMessage();
                _Message.what = TAG_TOAST;
                _Message.obj = "在子线程中Toast!";
                mHandler.sendMessage(_Message);
            };
        }.start();
}

}

2、既然子线程中没有Loop,那么咱们给他创建一个不就得了嘛,开始干!

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

public void onClick(View view){
            new Thread(){
            public void run() {
                Looper.prepare();
                Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                Looper.loop();
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
            };
        }.start();
}

}

你猜猜上面的子线程中会Toast的几次,两次,哈哈,错了,一次,因为第二种方法有个缺点就是Looper.loop();后面的方法得不到执行,当然也可以调用Looper.myLooper().quit();推出Looper,那么后面的方法就会继续执行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值