Android如何等待服务实际连接?
我有一个活动调用IDownloaderService.aidl中定义的服务:
public class Downloader extends Activity { IDownloaderService downloader = null; // ...
在Downloader.onCreate(Bundle)我试图bindService
Intent serviceIntent = new Intent(this, DownloaderService.class); if (bindService(serviceIntent, sc, BIND_AUTO_CREATE)) { // ...
在ServiceConnection对象sc中,我做了这个
public void onServiceConnected(ComponentName name, IBinder service) { Log.w("XXX", "onServiceConnected"); downloader = IDownloaderService.Stub.asInterface(service); // ...
通过添加各种Log.xx,我发现if(bindService(…))之后的代码实际上是在调用ServiceConnection.onServiceConnected之前进行的 – 也就是说,当下载器仍然为空时 – 这会让我陷入困境。 ApiDemos中的所有示例通过在用户操作触发时仅调用服务来避免此时间问题。 但是在bindService成功后我该怎么做才能正确使用这个服务呢? 我如何等待ServiceConnection.onServiceConnected被可靠地调用?
另一个问题有关。 是否所有的事件处理程序:Activity.onCreate,任何View.onClickListener.onClick,ServiceConnection.onServiceConnected等实际上调用在同一个线程(在文档中提到的“主线程”)? 它们之间是否存在交错,或者Android会安排逐个处理所有事件? 或者,什么时候ServiceConnection.onServiceConnected实际上被调用? 完成Activity.onCreate或A.oC仍在运行的某个时间?
我如何等待ServiceConnection.onServiceConnected被可靠地调用?
你没有。 你退出onCreate()
(或者你绑定的任何地方),你把onServiceConnected()
“需要build立连接”的代码。
是所有的事件处理程序:Activity.onCreate,任何View.onClickListener.onClick,ServiceConnection.onServiceConnected等实际上在同一个线程中调用
是。
什么时候ServiceConnection.onServiceConnected实际上被调用? 完成Activity.onCreate或A.oC仍在运行的某个时间?
你的绑定请求可能甚至不会开始,直到你离开onCreate()
。 因此, onServiceConnected()
会在你离开onCreate()
之后调用。
我有同样的问题。 不过,我不想把绑定的服务依赖代码放在onServiceConnected
,因为我想绑定/解除绑定onStart
和onStop,
但是我不希望代码每当活动回到前面时再次运行。 我只希望它在第一次创build活动时运行。
我终于onServiceConnected
了onStart()
隧道的视野,并使用布尔值来表示这是否是第一次onServiceConnected
运行。 这样,我可以在onStop
解除绑定服务,并在onStart
再次绑定服务,而不必每次运行所有的启动东西。
我结束了这样的事情:
1)给辅助材料一些范围,我创build了一个内部类。 至less,丑陋的内部与其他代码是分开的。 我需要一个远程服务来做一些事情 ,所以在类名字中Something
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper(); class RemoteSomethingHelper { //... }
2)调用远程服务方法需要两件事:IBinder和要执行的代码。 既然我们不知道哪一个先知道,我们存储它们:
private ISomethingService mISomethingService; private Runnable mActionRunnable;
每次我们写入这些文件之一,我们调用_startActionIfPossible()
:
private void _startActionIfPossible() { if (mActionRunnable != null && mISomethingService != null) { mActionRunnable.run(); mActionRunnable = null; } } private void performAction(Runnable r) { mActionRunnable = r; _startActionIfPossible(); }
这当然假设Runnable可以访问mISomethingService,但是对于在RemoteSomethingHelper
类的方法中创build的可运行对象来说,这是正确的。
在UI线程上调用 ServiceConnection
callback是非常好的:如果我们要从主线程调用服务方法,我们不需要关心同步。
ISomethingService
当然是通过AIDL定义的。
3)我们不只是将parameter passing给方法,而是创build一个Runnable,以便在调用时可以使用这些参数调用方法:
private boolean mServiceBound; void startSomething(final String arg1) { // ... starting the service ... final String arg2 = ...; performAction(new Runnable() { @Override public void run() { try { // arg1 and arg2 must be final! mISomethingService.startSomething(arg1, arg2); } catch (RemoteException e) { e.printStackTrace(); } } }); }
4)最后,我们得到:
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper(); class RemoteSomethingHelper { private ISomethingService mISomethingService; private Runnable mActionRunnable; private boolean mServiceBound; private void _startActionIfPossible() { if (mActionRunnable != null && mISomethingService != null) { mActionRunnable.run(); mActionRunnable = null; } } private ServiceConnection mServiceConnection = new ServiceConnection() { // the methods on this class are called from the main thread of your process. @Override public void onServiceDisconnected(ComponentName name) { mISomethingService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mISomethingService = ISomethingService.Stub.asInterface(service); _startActionIfPossible(); } } private void performAction(Runnable r) { mActionRunnable = r; _startActionIfPossible(); } public void startSomething(final String arg1) { Intent intent = new Intent(context.getApplicationContext(),SomethingService.class); if (!mServiceBound) { mServiceBound = context.getApplicationContext().bindService(intent, mServiceConnection, 0); } ComponentName cn = context.getApplicationContext().startService(intent); final String arg2 = ...; performAction(new Runnable() { @Override public void run() { try { mISomethingService.startSomething(arg1, arg2); } catch (RemoteException e) { e.printStackTrace(); } } }); } }
context
是我class的一个领域, 在一个Activity中,你可以把它定义为Context context=this;
我不需要排队。 如果你这样做,你可以实现它。
您可能需要在startSomething()中进行结果callback。 我做了,但是这个代码没有显示。
我之前做过类似的事情,唯一不同的是我没有约束服务,而是刚刚开始。
我会从服务广播一个意图通知调用者/活动有关它开始。
*基本思想与@ 18446744073709551615一样,但我也会分享我的代码。
作为主要问题的答案,
但是在bindService成功后,我该怎么做才能正确使用这个服务呢?
[最初的期望(但不工作)]
等到服务连接如下
@Override protected void onStart() { bindService(service, mWebServiceConnection, BIND_AUTO_CREATE); synchronized (mLock) { mLock.wait(40000); } // rest of the code continues here, which uses service stub interface // ... }
它不会工作,因为onCreate()/onStart()
和onServiceConnected()
在同一个主线程中被调用。 在等待完成之前, onServiceConnected()
永远不会被调用。
[替代解决scheme]
在“服务连接”之后定义自己的“可运行”,并在服务连接后执行此可运行。
按如下所示实现ServiceConnection的自定义类。
public class MyServiceConnection implements ServiceConnection { private static final String TAG = MyServiceConnection.class.getSimpleName(); private Context mContext = null; private IMyService mMyService = null; private ArrayList<Runnable> runnableArrayList; private Boolean isConnected = false; public MyServiceConnection(Context context) { mContext = context; runnableArrayList = new ArrayList<>(); } public IMyService getInterface() { return mMyService; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.v(TAG, "Connected Service: " + name); mMyService = MyService.Stub.asInterface(service); isConnected = true; /* Execute runnables after Service connected */ for (Runnable action : runnableArrayList) { action.run(); } runnableArrayList.clear(); } @Override public void onServiceDisconnected(ComponentName name) { try { mMyService = null; mContext.unbindService(this); isConnected = false; Log.v(TAG, "Disconnected Service: " + name); } catch(Exception e) { Log.e(TAG, e.toString()); } } public void executeAfterServiceConnected(Runnable action) { Log.v(TAG, "executeAfterServiceConnected"); if(isConnected) { Log.v(TAG, "Service already connected, execute now"); action.run(); } else { // this action will be executed at the end of onServiceConnected method Log.v(TAG, "Service not connected yet, execute later"); runnableArrayList.add(action); } } }
然后以下面的方式使用它(在你的Activity类或其他)
private MyServiceConnection myServiceConnection = null; @Override protected void onStart() { Log.d(TAG, "onStart"); super.onStart(); Intent serviceIntent = new Intent(getApplicationContext(), MyService.class); startService(serviceIntent); myServiceConnection = new MyServiceConnection(getApplicationContext()); bindService(serviceIntent, myServiceConnection, BIND_AUTO_CREATE); // Instead of "wait" here, create callback which will be called after service is connected myServiceConnection.executeAfterServiceConnected(new Runnable() { @Override public void run() { // Rest of the code comes here. // This runnable will be executed after service connected, so we can use service stub interface IMyService myService = myServiceConnection.getInterface(); // ... } }); }
它为我工作。 但是可能有更好的办法。
我发现这些解决方法只有在您的绑定服务在与应用程序的主进程不同的进程中运行时才值得努力和等待。
为了访问同一进程(或应用程序)中的数据和方法,我最终实现了单例类。 如果这些类需要一些方法的上下文,我将应用程序上下文泄露给单例类。 当然,这是一个坏的结果,因为它打破了“即时运行”。 但是,我认为这是一个更好的妥协。