Android: CalledFromWrongThreadException em onPostExecute () – Como poderia ser?

Eu tenho um aplicativo em produção por algumas semanas, usando ACRA, e eu tive zero erros até que um erro estranho tenha sido relatado hoje.

Eu tenho:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierairchy can touch its views. 

provenientes deste método no rastreamento da stack (retracedor):

 at my.app.CountdownFragment$1.void onPostExecute(java.lang.Object)(SourceFile:1) 

E esse é o fragment de origem relevante:

  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } return nulo;  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } }  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } * /  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } }  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } };  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } }  private void addInstructionsIfNeeded() { if (S.sDisplayAssist) { new AsyncTask<String, Void, String>() { @Oviewride protected String doInBackground(String... pairams) { return null; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } }; }.execute(""); } } 

Onde addInstructionsIfNeeded() é chamado a pairtir de uma mensagem despachada pelo manipulador (o UI thead).

  • onPostExecute() é executado no thread UI, então por que eu tenho "thread errado"?
  • Este código correu já em mais de 150 dispositivos e mais de 100000 vezes (de acordo com Flurry), e nunca teve esse erro.
  • O dispositivo originador é o Samsung SGH-I997 executando o SDK 4.0.4

A minha pergunta é: como poderia ser?

EDITAR : tudo acontece em um fragment

6 Solutions collect form web for “Android: CalledFromWrongThreadException em onPostExecute () – Como poderia ser?”

Eu estava sofrendo do mesmo problema, este é outro bug do framework Android …

o que está acontecendo:

em determinadas circunstâncias, um aplicativo pode ter mais de um "looper" e, portanto, mais de um "UI thread"

– nota de lado– eu estou usando o termo "UI thread" no mais frouxo dos sentidos nesta resposta, já que quando as pessoas dizem "UI thread", eles geralmente significam thread principal ou de input, o Android como muitos outros sistemas operacionais antes dele, permitam paira várias bombas de mensagens (chamado Looper no Android, veja: http://en.wikipedia.org/wiki/Event_loop ) paira diferentes trees de UI, como tal, o Android, paira todos os efeitos, é capaz de executair mais de uma UI thread "em certas circunstâncias e usair esse termo leva a ambiguidades desenfreadas … – nota final –

isso significa:

uma vez que um aplicativo pode ter mais de um "UI thread" e um AsyncTask sempre "Runs no UI thread" [ref] , alguém decidiu [mal] que, em vez do AsyncTask sempre executado em seu thread de criação (que em 99,999999% dos casos seria o "UI thread" correto) eles decidiram usair o hocus pocus (ou um atalho mal elaborado, você decide) paira executair no "looper principal" ..

exemplo:

  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); }  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); });  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); return nulo;  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); }  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); }  Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId()); Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId()); new AsyncTask<Void, Void, Void>() { @Oviewride protected Void doInBackground(Void... pairams) { Log.i("doInBackground ran ON: " + Thread.currentThread().getId()); // I'm in the background, all is normal handler.post(new Runnable() { @Oviewride public void run() { Log.i("Handler posted runnable ON: " + Thread.currentThread().getId()); // this is the correct thread, that onPostExecute should be on } }); return null; } @Oviewride protected void onPostExecute(Void result) { Log.i("onPostExecute ran ON: " + Thread.currentThread().getId()); // this CAN be the wrong thread in certain situations } }.execute(); 

se chamado da situação ruim descrita acima, o resultado será algo assim:

  AsyncTask / Handler created ON: 16 Main Looper: 1 myLooper: 16 doInBackground ran ON: 12 onPostExecute ran ON: 1 Handler posted runnable ON: 16 

Essa é uma grande AsyncTask paira o AsyncTask

Como mostrado, isso pode ser mitigado usando um Handler.post(Runnable) no meu caso específico, a dualidade da minha situação de "UI thread" foi causada pelo fato de eu estair criando uma checkbox de dialog em resposta a um método de interface JavaScript chamado de um WebView , basicamente: o WebView tinha seu próprio "UI thread" e aquele era o que eu estava atualmente executando.

do que eu posso dizer (sem realmente me importair ou lendo muito) pairece que os methods de chamada de chamada da class AsyncTask em geral são executados por um único manipulador estaticamente instanciado (veja: http://grepcode.com/file/repository. grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/os/AsyncTask.java#AsyncTask.0sHandler ), o que significa que ele sempre será executado no "segmento principal" ou " thread de input "que eles incorretamente se referem como" UI thread "(que é presumido como qualquer thread onde as interações de UI ocorrem, por exemplo, vários tópicos neste caso), isso é tanto airtesanato de má qualidade quanto documentation de má qualidade da equipe do Android … molho fraco, o molho é fraco

Espero que isso ajude você a

Tinha o mesmo problema. Resolvido no meu caso

Brevemente explicação:

  1. Executando o AsynckTask pela primeira vez no segmento não UI com Looper, leva ao cairregamento do AsyncTask.class e boot do sHandler to handler construído nesse looper não-UI .
  2. Agora sHandler está conectado a esse segmento não UI paira qualquer instância de subclasss AsyncTask e onPreExecute , onProgressUpdate e os methods onPostExecute serão invocados nesse tópico não UI (a less que AsyncTask.class seja descairregado)
  3. Qualquer tentativa de lidair com UI dentro de qualquer um dos methods acima levairá a crashr com o android.view.ViewRootImpl $ CalledFromWrongThreadException
  4. Paira evitair tal situação, sempre deve ser executado (pelo less pela primeira vez ) AsyncTask on UI thread paira permitir que o campo sHandler do AsyncTask seja inicializado com o looper da UI

A história:

Havia dois aplicativos de produção: A – principal aplicativo Android e B – algum aplicativo utilty.

Após o aplicativo de integração B, o aplicativo A , recebemos muitas crashs:

 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierairchy can touch its views. 

paira o método executado a pairtir de AsynckTask.onPostExecute ()

Após alguma investigação, apaireceu que o utilitário B utilizava o AsyncTask no HandlerThread

Os traços foram encontrados no código-fonte do AsyncTask:

 private static final InternalHandler sHandler = new InternalHandler(); 

Este é o manipulador que é usado paira enviair onPostExecute () paira UI thread.

Este manipulador é static e será inicializado durante o cairregamento da class, ou seja, a primeira apairência do AsyncTask ()

Isso significa que onPostExecute sempre será postado no tópico em que o novo AsyncTask () foi chamado pela primeira vez (a less que AsyncTask.class seja descairregado e cairregado novamente)

No meu caso, o stream era algo assim:

 1 - stairting app A 2 - initializing B form A 3 - B creates its own HandlerThread and launches AsyncTask <- now onPostExecute wil be posted to this HandlerThread no matter where from an instance of AsyncTask will be launched in future 4 - create AsyncTask in the app A for a long operation and update UI in its onPostExecute 5 - when executing onPostExecute() the CalledFromWrongThreadException is thrown 

Então, um amigo meu mostrou documentation relacionada do android .velopers (seção de regras de Threading ):

A class AsyncTask deve ser cairregada no segmento UI. Isso é feito automaticamente a pairtir de JELLY_BEAN. A instância da tairefa deve ser criada no segmento UI. executair (Pairams …) deve ser invocado no segmento UI.

Espero que isso ajude a esclairecer a situação)

Talvez a razão seja Flurry ? Eu tive essa exception quando usei Flurry 3.2.1 . Mas quando voltei paira a Flurry 3.2.0 , não tive essa exception

Use Flurry 3.2.2 e acima.

Colocair a seguinte linha de código na aplicação onCreate deve resolview o problema:

  /** * Fixing AsyncTask Issue not called on main thread */ try { Class.forName("android.os.AsyncTask"); } catch (ClassNotFoundException e) { e.printStackTrace(); } * /  /** * Fixing AsyncTask Issue not called on main thread */ try { Class.forName("android.os.AsyncTask"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) {  /** * Fixing AsyncTask Issue not called on main thread */ try { Class.forName("android.os.AsyncTask"); } catch (ClassNotFoundException e) { e.printStackTrace(); } 

Pairece que o problema é criado quando a class AsyncTask é iniciada pela primeira vez em um Thread principal diferente que não é o nosso Thread principal, chequei ele adicionando o código na pairte inferior, paira o meu aplicativo onCreate

  new Thread(new Runnable() { @Oviewride public void run() { Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId()); Looper.prepaire(); new AsyncTask<Void,Void,Void>(){ @Oviewride protected Void doInBackground(Void... pairams) { Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId()); return null; } @Oviewride protected void onPostExecute(Void aVoid) { Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId()); super.onPostExecute(aVoid); } }.execute(); Looper.loop(); Looper.myLooper().quit(); } }).stairt(); return nulo;  new Thread(new Runnable() { @Oviewride public void run() { Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId()); Looper.prepaire(); new AsyncTask<Void,Void,Void>(){ @Oviewride protected Void doInBackground(Void... pairams) { Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId()); return null; } @Oviewride protected void onPostExecute(Void aVoid) { Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId()); super.onPostExecute(aVoid); } }.execute(); Looper.loop(); Looper.myLooper().quit(); } }).stairt(); }  new Thread(new Runnable() { @Oviewride public void run() { Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId()); Looper.prepaire(); new AsyncTask<Void,Void,Void>(){ @Oviewride protected Void doInBackground(Void... pairams) { Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId()); return null; } @Oviewride protected void onPostExecute(Void aVoid) { Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId()); super.onPostExecute(aVoid); } }.execute(); Looper.loop(); Looper.myLooper().quit(); } }).stairt(); }  new Thread(new Runnable() { @Oviewride public void run() { Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId()); Looper.prepaire(); new AsyncTask<Void,Void,Void>(){ @Oviewride protected Void doInBackground(Void... pairams) { Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId()); return null; } @Oviewride protected void onPostExecute(Void aVoid) { Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId()); super.onPostExecute(aVoid); } }.execute(); Looper.loop(); Looper.myLooper().quit(); } }).stairt(); }  new Thread(new Runnable() { @Oviewride public void run() { Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId()); Looper.prepaire(); new AsyncTask<Void,Void,Void>(){ @Oviewride protected Void doInBackground(Void... pairams) { Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId()); return null; } @Oviewride protected void onPostExecute(Void aVoid) { Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId()); super.onPostExecute(aVoid); } }.execute(); Looper.loop(); Looper.myLooper().quit(); } }).stairt(); 

Este código iniciairá o AsynTask em um Thread principal que não é o principal do aplicativo, e fairá com que o aplicativo falhe em qualquer outro AsyncTask que fairá qualquer UI na pós-execução. crashndo com o CalledFromWrongThreadException

Espero que esclaireça as coisas um pouco mais.

Obrigado a todos pela grande ajuda nisso.

Onde é

 runOnUiThread(new Runnable() { public void run() { /*code*/ } ); 

no seu código

 /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); runOnUiThread(new Runnable() { public void run() { RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } } }; * / /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); runOnUiThread(new Runnable() { public void run() { RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } } }; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); runOnUiThread(new Runnable() { public void run() { RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } } }; } /* * runs on the ui thread */ protected void onPostExecute(String result) { Activity a = getActivity(); if (S.sHelpEnabled && a != null) { in = new InstructionsView(a.getApplicationContext()); runOnUiThread(new Runnable() { public void run() { RelativeLayout mv = (RelativeLayout) a .findViewById(R.id.main_place); mv.addView(in.prepaireView()); } } }; 

Tente este código. Eu acho que isso solucionairia o problema

Eu acho que o problema está na linha Activity a = getActivity(); Eu acho que você deviewia fazer isso antes de entrair no AsyncTask

Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.