稍微纖細一點兒的信息是: Handler (android.os.Handler) {215ddea8} sending message to a Handler on a dead thread。java
在另外一次在IntentService裏使用MediaPlayer 播放鈴聲也再現錯誤,信息是:Handler) {42414500} sending message to a Handler on a dead thread。android
本次的完整信息是:app
W/ActivityManager( 1394): getTasks: caller 10034 is using old GET_TASKS but privileged; allowing W/MessageQueue( 7666): Handler (android.os.Handler) {215ddea8} sending message to a Handler on a dead thread W/MessageQueue( 7666): java.lang.IllegalStateException: Handler (android.os.Handler) {215ddea8} sending message to a Handler on a dead thread W/MessageQueue( 7666): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325) W/MessageQueue( 7666): at android.os.Handler.enqueueMessage(Handler.java:635) W/MessageQueue( 7666): at android.os.Handler.sendMessageAtTime(Handler.java:604) W/MessageQueue( 7666): at android.os.Handler.sendMessageDelayed(Handler.java:574) W/MessageQueue( 7666): at android.os.Handler.postDelayed(Handler.java:398) W/MessageQueue( 7666): at com.bandwidthx.library.l.a(SourceFile:367) W/MessageQueue( 7666): at com.bandwidthx.library.l.B(SourceFile:357) W/MessageQueue( 7666): at com.bandwidthx.library.BxApproval.aZ(SourceFile:4563) W/MessageQueue( 7666): at com.bandwidthx.library.BxApproval.aT(SourceFile:4440) W/MessageQueue( 7666): at com.bandwidthx.library.BxApproval.aS(SourceFile:4431) W/MessageQueue( 7666): at com.bandwidthx.library.BxApproval.aG(SourceFile:4044) W/MessageQueue( 7666): at com.bandwidthx.library.l.a(SourceFile:1320) W/MessageQueue( 7666): at com.bandwidthx.library.l.j(SourceFile:1275) W/MessageQueue( 7666): at com.bandwidthx.library.q.w(SourceFile:2280) W/MessageQueue( 7666): at com.bandwidthx.library.q.a(SourceFile:3399) W/MessageQueue( 7666): at com.bandwidthx.library.q.a(SourceFile:3103) W/MessageQueue( 7666): at com.bandwidthx.library.q$1.a(SourceFile:1959) W/MessageQueue( 7666): at com.bandwidthx.library.q$1.doInBackground(SourceFile:1928) W/MessageQueue( 7666): at android.os.AsyncTask$2.call(AsyncTask.java:292) W/MessageQueue( 7666): at java.util.concurrent.FutureTask.run(FutureTask.java:237) W/MessageQueue( 7666): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) W/MessageQueue( 7666): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) W/MessageQueue( 7666): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) W/MessageQueue( 7666): at java.lang.Thread.run(Thread.java:818)
估計Looper啊,MessageQueue之類的,已經被大家說爛了。ide
我就簡單歸納一句(固然下面也有很詳細的)oop
一旦一個線程的消息循環退出後,不能再給其發送消息,不然會有RuntimeException拋出。
就是你通常性看到的:post
"RuntimeException: Handler{xxxx} sending message to a Handler on a dead thread"。
通常性的,若是是你實現本身的Looper和Handler,建議在Looper.prepare()後,調用Looper.myLooper()來獲取對這個線程Looper的引用。
用途:0. 能夠調用quit()終止服務線程 1. 接收消息時檢查消息循環是否已經退出ui
值得一說的是: 線程終止了,有時候並非你本身終止的,極可能能是系統的某個正常的時序致使的(只是你沒有注意到這個次序,而後寫代碼的時候沒有注意)this
上面已經說清楚了,下面是詳細信息&囉嗦分界線(下面再多說也就這麼回事兒,別往下看了,下面就說一個簡單的案例)spa
案例:(使用 IntentService 的發送短信,代碼以下)線程
在IntentService內部實際是開啓的一個工做線程。
@Override protected void onHandleIntent(Intent intent) { Bundle data = intent.getExtras(); String[] recipients = null; String message = getString(R.string.unknown_event); String name = getString(R.string.app_name); if (data != null && data.containsKey(Constants.Services.RECIPIENTS)) { recipients = data.getStringArray(Constants.Services.RECIPIENTS); name = data.getString(Constants.Services.NAME); message = data.getString(Constants.Services.MESSAGE); for (int i = 0; i < recipients.length; i++) { if(!StringUtils.isNullOrEmpty(recipients[i])) { try { Intent sendIntent = new Intent(this, SMSReceiver.class); sendIntent.setAction(Constants.SMS.SEND_ACTION); PendingIntent sendPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, sendIntent, PendingIntent.FLAG_UPDATE_CURRENT); Intent deliveryIntent = new Intent(this, SMSReceiver.class); deliveryIntent.setAction(Constants.SMS.DELIVERED_ACTION); PendingIntent deliveryPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, deliveryIntent, PendingIntent.FLAG_UPDATE_CURRENT); SmsManager.getDefault().sendTextMessage(recipients[i].trim(), null, "[" + name + "] " + message, sendPendingIntent, deliveryPendingIntent); } catch (Exception e) { Log.e(TAG, "sendTextMessage", e); e.printStackTrace(); Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); MainActivity.instance.writeToLogFile(e.getMessage(), System.currentTimeMillis()); } } } } }
(明眼人一看就知道,那個catch語句寫的有問題)
catch (Exception e) { Log.e(TAG, "sendTextMessage", e); e.printStackTrace(); Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); MainActivity.instance.writeToLogFile(e.getMessage(), System.currentTimeMillis()); }
結果就報錯了:
Google了一下資料,網址上是這麼說的:
(大意翻譯: 緣由是你在一個由intentService控制生命週期的線程種建立了toast,手機先會顯示這個toast,以後仍是用這個線程的handler去隱藏&關閉這個toast;可是當onHandleIntent結束的時候,這個線程就掛了,而後綁定在這個線程上的handler也就不存在了,完事兒這個toast就關閉不了了,怎麼辦呢?發給主線程吧,它的toast的Handeler還存活,能夠隱藏toast)
解決方案呢?(簡單,粗暴)
爲何會向一個死線程發消息呢?
由於onHandleIntent(Intent intent)結束了這個線程也就沒有了,到底怎麼回事兒?
handler沒有了,因此cannot hide toast?
若是我沒有記錯的話,這個toast的生命週期應該是NotificationManagerService控制的吧?
(IntentService源碼剖析)----TODO
(Toast生命週期源碼剖析)----TODO