new Thread(){
public void run(){
Toast.makeText(public_log.this,"圖片不存在",Toast.LENGTH_SHORT).show();
}
}
如果像這樣直接在子線程中彈出Toast,程序會報(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方法好像沒有什么不對赡矢,那么繼續(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
}
}
好像沒什么不對 但是看getService()不對勁就點(diǎn)擊進(jìn)去看一下
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
這里還是看不出有什么問題 然而show()里面這個(gè)又一次引起了我的注意
TN tn = mTN;
我點(diǎn)擊進(jìn)去查看源碼,好家伙終于發(fā)現(xià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ò)了废岂,對祖搓,就是Handler不能再子線程里運(yùn)行的 因?yàn)樽泳€程沒有創(chuàng)建Looper.prepare(); 所以就報(bào)錯(cuò)了。主線程不需要調(diào)用泪喊,是因?yàn)橹骶€程已經(jīng)默認(rèn)幫你調(diào)用了棕硫。
可以看到一個(gè)Toast的創(chuàng)建需要依賴Handler髓涯。那么 我不要 我不要 我一定要在子線程使用Toast那怎么辦袒啼。
其實(shí)很簡單,它卻什么就給它什么纬纪。
第一種方法
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線程默認(rèn)創(chuàng)建之外蚓再,其他線程不會自動創(chuàng)建調(diào)用 Looper.prepare()來給線程創(chuàng)建消息循環(huán),然后再通過包各,Looper.loop()來使消息循環(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對象傳給runOnUiThread(Runnable)。 這樣Runnable對像就能在ui程序中被調(diào)用肚菠。如果當(dāng)前線程是UI線程,那么行動是立即執(zhí)行
第三種方法和第一張差不多
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//這里寫你的Toast代碼
}
};
new Thread(){
@Override
public void run() {
super.run();
mHandler.sendEmptyMessage(0);
}
}.start();
另:在非主線程中直接new Handler() 會報(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() 原因是非主線程中默認(rèn)沒有創(chuàng)建Looper對象,需要先調(diào)用Looper.prepare()啟用Looper诉探。