从另一个应用程序绑定到服务 [英] Binding to a service from another app

查看:33
本文介绍了从另一个应用程序绑定到服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了两个应用程序(针对 Gingerbread).假设 app1 和 app2.App1 有两个以BOOT_COMPLETED"开头的服务,它们以返回值 START_STICKY 开头.它们在不同的线程中运行.使长话短说.其中一项服务是监视串行端口上的传入数据(一种用于与串行端口另一端的接口通信的应用程序的代理).另一个有一个监听器,监听一些系统状态并等待来自其他应用程序的一些指令".我知道它们运行良好,因为它们列在正在运行的服务中,并且我添加了一些代码,当某些特定数据来自串行端口时,它们会强制它们执行某些操作.

I wrote two apps (target Gingerbread). Let say app1 and app2. App1 has two services started with "BOOT_COMPLETED" and they are started with the return value START_STICKY. They run in separate threads. To make a long story short. One of the service is watching for incoming data on a serial port (a kind of proxy for app communicating with interfaces on the other end of the serial port). The other has a listener watching some system status and waiting for some "instructions" from other apps. I know they are running well because they are listed in the running services and I added some code that forces them to do some stuff when some specific data come from the serial port.

现在的问题是:我写了app2.它尝试绑定到 app1 中的服务之一.我使用了 android-developper 文档并在 app1 和 app2 中实现了服务之间的双向通信.由于我只有少量非常简单的数据要发送,因此我按照建议使用了信使.我基本上只使用什么,arg1 和 arg2"我没有像文档所建议的那样使用 AIDL 接口.

Now the problem: I wrote app2. It tries to bind to one of the service in app1. I used android-developper documentation and implemented a bidirectional communication between the service in app1 and app2. Since I just have a small amount of very simple data to send, I used a messenger, as suggested. I basically just use the "what, arg1 and arg2" I did not use the AIDL interface as the documentation was suggesting.

这是 androidmanifest 的部分,它在 app1 中声明了我也尝试绑定的服务.

Here is the section of the androidmanifest declaring the service in app1 I try to bind too.

    <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中处理这个问题的方法:

Then, here are the few method dealing with this issue in 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,这里是建立与双向通信的绑定的相关代码:

For app2, here is the relevant code to establish the binding with the bidirectional communication:

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 的有趣部分

Looking at the running services, I can see that the service I created in app2 is running. Logcat shows me that I try to bind the the ModemWatcherService but it is not found. Here is the interesting section of 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 中找不到服务吗?我只使用剪切和粘贴名称,这样我就不会在名称中犯愚蠢的错误输入错误.我是否缺少某种权限?我需要做一些额外的步骤来发布整个系统的服务吗?那是我第一次尝试从另一个应用程序访问一个应用程序中的某些内容,因此,我可能错过了某些内容.

My first thought was that I was missing a permission but bindService() can trow security exceptions and in this case it doesn't so, I checked and it returns false for an unknown reason. Also, I know that in app1, the onBind is never called proving that the binding never occurred. So the logcat message "not found" make sense but I declared that service public in its manifest. It's probably a simple mistake but I've been on this issu for a while now and I did not find why. Any idea why app2 can't find the service in app1 ? I used just cut and paste for names so I would not do stupid mistyping mistakes in names. Am I missing permissions of some sort? Do I need to do some extra step to publish the service for the whole system? That's the first time I try to access something in one app from another app so, I might have missed something.

推荐答案

您的 ComponentName 构造不正确.传入类名时,必须像这样完全限定:

Your ComponentName is incorrectly constructed. When passing in the class name is must be fully qualified like so:

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

另一件事,如果您在应用程序边界之外引用 Service,最好不要使用 ComponentName 来引用它,即使它确实工作正常.更常见的方法是为您的 Intent 创建自定义 ACTION 字符串,并让 Service 过滤该操作.

Another thing, if you are referencing a Service outside the boundaries of the application, it's probably best not to use ComponentName to reference it, even if it does work correctly. A more common approach would be to create a custom ACTION string for your Intent and have the Service filter that action.

这篇关于从另一个应用程序绑定到服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆