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

Tou*_*mal 5 service android ipc widget 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);
Run Code Online (Sandbox Code Playgroud)

我也有...

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!");
    }
};
Run Code Online (Sandbox Code Playgroud)

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

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

Tou*_*mal 15

我找到了自己问题的答案.为了让其他人更轻松,以下是解决方案:

在进行远程服务时,必须编写AIDL,它将被编译成一种存根接口,该接口的实现(即当有人调用远程方法时执行的代码),以及扩展"服务"的类"它返回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
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

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

<service android:name="RemoteService"> 
    <intent-filter>
        <action android:name="com.sofurry.favorites.IRemoteService"></action>
</intent-filter>
</service>
Run Code Online (Sandbox Code Playgroud)

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

要完成这个,这里是客户端的代码 - 是的,这个代码也适用于另一个服务,例如你从你的小部件开始的;)

IRemoteService mService;
RemoteServiceConnection mConnection = new RemoteServiceConnection();
getApplicationContext().bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
Run Code Online (Sandbox Code Playgroud)

...其中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;
    }
};
Run Code Online (Sandbox Code Playgroud)

现在,你可以自由地打电话..

mService.remoteDetonateBirthdayCake();
Run Code Online (Sandbox Code Playgroud)

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

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