Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to "quit" with looper?

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?

like image 676
Lele Avatar asked Jul 12 '13 14:07

Lele


People also ask

How do you terminate Looper without processing any more messages in the MessageQueue?

Be sure to call quit() to end the loop. Quits the looper. Causes the loop() method to terminate without processing any more messages in the message queue.

How many loopers and handlers can be attached to a thread?

2. Each thread can have only one Looper.

What does Looper prepare do?

A thread gets a Looper and MessageQueue by calling Looper. prepare() after its running. Looper. prepare() identifies the calling thread, creates a Looper and MessageQueue object and associate the thread with them in ThreadLocal storage class.

What is getMainLooper?

getMainLooper() is convenience API to get looper which is attached to the main thread of the activity.It is usefull when you want to excute some code on main thread from a background thread. It is usually used as follows: new Handler(Looper.getMainLooper()).post(task);


2 Answers

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()

like image 133
Amir Uval Avatar answered Oct 21 '22 21:10

Amir Uval


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).

like image 33
AndersGoransson Avatar answered Oct 21 '22 20:10

AndersGoransson