2

I try to make several connection in a class and update the multiple progressbar in the main screen.

But I've got the following error trying to use thread in android : Code: 05-06 13:13:11.092: ERROR/ConnectionManager(22854): ERROR:Can't create handler inside thread that has not called Looper.prepare()

Here is a small part of my code in the main Activity

public class Act_Main extends ListActivity 
{ 
 private ConnectionManager cm; 

 public void onCreate(Bundle savedInstanceState) 
 { 
      super.onCreate(savedInstanceState); 

      // Set up the window layout 
      requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); 
      setContentView(R.layout.main); 
      getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); 
 } 

 public void startConnection() 
 { 
      //open DB connection 
      db = new DBAdapter(getApplicationContext()); 
      db.open(); 

      cm = new ConnectionManager(handler, db); 
      showDialog(DIALOG_PROGRESS_LOGIN); 
 } 

 @Override 
 public void onStart() 
 { 
      super.onStart(); 
      startConnection(); 
 } 

 protected Dialog onCreateDialog(int id) 
 { 
      switch (id) 
      { 
      case DIALOG_PROGRESS_LOGIN: 
           progressDialog = new ProgressDialog(Act_Main.this); 
           progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 
           progressDialog.setMessage("Connecting.\nPlease wait..."); 
           progressThreadLogin = new ProgressThreadLogin(); 
           progressThreadLogin.start(); 

           return progressDialog; 
      case DIALOG_PROGRESS_NETWORK: 
           [b]progressDialog = new ProgressDialog(Act_Main.this);[/b] 
           progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 
           progressDialog.setMessage("Loading entire network.\nPlease wait..."); 
           progressThreadNetwork = new ProgressThreadNetwork(); 
           progressThreadNetwork.start(); 

           return progressDialog; 
      default: 
           return null; 
      } 
 } 


 // Define the Handler that receives messages from the thread and update the progress 
 final Handler handler = new Handler() 
 { 
      public void handleMessage(Message msg) 
      { 
           int total = msg.getData().getInt("total"); 
           int step = msg.getData().getInt("step"); 

           Log.d(TAG, "handleMessage:PROCESSBAR:"+total); 
           progressDialog.setProgress(total); 

           if (total >= 100) 
           { 
                switch (step) 
                { 
                     case UPDATE_NETWORK: 
                          dismissDialog(DIALOG_PROGRESS_LOGIN); 
                          showDialog(DIALOG_PROGRESS_NETWORK); 
                          cm.getNetwork(); 
                          break; 
                                    .... 
                     default: 
                          break; 
                } 
           } 
      } 
 }; 

 private class ProgressThreadLogin extends Thread 
 { 
      ProgressThreadLogin() { } 
      public void run() { cm.login(); } 
 } 

 private class ProgressThreadNetwork extends Thread 
 { 
      ProgressThreadNetwork() { } 
      public void run() { cm.getNetwork(); } 
 } 
}

And my connectionManager class:

public class ConnectionManager 
{ 
 public ConnectionManager(Handler handler, DBAdapter db) 
 { 
      this.handler = handler; 
      this.db = db; 
 } 

 public void updateProgressBar(int step, int value) 
 { 
      if (value == 0) 
           total = total+1; 
      else 
           total = value ; 

      Message msg = handler.obtainMessage(); 
            Bundle b = new Bundle(); 
            b.putInt("total", total); 
            b.putInt("step", step); 
            msg.setData(b); 
            handler.handleMessage(msg); 
 } 

 public void login() 
 { 
            //DO MY LOGIN TASK 
      updateProgressBar(Act_Main.UPDATE_NETWORK, 100); 
 }
}

The crash errors occurs on the first line of "case DIALOG_PROGRESS_NETWORK:". My first progressbar is hidden but the second one is not displayed.

I think I've done somthing wrong using the threads and handlers but I dont' know why.

I was first using handler.sendMessage in place of handler.handleMessage but when I had several task in my connectionManager, the progressbar was updated only at the end of all tasks.

Thank you in advance for your help

Hrk
  • 2,725
  • 5
  • 29
  • 44

3 Answers3

5

Make Handler handler non-final and init it inside onCreate().

yanchenko
  • 56,576
  • 33
  • 147
  • 165
  • 1
    This is correct. You cannot call `new Handler()` in your class definition (That's not even correct java; you can only initialize a *static* object in the class definition). Plus there's no reason for it to be final. A handler is tied to the thread in which it was created, and that thread must run a message queue / looper. Placing it in `onCreate()` is the proper way to tie your handler in to the main UI thread. – stormin986 May 09 '10 at 23:07
  • 8
    @stormin986 You are wrong.. We can definitely call the new method if we want ( however that is said to be a bad practice) next you said "you can only initialize a static object in the class definition" again you are wrong we can initialize any variable we like in Class definition ... – Amit Aug 10 '12 at 10:46
3

Having the Handler final and declaring it in the class definition is perfectly fine. Moreover in your last snippet of code when you initialize your Handler in onCreate, you are actually "shadowing" the handler declared in your class, so not initializing it. I'm quite surprised you didn't had any NullPointerException.

But in ConnectionManager::updateProgressBar you really need to call sendMessage() because by calling handleMessage() directly, the handler gets called from the thread of ConnectionManager::updateProgressBar (which is probably not the UI thread).

I'm not sure what caused you the problem of having the progress bars updated only once, but it's surely a logic bug somewhere. Try to log at different steps of your app -ie. before sending the message, and while handling it-.

Gautier Hayoun
  • 2,922
  • 24
  • 17
0

Hrk, the shadowing part is when you declare a local variable inside the method which has the same name as the field:

Handler handler = ...

To use the field, omit the type:

handler = ...

Regarding exception, just instantiate ProgressDialog objects in main thread (say, in onCreate() method) and call one of it's show() methods later.

Tomislav
  • 85
  • 6