Where to "quit" with looper?
Asked Answered
S

2

7

I have a problem with a looper. I call looper.prepare(), and after doing something it all works fine. But if I rotate the device I get an exception on the prepare.

07-12 16:40:09.760: E/activity(15809):  java.lang.RuntimeException: Only one Looper may be created per thread

I'm trying to quit the looper, but it doesn't do anything.

Here is my AsyncTask:

 @Override
    protected String doInBackground(String... args) {

        try{Looper.prepare();   //here start the exception

       try {  

            URL  url = new URL(link); 
            HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
         conn.setDoInput(true);   
            conn.connect();  
            InputStream is = conn.getInputStream();
          utente.measure(0, 0);
            bmImg = decodeSampledBitmapFromResource(is,(int) utente.getMeasuredWidth(), utente.getMeasuredHeight(), link);

 if(bmImg!=null){


        try{  

         getCroppedBitmap();
        }catch(Exception e){
            System.out.println(e);
        }

          }

        }
        catch (IOException e)
        {       
           Log.e("lele", "errore qui");
            e.printStackTrace();  

        }
        Looper.myLooper().quit();   //do nothings
        }catch(Exception e){
            Log.e("canta tu",  " "+e);
        }
        Looper.myLooper().quit();  //do nothings
        return null;   
    }
        @Override       
protected void onPostExecute(String args) {

            //Looper.myLooper().quit();   //generathed an error, main thread can't stop looper

       if(bmImg!=null){ 
           try{

           utente.setImageBitmap(bmImg);
           ellisse.setVisibility(View.VISIBLE);

           }catch(Exception e){
               Log.e("lele",""+e);
               Log.e("lele","errore probabile out of bound");
           }

           }
       else {

           Toast.makeText(getApplicationContext(), "Modifica la foto da \"profilo\"", Toast.LENGTH_LONG).show();
       }

Ideas?

Suction answered 12/7, 2013 at 14:49 Comment(0)
C
17

There are two cases to consider:

(1) looper threads you want to live the entire life of the app, and do not hold strong reference to a view (even not implicitly)

Quoting Google engineer, Christopher Tate - you can just leave the looper there until your app is destroyed, and it will go down with it. You don't need to worry about it.

"Speaking very generally, never quit() your looper threads. That method exists mostly for historical and testing reasons. In Real Life™, I recommend that you continue to reuse the same looper thread(s) for the life of the process rather than creating/quitting them."

I use such a looper thread as a multi purpose HandlerThread, and send Runnables to it whenever I want something to run outside the main thread (UI).

(2) looper threads that have reference to a view

This one falls out of the recommendation of Christopher Tate, because it will cause memory leak, for example if you rotate the screen.
(You better make the handler thread static and use weak reference - and you'll be back with option #1)
To kill it you must quit the loop. To do that, you need to run the quit command on the context of that thread.
So create a message with some whatever int as your msg.what, and in your handleMessage wait for this int, and when it arrives - call:

Looper myLooper = Looper.myLooper();
if (myLooper!=null) {
    myLooper.quit();
}

And don't forget to null all reference to views and activities.

Send this kill message to the handler from your activity onDestroy()

Corporation answered 22/11, 2013 at 0:5 Comment(1)
Unless you want to shut down and spawn a new similar thread: "Your thread will never exit -- it will remain in Looper.loop(), because you never told it to quit out of the loop."Bernt
Q
4

Looper.prepare() associates a Looper-instance with the thread that it is called on, but Looper.quit() does not remove this association (it merely stops the message dispatch mechanism). So, when you get a second call to Looper.prepare a RuntimeException is thrown.

The general recommendation is to not associate Looper-instances with AsyncTask-threads. The Looper is intended for passing messages between threads, but this is already handled internally in the AsyncTask, so that data can be sent between onPreExecute (UI thread) -> doInBackground (Worker thread) -> onPostExecute (UI thread).

Quinones answered 14/7, 2013 at 4:29 Comment(2)
thank's for your answer, but if i don't write loope.prepae() i've got an errorSuction
That you get an error without Looper.prepare() indicates that you are executing a task that requires a message queue on the doInBackground-thread — i.e. a Looper — but I don't see which part of your code that would require a Looper. The background thread used with AsyncTask is not intended to have a message queue. It would be better to use a HandlerThread for the background execution.Quinones

© 2022 - 2024 — McMap. All rights reserved.