Updating UI from a service (using a handler?)

2019-01-26 15:02发布

问题:

I am trying to update my UI in FirstActivity when I receive a notification but is confused by runOnUiThread , Runnable and Handler. Here is what I have: I am running FirstActivity and NotificationService. When NotificationService reeives a notification, it will update FirstActivity UI.

I also have another service AlarmService running. First Activity

@Override
public void onResume() {
      super.onResume();
      //some other code for alarm service
}

NotificationService

    //on receiving notification
    private void showNotification(String text) {

   //Get activity
   Class<?> activityClass = null;
     try {
         activityClass = Class.forName("com.pakage.FirstActivity");
         contextActivity = (Activity) activityClass.newInstance();

         //Update UI on FirstActivity not working
         contextActivity.runOnUiThread(new Runnable() {
             public void run()
             { 
               Looper.prepare();
               TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
               Looper.loop();

             }
             });

     } catch (Exception e) {
         e.printStackTrace();
     }

            //Shows the notification
            Notification n = new Notification();    
            //... etc   
}

I keep getting looper.prepare error. Do I need to put extra codes in my FirstActivity?

回答1:

My 1st instinct is that you should instead have the Activity bind to your service and handle the UI update on its side instead of the Service directly modifying the Activity.

See more info here:
http://developer.android.com/reference/android/app/Service.html#LocalServiceSample

And an example here:
Example: Communication between Activity and Service using Messaging



回答2:

I've always just had the service fire off a Broadcast and then in my Activity I have a BroadcastReciever listening for the Broadcast. It's an approach that is much simpler than the one you outlined above.



回答3:

I have no idea why you are putting a Looper in

contextActivity.runOnUiThread(new Runnable() {
    public void run()
    { 
        Looper.prepare();
        TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
        Looper.loop();

    }
});

because the UI (main) thread already has a Looper/Handler etc..

Even if it did work Looper.loop() is going to block and since you are running it on the UI thread, it will block the UI thread which is not what you want.

What you really want to do is

contextActivity.runOnUiThread(new Runnable() {
    public void run()
    { 
        TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
        tv.setText("do something that must be on UI thread") // or whatever
    }
});

You don't really need to do all this fancy stuff to get the Activity

activityClass = Class.forName("com.pakage.FirstActivity");
contextActivity = (Activity) activityClass.newInstance();

assuming the Service and Activity are both running in the same process you can just save a reference to the Activity but be careful to update the reference when the Activity gets destroyed.