Can bindService() be made to block?
我有一个使用远程服务的Android应用程序,并使用bindService()进行了绑定,该应用程序是异步的。
在绑定服务之前,该应用程序无用,因此我想简单地等到绑定完成后再开始任何Activity。有没有一种方法可以在调用onCreate()或onResume()之前绑定服务?我认为可能有一种方法可以在Application中进行绑定。有什么想法吗?
编辑:
如果在onCreate()中执行此操作。
1 2
| bindService(service, mWebServiceConnection, BIND_AUTO_CREATE);
synchronized (mLock) { mLock.wait(40000); } |
ServiceConnection.onServiceConnected在40秒钟内未得到调用。显然,如果要绑定服务,必须让onCreate()返回。
所以看来没有办法做我想做的事。
编辑2:
Android如何等待直到实际连接了服务?关于绑定服务时Android中发生的事情,有一些不错的评论。
- 在绑定了服务或服务完成其任务之前,该应用程序是没有用的吗?
-
@CaseyB在绑定之前无用。整个UI是来自服务的结果。
-
可能重复的Android,我如何等到实际上已连接服务?
-
除了重复之外,这里一半的答案仍然是错误的。我认为这也应该关闭。
当我需要等待服务绑定之后才能做其他事情时,我会玩锁。精确地,ServiceConnection拥有一个锁对象,并公开一个waitUntilConnected方法,该方法会阻塞该锁,直到唤醒信号为止。该通知位于onServiceConnected回调中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class MyServiceConnection implements ServiceConnection {
private volatile boolean connected = false;
private Object lock = new Object();
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
connected = true;
synchronized (lock) {
lock.notifyAll();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
connected = false;
}
public void waitUntilConnected() throws InterruptedException {
if (!connected) {
synchronized (lock) {
lock.wait();
}
}
}
} |
因此,例如,如果活动必须等待绑定服务,则它仅调用waitUntilConnected方法。
1 2 3 4 5 6 7 8 9 10
| protected void onStart() {
super.onStart();
bindService(myServiceIntent, myServiceConnection, Context.BIND_AUTO_CREATE);
try {
myServiceConnection.waitUntilConnected();
} catch (InterruptedException e) {
e.printStackTrace();
}
} |
作为示例,我将waitUntilConnected方法放在了onStart中,但是必须在另一个线程中调用它。我想听听一种更优雅的方式! :)
-
如何在没有死锁的情况下工作?一切都发生在同一线程中。
-
出于这个原因,我写了"我在onStart中放置了waitUntilConnected方法,但是如果在另一个线程中调用它显然更好"。该代码只是一个理论示例! :)
-
我只是对其进行了编辑,以使其更加清晰,谢谢! :)
-
也许CountDownLatch会比基本锁更好?他们俩都工作得很好。
-
这可以工作吗? ServiceConnection的文档说Like many callbacks from the system, the methods on this class are called from the main thread of your process.
-
它对我有用。总是最好以异步的方式思考。例如,如果我没记错的话,使用MessageQueue应该是Android的正确方法(自从我在Android上编码以来已经很久了...)
-
应该注意的是,在服务上调用取消绑定不会在onServiceDisconnected上调用。也就是说,此响应很好地回答了要阻塞的问题,直到bindservice完成。
您不能具有bindService()块。但是,您的ServiceConnection(bindService的第二个参数)具有回调函数来告诉您何时连接和断开服务,因此您可以使用其他代码块,直到您的onServiceConnected()方法取消阻塞它为止。
- 删除了一个愚蠢的回复:让我再试一次。我认为如果我阻塞onCreate()它将无法正常工作,因为它与必须传递ServiceConnection消息的线程相同。我将对其进行测试并做出回应。
-
似乎您的选择是让每个活动绑定自己的连接,或者如果您希望单个绑定,则可以重载Application类并在其中提供静态方法(确保它们同步以避免竞争情况)。绑定到该服务是否需要很长时间,以至于您无法在每次活动中都这样做呢?
-
您可以执行类似onStart中的bindService的操作,然后显示一个无限的ProgressDialog,直到触发ServiceConnection的onServiceConnected方法为止。如果bindService调用失败,请使用Handler超时。
-
@Femi您的评论似乎是该主题中最正确的答案。您会重新编写它作为答案,以备将来参考并获得一些赞誉吗?
-
@Femi不,您不能阻止bindService()。原因是您总是在主线程上被调用。如果您在调用后阻塞(例如wait()),则将永远不会被回叫...,因为您永远不会将控制权交还给循环器。
似乎有一种方法可以做到这一点。 KeyChain.java和几个Google编写的类使用LinkedBlockingQueue允许同步绑定到服务。
例如,请参见以下名为bind的方法:https://github.com/android/platform_frameworks_base/blob/master/keystore/java/android/security/KeyChain.java
由于使用了阻塞队列,似乎同步返回了服务对象。
不幸的是,如Android文档https://developer.android.com/reference/android/security/KeyChain.html所述,由于从队列中获取元素可能导致某些方法抛出InterruptedException。等待时中断。
-
使用BlockingQueue是阻塞运行KeyChain.bind()的线程直到服务连接的一种聪明方法。但这在从主/ UI线程调用时不起作用,因为对ServiceConnection.onServiceConnected()的调用已排队(例如,使用任务执行程序),无法并行运行。在线程的当前任务完成之前,对onServiceConnected()的调用将永远不会运行...但是它将被BlockingQueue.take()阻止。我假设KeyChain的Context的bindService()的实现并行调用onServiceConnected(),这与Activity的实现不同。
-
嗯,那完全是有道理的。我们可以同步地绑定到其他服务(例如警报,通知),但不能绑定到我们自己的服务,这很奇怪。
-
sureNotOnMainThread(context);所以仍然不能在主线程上阻塞bindService
Android 10在绑定到提供执行程序的服务时可以引入新的bindService方法签名(可以从执行程序创建)。
1 2 3 4 5 6 7 8 9 10 11
| /**
* Same as {@link #bindService(Intent, ServiceConnection, int)} with executor to control
* ServiceConnection callbacks.
* @param executor Callbacks on ServiceConnection will be called on executor. Must use same
* instance for the same instance of ServiceConnection.
*/
public boolean bindService(@RequiresPermission @NonNull Intent service,
@BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
@NonNull ServiceConnection conn) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
} |
查看此答案
bindService()不能被阻止。这样会破坏Service的全部目的。您说您的整个UI都包含该服务的结果。我认为您需要重新考虑您的UI,并使用某种中间表示来填充它,向用户显示应用程序正在收集数据。
-
不,使用Service避免阻塞网络I / O是非常合理的,但是首先只是在等待服务连接时才阻塞UI。
-
您知道服务通常在主UI线程上运行吗?您知道AIDL服务呼叫是同步的吗?所以不,它不会破坏服务的目的。服务不是(不一定)后台任务。它是业务(与UI相对)逻辑的封装。您也可以在UI层中运行后台任务(请参见AsyncTask),但这些不是服务。无论如何,我们在谈论的是绑定到服务,而不是调用服务。