バックグラウンドサービスが実行されているかどうかを確認する方法
サービスの状態を切り替えるAndroidアクティビティが必要です。オフになっている場合はオンにし、オンになっている場合はオフにします。
私はずっと前に同じ問題を抱えていました。私のサービスはローカルだったので、hackbod here で説明されているように、サービスクラスの静的フィールドを使用して状態を切り替えました。
EDIT(レコード用):
これがhackbodによって提案された解決策です:
クライアントとサーバーのコードが同じ.apkの一部であり、具体的なインテント(正確なサービスクラスを指定するもの)を使用してサービスにバインドしている場合は、実行中にサービスにグローバル変数を設定できますあなたのクライアントはチェックすることができます。
私たちは意図的にサービスが実行されているかどうかをチェックするためのAPIを持っていません。
私はアクティビティの中から次のものを使います。
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イベント、バインダーまたは静的変数を使用するすべてのアプローチは、Androidがプロセスを強制終了することを決定したとき、または前述のコールバックのうちどれが呼び出されるかどうかを知らない開発者としては信頼できません。 Androidのドキュメントの ライフサイクルイベントの表 の "killable"列に注意してください。
とった!
サービスを正しく登録するには _ _ _ startService()
を呼び出す必要があり、BIND_AUTO_CREATE
を渡すだけでは不十分です。
Intent bindIntent = new Intent(this,ServiceTask.class);
startService(bindIntent);
bindService(bindIntent,mConnection,0);
そして今ServiceToolsクラス:
public class ServiceTools {
private static String LOG_TAG = ServiceTools.class.getName();
public static boolean isServiceRunning(String serviceClassName){
final ActivityManager activityManager = (ActivityManager)Application.getContext().getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
return true;
}
}
return false;
}
}
小さな補数は以下のとおりです。
私の目標は、サービスが実行されていない場合、実際に実行せずにサービスが実行されているかどうかを知ることです。
BindServiceを呼び出すこと、またはサービスによって捕捉される可能性があるインテントを呼び出すことは、実行されていない場合にサービスを開始するため、お勧めできません。
そのため、miracle2kが示唆しているように、サービスが開始されているかどうかを知るためには、サービスクラスに静的フィールドを設定するのが最善です。
それをもっときれいにするために、私は非常に怠惰なフェッチを使用してシングルトンにサービスを変換することをお勧めします:つまり、静的メソッドを通して シングルトン インスタンスのインスタンス化はありません。 service/singletonの静的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
この解決策は洗練されていますが、サービスクラスへのアクセス権があり、クラスのみがサービスのアプリケーション/パッケージの横にある場合にのみ意味があります。あなたのクラスがサービスapp/packageの外にあるならば、あなたは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(インテントサービス)を参照してください。
これはサービスを開始しているので、私が思うにこれをチェックするのとは異なり、コードの下にstopService(someIntent);
を追加することができます。
public boolean checkServiceRunning(){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
if ("com.example.yourpackagename.YourServiceName"
.equals(service.service.getClassName()))
{
return true;
}
}
return false;
}
私は上で提示された解決策の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);
Android docsからの抜粋
sendBroadcast(Intent) と同じですが、Intentに受信者が存在する場合は、この関数はブロックされ、戻る前にすぐにそれらをディスパッチします。
このハックはService
を "pingする"と考えてください _私たちは同調してブロードキャストすることができるので、UIスレッド上でブロードキャストして結果を同期的に得ることができます。
Service
@Override
public void onCreate() {
LocalBroadcastManager
.getInstance(this)
.registerReceiver(new ServiceEchoReceiver(), IntentFilter("ping");
}
private class ServiceEchoReceiver{
public void onReceive (Context context, Intent intent) {
LocalBroadcastManager
.getInstance(this)
.sendBroadcastSync(new Intent("pong"));
}
}
Activity
bool serviceRunning = false;
protected void onCreate (Bundle savedInstanceState){
LocalBroadcastManager.getInstance(this).registerReceiver(pong, new IntentFilter("pong"));
LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("ping"));
if(!serviceRunning){
//run the service
}
}
private BroadcastReceiver pong = new BroadcastReceiver(){
public void onReceive (Context context, Intent intent) {
serviceRunning = true;
}
}
@Snicolasによる回答にメモを追加したいだけです。以下のステップは、onDestroy()
を呼び出すことで/使わずにサービス停止をチェックするために使用することができます。
onDestroy()
が呼び出されました:設定 - >アプリケーション - >実行中のサービス - >あなたのサービスを選択して停止します。
onDestroy()
が呼び出されませんでした:設定 - >アプリケーション - >アプリケーションの管理 - >あなたのサービスが実行されているアプリケーションを選択して "強制停止"してください。ただし、ここでアプリケーションが停止されると、サービスインスタンスも確実に停止されます。
最後に、シングルトンクラスで静的変数を使用してそこに記載されているアプローチが私のために働いていることを言及したいと思います。
onDestroy
は常にサービス内で呼び出されるわけではないので、これは役に立ちません。
例:Eclipseからの1つの変更で、アプリをもう一度実行してください。アプリケーションはSIG:9を使用して強制的に終了します。
サービスが実行されているかどうかを確認する適切な方法は、単にそれを要求することです。アクティビティからのpingに応答するBroadcastReceiverをサービスに実装します。サービスが開始したらBroadcastReceiverを登録し、サービスが破棄されたら登録を解除します。あなたの活動(または任意のコンポーネント)から、 local broadcast インテントをサービスに送信してください。それが応答すれば、実行中であることがわかります。以下のコードの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;
}
}
};
}
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;
}
まず最初に、ActivityManagerを使ってサービスにアクセスしようとはしません。 (話し合いました ここ )
サービスは単独で実行することも、アクティビティにバインドすることも、その両方を行うこともできます。サービスが実行されているかどうかをアクティビティにチェックインする方法は、アクティビティとサービスの両方が理解できるメソッドを宣言するインターフェース(Binderを拡張する)を作成することです。あなたが宣言するあなた自身のインターフェースを作ることによってこれをすることができます例えば "isServiceRunning()"。その後、アクティビティをサービスにバインドし、メソッドisServiceRunning()を実行すると、サービスは実行中かどうかを確認し、アクティビティにブール値を返します。
この方法を使用して、サービスを停止したり、他の方法でサービスと対話したりすることもできます。
私は自分のアプリケーションでこのシナリオを実装する方法を学ぶためにこの チュートリアル を使いました。
ここで紹介するユースケースでは、stopService()
メソッドの戻り値を利用することができます。指定されたサービスが存在し、それがkillされた場合はtrue
を返します。そうでなければfalse
を返します。そのため、結果がfalse
であればサービスを再開できます。それ以外の場合は現在のサービスが停止されていることが保証されます。 :) this を見ればもっと良いでしょう。
繰り返しになりますが、保留中のインテントを使用する場合、人々がよりクリーンに感じるかもしれない別の方法(例えばAlarmManager
:
public static boolean isRunning(Class<? extends Service> serviceClass) {
final Intent intent = new Intent(context, serviceClass);
return (PendingIntent.getService(context, CODE, intent, PendingIntent.FLAG_NO_CREATE) != null);
}
ここで、CODE
は、あなたのサービスに関連する保留中のインテントを識別するためにあなたがあなたのクラス内であなたが個人的に定義する定数です。
以下は、すべての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(...);
}
GeekQのレスポンスはコトリンクラスです。ありがとうgeekQ
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)
同じクラス名を持つ複数のサービスが存在する可能性があります。
私は2つのアプリを作成しました。最初のアプリのパッケージ名はcom.example.mock
です。アプリ内にlorem
というサブパッケージとMock2Service
というサービスを作成しました。その完全修飾名はcom.example.mock.lorem.Mock2Service
です。
次に、2番目のアプリとMock2Service
というサービスを作成しました。 2番目のアプリのパッケージ名はcom.example.mock.lorem
です。サービスの完全修飾名もcom.example.mock.lorem.Mock2Service
です。
これが私のlogcatの出力です。
03-27 12:02:19.985: D/TAG(32155): Mock-01: com.example.mock.lorem.Mock2Service
03-27 12:02:33.755: D/TAG(32277): Mock-02: com.example.mock.lorem.Mock2Service
ComponentName
のequals()
はパッケージ名とクラス名の両方を比較するため、ComponentName
インスタンスを比較することをお勧めします。また、同じパッケージ名を持つ2つのアプリをデバイスにインストールすることはできません。
ComponentName
のequals()メソッド.
@Override
public boolean equals(Object obj) {
try {
if (obj != null) {
ComponentName other = (ComponentName)obj;
// Note: no null checks, because mPackage and mClass can
// never be null.
return mPackage.equals(other.mPackage)
&& mClass.equals(other.mClass);
}
} catch (ClassCastException e) {
}
return false;
}
Kotlinでは、コンパニオンオブジェクトにブール変数を追加し、必要なクラスからその値を確認できます。
companion object{
var isRuning = false
}
サービスが作成および破棄されるときにその値を変更する
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
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)
を呼び出します。
以下に示すように、サービスサブクラスで静的ブール値を使用してサービスの状態を取得します。
MyService.kt
class MyService : Service() {
override fun onCreate() {
super.onCreate()
isServiceStarted = true
}
override fun onDestroy() {
super.onDestroy()
isServiceStarted = false
}
companion object {
var isServiceStarted = false
}
}
MainActivity.kt
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val serviceStarted = FileObserverService.isServiceStarted
if (!serviceStarted) {
val startFileObserverService = Intent(this, FileObserverService::class.Java)
ContextCompat.startForegroundService(this, startFileObserverService)
}
}
}
ActivityManager::getRunningServices
ベースの答えの私のコトリン変換。この機能を活動に入れる
private fun isMyServiceRunning(serviceClass: Class<out Service>) =
(getSystemService(ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Int.MAX_VALUE)
?.map { it.service.className }
?.contains(serviceClass.name) ?: false
サービスが別のプロセスまたはAPKに属している場合は、ActivityManagerに基づくソリューションを使用してください。
そのソースにアクセスできる場合は、静的フィールドに基づくソリューションを使用してください。しかし代わりにブール値を使用して、Dateオブジェクトを使用することをお勧めします。サービスの実行中に、その値を 'now'に更新し、それが終了したらnullに設定してください。アクティビティから、そのnullまたは日付が古すぎるかどうかを確認できます。これは、実行されていないことを意味します。
進行状況などの詳細情報に沿って実行されていることを示すブロードキャスト通知をサービスから送信することもできます。
このコードを使ってください。
if (isMyServiceRunning(MainActivity.this, xyzService.class)) { // Service class name
// Service running
} else {
// Service Stop
}
public static boolean isMyServiceRunning(Activity activity, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
自動作成を使わないでbindを使う - psを参照してください。更新しています...
public abstract class Context {
...
/*
* @return {true} If you have successfully bound to the service,
* {false} is returned if the connection is not made
* so you will not receive the service object.
*/
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
例:
Intent bindIntent = new Intent(context, Class<Service>);
boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);
なぜ使わないの? getRunningServices()
List<ActivityManager.RunningServiceInfo> getRunningServices (int maxNum)
Return a list of the services that are currently running.
注:このメソッドは、サービス管理タイプのユーザーインターフェイスをデバッグまたは実装するためのものです。
ps。 Androidのドキュメントは誤解を招く恐れがあります私は疑いを排除するためにGoogleトラッカーの問題をオープンしました:
https://issuetracker.google.com/issues/68908332
バインドサービスは、実際には、サービスキャッシュバインダーを介してActivityManagerバインダーを介してトランザクションを呼び出します。どのサービスがバインドを担当しているかは追跡できませんが、バインドの結果を確認できます。
int res = ActivityManagerNative.getDefault().bindService(...);
return res != 0;
取引はバインダーを通じて行われます。
ServiceManager.getService("activity");
次:
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
これはActivityThreadで次のように設定されます。
public final void bindApplication(...) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
これはActivityManagerServiceのメソッドで呼び出されます。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
thread.bindApplication(... , getCommonServicesLocked(),...)
その後:
private HashMap<String, IBinder> getCommonServicesLocked() {
しかし、「アクティビティ」だけのウィンドウパッケージとアラームはありません。
それで我々はコールに戻る必要があります:
return getIServiceManager().getService(name);
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
これは以下を介して呼び出します。
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
これは次のようになります。
BinderInternal.getContextObject()
これはネイティブメソッドです....
/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
public static final native IBinder getContextObject();
私は休憩を解剖するまで私は今cで掘る時間がないので私は私の答えを中断する。
しかし、サービスが実行されているかどうかをチェックする最もよい方法 はバインドを作成し(バインドが作成されていない場合はサービスが存在しない) - そしてバインドを通してその状態についてサービスに問い合わせます。
私はそれらが面白いと思った:
/**
* Provide a binder to an already-bound service. This method is synchronous
* and will not start the target service if it is not present, so it is safe
* to call from {@link #onReceive}.
*
* For peekService() to return a non null {@link Android.os.IBinder} interface
* the service must have published it before. In other words some component
* must have called {@link Android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
*
* @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
* @param service Identifies the already-bound service you wish to use. See
* {@link Android.content.Context#bindService(Intent, ServiceConnection, int)}
* for more information.
*/
public IBinder peekService(Context myContext, Intent service) {
IActivityManager am = ActivityManager.getService();
IBinder binder = null;
try {
service.prepareToLeaveProcess(myContext);
binder = am.peekService(service, service.resolveTypeIfNeeded(
myContext.getContentResolver()), myContext.getOpPackageName());
} catch (RemoteException e) {
}
return binder;
}
要するに :)
"既にバインドされているサービスにバインダーを提供します。このメソッドは同期的であり、存在しない場合はターゲットサービスを開始しません。"
パブリックIBinder peekService(インテントサービス、StringolvedType、String callingPackage)はRemoteExceptionをスローします。
*
public static IBinder peekService(IBinder remote, Intent service, String resolvedType)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("Android.app.IActivityManager");
service.writeToParcel(data, 0);
data.writeString(resolvedType);
remote.transact(Android.os.IBinder.FIRST_CALL_TRANSACTION+84, data, reply, 0);
reply.readException();
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
*
Kotlinの場合、以下のコードを使用できます。
fun isMyServiceRunning(calssObj: Class<SERVICE_CALL_NAME>): Boolean {
val manager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (calssObj.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
これはスレッドを生成するので、意図的サービスのデバッグにもっと当てはまりますが、通常のサービスでも同様に機能するかもしれません。 Bingingのおかげでこのスレッドが見つかりました
私の場合は、デバッガで遊んでスレッドビューを見つけました。 MS Wordの箇条書きアイコンのように見えます。とにかく、あなたはそれを使うためにデバッガモードにいる必要はありません。プロセスをクリックして、そのボタンをクリックしてください。インテントサービスは、実行中に少なくともエミュレータ上に表示されます。