web-dev-qa-db-ja.com

Androidアプリが閉じている/バックグラウンドに設定されているときにバックグラウンドタスクを実行する方法は?

My Android 4+アプリは、数分ごとにデータを同期するために使用されるカスタムWebサービスに接続されています。オンラインデータが常に最新であることを確認するために、アプリが閉じられたときに同期/バックグラウンドに送信します。

IOSでは、これは非常に簡単です。

  • AppDelegateで_applicationDidEnterBackground:_を聴く
  • _beginBackgroundTaskWithName:_を使用して、長時間実行されているバックグラウンドタスクを開始し、タスクの実行中に中断されないようにします

Androidでこれを行う方法

最初の問題は、_applicationDidEnterBackground:_に相当するものがないことです。私がこれまでに見つけたすべてのソリューションは、主要なアクティビティonPause()メソッドを使用することを提案しています。ただし、これはメインActivityが一時停止されるたびに呼び出されます。これは、アプリ内で別のActivityが開始された場合にも当てはまります。これは、ActivityLifecycleCallbacksインターフェイスのonActivityPaused()メソッドに当てはまります。

では、appActivityだけでなく)が閉じていることを検出する方法、またはバックグラウンドに送信する方法は?

2番目の問題は、バックグラウンドタスクの実行方法に関する情報が見つからなかったことです。すべてのソリューションは、単にAsyncTaskを開始することを指しますが、これは本当に正しいソリューションですか?

AsyncTaskは新しいタスクを開始しますが、アプリが閉じられた/非アクティブになったときにこのタスクも実行されますか?アプリとタスクが数秒後に中断されないようにするには、どうすればよいですか?アプリが完全に停止する前に、タスクが完了できることを確認するにはどうすればよいですか?

18
Andrei Herford

次のコードは、アプリが閉じているときまたはバックグラウンドでコンテンツを投稿するのに役立ちます。

import Android.app.Service;
import Android.content.Intent;
import Android.os.Binder;
import Android.os.Handler;
import Android.os.IBinder;
import Android.widget.Toast;

public class SendDataService extends Service {
    private final LocalBinder mBinder = new LocalBinder();
    protected Handler handler;
    protected Toast mToast;

    public class LocalBinder extends Binder {
        public SendDataService getService() {
            return SendDataService .this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() { 

// write your code to post content on server
            }
        });
        return Android.app.Service.START_STICKY;
    }

}

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

アプリケーションがバックグラウンドにある場合でも、サービスはバックグラウンドで実行されますが、バックグラウンド操作を実行するために別のスレッドを作成したため、常にメインスレッドで実行されることに注意してください。

何らかの理由でサービスがバックグラウンドで破壊された場合でも、サービスはスティッキーなものとして開始され、自動的に再起動されます。

詳細については、こちらをご覧ください。 https://developer.Android.com/guide/components/services.html

アプリがフォアグラウンド/バックグラウンドにあるかどうかを確認するには、次のコードが役立ちます。

  private boolean isAppOnForeground(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
    if (appProcesses == null) {
      return false;
    }
    final String packageName = context.getPackageName();
    for (RunningAppProcessInfo appProcess : appProcesses) {
      if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
        return true;
      }
    }
    return false;
  }
}

// Use like this:
boolean foregroud = new ForegroundCheckTask().execute(context).get();

ハッピーコーディング!!!!

17
Anjali

受け入れられた答えは正しくありません。

残念ながら、新しいRunnableタスクをHandler()に投稿すると、別のスレッドが作成されると著者は信じています-「バックグラウンド操作を実行するために別のスレッドを作成しました」

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    handler = new Handler();
    handler.post(new Runnable() {
        @Override
        public void run() { 
           // write your code to post content on server
        }
    });
    return Android.app.Service.START_STICKY;
}

onStartCommand()コールバックは常にメインスレッドで呼び出され、新しいHandler()は現在のスレッド(メイン)にアタッチされます。そのため、Runnableタスクはメインスレッドキューの最後にポストされます。

問題を修正し、実際に別のスレッドでRunnableを実行するには、AsyncTaskを最も簡単なソリューションとして使用できます。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            // ...
        }
    });
    return Android.app.Service.START_STICKY;
}

何かをコピー&ペーストする前に考えてみてください...

4

目的に合わせてサービスを使用できます。 stopService(..)またはstopSelf()メソッドを呼び出していない場合、アクティビティコンポーネントが破棄された場合でも、サービスはバックグラウンドでの実行を維持します

このサービスでは、できればレトロフィットを使用して非同期ネットワーク呼び出しを行うことができ、その後、Webサービスから取得した最新のデータでSqlite DBなどのローカルストレージを更新できます。

ここ は公式リンクです。目的に合わせて無制限のサービスを使用できます。

3
Kaveesh Kanwal