1

I am currently communicating with an external service from a plugin my app uses.

The bound service eventually starts an activity and then shall notify the main app that the Activity has finished.

The problem is, that the Service gets notified by a local broadcast. I have stored the Messenger msg.replyTo into a variable, so I can access it after the Handler has finished, but the Service still being bound.

It does actually work, however I want to be completely sure that there is no NullPointer possible, so I do not like that approach very much.

Is there a better way of communicating back to the Binder than storing the Messenger in a variable?

Here is some code for better understanding:

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                if (intent.getAction().equals("activity_closed") && mReplyMessenger != null) {

                mReplyMessenger.send(Message.obtain(null, MSG_RESULT_ACTIVITY_FINISHED));

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



class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        mReplyMessenger = null;
        try {
            switch (msg.what) {
            case MSG_START_ACTIVITY: {
                mReplyMessenger = msg.replyTo;

[...]

                BridgeBinder.this.startActivity(i);
                break;
            }
[...]

            default:
                super.handleMessage(msg);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

final Messenger mMessenger = new Messenger(new IncomingHandler());

@Override
public IBinder onBind(Intent arg0) {
    return mMessenger.getBinder();
}
Onik
  • 19,396
  • 14
  • 68
  • 91
Force
  • 6,312
  • 7
  • 54
  • 85
  • how can an AsyncTask solve this problem? – Force Jun 18 '12 at 17:20
  • Please read the question before posting random comments. It is about a bound service, the Handlers are IPCs between the main app and an external service. – Force Jun 18 '12 at 17:22

2 Answers2

0

I am implementing a two-way Messenger the same way. I don't really see your Handler getting a stale reference (and therefore a NPE) to your Activity, but if you want to be extra cautious you could try to update the reference each time in your onResume() method, or even every time you receive a Message.

telkins
  • 10,440
  • 8
  • 52
  • 79
0

my class if you need it:

MessageManager.java

import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;

import com.ubikinformatica.it.lib.commons.modules.log.EMaxLogger;

import java.util.ArrayList;
import java.util.List;

public class MessageManager {

    public interface IOnHandleMessage{
        // Message Whats
        int MSG_HANDSHAKE = 0x1;

        void onHandleMessage(Message msg);
    }

    private static final String TAG = MessageManager.class.getSimpleName();

    private Messenger mMsgSender;
    private Messenger mMsgReceiver;
    private List<Message> mMessages;

    public MessageManager(IOnHandleMessage callback, IBinder target){
        mMsgReceiver = new Messenger(new MessageHandler(this, callback, MessageHandler.TYPE_ACTIVITY));
        mMsgSender = new Messenger(target);
        mMessages = new ArrayList<>();
    }

    public MessageManager(IOnHandleMessage callback){
        mMsgReceiver = new Messenger(new MessageHandler(this, callback, MessageHandler.TYPE_SERVICE));
        mMsgSender = null;
        mMessages = new ArrayList<>();
    }

    /** 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);
    }

    /** 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) {
                EMaxLogger.onException(TAG, rE);
            }
        }
    }

    public void sendHandshake(){
        if(mMsgSender != null && mMsgReceiver != null){
            sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
        }
    }

}

MessageHandler.java

    import android.os.Handler;
import android.os.Message;

public class MessageHandler extends Handler {

    // Types
    final static int TYPE_SERVICE = 0x1;
    final static int TYPE_ACTIVITY = 0x2;

    private MessageManager mMessageManager;
    private MessageManager.IOnHandleMessage mCallback;
    private int mType;

    public MessageHandler(MessageManager msgManager, MessageManager.IOnHandleMessage callback, int type){
        this.mMessageManager = msgManager;
        this.mCallback = callback;
        this.mType = type;
    }

    /** Override Handler Methods **/
    @Override
    public void handleMessage(Message msg){
        this.mMessageManager.addMessage(msg);
        switch(msg.what){
            case MessageManager.IOnHandleMessage.MSG_HANDSHAKE:
                switch(mType){
                    case TYPE_SERVICE:
                        this.mMessageManager.setMsgSender(msg.replyTo);
                        this.mMessageManager.sendHandshake();
                        break;
                    case TYPE_ACTIVITY:
                        break;
                }
                break;
            default:
                if(mCallback != null){
                    mCallback.onHandleMessage(msg);
                }
                break;
        }
    }

}

More Info inside my post here: CHECK MY POST HERE

Actually there are some problem with new JobService & JobIntentService because they can't return another IBinder than the JobScheduler IBinder Engine, but I'm trying to solve them now :D

Hope this is helpful, have a nice coding :D bye

[Important Edit on: 02/05/2019 - 10:57]

OK problem solved LoL you can put the Messenger in a parcel and send it to the service through the intent. So this way, in a JobIntentService the "onBind" method will return the JobScheduler IBinder Engine and you will get your Binder for the Messenger inside the intent!

So you init the MessageManager in activity only with the callback, you start the service through "enqueueWork" and in the intent you put you MessengerReceiver. Inside the JobIntentService you can init your MessageManager by getting the IBinder by using the "getBinder()" method of the Messenger inside the intent you get in the "onBind" and "onHandleWork" method. When you have the IBinder of the MessengerReceiver of your activity you can init a MessageManager using the constructor "callback + binder". Then in the Service you send the Handshake and in the activity he will set his Sender.

TO DO THIS:

you MUST change the "handleMessage" override method of the MessageHandler: The method now is this:

@Override
    public void handleMessage(Message msg){
        this.mMessageManager.addMessage(msg);
        switch(msg.what){
            case MessageManager.IOnHandleMessage.MSG_HANDSHAKE:
                switch(mType){
                    case TYPE_SERVICE:
                        this.mMessageManager.setMsgSender(msg.replyTo);
                        this.mMessageManager.sendHandshake();
                        break;
                    case TYPE_ACTIVITY:
                        break;
                }
                break;
            default:
                if(mCallback != null){
                    mCallback.onHandleMessage(msg);
                }
                break;
        }
    }

You need to remove the Type because you don't need it anymore. So in the switch of the "handleMessage" method inside the class "MessageHandler" when you get and handshake you must do:

    [...]
case MessageManager.IOnHandleMessage.MSG_HANDSHAKE:
    if(this.mMessageManager.getMsgSender() == null){
        this.mMessageManager.setMsgSender(msg.replyTo);
        this.mMessageManager.sendHandshake();
    }
    [...]

This way the problem with JobIntentService is solved ;) Hope this help you! Have a nice coding <3

Z3R0
  • 1,011
  • 10
  • 19