17

クリーンアップが必要なグローバル状態 (いくつかSoundPoolの大きな を含む) を持つ Android アプリがあるため、前の質問への回答に従って、 を使用してこれを処理しようとしていServiceます。

私は現在、すべてのアクティビティが/にバインド/バインド解除されるバインドされたサービスを使用しています。すべてのアクティビティが停止すると、サービスがバインド解除されてサービスで呼び出され、.onStartonStoponDestroySoundPools

アクティビティのライフサイクルは意図的にオーバーラップするため (onStart古いアクティビティが起動する前に新しいアクティビティが起動するonStop)、アクティビティ間を移動するときは常に少なくとも 1 つのアクティビティがバインドされ、サービスは存続します。

ただし、画面を回転させて構成を再起動すると、アクティブなアクティビティが構成の再起動ライフサイクルを通過すると、サービスがバインドされなくなり、終了します。

アプリケーションが停止したときにサービスが終了することを許可しながら、これを回避して再起動後もサービスを維持するにはどうすればよいですか?

4

1 に答える 1

19

わかりました、これは特に難しい質問であることが判明したので、誰かが同様のことをした場合に備えて、ここに私の解決策を投稿すると思いました.

これには明らかにいくつかの方法がありますが、私が使用した最も簡単な方法は、いつシャットダウンするかを決定する「開始済み」サービスを用意することです。私のアクティビティはそれぞれサービスにバインド/バインド解除され、設定された時間の遅延 (私は 1 分を使用しました) の後、それ以上のアクティビティがバインドされていない場合、サービスは自動的にシャットダウンします。これは、ユーザーがアプリの使用を停止した場合に加えて、致命的なアクティビティ エラー。

シャットダウン タイマーは でスケジュールされ、 、、 でonUnbind()キャンセルされます。起動すると、サービスが正常にシャットダウンされ、サービスの .xml ファイルで管理状態のクリーンアップがトリガーされます。onStartCommand()onBind()onRebind()onDestroy()

私のServiceコードは次のとおりです。

public class LocalStateService extends Service {

    /** The binder to give to clients. */
    private final IBinder binder = new LocalStateBinder();

    /** Used for time-delayed shutdown. */
    private final Handler handler = new Handler();

    /**
     * Called before starting or the first binding.
     */
    @Override
    public void onCreate() {
        // initialise state...
    }

    /**
     * Called when this service is explicitly started.
     * @param intent    The intent passed on starting, unused
     * @param flags     Startup flags, unused
     * @param startId   Identifies each start request 
     * @return Desired restart behaviour
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        cancelShutdown();

        // if killed, we would like Android to restart this service, but don't bother re-delivering
        // the original intent used to start the service
        return START_STICKY;
    }

    /**
     * Called when the first client binds.
     * @param intent        The intent passed on binding
     * @return The binding to use
     */
    @Override
    public IBinder onBind(Intent intent) {
        cancelShutdown();
        return binder;
    }

    /**
     * Called when the first of previous clients re-binds.
     * @param intent        The intent passed on binding
     */
    @Override
    public void onRebind(Intent intent) {
        cancelShutdown();
    }

    /**
     * Called when all clients have unbound.
     * @param intent        The first intent originally passed on binding
     * @return Whether this service should be notified of rebinding
     */
    @Override
    public boolean onUnbind(Intent intent) {

        // post a callback to be run in 1 minute
        handler.postDelayed(delayedShutdown, 1000L * 60);

        // we do want onRebind called when clients return
        return true;
    }


    @Override
    public void onDestroy() {
        // state cleanup...
    }

    private Runnable delayedShutdown = new Runnable() {

        @Override
        public void run() {
            LocalStateService.this.stopSelf();
        }

    };

    /**
     * Cancel any shutdown timer that may have been set.
     */
    private void cancelShutdown() {
        // remove any shutdown callbacks registered
        handler.removeCallbacks(delayedShutdown);
    }
}

my からこれを行う代わりにApplication、メイン アクティビティが呼び出さstartService(..)onCreate()ます。これは、最初の起動時と、ユーザーが一時停止したアプリの使用に戻ったときの両方で機能するためです (サービスがシャットダウンすることを決定した場合としない場合があります)。

その後、各アクティビティは通常どおりにバインドおよびバインド解除されます。

見つけた:

  • アクティビティ間を移動するとき、サービス コールバックは発生しません。アクティビティのライフサイクルが重複しているため、これらはセカンダリ バインド/バインド解除です。

  • アクティビティが再開すると (画面の回転など)、サービスが呼び出さonUnbind()れます。onRebind()

  • アプリを一時停止するとき (メイン アクティビティからホーム ボタンを押すなど)、または終了するとき (メイン アクティビティから戻るボタンを押すなど)、サービスはonUnbind()タイマーを起動します。

于 2013-04-29T13:36:47.993 に答える