5

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

所以(我认为)我需要的是 IPC 和 AIDL。

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

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

或者我是这么想的。

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

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

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

在小部件的服务 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 是去这里的方式,我做错了什么?

4

1 回答 1

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

在你的 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 清单中有一个服务节,将“名称”设置为在其 onBind() 方法中返回实际实现的类,并且您还必须有一个意图过滤器,其操作定义指向AIDL 接口。

提示:如果您从不同 APK 中的应用程序调用远程服务,请也将“类别”元素添加到意图过滤器,并将其设置为 DEFAULT。

于 2011-01-11T20:14:32.450 回答