new Thread(){
public void run(){
Toast.makeText(public_log.this,"圖片不存在",Toast.LENGTH_SHORT).show();
}
}
如果像這樣直接在子線(xiàn)程中彈出Toast,程序會(huì)報(bào)錯(cuò)。
深入源碼了解一下原因:
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;
}
makeText方法好像沒(méi)有什么不對(duì),那么繼續(xù)向下看show()方法
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
好像沒(méi)什么不對(duì) 但是看getService()不對(duì)勁就點(diǎn)擊進(jìn)去看一下
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
這里還是看不出有什么問(wèn)題 然而show()里面這個(gè)又一次引起了我的注意
TN tn = mTN;
我點(diǎn)擊進(jìn)去查看源碼,好家伙終于發(fā)現(xiàn)問(wèn)題所在了
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();
。。。。。代碼省略。。。。。。
/**
* schedule handleShow into the right thread
*/
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
/**
* schedule handleHide into the right thread
*/
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
。。。。。代碼省略。。。。。。
}
我相信聰明的你們應(yīng)該看到了這里為什么錯(cuò)了,對(duì),就是Handler不能再子線(xiàn)程里運(yùn)行的 因?yàn)樽泳€(xiàn)程沒(méi)有創(chuàng)建Looper.prepare(); 所以就報(bào)錯(cuò)了。主線(xiàn)程不需要調(diào)用,是因?yàn)橹骶€(xiàn)程已經(jīng)默認(rèn)幫你調(diào)用了。
可以看到一個(gè)Toast的創(chuàng)建需要依賴(lài)Handler。那么 我不要 我不要 我一定要在子線(xiàn)程使用Toast那怎么辦。
其實(shí)很簡(jiǎn)單,它卻什么就給它什么。
第一種方法
new Thread(){
@Override
public void run() {
super.run();
Looper.prepare();
try {
Toast.makeText(MainActivity.this,"ceshi",Toast.LENGTH_SHORT).show();
}catch (Exception e) {
Logger.e("error",e.toString());
}
Looper.loop();
}
}.start();
因?yàn)槌薃ctivity ui線(xiàn)程默認(rèn)創(chuàng)建之外,其他線(xiàn)程不會(huì)自動(dòng)創(chuàng)建調(diào)用 Looper.prepare()來(lái)給線(xiàn)程創(chuàng)建消息循環(huán),然后再通過(guò),Looper.loop()來(lái)使消息循環(huán)起作用。
第二種方法就是
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"ceshi23333",Toast.LENGTH_SHORT).show();
}
});
new Thread(){
}.start();
Toast的代碼創(chuàng)建在Runnable中,然后在需要Toast時(shí),把這個(gè)Runnable對(duì)象傳給runOnUiThread(Runnable)。 這樣Runnable對(duì)像就能在ui程序中被調(diào)用。如果當(dāng)前線(xiàn)程是UI線(xiàn)程,那么行動(dòng)是立即執(zhí)行
第三種方法和第一張差不多
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//這里寫(xiě)你的Toast代碼
}
};
new Thread(){
@Override
public void run() {
super.run();
mHandler.sendEmptyMessage(0);
}
}.start();
另:在非主線(xiàn)程中直接new Handler() 會(huì)報(bào)如下的錯(cuò)誤: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線(xiàn)程中默認(rèn)沒(méi)有創(chuàng)建Looper對(duì)象,需要先調(diào)用Looper.prepare()啟用Looper。