通过跨进程引用返回 AIDL 接口实现
我正在进行一个小型 Android 项目,其中涉及一些 IPC,其中客户端活动绑定到我的服务。
我正在将 AIDL 用于 IPC 和 RPC,效果非常好,但我在将服务端实例化的 AIDL 接口实现返回给客户端时遇到了麻烦:
当客户端与服务在同一进程中运行时,这意味着运行本地服务——一切正常。
但是当客户端和服务分离在不同的进程中时,ILogDroidBinder.aidl 中定义的 startLogSession 方法始终返回 null。
此接口中实现的另一个方法 - getSessionIds - 返回包含整数的列表,始终有效(本地和跨进程)。
我大胆猜测,假设我的 ILogDroidSession 实现也应该实现 Parcelable,但这行不通,因为我无法打包包含 SQLiteDatabase 引用的对象(或者我可以吗?)。
这是相关代码。 如果有人能在这里帮助我,我真的很高兴。也许我只是在某个地方遗漏了一点,因为这是我的第一个 Android 项目,而且我还没有完全参与其中。
ILogDroidSession.aidl (这是我想要返回给客户端的实现):
package net.sourceforge.projects.logdroid;
interface ILogDroidSession {
/**
* Logs the given text to the error message channel of the current logging
* session.
* @param text Text to log.
*/
void logError(in String text);
}
ILogDroidBinder.aidl (传递给客户端 onServiceConnected 的 IBinder 接口):
package net.sourceforge.projects.logdroid;
import net.sourceforge.projects.logdroid.ILogDroidSession;
interface ILogDroidBinder {
/**
* Starts a new LogDroid session which handles all logging events.
* @param sessionName The name of the session.
* @return An instance of ILogDroidSession.
*/
ILogDroidSession startLogSession(in String sessionName);
/**
* Gets a list with all available LogSession ids.
*/
List getSessionIds();
}
LogDroidService.java (我的服务中的相关代码):
public class LogDroidService extends Service {
/**
* The binder interface needed for Activities to bind to the
* {@code LogDroidService}.
*/
private final ILogDroidBinder.Stub binder = new ILogDroidBinder.Stub() {
/**
* Starts a new LogDroidSession.
*/
public ILogDroidSession startLogSession(String sessionName) {
return LogDroidService.this.createSession(sessionName);
}
/**
* Gets all available session ids.
*/
public List<Integer> getSessionIds() {
return LogDroidService.this.getSessionIds();
}
};
/**
* The database connection to be used for storing and retrieving log entries.
*/
private LogDroidDb database;
@Override
public void onCreate() {
super.onCreate();
database = new LogDroidDb(getApplicationContext());
try {
database.open(); // opens as writable database
} catch ( SQLException ignorefornow ) {
}
}
@Override
public IBinder onBind(Intent ignore) {
return binder;
}
/**
* Creates a new LogDroidSession which will be returned to the user as a
* AIDL remote object.
* @param sessionName Name of the session.
* @return A new instance of ILogDroidSession
*/
ILogDroidSession createSession(String sessionName) {
LogDroidSession session = new LogDroidSession(database, sessionName);
session.addLoggingOccurredListener(this);
return session;
}
/**
* Retrieves all session ids.
* @return Array containing all LogDroidSession ids.
*/
ArrayList<Integer> getSessionIds() {
return database.getSessionIds();
}
}
MainActivity.java (相关客户端代码):
public class MainActivity extends Activity {
private ILogDroidSession session;
private ILogDroidBinder binder;
private ServiceConnection con = new ServiceConnection() {
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
binder = ILogDroidBinder.Stub.asInterface(arg1); // always works
try {
// works locally but always returns null when cross-process
session = binder.startLogSession("TestSession");
// always works
List<Integer> ids = binder.getSessionIds();
} catch ( Exception ex) {
// no exceptions are thrown either when running locally or cross-process
Toast.makeText(getApplicationContext(), ex.getMessage(),
Toast.LENGTH_LONG).show();
}
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
I have a little Android project going on which involves some IPC where client Activities bind to my service.
I'm using AIDL for IPC and RPC which works pretty good, but I'm having trouble returning a service-side instantiated AIDL interface implementation to the clients:
When the client is running in the same process as the service -- meaning running the service locally -- everything works just fine.
But when client and service are seperated in different processes the method startLogSession, which is defined in ILogDroidBinder.aidl always returns null.
The other method implemented in this interface -- getSessionIds -- which returns a List containing ints, always works (locally and cross-process).
I'm taking a wild guess and suppose my ILogDroidSession implementation should also implement Parcelable, but that wouldn't work, because I can't parcel an object containg a reference to an SQLiteDatabase (or can I?).
Here is the relevant code.
I'd really be glad if someone could help me out here. Maybe I'm just missing a point somewhere, since this is my first Android project and I'm not quite involved yet.
ILogDroidSession.aidl (An implementation of this is what I want to return to the client):
package net.sourceforge.projects.logdroid;
interface ILogDroidSession {
/**
* Logs the given text to the error message channel of the current logging
* session.
* @param text Text to log.
*/
void logError(in String text);
}
ILogDroidBinder.aidl (The IBinder interface passed to the client's onServiceConnected):
package net.sourceforge.projects.logdroid;
import net.sourceforge.projects.logdroid.ILogDroidSession;
interface ILogDroidBinder {
/**
* Starts a new LogDroid session which handles all logging events.
* @param sessionName The name of the session.
* @return An instance of ILogDroidSession.
*/
ILogDroidSession startLogSession(in String sessionName);
/**
* Gets a list with all available LogSession ids.
*/
List getSessionIds();
}
LogDroidService.java (Relevant code from my service):
public class LogDroidService extends Service {
/**
* The binder interface needed for Activities to bind to the
* {@code LogDroidService}.
*/
private final ILogDroidBinder.Stub binder = new ILogDroidBinder.Stub() {
/**
* Starts a new LogDroidSession.
*/
public ILogDroidSession startLogSession(String sessionName) {
return LogDroidService.this.createSession(sessionName);
}
/**
* Gets all available session ids.
*/
public List<Integer> getSessionIds() {
return LogDroidService.this.getSessionIds();
}
};
/**
* The database connection to be used for storing and retrieving log entries.
*/
private LogDroidDb database;
@Override
public void onCreate() {
super.onCreate();
database = new LogDroidDb(getApplicationContext());
try {
database.open(); // opens as writable database
} catch ( SQLException ignorefornow ) {
}
}
@Override
public IBinder onBind(Intent ignore) {
return binder;
}
/**
* Creates a new LogDroidSession which will be returned to the user as a
* AIDL remote object.
* @param sessionName Name of the session.
* @return A new instance of ILogDroidSession
*/
ILogDroidSession createSession(String sessionName) {
LogDroidSession session = new LogDroidSession(database, sessionName);
session.addLoggingOccurredListener(this);
return session;
}
/**
* Retrieves all session ids.
* @return Array containing all LogDroidSession ids.
*/
ArrayList<Integer> getSessionIds() {
return database.getSessionIds();
}
}
MainActivity.java (Relevant client code):
public class MainActivity extends Activity {
private ILogDroidSession session;
private ILogDroidBinder binder;
private ServiceConnection con = new ServiceConnection() {
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
binder = ILogDroidBinder.Stub.asInterface(arg1); // always works
try {
// works locally but always returns null when cross-process
session = binder.startLogSession("TestSession");
// always works
List<Integer> ids = binder.getSessionIds();
} catch ( Exception ex) {
// no exceptions are thrown either when running locally or cross-process
Toast.makeText(getApplicationContext(), ex.getMessage(),
Toast.LENGTH_LONG).show();
}
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
ILogDroidSession 可以定义为 java 文件中的接口,它不应该在 AIDL 中。
如果客户端和 LogDroidService 在不同的进程中运行,则 LogDroidSession 应该可打包以通过 IPC 发送/接收。
跨进程交换的数据应该只是发送者和接收者通过协议理解的字节流。
LogDroidSession 不能在这里进行打包,请向 ILogDroidBinder 添加新函数,以返回会话相关信息(以纯数据类型的形式)。
ILogDroidSession can be defined as just interface in java file, it shouldn't be in AIDL.
If the client and LogDroidService are running in different processes, LogDroidSession should be parcelable to send/receive over IPC.
Data that is exchanged across the processes should just be stream of bytes that both sender and receiver understands through a protocol.
LogDroidSession can't be parceled here, add new functions to ILogDroidBinder that returns session related information (in the form of plain data types).