Bindung an einen Dienst von einer anderen App

Ich habe zwei Apps geschrieben (Target Gingerbread). Sagen wir app1 und app2. App1 hat zwei Dienste mit "BOOT_COMPLETED" gestartet und sie werden mit dem Rückgabewert START_STICKY gestartet. Sie laufen in separaten Threads. Um es kurz zu machen. Einer der Dienste überwacht eingehende Daten über einen seriellen Port (eine Art Proxy für die App-Kommunikation mit Schnittstellen am anderen Ende des seriellen Ports). Der andere hat einen Listener, der den Systemstatus überwacht und auf "Anweisungen" von anderen Apps wartet. Ich weiß, dass sie gut laufen, weil sie in den laufenden Diensten aufgelistet sind und ich einen Code hinzugefügt habe, der sie dazu zwingt, einige Dinge zu tun, wenn bestimmte Daten von der seriellen Schnittstelle kommen.

Nun das Problem: ich habe app2 geschrieben. Es wird versucht, an einen der Dienste in App1 zu binden. ich benutzteAndroid-Entwickler Dokumentation und implementiert eine bidirektionale Kommunikation zwischen dem Dienst in App1 und App2. Da ich nur eine kleine Menge sehr einfacher Daten zu senden habe, habe ich, wie vorgeschlagen, einen Messenger verwendet. Ich benutze im Grunde nur "what, arg1 und arg2". Ich habe die AIDL-Schnittstelle nicht verwendet, wie in der Dokumentation vorgeschlagen.

Hier ist der Abschnitt des Android-Manifests, der den Dienst in App1 deklariert, den ich auch zu binden versuche.

    <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>

Dann sind hier die wenigen Methoden, die sich mit diesem Problem in App1 befassen:

    @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;
}

Für App2 ist hier der relevante Code zum Herstellen der Bindung mit der bidirektionalen Kommunikation:

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.");
    }
}

Wenn ich mir die ausgeführten Dienste ansehe, kann ich feststellen, dass der in App2 erstellte Dienst ausgeführt wird. Logcat zeigt mir, dass ich versuche, den ModemWatcherService zu binden, aber er wird nicht gefunden. Hier ist der interessante Abschnitt von 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

Mein erster Gedanke war, dass ich keine Berechtigung hatte, aber bindService () kann Sicherheitsausnahmen auslösen. In diesem Fall habe ich dies überprüft und es wird aus einem unbekannten Grund false zurückgegeben. Außerdem weiß ich, dass in App1 der Befehl onBind niemals aufgerufen wird, um zu beweisen, dass die Bindung niemals stattgefunden hat. Die logcat-Meldung "nicht gefunden" ist also sinnvoll, aber ich habe diesen Dienst in seinem Manifest als öffentlich deklariert. Es ist wahrscheinlich ein einfacher Fehler, aber ich bin jetzt schon eine Weile mit diesem Thema beschäftigt und habe nicht herausgefunden, warum. Irgendeine Idee, warum App2 den Dienst in App1 nicht finden kann? Ich habe nur Ausschneiden und Einfügen für Namen verwendet, damit ich keine dummen Tippfehler in Namen mache. Vermisse ich irgendwelche Berechtigungen? Muss ich einen zusätzlichen Schritt ausführen, um den Service für das gesamte System zu veröffentlichen? Es ist das erste Mal, dass ich von einer anderen App aus auf etwas in einer App zugreife, sodass ich möglicherweise etwas verpasst habe.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage