バックグラウンドサービスが実行されているかどうかを確認するにはどうすればよいですか?
サービスの状態を切り替えるAndroidアクティビティが必要です。オフの場合はオンに、オンの場合はオフにできます。
バックグラウンドサービスが実行されているかどうかを確認するにはどうすればよいですか?
サービスの状態を切り替えるAndroidアクティビティが必要です。オフの場合はオンに、オンの場合はオフにできます。
アクティビティ内から次を使用します。
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
そして、私はそれを次のように呼び出します:
isMyServiceRunning(MyService.class)
これは、 ActivityManager#getRunningServicesを通じて Android オペレーティング システムによって提供される実行中のサービスに関する情報に基づいているため、確実に機能します。
onDestroy または onSometing イベントまたは Binders または static 変数を使用するすべてのアプローチは、Android がいつプロセスを強制終了するか、または前述のコールバックが呼び出されるかどうかをいつ決定するかを開発者として知ることができないため、確実に機能しません。Android ドキュメントのライフサイクル イベント テーブルの「killable」列に注意してください。
私は少し前に同じ問題を抱えていました。私のサービスはローカルだったので、 hackbod here で説明されているように、サービスクラスの静的フィールドを使用して状態を切り替えるだけになりました
編集(記録用):
hackbod によって提案されたソリューションは次のとおりです。
クライアントとサーバーのコードが同じ .apk の一部であり、具体的なインテント (正確なサービス クラスを指定するインテント) を使用してサービスにバインドしている場合は、サービスの実行時にグローバル変数を設定するだけで済みます。クライアントが確認できます。
サービスが実行されているかどうかを確認するための API を意図的に用意していません。そのようなことを実行したい場合、コード内で競合状態が発生することはほぼ間違いありません。
小さな補足は次のとおりです。
私の目標は、サービスが実行されていない場合に実際に実行することなく、サービスが実行されているかどうかを知ることです。
bindService を呼び出すか、サービスがキャッチできるインテントを呼び出すことは、サービスが実行されていない場合にサービスを開始するため、お勧めできません。
したがって、miracle2k が示唆したように、サービスが開始されたかどうかを知るために、サービス クラスに静的フィールドを用意するのが最善です。
さらにきれいにするために、非常に遅延フェッチを使用してサービスをシングルトンに変換することをお勧めします。つまり、静的メソッドによるシングルトンインスタンスのインスタンス化はまったくありません。サービス/シングルトンの静的 getInstance メソッドは、作成されている場合、シングルトンのインスタンスを返すだけです。ただし、実際にはシングルトン自体を開始またはインスタンス化するわけではありません。サービスは、通常のサービス開始方法によってのみ開始されます。
次に、シングルトンの設計パターンを変更して、紛らわしい getInstance メソッドの名前を メソッドのような名前に変更すると、さらにクリーンになりますisInstanceCreated() : boolean
。
コードは次のようになります。
public class MyService extends Service
{
private static MyService instance = null;
public static boolean isInstanceCreated() {
return instance != null;
}//met
@Override
public void onCreate()
{
instance = this;
....
}//met
@Override
public void onDestroy()
{
instance = null;
...
}//met
}//class
このソリューションは洗練されていますが、サービス クラスにアクセスできる場合にのみ関連し、サービスのアプリ/パッケージ以外のクラスにのみ関連します。クラスがサービス アプリ/パッケージの外部にある場合は、Pieter-Jan Van Robays によって下線が引かれた制限付きで ActivityManager をクエリできます。
これを使用できます(まだ試していませんが、うまくいくことを願っています):
if(startService(someIntent) != null) {
Toast.makeText(getBaseContext(), "Service is already running", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getBaseContext(), "There is no service running, starting service..", Toast.LENGTH_SHORT).show();
}
すでに実行中のサービスがある場合、startService メソッドは ComponentName オブジェクトを返します。そうでない場合は、null が返されます。
public abstract ComponentName startService (Intent service)を参照してください。
これはチェックとは違うと思います。サービスを開始しているのでstopService(someIntent);
、コードの下に追加できます。
/**
* Check if the service is Running
* @param serviceClass the class of the Service
*
* @return true if the service is running otherwise false
*/
public boolean checkServiceRunning(Class<?> serviceClass){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
if (serviceClass.getName().equals(service.service.getClassName()))
{
return true;
}
}
return false;
}
サービスが実行されているかどうかを確認する適切な方法は、単純に尋ねることです。アクティビティからの ping に応答する BroadcastReceiver をサービスに実装します。サービスの開始時に BroadcastReceiver を登録し、サービスの破棄時に登録を解除します。アクティビティ (または任意のコンポーネント) からローカル ブロードキャストインテントをサービスに送信し、サービスが応答する場合は、サービスが実行中であることがわかります。以下のコードの ACTION_PING と ACTION_PONG の微妙な違いに注意してください。
public class PingableService extends Service {
public static final String ACTION_PING = PingableService.class.getName() + ".PING";
public static final String ACTION_PONG = PingableService.class.getName() + ".PONG";
public int onStartCommand (Intent intent, int flags, int startId) {
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(ACTION_PING));
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy () {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onDestroy();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive (Context context, Intent intent) {
if (intent.getAction().equals(ACTION_PING)) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
manager.sendBroadcast(new Intent(ACTION_PONG));
}
}
};
}
public class MyActivity extends Activity {
private boolean isSvcRunning = false;
@Override
protected void onStart() {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
manager.registerReceiver(mReceiver, new IntentFilter(PingableService.ACTION_PONG));
// the service will respond to this broadcast only if it's running
manager.sendBroadcast(new Intent(PingableService.ACTION_PING));
super.onStart();
}
@Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
super.onStop();
}
protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive (Context context, Intent intent) {
// here you receive the response from the service
if (intent.getAction().equals(PingableService.ACTION_PONG)) {
isSvcRunning = true;
}
}
};
}
上記のソリューションの 1 つを少し変更しましたが、同じメソッドから出力される文字列を確実に比較するために、一般的な文字列名の代わりにクラスを渡します。class.getName()
public class ServiceTools {
private static String LOG_TAG = ServiceTools.class.getName();
public static boolean isServiceRunning(Context context,Class<?> serviceClass){
final ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
Log.d(Constants.TAG, String.format("Service:%s", runningServiceInfo.service.getClassName()));
if (runningServiceInfo.service.getClassName().equals(serviceClass.getName())){
return true;
}
}
return false;
}
}
その後
Boolean isServiceRunning = ServiceTools.isServiceRunning(
MainActivity.this.getApplicationContext(),
BackgroundIntentService.class);
@Snicolasによる回答にメモを追加したいだけです。次の手順を使用して、 を呼び出すかどうかに関係なくサービスの停止を確認できますonDestroy()
。
onDestroy()
呼び出された: [設定] -> [アプリケーション] -> [実行中のサービス] -> [サービスを選択して停止] に移動します。
onDestroy()
呼び出されない: [設定] -> [アプリケーション] -> [アプリケーションの管理] -> [サービスを実行しているアプリケーションを選択して「強制停止」します。ただし、ここでアプリケーションが停止するため、サービス インスタンスも確実に停止します。
最後に、シングルトン クラスで静的変数を使用する方法について言及したいと思います。
onDestroy
サービスで常に呼び出されるとは限らないため、これは役に立ちません!
例: Eclipse から 1 つの変更を加えてアプリを再度実行するだけです。アプリケーションは、SIG: 9 を使用して強制的に終了されます。
まず、 を使用してサービスにアクセスしないでくださいActivityManager
。(ここで議論)
サービスは、単独で実行することも、アクティビティにバインドすることも、その両方を実行することもできます。Service が実行されているかどうかを Activity にチェックインする方法は、Activity
と の両方Service
が理解できるメソッドを宣言するインターフェイス (Binder を拡張する) を作成することです。これを行うには、たとえば " isServiceRunning()
" を宣言する独自のインターフェイスを作成します。次に、アクティビティをサービスにバインドし、isServiceRunning() メソッドを実行します。サービスは、実行中かどうかを自身でチェックし、ブール値をアクティビティに返します。
このメソッドを使用して、サービスを停止したり、別の方法で操作したりすることもできます。
以下は、すべてをカバーするエレガントなハックですIfs
。これはローカル サービス専用です。
public final class AService extends Service {
private static AService mInstance = null;
public static boolean isServiceCreated() {
try {
// If instance was not cleared but the service was destroyed an Exception will be thrown
return mInstance != null && mInstance.ping();
} catch (NullPointerException e) {
// destroyed/not-started
return false;
}
}
/**
* Simply returns true. If the service is still active, this method will be accessible.
* @return
*/
private boolean ping() {
return true;
}
@Override
public void onCreate() {
mInstance = this;
}
@Override
public void onDestroy() {
mInstance = null;
}
}
そして後で:
if(AService.isServiceCreated()){
...
}else{
startService(...);
}
Xamarin C# バージョン:
private bool isMyServiceRunning(System.Type cls)
{
ActivityManager manager = (ActivityManager)GetSystemService(Context.ActivityService);
foreach (var service in manager.GetRunningServices(int.MaxValue)) {
if (service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName)) {
return true;
}
}
return false;
}
ここで示した使用例では、stopService()
メソッドの戻り値を単純に利用できます。true
指定されたサービスが存在し、それが強制終了された場合に返されます。それ以外の場合は を返しますfalse
。したがって、結果がそうでない場合は、サービスを再起動できます。それ以外のfalse
場合は、現在のサービスが停止されていることが保証されます。:)これを見ていただければもっと良いでしょう。
geekQ の応答ですが、Kotlin クラスです。ありがとうオタクQ
fun isMyServiceRunning(serviceClass : Class<*> ) : Boolean{
var manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.name.equals(service.service.className)) {
return true
}
}
return false
}
呼び出し
isMyServiceRunning(NewService::class.java)
TheServiceClass 内で以下を定義します。
public static Boolean serviceRunning = false;
次に onStartCommand(...)
public int onStartCommand(Intent intent, int flags, int startId) {
serviceRunning = true;
...
}
@Override
public void onDestroy()
{
serviceRunning = false;
}
次に、if(TheServiceClass.serviceRunning == true)
任意のクラスから呼び出します。
kotlin では、コンパニオン オブジェクトにブール変数を追加し、必要なクラスからその値を確認できます。
companion object{
var isRuning = false
}
サービスの作成時および破棄時に値を変更します
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
これは、スレッドを生成するため、インテントサービスのデバッグにさらに当てはまりますが、通常のサービスでも機能する可能性があります。Bingingのおかげでこのスレッドを見つけました
私の場合、デバッガーをいじってスレッドビューを見つけました。MSWordの箇条書きアイコンのように見えます。とにかく、それを使用するためにデバッガーモードである必要はありません。プロセスをクリックして、そのボタンをクリックします。インテントサービスは、少なくともエミュレーターでは、実行中に表示されます。
サービスが別のプロセスまたは APK に属している場合は、ActivityManager に基づくソリューションを使用します。
そのソースにアクセスできる場合は、静的フィールドに基づくソリューションを使用してください。ただし、ブール値を使用する代わりに、Date オブジェクトを使用することをお勧めします。サービスの実行中に値を「now」に更新し、終了したら null に設定します。アクティビティから、null または日付が古すぎるかどうかを確認できます。これは、実行されていないことを意味します。
進行状況などの詳細情報に沿って実行されていることを示すブロードキャスト通知をサービスから送信することもできます。