从Activity / Static方法更新Android Service TextView

时间:2015-08-12 11:17:38

标签: android android-intent android-service

背景

我创建了一个自定义服务:

public class FloatingViewService extends Service {
    public static  FloatingViewService self;
     onCreate() { 
       self = this;
       addView(....)
     }

     ... 
     ...
   public void updateText ( String newText) { this.textView.setText(newText) };

 }

此服务的OnCreate事件,它使用WindowManager.addView(...)设置视图,并在自变量中设置实例指针以供将来使用。

现在这个视图只是一个文本视图,无论如何都会保持在活动之上。

我想要实现的目标:

我想从使用ExecutorService实例运行的静态方法发送一些数据,该实例应更新textview文本。

我如何使用此服务:

在活动内部,我调用一个记录一些值的静态方法:

public class MyActivity: Activity
{
   public void log() {

       LogUtil.log(new Runnable() {
            @Override
            public void run() {
               //log api call
               FloatingViewService.self.updateText("New Text");
            }
        }) ;
}
}

现在您可以看到我正在调用来自不同线程的服务中存在的updateText方法。

以下是LogUtil的用法:

public class LogUtil { 
private static ExecutorService  taskExecutorService = ThreadUtils.createTimedExecutorService(TASK_POOL_SIZE, TASK_POOL_IDLE_ALIVE_SECONDS,
                TimeUnit.SECONDS, new LowPriorityThreadFactory());

  public static log(Runnable runnable) { 

      taskExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    runnable.run();
                } catch (Exception ex) {
                    ../
                }
            }
        });

现在的问题是,它无法更新textview文本。我能理解这是由于线程。但我不清楚如何实现它 - 是否有任何用于服务的UIthread?

2 个答案:

答案 0 :(得分:0)

这是我的代码示例..你可以从中挑选必要的部分。正如Selvin所说,你必须在双方创建一个Incoming处理程序,以便将信息从一个线程发送到另一个线程......

以下是我的服务代码

 import java.util.ArrayList;
    import java.util.Timer;
    import java.util.TimerTask;
    import com.pekam.myandroidtheme.*;
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.Service;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.os.Messenger;
    import android.os.RemoteException;
    import android.util.Log;

    public class MyService  extends Service {

            private NotificationManager nm;
            private Timer timer = new Timer();
            private int counter = 0, incrementby = 1;
            private static boolean isRunning = false;

            ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
            int mValue = 0; // Holds last value set by a client.
            static final int MSG_REGISTER_CLIENT = 1;
            static final int MSG_UNREGISTER_CLIENT = 2;
            static final int MSG_SET_INT_VALUE = 3;
            static final int MSG_SET_STRING_VALUE = 4;
            final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.


            @Override
            public IBinder onBind(Intent intent) {
                return mMessenger.getBinder();
            }
            class IncomingHandler extends Handler { // Handler of incoming messages from clients.
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                    case MSG_REGISTER_CLIENT:
                        mClients.add(msg.replyTo);
                        break;
                    case MSG_UNREGISTER_CLIENT:
                        mClients.remove(msg.replyTo);
                        break;
                    case MSG_SET_INT_VALUE:
                        incrementby = msg.arg1;
                        break;
                    default:
                        super.handleMessage(msg);
                    }
                }
            }
            private void sendMessageToUI(int intvaluetosend) {
                for (int i=mClients.size()-1; i>=0; i--) {
                    try {
                        // Send data as an Integer
                        mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));

                        //Send data as a String
                        Bundle b = new Bundle();
                        b.putString("str1", "ab" + intvaluetosend + "cd");
                        Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
                        msg.setData(b);
                        mClients.get(i).send(msg);

                    } catch (RemoteException e) {
                        // The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
                        mClients.remove(i);
                    }
                }
            }

            @Override
            public void onCreate() {
                super.onCreate();
                Log.i("MyService", "Service Started.");
                showNotification();
                timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
                isRunning = true;
            }
            private void showNotification() {
                nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                // In this sample, we'll use the same text for the ticker and the expanded notification
                CharSequence text = getText(R.string.service_started);
                // Set the icon, scrolling text and timestamp
                Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
                // The PendingIntent to launch our activity if the user selects this notification
                PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, TabBarActivity.class), 0);
                // Set the info for the views that show in the notification panel.
                notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
                // Send the notification.
                // We use a layout id because it is a unique number.  We use it later to cancel.
                nm.notify(R.string.service_started, notification);
            }
            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                Log.i("MyService", "Received start id " + startId + ": " + intent);
                return START_STICKY; // run until explicitly stopped.
            }

            public static boolean isRunning()
            {
                return isRunning;
            }


            private void onTimerTick() {
                Log.i("TimerTick", "Timer doing work." + counter);
                try {
                    counter += incrementby;
                    sendMessageToUI(counter);

                } catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
                    Log.e("TimerTick", "Timer Tick Failed.", t);            
                }
            }

            @Override
            public void onDestroy() {
                super.onDestroy();
                if (timer != null) {timer.cancel();}
                counter=0;
                nm.cancel(R.string.service_started); // Cancel the persistent notification.
                Log.i("MyService", "Service Stopped.");
                isRunning = false;
            }
        }

这是我的Android表单应用代码

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import com.pekam.myandroidtheme.*;

public class MyServiceControllerActivity extends Activity {

        Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
        TextView textStatus, textIntValue, textStrValue;
        Messenger mService = null;
        boolean mIsBound;
        final Messenger mMessenger = new Messenger(new IncomingHandler());

        class IncomingHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case MyService.MSG_SET_INT_VALUE:
                    textIntValue.setText("Int Message: " + msg.arg1);
                    break;
                case MyService.MSG_SET_STRING_VALUE:
                    String str1 = msg.getData().getString("str1");
                    textStrValue.setText("Str Message: " + str1);
                    break;
                default:
                    super.handleMessage(msg);
                }
            }
        }
        private ServiceConnection mConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className, IBinder service) {
                mService = new Messenger(service);
                textStatus.setText("Attached.");
                try {
                    Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    // In this case the service has crashed before we could even do anything with it
                }
            }

            public void onServiceDisconnected(ComponentName className) {
                // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
                mService = null;
                textStatus.setText("Disconnected.");
            }
        };

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.exampleservice);
            btnStart = (Button)findViewById(R.id.btnStart);
            btnStop = (Button)findViewById(R.id.btnStop);
            btnBind = (Button)findViewById(R.id.btnBind);
            btnUnbind = (Button)findViewById(R.id.btnUnbind);
            textStatus = (TextView)findViewById(R.id.textStatus);
            textIntValue = (TextView)findViewById(R.id.textIntValue);
            textStrValue = (TextView)findViewById(R.id.textStrValue);
            btnUpby1 = (Button)findViewById(R.id.btnUpby1);
            btnUpby10 = (Button)findViewById(R.id.btnUpby10);

            btnStart.setOnClickListener(btnStartListener);
            btnStop.setOnClickListener(btnStopListener);
            btnBind.setOnClickListener(btnBindListener);
            btnUnbind.setOnClickListener(btnUnbindListener);
            btnUpby1.setOnClickListener(btnUpby1Listener);
            btnUpby10.setOnClickListener(btnUpby10Listener);

            restoreMe(savedInstanceState);

            CheckIfServiceIsRunning();
        }

        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putString("textStatus", textStatus.getText().toString());
            outState.putString("textIntValue", textIntValue.getText().toString());
            outState.putString("textStrValue", textStrValue.getText().toString());
        }
        private void restoreMe(Bundle state) {
            if (state!=null) {
                textStatus.setText(state.getString("textStatus"));
                textIntValue.setText(state.getString("textIntValue"));
                textStrValue.setText(state.getString("textStrValue"));
            }
        }
        private void CheckIfServiceIsRunning() {
            //If the service is running when the activity starts, we want to automatically bind to it.
            if (MyService.isRunning()) {
                doBindService();
            }
        }

        private OnClickListener btnStartListener = new OnClickListener() {
            public void onClick(View v){
                startService(new Intent(MyServiceControllerActivity.this, MyService.class));
            }
        };
        private OnClickListener btnStopListener = new OnClickListener() {
            public void onClick(View v){
                doUnbindService();
                stopService(new Intent(MyServiceControllerActivity.this, MyService.class));
            }
        };
        private OnClickListener btnBindListener = new OnClickListener() {
            public void onClick(View v){
                doBindService();
            }
        };
        private OnClickListener btnUnbindListener = new OnClickListener() {
            public void onClick(View v){
                doUnbindService();
            }
        };
        private OnClickListener btnUpby1Listener = new OnClickListener() {
            public void onClick(View v){
                sendMessageToService(1);
            }
        };
        private OnClickListener btnUpby10Listener = new OnClickListener() {
            public void onClick(View v){
                sendMessageToService(10);
            }
        };
        private void sendMessageToService(int intvaluetosend) {
            if (mIsBound) {
                if (mService != null) {
                    try {
                        Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
                        msg.replyTo = mMessenger;
                        mService.send(msg);
                    } catch (RemoteException e) {
                    }
                }
            }
        }


        void doBindService() {
            bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            textStatus.setText("Binding.");
        }
        void doUnbindService() {
            if (mIsBound) {
                // If we have received the service, and hence registered with it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
                        msg.replyTo = mMessenger;
                        mService.send(msg);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service has crashed.
                    }
                }
                // Detach our existing connection.
                unbindService(mConnection);
                mIsBound = false;
                textStatus.setText("Unbinding.");
            }
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            try {
                doUnbindService();
            } catch (Throwable t) {
                Log.e("TabBarActivity", "Failed to unbind from the service", t);
            }
        }
    }

答案 1 :(得分:0)

您需要继续使用UIThread来更新UI。解决方案可能是:

  • 创建活动的静态参考。请记住将其设置为活动的resume方法,并在pause方法中取消设置。
  • 在服务方面,您可以调用activiy方法来更新UI。

以伪代码翻译这些操作。活动将成为:

public class MainActivity extends Activity {

   public static MainActivity reference;

   ... 

   public onResume() {
       reference=this;
   }

   public onPause() {
       reference=null;
   }

   public void needToUpdateText(final String text)
   {
        runOnUiThread(new Runnable() { 
          public void run() { 
          Log.d("UI thread", "I am the UI thread with text "+text);
        });
    } 
   }

}

服务类:

public class FloatingViewService extends Service {

     ... 

   public void updateText ( String newText)
   { 
       if (MainActivity.reference!=null)
       {
         MainActivity.reference.needUpdateText(newText);
       }
 };

}