views:

1881

answers:

5

Hello,

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

+2  A: 

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

alex
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
A: 

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

I still have the same problem (Looper) at the same place. I try to declare "private Handler handler;" at the top and then put my handler in the onCreate() method.

Handler handler = new Handler() 
{ 
  public void handleMessage(Message msg) 
  { 
       int total = msg.getData().getInt("total"); 
       int step = msg.getData().getInt("step"); 

       progressDialog.setProgress(total); 

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

By the way, the handler example with final provides from developer.android.com

Any additional idea?

Thanks

Hrk
I use this ProgressDialog example :http://developer.android.com/guide/topics/ui/dialogs.html#ProgressDialog (expand 'Example ProgressDialog with a second thread')I try to not reinitialize the progressDialog () and I got the following :05-10 13:32:07.627: ERROR/ConnectionManager(12423): ERROR:Only the original thread that created a view hierarchy can touch its views.But it's the goal of the handler to avoid those kind of problem, isn't it ?
Hrk
A: 

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
A: 

Hello Gautier,

Thanks for your help. You told me that the declaration of the handler in the oncreate() was "shadowing" it. But where should I place it ??

I come back to sendMessage instead of handleMessage, but my progressbar isn't updated until the end, for example here is a function :

public void downloadData()
{
    //DO TASK 1
    updateProgressBar(Act_Main.NETWORK_EVOLUTION, 40);
    //DO TASK 2
    updateProgressBar(Act_Main.NETWORK_EVOLUTION, 80);
    //DO TASK 3
    updateProgressBar(Act_Main.NETWORK_EVOLUTION, 100);
}

My progressbar isn't updated until the 3 tasks are done.

Hrk
A: 

Hello everyone,

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