子线程不显示Toast?

Handler不仅在ANR过程中有用到,Toast中也用到了Handler。

代码如下:

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Toast.makeText(MainActivity.this,"子线程",Toast.LENGTH_SHORT).show();
            }
        }).start();

    }
}

运行安装,Logcat里面显示了异常如下:

    Process: com.exp.cpdemo, PID: 1961
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:200)
        at android.os.Handler.<init>(Handler.java:114)
        at android.widget.Toast$TN$2.<init>(Toast.java:336)
        at android.widget.Toast$TN.<init>(Toast.java:336)
        at android.widget.Toast.<init>(Toast.java:103)
        at android.widget.Toast.makeText(Toast.java:256)
        at com.exp.cpdemo.MainActivity$1.run(MainActivity.java:40)
        at java.lang.Thread.run(Thread.java:761)

设备是Android 7.1 系统,对应源码:
/frameworks/base/core/java/android/widget/Toast.java

为什么会抛出这个异常?

从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;
    }

初始化了一个Toast对象。看看Toast构造器里面的代码逻辑:

    public Toast(Context context) {
    
    
        mContext = context;
        mTN = new TN(context.getPackageName());
        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对象。继续看TN这个类:

    private static class TN extends ITransientNotification.Stub {
    
    
		......
        final Handler mHandler = new Handler() {
    
    
            @Override
            public void handleMessage(Message msg) {
    
    
				...
            }
        };


        TN(String packageName) {
    
    
			...
        }
		
		...
	}

TN是Toast的静态内部类。在生成TN对象时,成员变量mHandler赋值逻辑执行,调用了Handler的默认构造器。接着看Android 7.1 Handler的源码:
/frameworks/base/core/java/android/os/Handler.java

    public Handler() {
    
    
        this(null, false);
    }

    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;
    }

果然,在Handler的两参数构造方法中,抛出了上面看到的异常。

怎么解决?

知道了原因,添加两行代码就好了,如下:

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Looper.prepare();
                Toast.makeText(MainActivity.this,"子线程",Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }).start();

    }
}

Toast显示成功了。

猜你喜欢

转载自blog.csdn.net/zhangjin1120/article/details/131219245
今日推荐