38

バックグラウンドでサービスを含むAndroidアプリケーションを開発しました。このバックグラウンドサービスを実装するために、を使用しIntentServiceました。アプリケーションがサーバーを毎回ポーリングするようにします60 seconds。したがって、IntentServiceでは、サーバーはwhileループでポーリングされます。whileループの最後にThread.sleep(60000)、次の反復が60秒後にのみ開始されるように使用しました。
しかし、Logcatでは、アプリケーションがウェイクアップするのに5分以上かかることがあります(そのスリープから抜け出し、次の反復を開始します)。それは1 minute私たちが望むようには決してなりません。

これの理由は何ですか?バックグラウンドサービスは別の方法で実装する必要がありますか?

問題2

Androidは、しばらくするとこのバックグラウンドプロセス(インテントサービス)を強制終了します。いつ正確に言うことはできません。しかし、バックグラウンドサービスが強制終了されるまでの数時間、場合によっては数日かかることもあります。その理由を教えていただければ幸いです。サービスは殺されることを意図していないからです。それらは、必要な限りバックグラウンドで実行することを目的としています。

コード:

@Override
 protected void onHandleIntent(Intent intent) {
  boolean temp=true;
  while(temp==true) {
    try {
      //connect to the server 
      //get the data and store it in the sqlite data base
    }
    catch(Exception e) {
      Log.v("Exception", "in while loop : "+e.toString());
    }
    //Sleep for 60 seconds
    Log.v("Sleeping", "Sleeping");
    Thread.sleep(60000);
    Log.v("Woke up", "Woke up");

    //After this a value is extracted from a table
    final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
    cur.moveToLast();
    String present_value=cur.getString(0);
    if(present_value==null) {
       //Do nothing, let the while loop continue  
    }
    else if( present_value.equals("false") || present_value.equals("False") ) {
       //break out of the while loop
       db.close();
       temp=false;
       Log.v("run_in_bg", "false");
       Log.v("run_in_bg", "exiting while loop");
       break;
    }
  }

}

ただし、サービスが強制終了されると、プロセスがスリープ状態のときに発生します。最後のログは-を読み取りますSleeping : Sleeping。サービスが強制終了されるのはなぜですか?

4

10 に答える 10

80

主な問題は、私たちが言うことができないということです

サービスは殺されることを意図していません。それらは、必要な限りバックグラウンドで実行することを目的としています。

基本的に、それは真実ではありません。システムは、メモリ不足やその他の状況でサービスを終了できます。これを克服する2つの方法があります:

  1. サービスを実装している場合は、オーバーライドして結果としてonStartCommand()返し ますSTART_STICKY。メモリが不足しているためにサービスを強制終了したい場合でも、メモリが通常に戻ったらすぐに再作成する必要があることをシステムに通知します。
  2. 最初のアプローチが機能するかどうかわからない場合は、AlarmManagerhttp://developer.android.com/reference/android/app/AlarmManager.htmlを使用する必要があります。これはシステムサービスであり、たとえば定期的に通知するときにアクションを実行します。これにより、サービスが終了した場合、またはプロセス全体が停止した場合(たとえば、強制終了)、AlarmManagerによって100%再起動されます。

幸運を

于 2012-09-02T19:02:06.960 に答える
6

そのような目的のために特別に設計されたScheduledExecutorServiceを使用できます。

「Javaの並行性の実践」で示されているように、タイマーは非常に不正確になる可能性があるため、使用しないでください。

于 2012-08-31T16:59:28.083 に答える
5

IntentServicewhileループで実行し続けることを意図していません。アイデアは、に反応し、Intent何らかの処理を行い、完了したらサービスを停止することです。

それが機能していないという意味ではなく、なぜこのような長い遅延が発生するのかはわかりませんが、よりクリーンな解決策は、外部ソースを使用してサービスを定期的に突くことです。バニラJavaメソッドに加えて、ドキュメントに記載されているように、AlarmManagerまたはを確認することもできます。HandlerAlarmManager

方法はこのHandlerように機能します

public class TriggerActivity extends Activity implements Handler.Callback {
    // repeat task every 60 seconds
    private static final long REPEAT_TIME = 60 * 1000;
    // define a message id
    private static final int MSG_REPEAT = 42;

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // start cycle immediately
        mHandler.sendEmptyMessage(MSG_REPEAT);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // stop cycle
        mHandler.removeMessages(MSG_REPEAT);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler = null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        // enqueue next cycle
        mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
        // then trigger something
        triggerAction();
        return true;
    }

    private void triggerAction() {
        // trigger the service
        Intent serviceIntent = new Intent(this, MyService.class);
        serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
        startService(serviceIntent);
    }
}

実行中(ここではとの間)に常に自分自身を送信する単純なActivity(すべてのアクティビティでその機能を持つように拡張できます)MessageonStartonStop

于 2012-08-31T17:18:21.467 に答える
4

より良い解決策は、AlarmManagerを60秒ごとにオフにすることです。次に、このAlarmManagerはサーバーをポーリングするサービスを開始し、サービスは新しいAlarmManagerを開始します。これは、非常にうまく機能する再帰的なソリューションです。

このソリューションは、Android OSがサービスを強制終了し、迫り来るという脅威がないため、より信頼性が高くなります。APIによる:Alarm Managerは、アプリケーションが現在実行されていない場合でも、特定の時間にアプリケーションコードを実行する場合を対象としています。

UI /メインアクティビティなどで、このタイマーを60秒でオフになるように設定します。

long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);

   mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds

あなたはこれyourservice.classを持つことができます、それは接続状態をチェックします、それが良ければそれはタイマーをさらに60秒でオフにするように設定します:

    public class yourservice extends IntentService {

                    public yourservice() { //needs this constructor
                        super("server checker");
                    }

                    @Override
                    protected void onHandleIntent(Intent intent) {
                        WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);

                        if(check for a certain condition your app needs etc){
            //could check connection state here and stop if needed etc
                              stopSelf(); //stop service
                        }
                           else{ //poll the server again in 60 seconds
            long ct = System.currentTimeMillis();
            AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
            Intent i= new Intent(getApplicationContext(), yourservice.class);
            PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);

           mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); 
           stopSelf(); //stop service since its no longer needed and new alarm is set
                               }
                }
}
于 2012-09-06T00:13:10.353 に答える
2

サービスは強制終了されます。アプリが殺されるように。いつでも殺されることができるのはAndroidの哲学です。

他の人が書いたように、backgroundserviceが永久に実行されると仮定しないでください。

ただし、フォアグラウンドサービスを使用すると、殺されたり再開されたりする可能性を大幅に減らすことができます。これにより、常に表示される通知が強制されることに注意してください。たとえば、音楽プレーヤー、VPNアプリケーション、SportstrackerはこのAPIを使用します。

于 2012-09-07T12:27:11.200 に答える
1

問題1の場合、バニラJavaからThread.Sleep()、タイマーの期限が切れた後にスレッドをウェイクアップすることが保証されていますが、タイマーの期限が切れた直後ではなく、主に他のスレッドのステータス、優先度などに応じて後で発生する可能性があります。したがって、スレッドを1秒間スリープすると、少なくとも1秒間スリープしますが、多くの要因によっては10になる可能性があります。私は、Android開発にあまり精通していませんが、同じ状況であると確信しています。 。

問題2の場合、メモリが少なくなるか、ユーザーが手動でサービスを強制終了する可能性があるため、他の人がおそらくAlarmManagerを使用して一定時間後にサービスを再起動すると、サービスを常に実行できるようになります。

于 2012-09-07T18:59:47.437 に答える
0

Serviceの代わりにを使用する必要があるように聞こえますが、をIntentService使用しIntentServiceて60秒ごとに実行する場合は、単にスリープするAlarmManagerように指示するのではなく、を使用する必要があります。再度実行する必要があります。ThreadIntentServiceAlarmManager

于 2012-08-31T17:17:36.690 に答える
0
It could be probably for two reasons..
  1. whileループが問題を引き起こしている場合でも、ハンドラーがtemp==true
  2. それに追加するのはスレッドです。つまり、最大で作成されlong delaysます6 seconds。システムが大規模なデータベースで動作している場合、long delays各クエリの間に作成するとシステムメモリが追加されます。アプリケーションのメモリ使用量が非常に大きくなりsystem memory gets low、システムがterminate the process..

    問題の解決策。

  3. 上記をに置き換えてAlarm Manager、AlarmManagerを使用して特定の時間間隔の後にシステムサービスを取り消すことができます。

  4. また、システムがアプリケーションを終了から回復した後にインテントを取り戻すには、を使用する必要がありますSTART_REDELIVER_INTENT。アプリケーションが終了した後、最後の作業意図を取り戻すことです。その使用法については、https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENTをご覧ください
于 2012-09-07T05:36:05.640 に答える
0

Androidは、長時間実行されているサービスを停止するのに非常に優れています。CommonsWareのWakefulIntentServiceが私のアプリケーションで役立つことがわかりました:https ://github.com/commonsguy/cwac-wakeful

それはあなたが眠ることによってしようとしているようにあなたが時間間隔を指定することを可能にします。

于 2012-09-07T22:46:37.453 に答える
0

JobServiceをバックグラウンドで実行してJobschedulerの実装を試すことができます。これは、AndroidOよりも推奨されています。

于 2019-02-07T09:42:16.850 に答える