Toast,AlertDialog的误解
在一般的软件开发中,子线程中是不能更改UI主线程中创建的UI控件的。之前的理解是Toast也不能在子线程中创建。事实上并不是这样子的。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "test", Toast.LENGTH_LONG).show(); }}).start(); }
在Activity的onCreate中写入以上代码运行。LogCat中会抱错
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
不看这个错误信息第一反应肯定是UI控件不能在子线程调用
事实上并不是这样的
我们可以查看Toast的源码
public Toast(Context context) { mContext = context; mTN = new TN(); mTN.mY = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.toast_y_offset); } /** * Show the view for the specified duration. */ public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }
看这个mTN = new TN();构造函数
final Handler mHandler = new Handler();
Handler的构造函数
public Handler(Callback callback, boolean async) { ... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } ... }
而Looper.myLooper()
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
android中的子线程默认ThreadLocal中未设置Looper,所有会抛出这个异常,至于Looper是啥,可以参考我另一篇文章:http://www.cnblogs.com/cqcmdwym/archive/2013/05/12/3074138.html
解决方法
new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub Looper.prepare(); Toast.makeText(MainActivity.this, "test", Toast.LENGTH_LONG).show(); Looper.loop(); }}).start();
或者在主线程中声明一个Handler,然后在run方法中hanlder.post(new Runnable(){})一下。
同样AlertDialog也是如此,他包含一个AlertController字段,其内部也需要创建一个Handler.