Can't create handler inside thread that has not called Looper.prepare()

下面是轉載一哥們的博客,之所以轉載他的是因爲   他的解決問題的 方式挺好,層層遞進,步步追溯!

轉載:https://www.jianshu.com/p/86459c23bdf5

Crach描述:

在子線程中 調用了這句:

Toast.makeText(this, "", Toast.LENGTH_LONG) .show();

然後就崩潰了:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// at android.os.Handler.<init>(Handler.java:121)
// at android.widget.Toast$TN.<init>(Toast.java:361)
// at android.widget.Toast.<init>(Toast.java:97)
// at android.widget.Toast.makeText(Toast.java:254)

分析:跟蹤源碼

查看makeText方法,可以看到 new了一個Toast實例

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構造函數中又實例化了一個 TN對象

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中,可以讀到這一句:

final Handler mHandler = new Handler(); 

創建了一個Handler對象。

private static class TN extends ITransientNotification.Stub {
    final Runnable mShow = new Runnable() {
        @Override
        public void run() {
            handleShow();
        }
    };

    final Runnable mHide = new Runnable() {
        @Override
        public void run() {
            handleHide();
            // Don't do this in handleHide() because it is also invoked by handleShow()
            mNextView = null;
        }
    };

    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
    final Handler mHandler = new Handler();

讀 Handler構造函數:

/**
 * * Default constructor associates this handler with the {@link Looper} for
 * the * current thread. * * If this thread does not have a looper, this
 * handler won't be able to receive messages * so an exception is thrown.
 */
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後會默認創建一個looper對象。但是子線程不會,需要手動創建。其ThreadLoacl中沒有設置過Looper,mLooper==null ,所以會拋出異常。

解決方法

方法一:增加Looper.prepare();

new Thread() {

   public void run() {

    Log.i("log", "run");

    Looper.prepare();

    Toast.makeText(ActivityTestActivity.this, "toast", 1).show();

    Looper.loop();// 進入loop中的循環,查看消息隊列

    };
 }.start();

方法二:post 給主線程去處理

// if current thread is background thread
// post show-toast-runnable to main handler
// if current thread is background thread
// post show-toast-runnable to main handler

mainHandler.post(new Runnable() {

   @Override
   public void run() {
      if (toast == null) {
         toast = Toast.makeText(context, "",     Toast.LENGTH_SHORT);
  }
  toast.setText(msg);
  toast.setDuration(Toast.LENGTH_SHORT);
  toast.show();
   }
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章