简体   繁体   中英

How to get an android service return message from a phonegap plugin

I am trying to make a plugin for Phonegap (Android) that allows my javascript to send and receive messages to / from a service. My exact problem is, that because the messages return asynchronous, I cannot send the PluginResult to the execute function of the plugin.

This is the plugin code:

public class ServiceClient_plugin extends Plugin {
    Messenger messenger_service=null;
    boolean connected_to_service=false;
    final Messenger messenger_receive = new Messenger(new IncomingHandler());

    @Override
    public PluginResult execute(String action, JSONArray data, String callbackId) {
        PluginResult result = null;

        try {
            if (action.toUpperCase().equals("CONNECT")) {
                result = ConnectService();
            } else if (action.toUpperCase().equals("DISCONNECT")) {
                result = DisconnectService();
            } else if (action.toUpperCase().equals("IS_CONNECTED")) {
                result = new PluginResult(Status.OK,connected_to_service);
            } else if (action.toUpperCase().equals("COMMAND")) {
                sendMSG (data.getString(0));
                result = new PluginResult(Status.OK);
            } else {
                result = new PluginResult(Status.INVALID_ACTION);
            }
        } catch(JSONException e) {
             result= new PluginResult(PluginResult.Status.JSON_EXCEPTION);
        }
        return result;
    }

    private PluginResult ConnectService() {
        doBindService();
        return new PluginResult(Status.OK);
    }
    private PluginResult DisconnectService() {
        doUnbindService();
        return new PluginResult(Status.OK);
    }

    class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MoMe_Service.MSG_COMMAND:
                Log.i("CLIENT","Received from service: " + msg.getData().getString("MSG"));
                break;
            default:
                super.handleMessage(msg);
        }
    }
}

private ServiceConnection service_connection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,  IBinder service) {
        messenger_service = new Messenger(service);
        connected_to_service=true;
        try {
            Message msg = Message.obtain(null,  My_Service.MSG_REGISTERED);
            msg.replyTo = messenger_receive;
            messenger_service.send(msg);
        } catch (RemoteException e) {
            // In this case the service has crashed before we could even
            // do anything with it; we can count on soon being
            // disconnected (and then reconnected if it can be restarted)
            // so there is no need to do anything here.
        }

    }

    public void onServiceDisconnected(ComponentName className) {
        // This is called when the connection with the service has been
        // unexpectedly disconnected -- that is, its process crashed.
        messenger_service = null;
        connected_to_service=false;
    }
};    

private void doBindService() {
    // Establish a connection with the service.  We use an explicit
    // class name because there is no reason to be able to let other
    // applications replace our component.
    this.ctx.bindService(new Intent(this.ctx, My_Service.class), service_connection, Context.BIND_AUTO_CREATE);
}

private void doUnbindService() {
    if (connected_to_service) {
        if (messenger_service != null) {
            try {
                Message msg = Message.obtain(null, My_Service.MSG_UNREGISTERED);
                msg.replyTo = messenger_receive;
                messenger_service.send(msg);
            } catch (RemoteException e) {
                // There is nothing special we need to do if the service
                // has crashed.
            }
        }

        // Detach our existing connection.
        this.ctx.unbindService(service_connection);
        connected_to_service = false;
    }
}

private void sendMSG (String message) {
    try {
        Message msg=Message.obtain(null, My_Service.MSG_COMMAND);
        Bundle msg_bundle=new Bundle();
        msg_bundle.putString("MSG", message);
        msg.setData(msg_bundle);
        messenger_service.send(msg);
    } catch (RemoteException e) {
        doUnbindService();
    }
}

}

From this plugin the real trouble comes with this part of code, which handles the return messages and the plugin return (which goes to the javascript):

    @Override
    public PluginResult execute(String action, JSONArray data, String callbackId) {
        PluginResult result = null;

        try {
            result = new PluginResult(Status.ok);
            }
        } catch(JSONException e) {
             result= new PluginResult(PluginResult.Status.JSON_EXCEPTION);
        }
        return result;
    }

    class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MoMe_Service.MSG_COMMAND:
                msg.getData().getString("MSG")); // THIS IS THE DATA I NEED RETURNED
                break;
            default:
                super.handleMessage(msg);
        }
      }
     }

The only solution I can think of, is storing the response in either a database or a variable and have the javascript do a setInterval to keep checking for changes. However I am not very fond of this solution. I would like to use some sort of callback function to let the javascript know the message has returned but I have no idea how. I would greatly appreciate any help and ideas.

Thank you, Vlad

This might be a late answer but I started to work with Cordova Plugin around 5 months ago and I just saw your question. Since you did not choose the correct answer I wanted to answer your question.

Assuming you have asynchronous process and you have a listener and methods, success and fail , lets call it onSuccess() and onFail() . As long as you send true with pluginResult.setKeepCallback(true) , the process will remain as unfinished, so you can send your plugin result data later on when you are done with background process. Here is an example take a look.

@Override
public boolean execute(String action, JSONArray data, String callbackId)  throws JSONException {
        if (action.equals("start")) {

              start();
        } else {
            PluginResult pluginResult = new PluginResult(PluginResult.Status.INVALID_ACTION);
            callbackContext.sendPluginResult(pluginResult);
            return false;
        }
}


    private boolean start() throws JSONException {

        MyClass.startProcess(new MyInterface() {

            @Override
            public void onSuccess(String data) {

                PluginResult result = new PluginResult(PluginResult.Status.OK, data);
                result.setKeepCallback(false);
                callbackContext.sendPluginResult(result);
            }

            @Override
            public void onFail() {

                PluginResult result = new PluginResult(PluginResult.Status.ERROR);
                result.setKeepCallback(false);
                callbackContext.sendPluginResult(result);
            }

        });

    PluginResult.Status status = PluginResult.Status.NO_RESULT;

    PluginResult pluginResult = new PluginResult(status);
    pluginResult.setKeepCallback(true);
    callbackContext.sendPluginResult(pluginResult);
    return true;

    }

The answer to my problem was actually in the the PluginResult object and success method. I've found a plugin that had to face the same problem in order to work, and from this code i was able to figure out my answer.This is a onPhoneStatusChange plugin, which can be found here !

The mystery lies in these lines:

PluginResult res = new PluginResult(PluginResult.Status.OK, obj);
res.setKeepCallback(true);
success(res, callbackId);

The generic solution to this problem is to have the service store the response into persistent storage (like a database) and then fire off a broadcast intent. Then just have a BroadcastReciever in your ServiceClient_plugin class listening for the broadcast. This way you woun't have to keep polling to see if the data has arrived yet.

You can send PluginResult using success() function like this:

public PluginResult execute(String action, JSONArray data, String callbackId) {}
private BroadcastReceiver Wifi_Receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        MyClass.this.success(new PluginResult(PluginResult.Status.OK,"count"+count),callback);
    }
}

here callback is callbackId of execute() function

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM