【问题标题】:How do I keep a Service alive indefinitely我如何让服务无限期地活着
【发布时间】:2026-01-18 00:15:01
【问题描述】:

我正在编写一个应用程序,该应用程序检测来自耳机插孔的音频并在满足某些音频条件时广播 Intent(特别是当它检测到通过辅助音频设备刷卡时)。

我的应用没有活动,它只是一个应用程序和一个服务。

除了服务在相对较短的时间段(约一分钟)后被 Android 清理之外,一切都运行良好。

我很好奇我需要做什么来告诉 Android 这是一个有用的服务,除非它真的需要它的内存,否则它应该不理它。我意识到我可以创建一个每隔几秒钟唤醒一次的警报,如果它注意到它不存在,它就会启动服务,但这似乎是一个 kluge。有没有比这更多的 Android(y) 方式来让它保持活力?

【问题讨论】:

    标签: android service


    【解决方案1】:

    我的应用没有活动,它只是一个应用程序和一个服务。

    这意味着您的应用永远不会在任何 Android 3.1+ 设备上运行,除非您与其他一些将手动启动您的服务的应用有联系。

    我正在编写一个应用程序,它可以检测来自耳机插孔的音频,并在满足某些音频条件时(特别是当它检测到通过辅助音频设备刷卡时)广播 Intent。

    不要以几何形状命名您的公司,这已经完成了。 :-)

    我很好奇我需要做什么来告诉 Android 这是一个有用的服务,除非它真的需要它的内存,否则它应该不理它。

    欢迎您使用startForeground() 将其声明为前台服务。权衡是您需要显示Notification,最好是允许用户停止服务。请记住,用户可能不喜欢这个Notification,而是you are stuck with it, particularly on Android 4.3+。从服务切换到活动可能会为您提供更好的服务,因此用户可以启动它、刷卡并处理任何交易。

    【讨论】:

    • 嘿,马克。谢谢您的帮助。与(几乎)我编写的所有应用程序一样,这是一个自定义的 Kiosk 应用程序,它不会给用户留下太多的空间来做任何事情。信息亭永远处于“前台”,服务将刷卡传送给它(通过广播)。有一个很好的理由(我将省略)为什么它需要作为一个单独的应用程序运行,而不是直接融入 Kiosk。所以,我很高兴让 Activity 启动整个过程,并且我对前景很好。位于顶部的 Kiosk 是否会以任何方式干扰前景?
    • @Dr.Dredel:“Kiosk 位于顶部是否会以任何方式干扰前景?” ——不,这应该不是问题。如果您曾经在 Android 设备上使用过音乐播放器,并且在播放过程中看到它在状态栏中放置了 Notification,那是因为播放是由前台服务管理的。这或在事务期间短暂(例如,记住牛奶基于 GCM 消息下载更新)是前台服务的典型用例。
    • 你的救命稻草。这是我第五次有这种感觉。Thnx
    【解决方案2】:

    使用 START_STICKY 保留您的服务。

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
           Log.i("LocalService", "Received start id " + startId + ": " + intent);
           // We want this service to continue running until it is explicitly
           // stopped, so return sticky.
           return START_STICKY;
    }
    

    使用示例:

    http://developer.android.com/reference/android/app/Service.html#LocalServiceSample

    更多信息:

    http://developer.android.com/reference/android/app/Service.html#START_STICKY

    【讨论】:

      【解决方案3】:

      我试过AlarmManager。有用。我在 AlarmReceiver 中使用了 Contex.startService()。我可以检查我的服务是否正在运行,以决定是否重新启动服务。因此,即使用户在设置中手动停止我的服务或被某些清洁应用程序杀死,该服务也可以再次启动。下面是主要代码。

          package com.example.androidnetwork;
      
          import android.app.ActivityManager;
          import android.app.ActivityManager.RunningServiceInfo;
          import android.app.AlarmManager;
          import android.app.PendingIntent;
          import android.content.BroadcastReceiver;
          import android.content.Context;
          import android.content.Intent;
          import android.os.Bundle;
          import android.support.v7.app.ActionBarActivity;
          import android.util.Log;
          import android.view.View;
          import android.view.View.OnClickListener;
          import android.widget.Button;
      
      public class MainActivity extends ActionBarActivity {
      
          private static final long INTERVAL = 5 * 1000; // unit: ms
      
          private Button mRegisterReceiverBtn;
          private Button mCheckNetStatusBtn;
          private Button mStopButton;
          private AlarmManager mAlarmManager;
          private PendingIntent mPendingIntent;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              initData();
      
              initView();
          }
      
          private void initData() {
              mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
          }
      
          private void initView() {
              mRegisterReceiverBtn = (Button) findViewById(R.id.activity_main_start_btn);
              mCheckNetStatusBtn = (Button) findViewById(R.id.activity_main_start_check_net_status_bnt);
              mStopButton = (Button) findViewById(R.id.activity_main_stop);
              mRegisterReceiverBtn.setOnClickListener(new OnClickListener() {
      
                  @Override
                  public void onClick(View v) {
                      MainActivity.this.createServiceControllerPendIntent();
                      mAlarmManager.setInexactRepeating(
                              AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, INTERVAL,
                              mPendingIntent);
                  }
              });
              mCheckNetStatusBtn.setOnClickListener(new OnClickListener() {
      
                  @Override
                  public void onClick(View v) {
                      MainActivity.this.startActivity(new Intent(MainActivity.this,
                              NetStatusActivity.class));
                  }
              });
              mStopButton.setOnClickListener(new OnClickListener() {
      
                  @Override
                  public void onClick(View v) {
                      if (mPendingIntent != null) {
                          mAlarmManager.cancel(mPendingIntent);
                      } else {
                          createServiceControllerPendIntent();
                          mAlarmManager.cancel(mPendingIntent);
                      }
                      // MainActivity.this.stopService(ServiceController
                      // .getServiceIntent());
                      Intent intent = new Intent();
                      intent.setClass(getApplicationContext(),
                              NetStatusMonitorService.class);
                      stopService(intent);
                  }
              });
      
          }
      
          private void createServiceControllerPendIntent() {
              Intent intent = new Intent(MainActivity.this,
                      ServiceController.class);
              mPendingIntent = PendingIntent.getBroadcast(MainActivity.this,
                      0, intent, 0);
          }
      
          @Override
          protected void onDestroy() {
              deInitView();
              super.onDestroy();
          }
      
          private void deInitView() {
      
          }
      
      
          public static class ServiceController extends BroadcastReceiver {
      
              private static Intent sMonitorServiceIntent = null;
      
              @Override
              public void onReceive(Context context, Intent intent) {
                  Log.d("Service Controller", "onReceiver");
                  if (isServiceRunning(context, NetStatusMonitorService.class) == false) {
                      String info = "starting service by AlarmManager";
                      Log.d("Service Controller", info);
                      sMonitorServiceIntent = new Intent(context,
                              NetStatusMonitorService.class);
                      context.startService(sMonitorServiceIntent);
                  }
              }
      
                  // this method is very important
              private boolean isServiceRunning(Context context, Class<?> serviceClass) {
                  ActivityManager am = (ActivityManager) context
                          .getSystemService(Context.ACTIVITY_SERVICE);
                  for (RunningServiceInfo serviceInfo : am
                          .getRunningServices(Integer.MAX_VALUE)) {
                      String className1 = serviceInfo.service.getClassName();
                      String className2 = serviceClass.getName();
                      if (className1.equals(className2)) {
                          return true;
                      }
                  }
                  return false;
              }
      
              public static Intent getServiceIntent() {
                  return sMonitorServiceIntent;
              }
          }
      
      }
      

      【讨论】:

      • AlarmManager 不稳定。如果您在一天中检查登录,时间间隔是 2 分钟,您会看到它有时会停止大约 30 分钟。
      【解决方案4】:

      您将无法限制用户从设置手动终止您的服务,运行服务。即使是设备管理员。

      您可以添加启动接收器、屏幕锁定器和任何事件,这些事件将重新启动被杀死的服务和应用程序(如果是),如果被杀死的应用程序杀死,您可以使用标志设置重新启动服务高级任务杀手.

      【讨论】: