4

关于它有几个问题,但我总是读到同样的东西:“如果系统需要资源,服务将被杀死”或“你不能构建一个永远运行的服务,因为它在后台运行的越多,它就越容易受到影响到系统杀死它”等。

我面临的问题是:我的服务运行良好,正如预期的那样,如果我运行我的应用程序然后退出它,我的服务仍在运行,但是当我杀死我的应用程序时(通过转到“最近的应用程序”并滑动它离开)服务停止。此时,如果我转到设置>>应用程序>>运行,我会看到服务正在重新启动。过了一会儿,它又回来了,我的服务运行没有问题。

我用谷歌搜索,我发现了一些我可以做的事情,但让我们先看看我的代码:

我通过这种方式启动我的服务(单击按钮后):

Intent intent = new Intent (MainActivity.this, MyService.class);
startService(intent);

我还有 3 个额外的整数,所以我有这样的东西:

final Integer i, i2, i3;
i = 5; //for example
i2 = 10; //for example
i3 = 15; //for example
final Intent intent = new Intent (MainActivity.this, MyService.class);
intent.putExtra("INTEGER1", i);
intent.putExtra("INTEGER2", i2);
intent.putExtra("INTEGER3", i3);
startService(intent);

在 MyService 中,我有以下内容:

public class MyService extends Service
{

  AlarmManager am;
  BroadcastReceiver br;
  PendingIntent pi;
  Integer i, i2, i3;

  @Override
  public void onCreate()
  {
    super.onCreate();
    am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    pi = PendingIntent.getBroadcast(this, 0, new Intent("anyany"); 0) //Why those zeros?

    br = new BroadcastReceiver ()
    {
      public void onReceive (Context context, Intent i) {
        new thread(new Runnable()
        {
          public void run()
          {
            //do something
          }
        }).start();
      }
    };
  }

  @Override
  public void onStartCommand(Intent intent, int flags, int startId)
  {
    super.onStartCommand(intent, flags, startId);
    try
    {
      i = intent.getIntExtra("INTENT1", 0) // I don't understant yet why this zero are here
      i2 = intent.getIntExtra("INTENT2", 0)
      i3 = intent.getIntExtra("INTENT3", 0);
    }
    catch(NullPointerException e) {}

    this.registerReceiver(br, new IntentFilter("anyany"));

    new thread(new Runnable()
    {
    public void run()
      {
      am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock. elapsedRealtime() + i*1000, i2*1000, pi);
      }
    }).start();
  return START_REDELIVER_INTENT; //so I can get my Extra even with my Activity closed
}

我的 onDestroy:

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

我也有 onBind() 方法(没有@Override),但它返回 null。我谷歌了很多,我试图在前台运行该服务,所以我这样做了(在 de onStartCommand 内部):

Notification n = new Notification(R.drawable.ic_laucher), getText(R.string.app_name), System.currentTimeMillis());
PendingIntent npi = PendingIntent.getActivity(this, MainActivity.class);
n.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), npi);
startForeground(3563, n);

我的通知出现了,当我点击它时,我的应用程序运行,但我的服务问题没有得到解决(我相信它仍然没有在前台运行)。通知也重新启动。

我还删除了 Try catch 并为整数定义了一个值(所以我没有使用 getIntExtra() 方法),但没有任何改变

经过几次测试后,我尝试查看日志,当我杀死我的应用程序时,我收到以下消息:计划重启崩溃的服务。

那么,由于某种原因,当我的 MainActivity 死亡时,我的服务崩溃了,为什么?这里的目的不是将服务转换为无法杀死的上帝(我认为这根本不是不可能的,WhatsApp 运行了 105 小时!)而是防止我的服务在我的应用程序死后不崩溃.

我不知道这是否有帮助,但这是我在 Manifest.xml 中添加的内容

<Activity android:name = ".MyService"/>
<service android:name ="Myservice" android:enabled="true" android: exported="false"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

最小 API = 9,目标 API = 17。运行时服务的大小:约 3MB。

希望我对我的英语很清楚和抱歉。

PS:整个代码都按预期运行,所以如果你看到任何 sintax 错误,可以自由编辑它。

编辑

如果我在 AndroidManifest.xml 中添加android:isolatedProcess="true"<service>我会在 logCat 中收到此错误:java.lang.RuntimeException: Unable to create a service in com.mycompany.myapp.myservice: java.lang.SecurityException: Isolated process not allow ed to call getIntentSender

当我使用它启动我的服务时,MainActivity 没有显示任何错误,只有服务崩溃。

4

3 回答 3

11

我终于找到了解决方案!我从服务中删除了 AlarmManager,服务不再兑现,但我必须使用它

问题是用户从“最近的应用程序”中删除应用程序后服务崩溃,所以我所做的就是阻止该应用程序出现在该窗口中。将以下内容添加到您的 AndroidManifest.xml 作为<activity>

android:excludeFromRecents="true"

现在当用户退出您的应用程序时,它不会出现在最近的应用程序窗口中,这意味着系统在您退出它后立即将其杀死,因此它不会浪费任何资源。

PS:不要忘记将服务设置为在单独的进程中运行,将以下内容添加到您的 AndroidManifest.xml 中,作为<service>

android:process=":remote"

编辑 - 找到真正的解决方案

经过大量的研究和研究(几个月的研究),我深入研究了 android API,这是一个发现,这是仅在 API 16+ 时发生的预期行为,android arquiteture 的更改改变了 PendingIntents 的方式由系统广播,所以谷歌添加了标志FLAG_RECEIVER_FOREGROUND,你必须将此标志作为参数传递给你正在使用的意图PendingIntent.getBroadcast(),这里是一个例子:

if(Build.VERSION.SDK_INT >= 16)     //The flag we used here was only added at API 16    
    myIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    //use myIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); if you want to add more than one flag to this intent;



PendingIntent pi = PendingIntent.getBroadcast(context, 1, myIntent, 0); // the requestCode must be different from 0, in this case I used 1;

早于 API 16 的 Android 版本将按预期工作,如果您从“最近的应用程序”页面轻扫该应用程序,该服务不会崩溃。

于 2013-08-13T02:34:24.940 回答
3

正如文档所说,aService在其被调用者的主线程中运行,通常是 UI 线程。所以发生的事情是,当你杀死你的应用程序时,你杀死了你的应用程序进程,因此服务也被杀死了。

您可以通过在文件的标签中Service使用 android:process 在不同的进程中创建您的行为来解决此问题。<service>Manifest.xml

但是,通常,如果需要独立于被调用方并且它可能被不同的应用程序使用,则您Service在自己的进程中启动一个。Service如果您Service只供您自己的应用程序使用,那么请坚持默认行为并且不要杀死您的应用程序。

编辑1:

android:isolatedProcess 的文档说:

如果设置为 true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限。与它的唯一通信是通过服务 API(绑定和启动)。

于 2013-07-21T04:24:57.367 回答
1

从另一个 SO 答案(Link)来看,这是预期的行为。但可以肯定的是,这里有人会有解决方法或解决方案。

您的代码问题:

pi = PendingIntent.getBroadcast(this, 0, new Intent("anyany"); 0) //为什么是那些零?

您看到的第一个零被称为 arequesCode并被描述为目前未使用:

requestCode:发件人的私人请求代码(当前未使用)。

第二个零实际上应该是给定的标志之一(此处)

i = intent.getIntExtra("INTENT1", 0) // 我还不明白为什么这个零在这里

Intent的getIntExtra(String, int)方法不需要0作为它的第二个参数:它可以是任何整数。getIntExtra(String, int)返回与您提供的 String 键对应的整数。如果该键不再存在(或从未存在),getIntExtra(String, int)则返回我们作为第二个参数传递的整数。这是密钥失败时的默认值。

于 2013-07-21T04:44:02.493 回答