18

2 つのアプリ (Gingerbread をターゲット) を作成しました。app1 と app2 としましょう。App1 には「BOOT_COMPLETED」で開始された 2 つのサービスがあり、戻り値 START_STICKY で開始されます。それらは別々のスレッドで実行されます。長い話を短くします。サービスの 1 つは、シリアル ポート (シリアル ポートの反対側のインターフェイスと通信するアプリの一種のプロキシ) で着信データを監視しています。もう 1 つは、システム ステータスを監視し、他のアプリからの「指示」を待機するリスナーです。実行中のサービスにリストされているため、正常に実行されていることがわかります。シリアルポートから特定のデータが送信されたときに強制的に何らかの処理を実行させるコードを追加しました。

今問題: app2 を書きました。app1 のサービスの 1 つにバインドしようとします。android-developper のドキュメントを使用して、app1 と app2 のサービス間の双方向通信を実装しました。送信する非常に単純なデータが少量しかないため、提案されているようにメッセンジャーを使用しました。私は基本的に、ドキュメントが示唆していたように、AIDL インターフェイスを使用しなかった「what、arg1 および arg2」を使用するだけです。

これは、私もバインドしようとしている app1 でサービスを宣言する androidmanifest のセクションです。

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

次に、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;
}

app2 の場合、双方向通信とのバインディングを確立するための関連コードは次のとおりです。

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

実行中のサービスを見ると、app2 で作成したサービスが実行されていることがわかります。Logcat は、ModemWatcherService をバインドしようとしたが見つからないことを示しています。これが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

私が最初に考えたのは、アクセス許可が不足しているが、bindService() がセキュリティ例外をスローする可能性があるということでした。この場合はそうではありません。確認したところ、不明な理由で false が返されました。また、app1 では onBind が呼び出されず、バインディングが発生しなかったことを証明していることもわかっています。したがって、「見つかりません」というlogcatメッセージは理にかなっていますが、そのサービスをマニフェストでパブリックに宣言しました。これはおそらく単純なミスですが、私はこの問題にしばらく取り組んできましたが、その理由がわかりませんでした。app2 が app1 でサービスを見つけられない理由は何ですか? 私は名前にカットアンドペーストを使用したので、名前の愚かなタイプミスをしませんでした. 何らかの権限がありませんか? システム全体のサービスを公開するには、追加の手順を実行する必要がありますか? あるアプリの何かに別のアプリからアクセスしようとするのはこれが初めてなので、

4

1 に答える 1

21

あなたComponentNameは間違って構築されています。クラス名を渡すときは、次のように完全に修飾する必要があります。

intentForMcuService.setComponent(new ComponentName("com.admetric.modemwatcher",
        "com.admetric.modemwatcher.ModemWatcherService"));

もう1つService、アプリケーションの境界の外側ComponentNameを参照している場合は、正しく機能していても、参照に使用しないのがおそらく最善です。より一般的なアプローチは、のカスタムACTION文字列を作成し、そのアクションIntentServiceフィルターに設定することです。

于 2012-12-06T16:30:50.247 に答える