views:

4075

answers:

4

I have an activity called A, and on the selection of menu item 0, it spawns service B, which starts a runnable C in a new thread. I have a TextView in activity A, which I want to access in thread C.

I've tried making the TextView a public static field, but that generates the following error:

07-21 07:26:25.723: ERROR/AndroidRuntime(1975): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.view.ViewRoot.checkThread(ViewRoot.java:2440)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.view.ViewRoot.invalidateChild(ViewRoot.java:522)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:540)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.view.ViewGroup.invalidateChild(ViewGroup.java:2332)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.view.View.invalidate(View.java:4437)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.widget.TextView.updateAfterEdit(TextView.java:4593)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.widget.TextView.handleTextChanged(TextView.java:5932)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:6081)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.sendTextChange(SpannableStringBuilder.java:889)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.change(SpannableStringBuilder.java:352)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.change(SpannableStringBuilder.java:269)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:432)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.append(SpannableStringBuilder.java:259)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.text.SpannableStringBuilder.append(SpannableStringBuilder.java:28)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.widget.TextView.append(TextView.java:2191)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at android.widget.TextView.append(TextView.java:2178)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at com.android.peekaboo.DoScan$scanBody.run(DoScan.java:36)
07-21 07:26:25.723: ERROR/AndroidRuntime(1975):     at java.lang.Thread.run(Thread.java:1058)

I have also considered trying to pass the View through an intent, but do not know how that would work. What do I need to make this work?

+4  A: 

You have to update widgets from the GUI thread, aka 'the thread that created the view hierarchy'. The standard way to do this is via Handlers and an example of how to use handlers can be found in the ProgressDialog Example (expand 'Example ProgressDialog with a second thread').

Josef
+1  A: 

The other way is to utilize os.android.AsyncTask for processing.

alex
+1  A: 

You really do not want to be directly manipulating widgets from a service.

For example, suppose the user slides out the keyboard of her G1. Your activity is destroyed and recreated. Your service, however, is holding onto widgets from a now-defunct activity. At best, the updates will not occur. At worst, the updates will cause a crash, or your application will leak memory because the old activity cannot be garbage-collected, because your service still holds onto it.

Having services notify activities is OK, so long as you have decent isolation between them and the activity detaches itself from the service when it is destroyed.

CommonsWare
+2  A: 

I was having the similar issue where a listview was required to be updated on the web-service response coming from a separate thread.

After analyzing the similar question & example from: helloandroid.com/tutorials/using-threads-and-progressdialog, here is the solution which should work for you:

public class A extends Activity implements Callback {

     callserviceB () { } // where your service B being called;

     @Override
     public void returnServiceResponse() {
          workOnResponse();
          handler.sendEmptyMessage(0);
     }

     private Handler handler = new Handler() {
          public void  handleMessage(Message msg) {
               //update your view from here only.
          }
     }
}

public class B implements Runnable {
     Callback callback;

     public void run() {
         //your business logic.
         callback.returnServiceResponse();
     }
}

public intereface Callback {
     public void returnServiceResponse();
}

Cheers!!

Sagar