Toast "在已死线程上向Handler发送消息"

20

我正在尝试通过Toast显示一个简单的消息,但是遇到了运行时异常“在已经停止的线程上向Handler发送消息”。尝试显示Toast消息的类扩展了IntentService。该类(C2DMReceiver)实际上来自C2DM的ChromeToPhone示例。以下是该方法:

/**
 * Called when a cloud message has been received.
 */
@Override
public void onMessage(Context context, Intent intent) {
    Log.i(LOG_TAG, "A message notification has occured with the cloud.");

        Log.i(LOG_TAG, "Showing toast message of the broadcast...");
        Toast toast = Toast.makeText(context, "Some text", Toast.LENGTH_LONG);
        toast.show();

        Log.i(LOG_TAG, "Sending notification of the broadcast...");
        LauncherUtils.generateNotification(this, "this is where the text would go.", "Broadcast", intent);

    }
}

我认为由于这个类扩展了IntentService,应该可以通过这种方式在这里请求一个简单的Toast消息。这不正确吗?

6个回答

28

这是由于Android框架中AsyncTask的一个bug造成的。AsyncTask.java文件中有以下代码:

private static final InternalHandler sHandler = new InternalHandler();

它期望在主线程上初始化,但是不能保证这一点,因为它将在任何触发类运行静态初始化器的线程上进行初始化。我重现了Handler引用工作线程的问题。

导致这种情况发生的常见模式是使用IntentService类。C2DM示例代码就是这样做的。

一个简单的解决方法是在应用程序的onCreate方法中添加以下代码:

Class.forName("android.os.AsyncTask");

这将强制AsyncTask在主线程中初始化。我在Android错误数据库中报告了一个错误。请参见http://code.google.com/p/android/issues/detail?id=20915


4
我做了这件事,但没有任何帮助。每次收到c2dm通知时,它都会记录相同的异常。 - User

24
public class NetworkService extends IntentService {
    Handler mMainThreadHandler = null;

    public NetworkService() {
        super(NetworkService.class.getName());

        mMainThreadHandler = new Handler();
    }

    @Override
    protected void onHandleIntent(Intent arg) {
        System.out.printf("Network service intent handling: %s\n", arg.toString());
        mMainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "BusyBox updated", Toast.LENGTH_LONG).show();
            }
        });
    }
}

3
正如乔纳森·珀洛在他的回答中所说,Android框架存在一个错误,会导致在非主线程创建的Toast,在未连接到主线程的Handler中运行。通过在构造函数中在主线程上创建mMainThreadHandler,我们确保Toast在主线程上运行。这样我们就绕过了这个错误。 - sulai

12

另一种在后台线程中实现在主线程中显示提示消息的方法是使用一个小的实用程序方法。关键是创建一个附加到主线程 looper(Looper.getMainLooper())的 handler。

public class ToastUtils {

    public static void showToastInUiThread(final Context ctx,
        final int stringRes) {

        Handler mainThread = new Handler(Looper.getMainLooper());
        mainThread.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(ctx, ctx.getString(stringRes), Toast.LENGTH_SHORT).show();
            }
       });
    }
}

0

您可以像这样在UI线程上运行您的代码:

runOnUiThread(new Runnable() {
    public void run() {
    try {
  //YOUR CODE
  } catch (Exception e) {
   Log.d(TAG, e.getMessage());
  }
 }
});

这应该可以正常工作


0

onMessage方法没有在主线程上调用。

因此,您需要创建一个新的Handler。

应用以下代码:

public class GCMIntentService extends GCMBaseIntentService
{
   Handler handler;
   public GCMIntentService()
   {
      handler = new Handler();
   }
}

0

你必须从主线程中显示toast,否则它不会显示在屏幕上。Handler在创建它的线程中执行。如果你在IntentService的onCreate方法中创建handler,并在发送消息时使用它,它应该按预期工作。


我不确定我理解了,因为在toast对象中没有直接引用handler。我已经实现了onCreate方法,但仍然出现死线程异常。在这种情况下创建handler的正确方式是什么?这是我的onCreate,带有protected Handler handler; - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接