我在Android O操作系统上使用服务类。

我计划在后台使用服务。

Android文档指出

如果你的应用程序的API级别为26或更高,系统会对使用或创建后台服务施加限制,除非应用程序本身在前台。如果应用程序需要创建前台服务,应用程序应该调用startForegroundService()。

如果使用startForegroundService(),服务抛出以下错误。

Context.startForegroundService() did not then call
Service.startForeground() 

这有什么问题?


当前回答

请不要在onCreate()方法中调用任何StartForgroundServices,你必须在onStartCommand()中调用StartForground services,否则你将总是得到ANR,所以请不要在onStartCommand()的主线程中编写复杂的登录;

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

编辑:小心!start前台函数不能以0作为第一个参数,它将引发异常!这个例子包含错误的函数调用,将0更改为你自己的const,它不能为0或大于Max(Int32)

其他回答

我也面临着同样的问题,花时间找到了一个解决方案,你可以尝试下面的代码。如果你使用服务,然后把这段代码放在onCreate,否则你使用意图服务,然后把这段代码放在onHandleIntent。

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

以下是谷歌在Android 8.0上的行为变化:

系统允许应用程序调用Context.startForegroundService(),即使应用程序处于后台。但是,应用程序必须在服务创建后的5秒内调用该服务的startForeground()方法。

解决方案: 在onCreate()中为你使用的服务调用start前台()

请参见:Android 8.0 (Oreo)的后台执行限制

Android O API 26的问题

如果你立即停止服务(所以你的服务实际上并没有真正运行(措辞/理解),并且你在ANR间隔之下,你仍然需要在stopSelf之前调用start前台

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

尝试了这种方法,但它仍然创建一个错误:-

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

我正在使用这个,直到错误被解决

mContext.startService(playIntent);

在我的例子中,我称之为上下文。在服务有机会在内部调用start前台之前,停止服务(外部服务)。

好吧,我注意到的一些东西可能也会对其他人有所帮助。这是严格的测试,看看我是否能找出如何解决我所看到的事件。为了简单起见,假设我有一个从演示者调用这个的方法。

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

这将导致同样的错误。在方法完成之前,服务不会启动,因此服务中没有onCreate()。

So even if you update the UI off the main thread, IF you have anything that might hold up that method after it, it won't start on time and give you the dreaded Foreground Error. In my case we were loading some things onto a queue and each called startForegroundService, but some logic was involved with each in the background. So if the logic took too long to finish that method since they were called back to back, crash time. The old startService just ignored it and went on it's way and since we called it each time, the next round would finish up.

这让我想知道,如果我从后台线程调用服务,它是否可以在启动时完全绑定并立即运行,因此我开始试验。即使这样不会立即启动,它也不会崩溃。

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

我不会假装知道为什么它没有崩溃,尽管我怀疑这迫使它等待,直到主线程能够及时处理它。我知道将它绑定到主线程并不理想,但由于我的使用是在后台调用它,所以我并不真正关心它是否等待完成而不是崩溃。