Toast使用报错java.lang.RuntimeException: Can't create handler inside thread 原因及解决方案(一)

本文解析了在子线程未初始化Looper时调用Toast导致的问题,并提供了两种解决方案:在子线程中初始化Looper或确保操作运行在主线程中。深入探讨了Toast的实现机制,包括其依赖的Handler与MessageQueue。
   这个问题先前没怎么注意,只是知道Toast会有类似于一个队列的东西,你每调用一次都会加入队列,
先进先出。今天正好趁解决这个问题来详细研究一下Toast的实现原理。
   这个问题一般出现在子线程没有初始化Looper时调用Toast时出现,既然没有初始化Looper那就给
他初始化或者让他在UI线程中弹吐司就可以了,话不多说直接给出解决方案:

方案一:在子线程中初始化Looper
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                Looper.prepare();
                Toast.makeText(MainActivity.this,"hello world!",Toast.LENGTH_SHORT).show();
                Looper.loop();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

方案二:运行在主线程中
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,"hello world!",Toast.LENGTH_SHORT).show();
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

    问题解决了,现在可以看一下为什么会出现这种问题了,弹吐司的标准写法是   Toast.makeText(context,"hello world!",Toast.LENGTH_SHORT).show();
我们先看一下makeText这个方法的实现:
public static Toast makeText(Context context, CharSequence text, 
@Duration int duration) {
    Toast result = new Toast(context);
    //通过布局填充器加载布局
    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;
}
    这个方法比较明了就是进行布局的加载,这个布局里面也很简单,LinearLayout包裹着一个TextView,
看到这里并没有看到任何与问题相关的东西,接着看。

   控制Toast显示和隐藏的方法是.show()/hide();
看看show()方法
public void show() {
    if (mNextView == null) {
        throw new RuntimeException("setView must have been called");
    }
    //JNI调用
    INotificationManager service = getService();
    String pkg = mContext.getOpPackageName();
    //着重看这个
    TN tn = mTN;
    tn.mNextView = mNextView;

    try {
        service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
        // Empty
    }
}

TN这个静态内部类代码较多,直接看重点就好了
private static class TN extends ITransientNotification.Stub {
    final Runnable mHide = new Runnable() {
        @Override
        public void run() {
            handleHide();
            mNextView = null;
        }
    };

    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
    //使用handler进行显示隐藏处理
    final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            IBinder token = (IBinder) msg.obj;
            handleShow(token);
        }
    };

    @Override
    public void show(IBinder windowToken) {
        if (localLOGV) Log.v(TAG, "SHOW: " + this);
        mHandler.obtainMessage(0, windowToken).sendToTarget();
    }

    @Override
    public void hide() {
        if (localLOGV) Log.v(TAG, "HIDE: " + this);
        mHandler.post(mHide);
    }

   可以看出来Toast的show和hide是通过Handler进行处理的,而handler是需要MessageQueue来
管理handler消息,如果没有的话就会报错了,主线程中可以使用的原因是ActivityThread的main()
方法中对Looper进行了初始化,Looper.prepareMainLooper();这里也是程序的入口。
   到这里就分析完了,下一篇文章看看能不能对深入理解一下Toast的实现吧。
package com.tongfu.popquizgen; import android.os.Bundle; import android.view.View; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.google.android.material.button.MaterialButton; import java.util.ArrayList; public class StudentQuizActivity extends AppCompatActivity { private RadioGroup radioGroup; int index=0; private RadioButton radioButton1,radioButton2,radioButton3,radioButton4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_student_quiz); MaterialButton backButton = findViewById(R.id.backButton); MaterialButton submitButton = findViewById(R.id.submitButton); radioGroup = findViewById(R.id.radioGroup); backButton.setOnClickListener(v -> finish()); radioButton1=findViewById(R.id.radioButton1); radioButton2=findViewById(R.id.radioButton2); radioButton3=findViewById(R.id.radioButton3); radioButton4=findViewById(R.id.radioButton4); submitButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int checkedRadioButtonId = radioGroup.getCheckedRadioButtonId(); if (checkedRadioButtonId != -1) { login(v); // 处理进入下题的逻辑,这里简单用 Toast 提示 Toast.makeText(StudentQuizActivity.this, "进入下题", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(StudentQuizActivity.this, "请选择个选项", Toast.LENGTH_SHORT).show(); } } }); } public void login(View view){ new Thread(){ @Override public void run() { UserDao userDao = new UserDao(); userDao.test(); global_variable gv=(global_variable) getApplication(); ArrayList<String> question = gv.getquestion_Text(); ArrayList<String> option_a = gv.getquestion_Text(); ArrayList<String> option_b = gv.getquestion_Text(); ArrayList<String> option_c = gv.getquestion_Text(); ArrayList<String> option_d = gv.getquestion_Text(); ArrayList<String> correct_option = gv.getquestion_Text(); radioButton1.setText(option_a.get(index)); radioButton2.setText(option_b.get(index)); radioButton3.setText(option_c.get(index)); radioButton4.setText(option_d.get(index)); index++; } }.start(); } } 这部分代码中出现java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
最新发布
11-28
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值