2013-01-10 26 views
6

Ứng dụng của tôi sử dụng dịch vụ được cung cấp bởi ứng dụng khác (của tôi). Tôi đang sử dụng một số bound service wit a Messenger để truy cập và liên lạc với nó (và vì nó là một ứng dụng khác, nó cũng là một dịch vụ từ xa).bindService() trả về false nhưng unbindService() cần được gọi?

Khi tôi gọi bindService với mục đích phù hợp và cuộc gọi trả về false (như dự kiến ​​khi APK cung cấp dịch vụ không xung quanh), tôi giả định từ tài liệu và mã ví dụ, tôi không cần phải unbindServiceConnection. Tuy nhiên, khi làm như vậy trên thiết bị Galaxy Nexus (Jelly Bean) của tôi, tôi nhận được thông báo ServiceConnectionLeaked nổi tiếng khi hoàn thành Activity.

Tôi đã vớt rằng điều này bằng cách làm

if (!ctxt.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) { 
    try { 
     ctxt.unbindService(serviceConnection); 
    } catch (Throwable t) {} 
    // Clean up 
    return; 
} 
// Store serviceConnection for future use 

Tôi tò mò: Có phải tôi chỉ bỏ lỡ điều gì đó trong tài liệu hướng dẫn và nó phải làm việc theo cách này? Tôi đã thêm try ... catch để đảm bảo rằng ngay cả khi hành vi này thực sự khác trên các thiết bị khác hoặc phiên bản Android, ứng dụng của tôi không bị ảnh hưởng (tiêu cực).

Trả lời

7

Nói chung, ServiceConnection luôn được phân bổ và đăng ký theo khuôn khổ, bất kể là bindService() trả lại cuộc gọi đúng hay sai. Xem bindService() thực hiện trong android.app.ContextImpl:

public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) { 
    IServiceConnection sd; 
    if (conn == null) { 
     throw new IllegalArgumentException("connection is null"); 
    } 
    if (mPackageInfo != null) { 
     // A new ServiceDispatcher will be created and registered along with 
     // ServiceConnection in LoadedApk.mService for your application context. 
     sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), 
       mMainThread.getHandler(), flags); 
    } else { 
     throw new RuntimeException("Not supported in system context"); 
    } 
    try { 
     ... ... 
     return res != 0; 
    } catch (RemoteException e) { 
     return false; 
    } 
} 

Bạn luôn nên unbind dịch vụ khi bạn đang thực hiện với nó, theo đề nghị của the official dev guide, như một cách lập trình tốt:

  • Để ngắt kết nối từ dịch vụ, hãy gọi unbindService().

    Khi khách hàng của bạn bị phá hủy, nó sẽ hủy liên kết khỏi dịch vụ, nhưng bạn luôn nên hủy liên kết khi bạn đang tương tác với dịch vụ hoặc khi hoạt động của bạn tạm dừng để dịch vụ có thể tắt khi không sử dụng. (Thời điểm thích hợp để ràng buộc và unbind được thảo luận dưới đây hơn.)

Các ServiceConnectionLeaked được nâng lên khi khuôn khổ bắt đầu thực hiện một dọn dẹp thức (ví dụ, khi ứng dụng của bạn là bỏ thuốc lá) và tìm thấy ở đó là không đăng ký ServiceConnection, và sau đó khung sẽ cố gắng hủy liên kết cho bạn. Xem removeContextRegistrations() thực hiện trong android.app.LoadedApk:

public void removeContextRegistrations(Context context, 
     String who, String what) { 
    final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled(); 
    ... ... 
    //Slog.i(TAG, "Receiver registrations: " + mReceivers); 
    HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap = 
     mServices.remove(context); 
    if (smap != null) { 
     Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator(); 
     while (it.hasNext()) { 
      LoadedApk.ServiceDispatcher sd = it.next(); 
      ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
        what + " " + who + " has leaked ServiceConnection " 
        + sd.getServiceConnection() + " that was originally bound here"); 
      leak.setStackTrace(sd.getLocation().getStackTrace()); 
      Slog.e(ActivityThread.TAG, leak.getMessage(), leak); 
      if (reportRegistrationLeaks) { 
       StrictMode.onServiceConnectionLeaked(leak); 
      } 
      try { 
       ActivityManagerNative.getDefault().unbindService(
         sd.getIServiceConnection()); 
      } catch (RemoteException e) { 
       // system crashed, nothing we can do 
      } 
      sd.doForget(); 
     } 
    } 
    mUnboundServices.remove(context); 
    //Slog.i(TAG, "Service registrations: " + mServices); 
} 
+0

Cảm ơn bạn đã trả lời, nhưng nó vẫn không phù hợp với các mẫu (ví dụ RemoteService trong ApiDemos nào cũng chỉ unbind nếu 'onServiceConnected()' được gọi). Tuy nhiên, vì với sự giúp đỡ của bạn bây giờ tôi có thể thấy rằng ngay cả những mô tả cấp cao dường như cho phép giải thích như vậy, tôi sẽ chấp nhận câu trả lời. – Stephan

Các vấn đề liên quan