6

私のアプリは、別の(私の)アプリによって提供されるサービスを使用しています。私はそれにアクセスして通信するためにバインドされたサービスを使用していますMessenger(そしてそれは別のアプリであるため、リモートサービスでもあります)。

bindService適切な意図で電話をかけ、電話が戻ってきfalseたとき(サービスを提供するAPKがない場合に予想されるように)、ドキュメントとサンプルコードから、必要がないと思いましunbindServiceConnection。ただし、Galaxy Nexus(Jelly Bean)デバイスでこれを行うと、ServiceConnectionLeaked終了時によく知られたメッセージが表示されますActivity

私はこれを行うことによってそれを救いました

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

私は興味があります:私はドキュメントの何かを見逃しただけで、それはこのように機能するはずですか?try ... catchこの動作が他のデバイスやAndroidバージョンで実際に異なっていても、アプリが(悪影響を)受けないようにするためにを追加しました。

4

1 に答える 1

8

bindService()一般的に言えば、呼び出しが true を返すか false を返すかに関係なく、ServiceConnection は常にフレームワークによって割り当てられ、登録されます。android.app.ContextImplの bindService() 実装を参照してください。

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;
    }
}

公式の開発者ガイドで提案されているように、適切なプログラミング方法として、サービスを使い終わったら、常にサービスのバインドを解除する必要があります。

  • サービスから切断するには、unbindService() を呼び出します。

    クライアントが破棄されると、サービスからバインド解除されますが、サービスとの対話が終了したとき、またはアクティビティが一時停止したときは、常にバインド解除して、サービスが使用されていないときにサービスをシャットダウンできるようにする必要があります。(バインドとバインド解除の適切な時間については、以下で詳しく説明します。)

ServiceConnectionLeaked は、フレームワークが最終的なクリーンアップの実行を開始したとき (たとえば、アプリが終了したとき) に発生し、未登録の ServiceConnection が見つかった場合、フレームワークはそれをアンバインドしようとします。android.app.LoadedApkの removeContextRegistrations() 実装を参照してください。

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);
}
于 2013-01-13T21:46:41.783 に答える