Enlace a un servicio desde otra aplicación.

Escribí dos aplicaciones (target Gingerbread). Digamos app1 y app2. App1 tiene dos servicios iniciados con "BOOT_COMPLETED" y se inician con el valor de retorno START_STICKY. Se ejecutan en hilos separados. Para acortar una historia larga. Uno de los servicios está observando los datos entrantes en un puerto serie (un tipo de proxy para la aplicación que se comunica con interfaces en el otro extremo del puerto serie). El otro tiene un oyente que observa el estado del sistema y espera algunas "instrucciones" de otras aplicaciones. Sé que están funcionando bien porque están listados en los servicios en ejecución y agregué un código que los obliga a hacer algunas cosas cuando algunos datos específicos provienen del puerto serie.

Ahora el problema: escribí app2. Intenta enlazar a uno de los servicios en app1. solíadesarrollador de android Documentación e implementado una comunicación bidireccional entre el servicio en app1 y app2. Como solo tengo una pequeña cantidad de datos muy simples para enviar, usé un mensajero, como se sugiere. Básicamente, solo uso el "what, arg1 y arg2". No usé la interfaz AIDL como sugería la documentación.

Aquí está la sección de androidmanifest que declara el servicio en app1 Intento enlazar también.

    <service android:name=".ModemWatcherService"
              android:label="@string/app_name"
              android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <!-- Service name -->
            <action android:name="com.admetric.modemwatcher.Service" />
        </intent-filter>
    </service>

Entonces, aquí están los pocos métodos que tratan este problema en app1:

    @Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "entering onBind");
    return mMessenger.getBinder();
}

/**
 * Handler of incoming messages from clients.
 */
class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        String logMessage = "Received meaasge what= %d, arg1= %d, arg2= %d" + String.valueOf(msg.what) + String.valueOf(msg.arg1) + String.valueOf( msg.arg2);
        Log.d(TAG, logMessage);
        switch (msg.what) {
            case MSG_REGISTER_CLIENT:
                mClients.add(msg.replyTo);
                break;
            case MSG_UNREGISTER_CLIENT:
                mClients.remove(msg.replyTo);
                break;
            case .....  
             more code here for the application
            default:
                super.handleMessage(msg);
        }
    }
}


@Override
public void onCreate() {
    mHandler = new Handler();
    startSignalLevelListener();
    Log.i(TAG, "Just did onCreated");
    // Display a notification about us starting.  We put an icon in the status bar.
    // showNotification();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i(TAG, "Received start id " + startId + ": " + intent);
    // We want this service to continue running until it is explicitly
    // stopped, so return sticky.
    return START_STICKY;
}

Para app2, aquí está el código relevante para establecer el enlace con la comunicación bidireccional:

public final class ComWithIoMcu extends Service {
private static final String TAG = "ComWithIoMcu";
/** Messenger for communicating with service. */
static Messenger mServiceMcu = null;
/** Flag indicating whether we have called bind on the service. */
boolean mIsBound;

/**
 * Command to the service to register a client, receiving callbacks
 * from the service.  The Message's replyTo field must be a Messenger of
 * the client where callbacks should be sent.
 */
static final int MSG_REGISTER_CLIENT = 1;

/**
 * Command to the service to unregister a client, ot stop receiving callbacks
 * from the service.  The Message's replyTo field must be a Messenger of
 * the client as previously given with MSG_REGISTER_CLIENT.
 */
static final int MSG_UNREGISTER_CLIENT = 2;
/**
 * Command to forward a string command to the I/O MCU
 */    
public static final int MSG_SEND_STRING_TO_IOMCU = 3;
/** List of supported commands
 * 
 */
   ...... more code ....

/**
 * Handler of incoming messages from service.
 */
class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_UNSOL_MESSAGE:
                Log.d(TAG, "Received from service: " + msg.arg1);
                break;
            default:
                super.handleMessage(msg);
        }
    }
}

/**
 * Target we publish for clients to send messages to IncomingHandler.
 */
final Messenger mMessenger = new Messenger(new IncomingHandler());
boolean mBound;

/**
 * Class for interacting with the main interface of the service.
 */
private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder service) {
        // This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  We are communicating with our
        // service through an IDL interface, so get a client-side
        // representation of that from the raw service object.
        mServiceMcu = new Messenger(service);
        Log.d(TAG, "Attached.");

        // We want to monitor the service for as long as we are
        // connected to it.
        try {
            Message msg = Message.obtain(null,
                    MSG_REGISTER_CLIENT);
            msg.replyTo = mMessenger;
            mServiceMcu.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.
            Log.e(TAG, "ModemWatcherService is not running");
        }
    }

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


    }
};

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.
    //bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
    try {
        Intent intentForMcuService = new Intent();
        Log.d(TAG, "Before init intent.componentName");
        intentForMcuService.setComponent(new ComponentName("com.admetric.modemwatcher", "ModemWatcherService"));
        Log.d(TAG, "Before bindService");
        if (bindService(intentForMcuService, mConnection, 0)){
            Log.d(TAG, "Binding to Modem Watcher returned true");
        } else {
            Log.d(TAG, "Binding to Modem Watcher returned false");
        }
    } catch (SecurityException e) {
        Log.e(TAG, "can't bind to ModemWatcherService, check permission in Manifest");
    }
    mIsBound = true;
    Log.d(TAG, "Binding.");
}

void doUnbindService() {
    if (mIsBound) {
        // If we have received the service, and hence registered with
        // it, then now is the time to unregister.
        if (mServiceMcu != null) {
            try {
                Message msg = Message.obtain(null, MSG_UNREGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mServiceMcu.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;
        Log.d(TAG, "Unbinding.");
    }
}

En cuanto a los servicios en ejecución, puedo ver que el servicio que creé en app2 se está ejecutando. Logcat me muestra que intento enlazar el ModemWatcherService pero no se encuentra. Aquí está la interesante sección de logcat.

12-05 17:22:59.884 D/ComWithIoMcu(  547): Before init intent.componentName
12-05 17:22:59.884 D/ComWithIoMcu(  547): Before bindService
12-05 17:22:59.888 D/ComWithIoMcu(  547): Binding to Modem Watcher returned false
12-05 17:22:59.888 D/ComWithIoMcu(  547): Binding.
12-05 17:22:59.888 W/ActivityManager(   89): Unable to start service Intent { cmp=com.admetric.modemwatcher/ModemWatcherService }: not found

Mi primer pensamiento fue que me faltaba un permiso, pero bindService () puede desechar excepciones de seguridad y en este caso no lo hace, lo verifiqué y devolví falso por una razón desconocida. Además, sé que en la aplicación 1, nunca se llama a onBind demostrando que la vinculación nunca ocurrió. Así que el mensaje logcat "no encontrado" tiene sentido, pero declaré que el servicio era público en su manifiesto. Probablemente sea un error simple, pero he estado en esta cuestión por un tiempo y no he encontrado por qué. ¿Alguna idea de por qué app2 no puede encontrar el servicio en app1? Solo utilicé cortar y pegar nombres para no cometer errores estúpidos en los nombres. ¿Me faltan permisos de algún tipo? ¿Debo realizar algún paso adicional para publicar el servicio para todo el sistema? Esa es la primera vez que trato de acceder a algo en una aplicación desde otra aplicación, así que es posible que me haya perdido algo.

Respuestas a la pregunta(1)

Su respuesta a la pregunta