2010-11-25 111 views
16

我有一个AsyncTask,它提取一些数据,然后用这个新数据更新UI。几个月来一直工作正常,但我最近添加了一项功能,可以在有新数据时显示通知。现在,当我的应用程序通过通知启动时,有时候我会得到此异常,并且不会调用onPostExecuteonPostExecute未在AsyncTask中调用(处理程序运行时异常)

这是当应用启动会发生什么:

1)展开UI,发现意见

2)取消报警(通过AlarmManager)来检查新数据和解除报警。 (这样,如果用户禁用该警报,则在他/她下次重新启动之前取消该警报。)

3)启动AsyncTask。如果应用程序是从通知启动的,请传入一点数据,然后取消通知。

我被困在什么可能会导致此异常。看起来这个例外是来自AsyncTask的代码,所以我不知道如何解决它。

谢谢!

这里是个例外:

I/My App( 501): doInBackground exiting 
W/MessageQueue( 501): Handler{442ba140} sending message to a Handler on a dead thread 
W/MessageQueue( 501): java.lang.RuntimeException: Handler{442ba140} sending message to a Handler on a dead thread 
W/MessageQueue( 501): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:179) 
W/MessageQueue( 501): at android.os.Handler.sendMessageAtTime(Handler.java:457) 
W/MessageQueue( 501): at android.os.Handler.sendMessageDelayed(Handler.java:430) 
W/MessageQueue( 501): at android.os.Handler.sendMessage(Handler.java:367) 
W/MessageQueue( 501): at android.os.Message.sendToTarget(Message.java:348) 
W/MessageQueue( 501): at android.os.AsyncTask$3.done(AsyncTask.java:214) 
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:252) 
W/MessageQueue( 501): at java.util.concurrent.FutureTask.set(FutureTask.java:112) 
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:310) 
W/MessageQueue( 501): at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068) 
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561) 
W/MessageQueue( 501): at java.lang.Thread.run(Thread.java:1096) 

编辑:这是我在我的主要活动(一经通知开)onCreate方法。我省略了一些onClickListeners以节省空间。我不认为它们应该有任何效果,因为它们所连接的按钮没有被按下。编辑2:我一直在挖掘Android源代码追踪异常来自哪里。这是行456和HandlersendMessageAtTime 457:

msg.target = this; 
sent = queue.enqueueMessage(msg, uptimeMillis); 

这是enqueueMessageMessageQueue

final boolean enqueueMessage(Message msg, long when) { 
     if (msg.when != 0) { 
      throw new AndroidRuntimeException(msg 
        + " This message is already in use."); 
     } 
     if (msg.target == null && !mQuitAllowed) { 
      throw new RuntimeException("Main thread not allowed to quit"); 
     } 
     synchronized (this) { 
      if (mQuiting) { 
       RuntimeException e = new RuntimeException(
        msg.target + " sending message to a Handler on a dead thread"); 
       Log.w("MessageQueue", e.getMessage(), e); 
       return false; 
      } else if (msg.target == null) { 
       mQuiting = true; 
      } 

      msg.when = when; 
      //Log.d("MessageQueue", "Enqueing: " + msg); 
      Message p = mMessages; 
      if (p == null || when == 0 || when < p.when) { 
       msg.next = p; 
       mMessages = msg; 
       this.notify(); 
      } else { 
       Message prev = null; 
       while (p != null && p.when <= when) { 
        prev = p; 
        p = p.next; 
       } 
       msg.next = prev.next; 
       prev.next = msg; 
       this.notify(); 
      } 
     } 
     return true; 
    } 

我有点无所适从mQuiting是,但它看起来像以前的时间enqueueMessage被称为msg.target为空。

+0

创建您的AsyncTask线程必须已经退出。你如何创建你的AsyncTask? – 2010-11-25 19:50:26

+0

我用代码更新了我的问题。当我仍然可以看到加载屏幕时,我看不到Activity如何退出。 – Computerish 2010-11-30 21:45:35

+0

我们需要看到更多的代码。 – Falmarri 2010-11-30 22:00:39

回答

17

为了将Jonathan Perlow的解决方案概括为他明确指出的错误,我在任何使用AsyncTask的类中使用了以下内容。 looper/handler/post是您如何在Android应用程序中的任何位置的UI线程上运行某些内容,而无需传递活动或其他上下文的句柄。在类中添加这个静态初始化块:

{ // https://stackoverflow.com/questions/4280330/onpostexecute-not-being-called-in-asynctask-handler-runtime-exception 
    Looper looper = Looper.getMainLooper(); 
    Handler handler = new Handler(looper); 
    handler.post(new Runnable() { 
     public void run() { 
     try { 
      Class.forName("android.os.AsyncTask"); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
     } 
    }); 
} 

当试图运行单元测试时,我们遇到了问题。我找到了一个解决方法,但没有具体确定问题。我们只知道在Android JUnit测试中试图使用AsyncTask <>导致onPostExecute()不被调用。现在我们知道为什么。

这篇文章展示了如何在Android JUnit测试运行多线程异步代码:

Using CountDownLatch in Android AsyncTask-based JUnit tests

对于非UI单元测试使用,我创建android.test.InstrumentationTestCase的一个简单的子类。它有一个“ok”标志和一个CountDownLatch。reset()或reset(count)会创建一个新的CountDownLatch({1,count})。 good()在锁存器上设置ok = true,count--和calls.countDown()。 bad()设置ok = false,并一路倒计时。 waitForIt(秒)等待超时或coundown锁定为零。然后它调用assertTrue(ok)。

然后测试,如:

someTest() { 
    reset(); 
    asyncCall(args, new someListener() { 
    public void success(args) { good(); } 
    public void fail(args) { bad(); } 
    }); 
    waitForIt(); 
} 

因为的AsyncTask静态初始化错误的,我们必须运行我们的内部一个Runnable传递给runTestOnUiThread()实际测试。通过如上所述的适当的静态初始化,除非正在测试的调用需要在UI线程上运行,否则这不是必需的。

其他成语我现在用的是测试当前线程是否是UI线程,然后运行适当的线程上的请求的动作不管。有时候,允许调用者请求同步与异步,必要时覆盖它是有意义的。例如,网络请求应始终在后台线程上运行。在大多数情况下,AsyncTask线程池是完美的。只要意识到只有一定的数字会立即运行,阻止了额外的请求。为了测试当前线程是否是UI线程:

boolean onUiThread = Looper.getMainLooper().getThread() == Thread.currentThread(); 

然后使用一个简单的子类(只是doInBackground()和onPostExecute()需要)的AsyncTask <的>到在非UI线程或处理器上运行。 post()或postDelayed()在UI线程上运行。

给发话运行同步或异步的选项看起来像(获得本地有效onUiThread值这里没有显示,添加为以上地方布尔值):

void method(final args, sync, listener, callbakOnUi) { 
    Runnable run = new Runnable() { public void run() { 
    // method's code... using args or class members. 
    if (listener != null) listener(results); 
    // Or, if the calling code expects listener to run on the UI thread: 
    if (callbackOnUi && !onUiThread) 
     handler.post(new Runnable() { public void run() {listener()}}); 
    else listener(); 
    }; 
    if (sync) run.run(); else new MyAsync().execute(run); 
    // Or for networking code: 
    if (sync && !onUiThread) run.run(); else new MyAsync().execute(run); 
    // Or, for something that has to be run on the UI thread: 
    if (sync && onUiThread) run.run() else handler.post(run); 
} 

而且,使用的AsyncTask可以做得很简单简洁。使用RunAsyncTask.java的定义之下,然后写这样的代码:

RunAsyncTask rat = new RunAsyncTask(""); 
    rat.execute(new Runnable() { public void run() { 
     doSomethingInBackground(); 
     post(new Runnable() { public void run() { somethingOnUIThread(); }}); 
     postDelayed(new Runnable() { public void run() { somethingOnUIThreadInABit(); }}, 100); 
    }}); 

或者干脆:新RunAsyncTask( “”)执行(新的Runnable(){公共无效的run(){doSomethingInBackground();} });

RunAsyncTask.java:

package st.sdw; 
import android.os.AsyncTask; 
import android.util.Log; 
import android.os.Debug; 

public class RunAsyncTask extends AsyncTask<Runnable, String, Long> { 
    String TAG = "RunAsyncTask"; 
    Object context = null; 
    boolean isDebug = false; 
    public RunAsyncTask(Object context, String tag, boolean debug) { 
     this.context = context; 
     TAG = tag; 
     isDebug = debug; 
    } 
    protected Long doInBackground(Runnable... runs) { 
     Long result = 0L; 
     long start = System.currentTimeMillis(); 
     for (Runnable run : runs) { 
     run.run(); 
     } 
     return System.currentTimeMillis() - start; 
    } 
    protected void onProgressUpdate(String... values) {  } 
    protected void onPostExecute(Long time) { 
     if (isDebug && time > 1) Log.d(TAG, "RunAsyncTask ran in:" + time + " ms"); 
     v = null; 
    } 
    protected void onPreExecute() {  } 
    /** Walk heap, reliably triggering crash on native heap corruption. Call as needed. */ 
    public static void memoryProbe() { 
     System.gc(); 
     Runtime runtime = Runtime.getRuntime(); 
     Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/1048576.0; 
     Double available = new Double(Debug.getNativeHeapSize())/1048576.0; 
     Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0; 
     long maxMemory = runtime.maxMemory(); 
     long totalMemory = runtime.totalMemory(); 
     long freeMemory = runtime.freeMemory(); 
    } 
} 
40

这是由于在的AsyncTask Android框架的错误。的AsyncTask。Java有以下代码:

private static final InternalHandler sHandler = new InternalHandler(); 

据预计,在主线程中进行初始化,但不能保证,因为它会在哪个线程碰巧导致类运行其静态初始化进行初始化。我在Handler引用一个工作线程的情况下转载了这个问题。

导致这种情况发生的常见模式是使用IntentService类。 C2DM示例代码执行此操作。

一个简单的解决方法是将下面的代码添加到应用程序的onCreate方法:

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

这将迫使的AsyncTask在主线程进行初始化。我在android bug数据库中提出了一个错误。见http://code.google.com/p/android/issues/detail?id=20915

0

尽管这并不直接回答OP的问题,但我认为它对于在运行测试时搜索同一问题的解决方案的人员非常有用。

总的来说,Peter Knego's answer总结得很好。

我的问题是专门针对使用Android的AsyncTask进行API调用的Activity之外的类上运行测试。该类在应用程序中工作,因为它被Activity使用,但我想运行一个测试来测试实际的API调用。

虽然Jonathan Perlow's answer工作,但我不喜欢仅仅因为测试而引入对我的应用程序的更改。

因此,在测试的情况下,可以使用runTestOnUiThread@UiThreadTest不能使用,因为您无法等待使用该注释的测试的结果)。

public void testAPICall() throws Throwable { 
    this.runTestOnUiThread(new Runnable() { 
     public void run() { 
      underTest.thisMethodWillMakeUseOfAnAsyncTaskSomehow(); 
     }   
    }); 

    // Wait for result here * 
    // Asserts here 
} 

虽然有时候,特别是在功能测试中,Jonathan Perlow的答案似乎是唯一可行的。


* Take a look here查看如何暂停测试等待结果。

2

我不得不采用Android 4.0.4与IntentService的设备上同样的问题,解决了它作为SDW用的Class.forName( “android.os.AsyncTask” 说)。在Android 4.1.2,4.4.4或5.0上也没有发生。我不知道这是否谷歌从2011年开始

解决马丁西方的问题,我加入这个代码在我的应用程序的onCreate和它的工作:

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { 
     try { 
      Class.forName("android.os.AsyncTask"); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 

这将是很好知道是否需要改变的Android版本到别的东西。

相关问题