Hrk
Hrk

Reputation: 2764

Update UI in the main activity through handler in a thread (Android)

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

Upvotes: 2

Views: 43262

Answers (3)

Tomislav
Tomislav

Reputation: 85

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.

Upvotes: 0

Gautier Hayoun
Gautier Hayoun

Reputation: 2922

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

Upvotes: 3

yanchenko
yanchenko

Reputation: 57176

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

Upvotes: 5

Related Questions