2010-06-16 35 views
42

我有一个活动呼叫中IDownloaderService.aidl定义的服务:Android如何等待服务实际连接?

public class Downloader extends Activity { 
IDownloaderService downloader = null; 
// ... 

在Downloader.onCreate(束)我试图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(...))之后的代码实际上是BEFORE ServiceConnection.onSer viceConnected正在被调用 - 也就是说,当下载器仍然是空的 - 这让我陷入困境。 ApiDemos中的所有示例通过在用户操作触发时仅调用服务来避免此时间问题。但是我应该怎么做才能在bindService成功后正确使用这个服务?我如何等待ServiceConnection.onServiceConnected被可靠地调用?

另一个问题有关。是否所有的事件处理程序:Activity.onCreate,任何View.onClickListener.onClick,ServiceConnection.onServiceConnected等实际上是在同一个线程中调用的(在doc中称为“主线程”)?它们之间是否存在交错,或者Android会安排逐个处理所有事件?或者,什么时候ServiceConnection.onServiceConnected实际上会被调用?完成Activity.onCreate或A.oC仍在运行时?

+0

如果你仍然需要一个答案,我给了一个解决方案在http://stackoverflow.com/a/22134635/1336747 – Alessio 2014-03-02 22:43:29

+0

@Alessio,我不认为你的解决方案实际工作。 – Sam 2015-06-19 08:44:23

回答

47

我如何可以等待 ServiceConnection.onServiceConnected 被称为可靠?

你不知道。您退出onCreate()(或您绑定的任何位置),并且您在onServiceConnected()中将“需要建立连接”代码。

是否所有的事件处理程序: Activity.onCreate,任何 View.onClickListener.onClick, ServiceConnection.onServiceConnected, 等实际调用在同一 线程

是。

究竟是什么时候 ServiceConnection.onServiceConnected 实际上是否会被调用?在完成Activity.onCreate或 时有时候A.oC仍在运行?

你绑定请求可能甚至不打算开始直到离开onCreate()后。因此,onServiceConnected()将在您离开onCreate()之后调用。

+3

感谢您的信息。希望Android文档能够像这样弄清事情。 – Ryan 2010-06-17 01:51:43

+2

各种服务API演示都有示例;具体请参阅本地服务绑定和远程服务绑定:http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/index.html Service java doc也提供了示例本地服务绑定代码:http://developer.android.com/reference/android/app/Service.html#LocalServiceSample – hackbod 2010-06-17 06:38:51

+0

谢谢hackbod。显然这一部分没有出现在我的本地文档副本中。我会在网上看看。 – Ryan 2010-06-18 15:28:46

1

我之前做过类似的事情,唯一不同的是我没有绑定到服务,而是刚刚启动它。

我会通过服务广播一个意图来通知调用者/活动它已启动。

+0

感谢您的快速回复。因此,在广播接收器中,您将绑定到服务以调用RPC方法,或者在您的情况下是不是要调用它们? – Ryan 2010-06-16 17:27:07

+1

您无法绑定到来自广播接收器的服务(因为广播接收是在从onReceive返回后完成的)。 – hackbod 2010-06-17 06:37:05

2

我有同样的问题。虽然我不想将绑定的服务相关代码放入onServiceConnected,因为我想绑定/解除绑定onStartonStop,,但我不希望代码在每次活动回到前面时再次运行。我只希望它在活动首次创建时运行。

我终于克服了我的onStart()隧道视野,并使用布尔值来表示这是否是第一个onServiceConnected奔跑或不。这样,我可以在onStop中取消绑定服务,并在onStart中再次绑定服务,而无需每次运行所有启动的东西。

2

我结束了这样的事情:

1),提供辅助的东西一定的范围,我创建了一个内部类。至少,丑陋的内部与其他代码是分开的。我需要一个远程服务做东西,因此字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类的方法中创建的可运行对象来说,情况也是如此。

ServiceConnection回调are called on the UI thread确实不错:如果我们打算从主线程调用服务方法,我们不需要关心同步。

ISomethingService当然是通过AIDL定义的。

3)而不是仅仅将参数传递给方法,我们创建一个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是我班的一个领域;在活动中,您可以将其定义为Context context=this;

我不需要排队操作;如果你这样做,你可以实现它。

您可能需要startSomething()中的结果回调;我做了,但是这个代码没有显示。

0

*基本思想与@ 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()两个bindService()被称为在服务相同的主线程onServiceConnected()在等待完成前从未被调用。

[替代溶液]

代替“等待”,定义自己的可运行到后服务关连调用和服务连接之后执行此运行的。

按如下方式实现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(); 
      // ... 
     } 
    }); 
} 

它为我工作。但可能有更好的方法。

0

我发现这些解决方法只有在您的绑定服务在与应用程序的主进程不同的进程中运行时才值得努力和等待。

为了访问同一进程(或应用程序)中的数据和方法,我最终实现了单例类。如果这些类需要一些方法的上下文,我将应用程序上下文泄露给单例类。当然,由于它打破了“即时运行”,所以它有一个不好的后果。但我认为这是一个更好的妥协。