如何从Widget /本地服务调用Android远程服务(IPC)?

时间:2011-01-11 17:26:49

标签: android service widget ipc aidl

我正在尝试从小部件远程控制动态壁纸。他们在同一个APK中,但显然是不同的过程。调用动态壁纸的“活动”对我来说没什么用处,因为它是一个不同的过程。小部件有简单的按钮,按下时,

那么(我认为)我需要的是IPC和AIDL。

首先我在壁纸一侧创建了AIDL,效果很好。它有三种方法,没有额外的参数。但是当我将客户端添加到窗口小部件时,我收到一个错误,告诉我无法绑定到该远程接口,因为窗口小部件已经是BroadcastListener。我尝试在不需要Widget成为BroadcastListener的情况下获取按钮处理,但这似乎是不可能的。

没问题,对吧?我刚刚在窗口小部件中创建了一个绑定到远程接口的服务,因为虽然窗口小部件是BroadcastListener,但服务不是,一切都应该没问题。

或者我想。

好吧,我正在获取小部件的按钮以触发小部件服务。绑定到远程服务会产生以下警告:

无法启动服务Intent(act = com.blabla.IRemoteService):找不到。

我在小部件的服务中使用getApplicationContext()来绑定到远程的东西。我在清单中有小部件服务,但我没有远程服务。当我把它放在那里时,我得到一个非特定的InstantiationException。

在Widget的服务onStart()中,我这样做:

getApplicationContext().bindService(new Intent(IWallpaperRemote.class.getName()), 
  mConnection, Context.BIND_AUTO_CREATE);

我也有......

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder service) {
        mService = IWallpaperRemote.Stub.asInterface(service);
        isBound = true;
        Log.i("WidgetServiceConnection", "binding to service succeeded");
    }

    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        isBound = false;
        Log.i("WidgetServiceConnection", "binding to service lost!");
    }
};

我的问题是:有没有人成功地从一个小部件到另一个应用程序进行远程调用?考虑到我在这里谈论一个动态壁纸,以及我对在小部件过程中调用活动但在动态壁纸中引起函数调用不感兴趣的事实,除了IPC之外我还有哪些选项,如果有的话?

如果IPC是去这里的方式,我做错了什么?

1 个答案:

答案 0 :(得分:15)

我找到了自己问题的答案。为了让其他人更容易,这是解决方案:

在进行远程服务时,必须编写AIDL,它将被编译成一种存根接口,该接口的实现(即当有人调用远程方法时执行的代码),以及扩展“Service”,它返回onBind()方法中的实现类。 (正常的本地服务将在该方法中返回null)

现在我不明白的是你必须在清单中有一个服务定义 - 使用过滤器!

假设您的AIDL名为IRemoteService.aidl,那么您有一个名为RemoteService的类,如下所示:

public class RemoteService extends Service {
  public IBinder onBind(Intent intent) {
    Log.i("RemoteService", "onBind() called");
    return new RemoteServiceImpl();
  }
  /**
   * The IRemoteInterface is defined through IDL
   */
  public class RemoteServiceImpl extends IRemoteService.Stub {
    public void remoteDetonateBirthdayCake() throws RemoteException {
        //your code here
    }
  };
}

在你的Android清单中,你想要这个:

<service android:name="RemoteService"> 
    <intent-filter>
        <action android:name="com.sofurry.favorites.IRemoteService"></action>
</intent-filter>
</service>

注意服务名称:它是“RemoteService”,而不是“IRemoteService”甚至是“RemoteServiceImpl”。您需要扩展“Service”的类的名称,我们覆盖其onBind方法。

要完成这件事,这里是客户端的代码 - 是的,这个代码也可以在另一个服务中运行,例如你从小部件启动的代码;)

IRemoteService mService;
RemoteServiceConnection mConnection = new RemoteServiceConnection();
getApplicationContext().bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);

...其中RemoteServiceConnection可以是这样的内部类:

class RemoteServiceConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName className, 
        IBinder service ) {
        mService = IRemoteService.Stub.asInterface(service);
        isBound = true;
    }

    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        isBound = false;
    }
};

现在,您可以随时致电..

mService.remoteDetonateBirthdayCake();

总结:确保在android清单中有一个服务节,将“name”设置为在其onBind()方法中返回实际实现的类,并且你还必须有一个带有操作定义的intent过滤器指向AIDL界面。

提示:如果您从其他APK内的应用程序调用远程服务,请将“category”元素添加到intent过滤器,并将其设置为DEFAULT。