简体   繁体   English

服务崩溃并重新启动

[英]Service crashing and restarting

There are several questions about it but I always read the same thing: "the service will be killed if the system need resources" or "you can't build an service that runs forever because the more it runs in background, more susceptible it is to the system kills it" and etc. 关于它有几个问题,但我总是读同样的事情:“如果系统需要资源,服务就会被杀死”或“你不能建立一个永远运行的服务,因为它在后台运行的越多,就越容易受到影响系统杀死它“等等

The problem I'm facing is: My service runs fine and as it is expected, if I run my app then exit it my service is still running, but when I kill my app (by going to the "recent apps" and swype it away) the service stops. 我面临的问题是:我的服务运行良好并且正如预期的那样,如果我运行我的应用程序然后退出它我的服务仍在运行,但是当我杀了我的应用程序时(通过转到“最近的应用程序”并转换它离开)服务停止。 In this moment, if I go to the Settings >> aplications >> running I'll see that the service is restarting. 在这一刻,如果我转到设置>> aplications >>运行,我会看到该服务正在重启。 After a while, it goes back and my Service run with no problem. 过了一会儿,它回来了,我的服务运行没有问题。

I google it and I find some things I could do but lets see my code first: 我谷歌它,我找到了一些我能做的事情,但让我们先看看我的代码:

I start my service by this way (after a button click): 我通过这种方式启动我的服务(点击一下按钮后):

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

I also have 3 Integers I put in extra, so I have something like this: 我还有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);

In MyService I have the folloywing: 在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
}

My onDestroy: 我的onDestroy:

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

I also have onBind() method (without @Override), but it returns null. 我也有onBind()方法(没有@Override),但它返回null。 I google a lot and I tried to run the service in foreground, so I did this (inside de onStartCommand): 我谷歌很多,我试图在前台运行该服务,所以我这样做(在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);

My notification appears and when I click on it my app runs, but the problem with my service wasn't fixed (I believe it still not run on foreground). 我的通知出现了,当我点击它我的应用程序运行,但我的服务问题没有修复(我相信它仍然不在前台运行)。 The notification is restarted too. 通知也重新启动。

I also deleted the Try catch and I define a value for the integers (so I didn't use the getIntExtra() method), but nothing changed 我还删除了Try catch,并为整数定义了一个值(所以我没有使用getIntExtra()方法),但没有改变

After several tests I tried to see the logs, when I kill my App I have the following message: Scheduling restart of crashed service. 经过几次测试后,我试图查看日志,当我杀死我的应用程序时,我有以下消息:调度崩溃服务的重新启动。

So, for some reason my service crash when my MainActivity dies, why? 因此,由于某些原因,当我的MainActivity死亡时我的服务崩溃,为什么? The intention here is not to transform the service in a god that can not be killed (I don't think it is impossible at all, the WhatsApp are running for 105 hours !) but prevent my Service to not being crashed after my App dies. 这里的目的不是要改变一个无法杀死的神的服务(我不认为这根本不可能,WhatsApp运行了105个小时!)但是在我的应用程序死后我的服务没有被破坏。

I don't know if this'll help but this is what I add on my Manifest.xml 我不知道这是否有帮助,但这是我在Manifest.xml上添加的内容

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

Min API = 9, target API = 17. Size of the Service when running: about 3MB. 最小API = 9,目标API = 17.运行时服务的大小:大约3MB。

Hope I was clear and sorry for my English. 希望我很清楚并抱歉我的英语。

PS: the entire code are running as expected, so if you see any sintax error fell free to edit it. PS:整个代码都按预期运行,所以如果你看到任何sintax错误可以随意编辑它。

EDIT 编辑

If I add android:isolatedProcess="true" in the <service> in AndroidManifest.xml I receive this error in 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 如果我在AndroidManifest.xml的<service>中添加android:isolatedProcess="true" ,我在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

When I start my service using this, the MainActivity does not show any erros, only the service crashes. 当我使用它启动我的服务时,MainActivity不会显示任何错误,只有服务崩溃。

I finally found the solution ! 我终于找到了解决方案! I removed the AlarmManager from the Service and the service does not cashed anymore, but I have to use it 我从服务中删除了AlarmManager,服务不再兑现,但我必须使用它

The problem is the service crash after the user swype away the app from Recent App, so what I did was prevent the app to appear in that window. 问题是用户从最近的应用程序中删除应用程序后服务崩溃,所以我所做的是阻止应用程序出现在该窗口中。 Add the following to your AndroidManifest.xml as a child of <activity> 将以下内容作为<activity>的子项添加到AndroidManifest.xml中

android:excludeFromRecents="true"

Now when the user exit from your app it wil not appear in the recent apps window, what means the system kills the Activity right after you exit it, so it'll not waste any resources. 现在,当用户从您的应用程序退出时,它不会出现在最近的应用程序窗口中,这意味着系统在您退出后立即终止该活动,因此它不会浪费任何资源。

PS: don't forget to set the service to run in a separate process, add the following to your AndroidManifest.xml, as a child of <service> PS:不要忘记将服务设置为在单独的进程中运行,将以下内容添加到AndroidManifest.xml中,作为<service>的子进程

android:process=":remote"

EDIT - REAL SOLUTION FOUND 编辑 - 找到真正的解决方案

After a lot of research and study (months of study) I took a deep look at android APIs and here is what a found, this is na expected behaviour that occours only at API 16+, a change at android arquiteture changed the way that PendingIntents are broadcasted by the system, so Google added the flag FLAG_RECEIVER_FOREGROUND , you must pass this flag to the intent you are using as a parameter on the PendingIntent.getBroadcast() , here is na example: 经过大量的研究和研究(几个月的研究),我深入研究了android API,这是一个发现,这是仅在API 16+发生的预期行为,android arquiteture的变化改变了PendingIntents的方式由系统广播,所以Google添加了标志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;

Android versions older than API 16 will work as expected, the service won't crash if you swype away the app from Recent Apps page. 早于API 16的Android版本将按预期工作,如果您从“最近的应用”页面中删除该应用,该服务将不会崩溃。

As the documentation says, a Service runs in the main thread of its callee, that usually is the UI Thread. 正如文档所说, Service在其被调用者的主线程中运行,通常是UI线程。 So what is happening is that when you kill your application, you kill your application process and thus the service is killed too. 所以发生的事情是,当你杀死你的应用程序时,你会杀死你的应用程序进程,因此服务也会被杀死。

You can workaround this behavior by creating your Service in a different process by using android:process in your <service> tag in the Manifest.xml file. 您可以通过在Manifest.xml文件的<service>标记中使用android:process在不同的进程中创建Service来解决此问题。

Usually, though, you start a Service in its own process if the Service needs to be independent from the callee and if it may be used by different application. 但是,通常,如果Service需要独立于被调用者并且可能由不同的应用程序使用,则在其自己的进程中启动Service If your Service is for your own application use only, then stick with the default behavior and simply don't kill you application. 如果您的Service仅供您自己的应用程序使用,那么请坚持使用默认行为,并且不要杀死您的应用程序。

EDIT 1: 编辑1:

The documentation for android:isolatedProcess says: android:isolatedProcess的文档说:

If set to true, this service will run under a special process that is isolated from the rest of the system and has no permissions of its own. 如果设置为true,则此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限。 The only communication with it is through the Service API (binding and starting). 与它的唯一通信是通过Service API(绑定和启动)。

From another SO answer (Link) , this is the expected behavior. 从另一个SO答案(链接) ,这是预期的行为。 But surely, someone here will have a workaround or a solution. 但可以肯定的是,这里有人会有解决方法或解决方案。

Your questions from code: 您的代码问题:

pi = PendingIntent.getBroadcast(this, 0, new Intent("anyany"); 0) //Why those zeros? pi = PendingIntent.getBroadcast(this,0,new Intent(“anyany”); 0)//为什么那些零?

The first zero you see is mentioned as a requesCode and decribed as not being used presently: 你看到的第一个零被称为requesCode并被描述为目前没有被使用:

requestCode: Private request code for the sender (currently not used). requestCode:发件人的私有请求代码(当前未使用)。

The second zero should actually be one of the flags given (here) . 第二个零实际上应该是给定的标志之一(这里)

i = intent.getIntExtra("INTENT1", 0) // I don't understant yet why this zero are here i = intent.getIntExtra(“INTENT1”,0)//我不明白为什么这个零在这里

The getIntExtra(String, int) method of Intent doesn't need to have 0 as its second argument: it can be any integer. getIntExtra(String, int)方法不需要将0作为其第二个参数:它可以是任何整数。 getIntExtra(String, int) returns an integer corresponding to the String key you provide. getIntExtra(String, int)返回与您提供的String键对应的整数。 In the event that this key no long exists(or never did), getIntExtra(String, int) returns the integer we pass as the second argument. 如果此键不存在(或从未执行过),则getIntExtra(String, int)将返回作为第二个参数传递的整数。 It is the default value when the key fails. 密钥失败时的默认值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM