【问题标题】:How to call an Android remote service (IPC) from a Widget / local service?如何从小部件/本地服务调用 Android 远程服务 (IPC)?
【发布时间】:2011-01-11 17:26:48
【问题描述】:

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

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

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

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

我是这么想的。

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

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

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

在 Widget 的 Service 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 是通往这里的路,那我做错了什么?

【问题讨论】:

  • ~ "它们在同一个 APK 中,但进程明显不同。"如果它们在同一个 APK 中,它们绝对不需要是单独的进程!
  • AFAIK 你不能有一个小部件也可以是一个动态壁纸。这应该如何工作?

标签: android service widget ipc aidl


【解决方案1】:

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

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

现在我不明白的是,您必须在清单中有一个服务定义 - WITH INTENT FILTER!

假设您的 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 manifest 中,你想要这个:

<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 中的应用调用远程服务,请也向 Intent 过滤器添加“类别”元素,并将其设置为 DEFAULT。

【讨论】:

  • 嗨..你能把你的示例应用程序发给我吗..或者请尝试给我答案...stackoverflow.com/questions/11116614/…
  • 注意:现在使用 Android Studio,AIDL 文件进入一个新位置:“./app/src/main/aidl/...”(文件必须与它所引用的类匹配相同的包层次结构) 并且您希望通过 AIDL 传递的不是字符串(或原语)的每个类/类型都需要实现 Parcelable 接口(如果使用它们,则导入到其他 AIDL 文件中,类似于 java 导入)。另外:AIDL 在任何 XML/Java 之前被解析,并且错误的调试消息可以使用巨大的改进。但是一旦你设置了它们 + parcelable 类,我发现这是进行 IPC 的最佳方法(加上集成测试很好)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 2013-01-13
  • 2023-03-22
相关资源
最近更新 更多