web-dev-qa-db-ja.com

アクティビティのキャンセルボタンでIntentServiceをすぐに停止させるにはどうすればよいですか?

アクティビティから開始されたIntentServiceがあり、アクティビティの「キャンセル」ボタンを使用して、アクティビティからすぐにサービスを停止できるようにしたいと考えています。その「キャンセル」ボタンが押されるとすぐに、サービスがコードの行の実行を停止するようにします。

私はこれに似たいくつかの質問を見つけました(すなわち herehereherehere )、しかし、良い答えはありません。 Activity.stopService()Service.stopSelf()Service.onDestroy()メソッドをすぐに実行しますが、onHandleIntent()のコードを最後まで終わらせてからサービスを破棄します。

サービスのスレッドをすぐに終了する保証された方法は明らかにないので、私が見つけることができる唯一の推奨される解決策は( hereonDestroy()メソッドを使用し、onHandleIntent()のコードのほぼすべての行を、その変数を参照する独自の「if」句でラップします。それはコードを書くひどい方法です。

IntentServiceでこれを行うより良い方法を知っている人はいますか?

42
pvans

多くの場合、スレッドまたはプロセスをすぐに停止することは汚いことです。ただし、サービスがステートレスであれば問題ありません。

マニフェストで別のプロセスとしてサービスを宣言します。

<service
     Android:process=":service"
     ...

そして、実行を停止したい場合は、そのプロセスを強制終了します。

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();

Iterator<RunningAppProcessInfo> iter = runningAppProcesses.iterator();

while(iter.hasNext()){
    RunningAppProcessInfo next = iter.next();

    String pricessName = getPackageName() + ":service";

    if(next.processName.equals(pricessName)){
        Process.killProcess(next.pid);
        break;
    }
}
12
kupsef

ここにトリックがあります。揮発性の静的変数を利用し、サービスのいくつかの行で継続条件をチェックして、サービス継続をチェックする必要があります。

class MyService extends IntentService {
    public static volatile boolean shouldContinue = true;
    public MyService() {
        super("My Service");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        doStuff();
    }

    private void doStuff() {
        // do something 

        // check the condition
        if (shouldContinue == false) {
            stopSelf();
            return;
        }

       // continue doing something

       // check the condition
       if (shouldContinue == false) {
           stopSelf();
           return;
       }

       // put those checks wherever you need
   }
}

アクティビティでこれを実行してサービスを停止します。

 MyService.shouldContinue = false;
32
Sadegh

サービス内でBroadcastReceiverを使用して、単純に停止ブール値をtrueに設定しました。例:

private boolean stop=false;

public class StopReceiver extends BroadcastReceiver {

   public static final String ACTION_STOP = "stop";

   @Override
   public void onReceive(Context context, Intent intent) {
       stop = true;
   }
}


@Override
protected void onHandleIntent(Intent intent) {
    IntentFilter filter = new IntentFilter(StopReceiver.ACTION_STOP);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    StopReceiver receiver = new StopReceiver();
    registerReceiver(receiver, filter);

    // Do stuff ....

    //In the work you are doing
    if(stop==true){
        unregisterReceiver(receiver);
        stopSelf();
    }
}

次に、アクティビティ呼び出しから:

//STOP SERVICE
Intent sIntent = new Intent();
sIntent.setAction(StopReceiver.ACTION_STOP);
sendBroadcast(sIntent);

サービスを停止します。

PD:私の場合、ループ中にサービスを停止するため、ブール値を使用しますが、おそらくonReceiveでunregisterReceiverとstopSelfを呼び出すことができます。

PD2:サービスが正常に動作を終了した場合、またはuntentReceiverエラーが発生する場合は、unregisterReceiverを呼び出すことを忘れないでください。

5
JoeyCK

IntentServiceの場合、onHandleIntentメソッドが前のリクエストを完了するまで、何らかのインテントアクションを介して他のリクエストを停止または取得しません。

他のアクションでIntentServiceを再度開始しようとすると、onHandleIntentは前のインテント/タスクが終了したときにのみ呼び出されます。

また、stopService(intent);またはstopSelf();は、onHandleIntent()メソッドがタスクを完了するまで機能しません。

したがって、ここでのより良い解決策は、ここで通常のServiceを使用することだと思います。

私はそれが役立つことを願っています!

2
Manish DEWAN

IntentServiceを使用している場合は、onHandleIntent()コードがその「停止」信号をポーリングする必要がある、説明したようなことをしていると思われます。

バックグラウンドタスクが長時間実行される可能性があり、それを停止できるようにする必要がある場合は、代わりにプレーンServiceを使用する方が良いと思います。大まかに言って、サービスを以下に記述してください。

  • バックグラウンド作業を実行するためにAsyncTaskを開始する「開始」インテントを公開し、新しく作成されたAsyncTaskへの参照を保存します。
  • AsyncTask.cancel(true)を呼び出す「キャンセル」インテントを公開するか、onDestroy()がAsyncTask.cancel(true)を呼び出すようにします。
  • その後、アクティビティは「キャンセル」インテントを送信するか、単にstopService()を呼び出します。

バックグラウンド作業をキャンセルする能力と引き換えに、サービスは以下の責任を負います:

  • AsyncTask doInBackground()は、InterruptedExceptionを適切に処理し、定期的にThread.interrupted()をチェックし、「早期」に戻る必要があります。
  • サービスは、stopSelf()が呼び出されることを保証する必要があります(AsyncTask onPostExecute/onCancelledで)。
1
rtsai2000
@Override
protected void onHandleIntent(Intent intent) {
    String action = intent.getAction();
    if (action.equals(Action_CANCEL)) {
        stopSelf();
    } else if (action.equals(Action_START)) {
        //handle
    }
}

それがうまくいくことを願っています。

1
cycDroid

@budiusが既にコメントで言及しているように、そのボタンをクリックするときに、Serviceにブール値を設定する必要があります。

// your Activity.Java
public boolean onClick() {
   //...
   mService.performTasks = false;
   mService.stopSelf();
}

そして、Intent処理で、インテント情報をコミット/送信するという重要なタスクを実行する前に、そのブール値を使用します。

// your Service.Java
public boolean performTasks = true;

protected void onHandleIntent(Intent intent) {
   Bundle intentInfo = intent.getBundle();
   if (this.performTasks) {
      // Then handle the intent...
   }
}

それ以外の場合、サービスIntentを処理するタスクを実行します。それが、それが使われることを意図された方法です そうでなければあなたがそれを解決する方法を私は全く見ることができないので あなたがコアコードを見るならば。

0
tolgap