Nie można utworzyć programu obsługi wewnątrz wątku, który nie wywołał Looper.prepare ()

81

Mam aktywność, w której mam zajęcia.

text=new Dynamictext(...);
text.setText("txt");

w mojej java DynamicText mam ten kod:

public void setText(String text) {
    this.text=text;
    new asyncCreateText().execute();
    //this.createText(text);
}

//private Handler handler = new Handler();

private class asyncCreateText extends AsyncTask<Void, Void, Void> 
{
    @Override
    protected Void doInBackground(Void... unused) {
        return null;
    }

    @Override
    protected void onPostExecute(Void unused) {

    }
}

Dostaję:

BŁĄD / AndroidRuntime (5176): spowodowane przez: java.lang.RuntimeException: nie można utworzyć modułu obsługi wewnątrz wątku, który nie wywołał Looper.prepare ()

Jak mogę sobie poradzić z tym błędem?

ERROR/AndroidRuntime(5370): java.lang.ExceptionInInitializerError
ERROR/AndroidRuntime(5370):     at com.l.start.DynamicText.setText(DynamicText.java:125)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.initfonts(OpenGLRenderer.java:168)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.init(OpenGLRenderer.java:119)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.onSurfaceChanged(OpenGLRenderer.java:90)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1120)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:975)

ERROR/AndroidRuntime(5370): Caused by: java.lang.RuntimeException: 
    Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(5370):     at android.os.Handler.<init>(Handler.java:121)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
ERROR/AndroidRuntime(5370):     ... 6 more
lacas
źródło
Czy możesz opublikować pełny ślad stosu? Wygląda na to, że wywołujesz jakąś metodę interfejsu użytkownika z wątku w tle lub coś w tym stylu.
alexanderblom
1
To szczegółowo wyjaśni problem.
mjosh

Odpowiedzi:

146

Błąd jest oczywisty ... doInBackground()działa w wątku w tle, który, ponieważ nie jest przeznaczony do zapętlenia, nie jest podłączony do Looper.

Najprawdopodobniej w ogóle nie chcesz bezpośrednio tworzyć instancji programu obsługi ... wszelkie dane zwrócone przez Twoją doInBackground()implementację zostaną przekazane, onPostExecute()które działają w wątku interfejsu użytkownika.

   mActivity = ThisActivity.this; 

    mActivity.runOnUiThread(new Runnable() {
     public void run() {
     new asyncCreateText().execute();
     }
   });

DODANO WEDŁUG ŚLADU STOSU POJAWIAJĄCEGO SIĘ W PYTANIU:

Wygląda na to, że próbujesz rozpocząć AsyncTaskod wątku renderującego GL ... nie rób tego, bo oni też nigdy nie będą Looper.loop(). AsyncTasks są tak naprawdę zaprojektowane do uruchamiania tylko z wątku interfejsu użytkownika.

Najmniej uciążliwą poprawką byłoby prawdopodobnie wezwanie Activity.runOnUiThread()osoby, Runnablektóra uruchomi Twój AsyncTask.

Reuben Scratton
źródło
1
Odpowiedź jest całkowicie poprawna. Jednak w moim przypadku musiałem tylko usunąć niektóre Toasty (próbując uruchomić w wątku interfejsu użytkownika) z mojej niestandardowej klasy FtpUpload AsyncTask i voila.
Josh
zbliża się do mnie ten sam błąd, jestem zdezorientowany, jak go rozwiązać, czytam opis, ale nie rozumiem, jak to zrobić, czy ktoś może dać mi fragment kodu, proszę
Android jest dla mnie wszystkim
32

Wszystkie powyższe odpowiedzi są poprawne, ale myślę, że jest to najprostszy możliwy przykład:

public class ExampleActivity extends Activity {
    private Handler handler;
    private ProgressBar progress;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        progress = (ProgressBar) findViewById(R.id.progressBar1);
        handler = new Handler();
    }

    public void clickAButton(View view) {
        // Do something that takes a while
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() { // This thread runs in the UI
                    @Override
                    public void run() {
                        progress.setProgress("anything"); // Update the UI
                    }
                });
            }
        };
        new Thread(runnable).start();
    }
}

To powoduje aktualizację paska postępu w wątku interfejsu użytkownika z zupełnie innego wątku przekazanego przez metodę post () procedury obsługi zadeklarowanej w działaniu.

Mam nadzieję, że to pomoże!

Lisandro
źródło
22

W ten sposób tworzysz procedurę obsługi w wątku w tle

private void createHandler() {
        Thread thread = new Thread() {
          public void run() {
               Looper.prepare();

               final Handler handler = new Handler();
               handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                       // Do Work
                        handler.removeCallbacks(this);
                        Looper.myLooper().quit();
                   }
                }, 2000);

                Looper.loop();
            }
        };
        thread.start();
    }
Ayman Mahgoub
źródło
Nie tylko rozwiązuje problem, ale także bardzo wyjaśnia. Ciasto z niewielkimi problemami. Jak zmieni się struktura, jeśli zechcesz removeCallbacksi quitkiedy Workzostanie to zrobione, ale nie nastąpi opóźnienie. Ponieważ praca może trwać dłużej niż wstępnie zdefiniowane opóźnienie.
Farid
12

Activity.runOnUiThread()nie działa dla mnie. Omówiłem ten problem, tworząc zwykły wątek w ten sposób:

public class PullTasksThread extends Thread {
   public void run () {
      Log.d(Prefs.TAG, "Thread run...");
   }
}

i wywołując go z aktualizacji GL w ten sposób:

new PullTasksThread().start();
Eli
źródło
To rozwiązanie zadziałało, gdy dzwoniłem z GL, ale musisz być pewien, że nie wywołujesz metody Toasts on run !! AsyncTask nie zadziałał wywołując go z GL. Dzięki!
Alberto M.
Działa świetnie dla mnie w zastępowaniu AsyncTask. Dzięki!
IgorGanapolsky
5

Spróbuj uruchomić swoje zadanie jako zadanie z poziomu wątku interfejsu użytkownika. Zmierzyłem się z tym problemem, kiedy nie robiłem tego samego!

user2560131
źródło
3

Spróbuj tego

    Handler mHandler = new Handler(Looper.getMainLooper());
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
 //your code here that talks with the UI level widgets/ try to access the UI 
//elements from this block because this piece of snippet will run in the UI/MainThread.                                     
                            }
                        });
Marcos Militão
źródło
1
Spróbuj dodać małe wyjaśnienie. Odpowiedzi zawierające tylko kod nie są bardzo cenione w SO.
Alexei