stop - android system service lifecycle




如何檢查服務是否在Android上運行? (16)

如何檢查後台服務(在Android上)是否正在運行?

我想要一個切換服務狀態的Android活動 - 如果它處於關閉狀態,我可以將其打開。


geekQ的反應,但在Kotlin班。 謝謝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)

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;
        }

簡單使用綁定與不創建自動 - 見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文檔是誤導我已經打開谷歌跟踪器的問題,以消除任何疑慮:

https://issuetracker.google.com/issues/68908332

正如我們可以看到的綁定服務實際上通過ActivityManager綁定器通過服務緩存粘合劑調用事務 - 我dint跟踪哪個服務負責綁定,但我們可以看到綁定的結果是:

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,所以直到我解剖休息時才打電話給我暫停我的答案。

但檢查服務是否正在運行的最好方法是創建綁定(如果綁定未創建服務不存在) - 並通過綁定(使用存儲的內部標誌狀態)查詢服務的狀態。


一個小補充是:

我的目標是知道服務正在運行,如果它沒有運行,實際上不運行它。

調用bindService或調用服務可以捕獲的意圖不是一個好主意,那麼它將在服務未運行時啟動服務。

所以,正如miracle2k所建議的那樣,最好是在服務類中有一個靜態字段來知道服務是否已經啟動。

為了使它更乾淨,我建議用一個非常非常緩慢的讀取來在單例中轉換服務:也就是說,沒有通過靜態方法在所有singleton實例上實例化。 你的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

此解決方案非常優雅,但只有在您可以訪問服務類並且僅用於服務的應用程序/包時才適用。 如果您的課程不在服務應用程序/包內,那麼您可以查詢具有Pieter-Jan Van Robays強調的限制的ActivityManager。


不久前我有同樣的問題。 由於我的服務是本地的,我最終只是在服務類中使用靜態字段來切換狀態,正如hackbod所描述的

編輯(備案):

這是hackbod提出的解決方案:

如果您的客戶端和服務器代碼是同一個.apk的一部分,並且您使用具體的Intent(指定確切的服務類)綁定到該服務,那麼您可以簡單地讓您的服務在運行時設置一個全局變量你的客戶可以檢查。

我們故意沒有API來檢查服務是否正在運行,因為幾乎沒有失敗,當您想要做類似的事情時,最終會在代碼中出現競爭條件。


你可以使用這個(我沒有嘗試這個,但我希望這可以):

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); 根據代碼。


同樣,如果人們使用未決意圖,人們可能會發現更清潔的替代方案(例如,使用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是您在班級中私下定義的常數,用於識別與您的服務相關的未決意圖。


在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)


對於這裡給出的用例,我們可以簡單地使用stopService()方法的返回值。 如果存在指定的服務並且它被終止,它將返回true 。 否則它返回false 。 因此,如果結果為false您可以重新啟動服務,否則會確保當前服務已停止。 :)如果你看看this會更好。


得到它了!

必須調用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;
     }
}

我在活動中使用以下內容:

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)

這是可靠的,因為它基於Android操作系統通過ActivityManager#getRunningServices提供的運行服務的信息。

所有使用onDestroy或onSometing事件或綁定器或靜態變量的方法都無法可靠地工作,因為作為開發人員,您永遠不知道,Android何時決定殺死您的流程,或者調用了哪些提及的回調。 請注意Android文檔生命週期事件表中的“killable”列。


我稍微修改了上面介紹的解決方案之一,但傳遞類而不是通用字符串名稱,以確保比較來自同一方法的字符串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);

檢查服務是否正在運行的正確方法是簡單地詢問它。 在您的服務中實施BroadcastReceiver,以響應您活動中的ping。 在服務啟動時註冊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;
            }
        }
    };
}

這是Android文檔的摘錄

有序廣播(使用Context.sendOrderedBroadcast發送)一次傳遞給一個接收者。 當每個接收器依次執行時,它可以將結果傳播到下一個接收器,或者它可以完全中止廣播,使其不會傳遞給其他接收器。

這有點破解,但將此視為“ping” Service因為我們可以同步廣播,我們可以在UI線程上同步廣播並獲得結果。

Service

BroadcastReceiver

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), IntentFilter("echo");
}

private class ServiceEchoReceiver{
    public void onReceive (Context context, Intent intent) {
      LocalBroadcastManager
         .getInstance(this)
         .sendBroadcastSync(new Intent("echo"));
    }
}

Activity

    bool serviceRunning = false;

    protected void onCreate (Bundle savedInstanceState){
        LocalBroadcastManager.getInstance(this).registerReceiver(echo);
        LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo"));
        if(!serviceRunning){
           //try and run the service
        }
    }

    private BroadcastReceiver echo = new BroadcastReceiver(){
        public void onReceive (Context context, Intent intent) {
          serviceRunning = true;   
        }
    }

首先,你無法通過使用ActivityManager來獲得服務。 ( here討論)

服務可以自行運行,綁定到一個活動或兩者。 如果你的服務正在運行,檢查一個Activity的方法是創建一個接口(它擴展了Binder),在這個接口中聲明Activity和Service都理解的方法。 您可以通過在聲明例如“isServiceRunning()”的地方創建自己的接口來完成此操作。 然後,您可以將您的Activity綁定到您的服務,運行方法isServiceRunning(),如果服務正在運行,服務將檢查自身,並向您的活動返回一個布爾值。

您還可以使用此方法停止服務或以其他方式與之交互。

我使用這個tutorial來學習如何在我的應用程序中實現這個場景。


    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;
    }




android-service