Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access original activity's views from spawned background service

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?

like image 470
montooner Avatar asked Jul 21 '09 07:07

montooner


4 Answers

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

After analyzing a similar question & example, here is a 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 interface Callback {
     public void returnServiceResponse();
}
like image 118
Sagar Avatar answered Nov 07 '22 01:11

Sagar


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

like image 31
Josef Pfleger Avatar answered Nov 07 '22 00:11

Josef Pfleger


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.

like image 2
CommonsWare Avatar answered Nov 07 '22 02:11

CommonsWare


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

like image 1
yanchenko Avatar answered Nov 07 '22 02:11

yanchenko