How to have Android Service communicate with Activity

前端 未结 13 1877
说谎
说谎 2020-11-22 02:54

I\'m writing my first Android application and trying to get my head around communication between services and activities. I have a Service that will run in the background an

相关标签:
13条回答
  • 2020-11-22 03:24

    Use LocalBroadcastManager to register a receiver to listen for a broadcast sent from local service inside your app, reference goes here:

    http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

    0 讨论(0)
  • 2020-11-22 03:24

    My method:

    Class to manage send and receive message from/to service/activity:

    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 java.util.ArrayList;
    import java.util.List;
    
    public class MessageManager {
    
        public interface IOnHandleMessage{
            // Messages
            int MSG_HANDSHAKE = 0x1;
    
            void onHandleMessage(Message msg);
        }
    
        private static final String LOGCAT = MessageManager.class.getSimpleName();
    
        private Messenger mMsgSender;
        private Messenger mMsgReceiver;
        private List<Message> mMessages;
    
        public MessageManager(IOnHandleMessage callback, IBinder target){
            mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
            mMsgSender = new Messenger(target);
            mMessages = new ArrayList<>();
        }
    
        public MessageManager(IOnHandleMessage callback){
            mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
            mMsgSender = null;
            mMessages = new ArrayList<>();
        }
    
        /* START Getter & Setter Methods */
        public Messenger getMsgSender() {
            return mMsgSender;
        }
    
        public void setMsgSender(Messenger sender) {
            this.mMsgSender = sender;
        }
    
        public Messenger getMsgReceiver() {
            return mMsgReceiver;
        }
    
        public void setMsgReceiver(Messenger receiver) {
            this.mMsgReceiver = receiver;
        }
    
        public List<Message> getLastMessages() {
            return mMessages;
        }
    
        public void addMessage(Message message) {
            this.mMessages.add(message);
        }
        /* END Getter & Setter Methods */
    
        /* START Public Methods */
        public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
            if(mMsgSender != null && mMsgReceiver != null) {
                try {
                    Message msg = Message.obtain(null, what, arg1, arg2);
                    msg.replyTo = mMsgReceiver;
                    if(msgData != null){
                        msg.setData(msgData);
                    }
                    mMsgSender.send(msg);
                } catch (RemoteException rE) {
                    onException(rE);
                }
            }
        }
    
        public void sendHandshake(){
            if(mMsgSender != null && mMsgReceiver != null){
                sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
            }
        }
        /* END Public Methods */
    
        /* START Private Methods */
        private void onException(Exception e){
            Log.e(LOGCAT, e.getMessage());
            e.printStackTrace();
        }
        /* END Private Methods */
    
        /** START Private Classes **/
        private class MessageHandler extends Handler {
    
            // Types
            final static int TYPE_SERVICE = 0x1;
            final static int TYPE_ACTIVITY = 0x2;
    
            private IOnHandleMessage mCallback;
            private int mType;
    
            public MessageHandler(IOnHandleMessage callback, int type){
                mCallback = callback;
                mType = type;
            }
    
            @Override
            public void handleMessage(Message msg){
                addMessage(msg);
                switch(msg.what){
                    case IOnHandleMessage.MSG_HANDSHAKE:
                        switch(mType){
                            case TYPE_SERVICE:
                                setMsgSender(msg.replyTo);
                                sendHandshake();
                                break;
                            case TYPE_ACTIVITY:
                                Log.v(LOGCAT, "HERE");
                                break;
                        }
                        break;
                    default:
                        if(mCallback != null){
                            mCallback.onHandleMessage(msg);
                        }
                        break;
                }
            }
    
        }
        /** END Private Classes **/
    
    }
    

    In Activity Example:

    public class activity extends AppCompatActivity
          implements     ServiceConnection,
                         MessageManager.IOnHandleMessage { 
    
        [....]
    
        private MessageManager mMessenger;
    
        private void initMyMessenger(IBinder iBinder){
            mMessenger = new MessageManager(this, iBinder);
            mMessenger.sendHandshake();
        }
    
        private void bindToService(){
            Intent intent = new Intent(this, TagScanService.class);
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
            /* START THE SERVICE IF NEEDED */
        }
    
        private void unbindToService(){
        /* UNBIND when you want (onDestroy, after operation...)
            if(mBound) {
                unbindService(mServiceConnection);
                mBound = false;
            }
        }
    
        /* START Override MessageManager.IOnHandleMessage Methods */
        @Override
        public void onHandleMessage(Message msg) {
            switch(msg.what){
                case Constants.MSG_SYNC_PROGRESS:
                    Bundle data = msg.getData();
                    String text = data.getString(Constants.KEY_MSG_TEXT);
                    setMessageProgress(text);
                    break;
                case Constants.MSG_START_SYNC:
                    onStartSync();
                    break;
                case Constants.MSG_END_SYNC:
                    onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
                    mBound = false;
                    break;
            }
        }
        /* END Override MessageManager.IOnHandleMessage Methods */
    
        /** START Override ServiceConnection Methods **/
        private class BLEScanServiceConnection implements ServiceConnection {
    
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                initMyMessenger(iBinder);
                mBound = true;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                mMessenger = null;
                mBound = false;
            }
        }
        /** END Override ServiceConnection Methods **/
    

    In Service Example:

    public class Blablabla extends Service
        implements     MessageManager.IOnHandleMessage {
    
        [...]
    
        private MessageManager mMessenger;
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            super.onBind(intent);
            initMessageManager();
            return mMessenger.getMsgReceiver().getBinder();
        }
    
        private void initMessageManager(){
            mMessenger = new MessageManager(this);
        }
    
        /* START Override IOnHandleMessage Methods */
        @Override
        public void onHandleMessage(Message msg) {
        /* Do what you want when u get a message looking the "what" attribute */
        }
        /* END Override IOnHandleMessage Methods */
    

    Send a message from Activity / Service:

    mMessenger.sendMessage(what, arg1, arg2, dataBundle);
    

    How this works:

    on the activity you start or bind the service. The service "OnBind" methods return the Binder to his MessageManager, the in the Activity through the "Service Connection" interface methods implementation "OnServiceConnected" you get this IBinder and init you MessageManager using it. After the Activity has init his MessageManager the MessageHandler send and Handshake to the service so it can set his "MessageHandler" sender ( the "private Messenger mMsgSender;" in MessageManager ). Doing this the service know to who send his messages.

    You can also implement this using a List/Queue of Messenger "sender" in the MessageManager so you can send multiple messages to different Activities/Services or you can use a List/Queue of Messenger "receiver" in the MessageManager so you can receive multiple message from different Activities/Services.

    In the "MessageManager" instance you have a list of all messages received.

    As you can see the connection between "Activity's Messenger" and "Service Messenger" using this "MessageManager" instance is automatic, it is done through the "OnServiceConnected" method and through the use of the "Handshake".

    Hope this is helpful for you :) Thank you very much! Bye :D

    0 讨论(0)
  • 2020-11-22 03:25

    Besides LocalBroadcastManager , Event Bus and Messenger already answered in this question,we can use Pending Intent to communicate from service.

    As mentioned here in my blog post

    Communication between service and Activity can be done using PendingIntent.For that we can use createPendingResult().createPendingResult() creates a new PendingIntent object which you can hand to service to use and to send result data back to your activity inside onActivityResult(int, int, Intent) callback.Since a PendingIntent is Parcelable , and can therefore be put into an Intent extra,your activity can pass this PendingIntent to the service.The service, in turn, can call send() method on the PendingIntent to notify the activity via onActivityResult of an event.

    Activity

    public class PendingIntentActivity extends AppCompatActivity
    {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    PendingIntent pendingResult = createPendingResult(
    100, new Intent(), 0);
    Intent intent = new Intent(getApplicationContext(), PendingIntentService.class);
    intent.putExtra("pendingIntent", pendingResult);
    startService(intent);
    
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 100 && resultCode==200) {
    Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show();
    }
    super.onActivityResult(requestCode, resultCode, data);
    }
    }
    

    Service

    public class PendingIntentService extends Service {
    
        private static final String[] items= { "lorem", "ipsum", "dolor",
                "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi",
                "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam",
                "vel", "erat", "placerat", "ante", "porttitor", "sodales",
                "pellentesque", "augue", "purus" };
        private PendingIntent data;
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            data = intent.getParcelableExtra("pendingIntent");
    
            new LoadWordsThread().start();
            return START_NOT_STICKY;
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    
        class LoadWordsThread extends Thread {
            @Override
            public void run() {
                for (String item : items) {
                    if (!isInterrupted()) {
    
                        Intent result = new Intent();
                        result.putExtra("name", item);
                        try {
                            data.send(PendingIntentService.this,200,result);
                        } catch (PendingIntent.CanceledException e) {
    
                            e.printStackTrace();
                        }
                        SystemClock.sleep(400);
    
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:34

    The asker has probably long since moved past this, but in case someone else searches for this...

    There's another way to handle this, which I think might be the simplest.

    Add a BroadcastReceiver to your activity. Register it to receive some custom intent in onResume and unregister it in onPause. Then send out that intent from your service when you want to send out your status updates or what have you.

    Make sure you wouldn't be unhappy if some other app listened for your Intent (could anyone do anything malicious?), but beyond that, you should be alright.

    Code sample was requested:

    In my service, I have this:

    // Do stuff that alters the content of my local SQLite Database
    sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
    

    (RefreshTask.REFRESH_DATA_INTENT is just a constant string.)

    In my listening activity, I define my BroadcastReceiver:

    private class DataUpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
              // Do stuff - maybe update my view based on the changed DB contents
            }
        }
    }
    

    I declare my receiver at the top of the class:

    private DataUpdateReceiver dataUpdateReceiver;
    

    I override onResume to add this:

    if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
    IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
    registerReceiver(dataUpdateReceiver, intentFilter);
    

    And I override onPause to add:

    if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
    

    Now my activity is listening for my service to say "Hey, go update yourself." I could pass data in the Intent instead of updating database tables and then going back to find the changes within my activity, but since I want the changes to persist anyway, it makes sense to pass the data via DB.

    0 讨论(0)
  • 2020-11-22 03:34

    Another way could be using observers with a fake model class through the activity and the service itself, implementing an MVC pattern variation. I don't know if it's the best way to accomplish this, but it's the way that worked for me. If you need some example ask for it and i'll post something.

    0 讨论(0)
  • 2020-11-22 03:37

    You may also use LiveData that works like an EventBus.

    class MyService : LifecycleService() {
        companion object {
            val BUS = MutableLiveData<Any>()
        }
    
        override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
            super.onStartCommand(intent, flags, startId)
    
            val testItem : Object
    
            // expose your data
            if (BUS.hasActiveObservers()) {
                BUS.postValue(testItem)
            }
    
            return START_NOT_STICKY
        }
    }
    

    Then add an observer from your Activity.

    MyService.BUS.observe(this, Observer {
        it?.let {
            // Do what you need to do here
        }
    })
    

    You can read more from this blog.

    0 讨论(0)
提交回复
热议问题