工作线程过早结束

Worker-Thread ends prematurely

我的目标是奥利奥。如您所知,oreo 对后台任务执行时间进行了限制。根据 google,解决方法是将后台任务置于前台。这就是我想要做的,但是一旦前台服务 运行ning,它就会在一段时间后被销毁。 首先 phone 关闭它的屏幕,然后一旦我再次激活它,后台任务就会继续。有时会调用前台服务的onDestroy,任务还没有完成。

我的目标是让 enqueueWork 设置的所有任务在不调用 ondestroy 且没有 phone 睡眠模式中断它的情况下执行。

ForeGroundService

public class ForeGroundService extends JobIntentService {

    static final int JOB_ID = 1000;
    static final int ONGOING_NOTIFICATION_ID = 33;

    static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, ForeGroundService.class, JOB_ID, work);
    }

    Notification.Builder notification;
    NotificationManager mNotificationManager;

    @RequiresApi(api = Build.VERSION_CODES.O)
    void einleitung(String Titel, String Text)
    {
        Intent notificationIntent = new Intent(this, ForeGroundService.class);
        PendingIntent pendingIntent =
                PendingIntent.getActivity(this, 0, notificationIntent, 0);


        mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(Titel,
                    Text,
                    NotificationManager.IMPORTANCE_HIGH);
            channel.setSound(null,null);
            mNotificationManager.createNotificationChannel(channel);
        }
        notification =
                new Notification.Builder(this,Titel)
                        .setContentTitle(Titel)
                        .setContentText(Text)
                        .setSmallIcon(R.drawable.kleinesicon)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setContentIntent(pendingIntent)
                        .setTicker("setTicker");
        mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
        startForeground(ONGOING_NOTIFICATION_ID, notification.build());
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    void vordergrund(String Titel, String Text)
    {
        notification.setContentTitle(Titel);
        notification.setContentText(Text);
        mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
    }
    PowerManager.WakeLock wakeLock;
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void onHandleWork(Intent intent) {
        if (beginn) {
            einleitung("Test", "Test");
            beginn = false;
        }
        PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                "MyWakelockTag");
        wakeLock.acquire();
        //Do Work

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Intent local = new Intent();
        local.setAction("de.test.action");
        this.sendBroadcast(local);
        stopForeground(true);
        //toast("Fertig");
        if (wakeLock != null)
            wakeLock.release();
    }

    final Handler mHandler = new Handler();
}

MainActivity

public class MainActivity extends AppCompatActivity {
    private int JI = 1000;
    private BroadcastReceiver updateUIReciver;

    @RequiresApi(api = Build.VERSION_CODES.O)
    void somefunction(someparameters)
    {
        Intent mServiceIntent = new Intent();
        mServiceIntent.putExtra...
        ForeGroundService.enqueueWork(getBaseContext(),ForeGroundService.class,JI,mServiceIntent);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(updateUIReciver);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        IntentFilter filter = new IntentFilter();
        filter.addAction("de.test.action");
        updateUIReciver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                ForeGroundService.shouldContinue = false;
            }
        };
        registerReceiver(updateUIReciver,filter);


        btnB.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            public void onClick(View v) {
                if (startcondition)
                {

                    Intent startIntent = new Intent(MainActivity.this, MyService.class);
                    startIntent.setAction(Constants.ACTION.START_ACTION);
                    startService(startIntent);


                    Intent serviceIntent = new Intent(MainActivity.this,ForeGroundService.class);
                    startForegroundService(serviceIntent);

                    somefunction(someparameters);
                }
                else
                {
                    Intent stopIntent = new Intent(MainActivity.this, MyService.class);
                    stopIntent.setAction(Constants.ACTION.STOP_ACTION);
                    startService(stopIntent);
                }
            }
        });
    }
}

编辑:我让它与 sandhya sasane 的解决方案一起工作并且

public int onStartCommand(Intent intent, int flags, int startId)
{
    if (beginn) {
        executorService = Executors.newFixedThreadPool(1);
        beginn = false;
    }
    final Intent i2 = intent;
    executorService.execute(new Runnable(){
        @Override
        public void run(){
            abarbeiten(i2);
        }
    });
    return START_STICKY;
}

重要的是 newFixedThreadPool(1) 中的 1;一次只有一个线程 运行

I am targeting Oreo. As you know, oreo introduced limits on background task execution time.

是的,确实如此。我能理解你,因为 google 首先让事情变得非常奇怪和复杂......问题和问题,表示结果/结果/证明。

Workarounds are - according to google ...

请节省时间和您自己...Google 文档最差.. 我给他们的文档打了 -10 分(满分 10 分)。

to put the background task in the foreground.

你对前景概念的理解有误..!!仔细逐字逐句阅读完整答案,您的问题将得到解决..!!

This is what I was trying to do, yet once the foreground service is running, it gets destroyed after some time...

现在非常简单...您的概念和实现都是错误的...,因此尝试使用此处提供的新示例项目和指南以及示例工作和测试代码 across 4.0 到最新 android P .

First the phone switches off it's screen, then once I activate it again, the background task continues. Sometimes onDestroy on the foreground service is called without the task being completed.

它与前台服务无关,无论如何......忘了这个。

My goal is to have all tasks being set by enqueueWork to be executed without ondestroy being called and without phone sleep mode to interrupt it.

这个也算了...先看看前台服务是什么,是怎么创建的...


什么是前台服务

  • 保持活动状态的服务(这并不意味着......持续 运行 就像永无止境的 do-while 循环)
  • 保持活动状态直到下次启动/重新启动
  • 即使用户从最近的应用程序中删除应用程序,它仍然存在
  • 但它不会在下次启动时保持活动状态 pos
  • 需要用户通过再次打开应用程序或通过 ON_BOOT_COMPLETE 的广播接收器、AlarmManager 或 JobScedular
  • 来重新启动

何时使用

根据我的看法,用户不喜欢显示消息的永久通知^这是运行在前台,可能很快就会耗尽你的电池^,同样,用户将无法将其滑开,只能强制停止或卸载应用程序以停止它。所以根据我的实现观点,^开发人员必须使用它来实现运行时接收器作为 post - oreo 设备不欢迎通过扩展 Broadcastreceiver 并将其 intent 条目放入实现的静态接收器manifest.xml 文件...即使开发人员尝试这样做,接收器也永远不会在 post - oreo 设备上被调用...,是的,它会在 oreo 设备下被调用。所以只实现一个 ON_BOOT_COMPLETE 接收器并在服务中休息。

如何实现前台服务

右键单击项目结构并创建一个名为RunnerService 的服务,然后生成所有必需的方法。它不需要您手动输入所有代码。按照说明生成它。示例前台服务:

public class RunnerService extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "1";


public RunnerService() { }

@Override
public void onCreate()
{
    super.onCreate();

    Log.d("RUNNER : ", "PROGRAMMED.... \n");

    Bitmap IconLg = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);

    mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
    mBuilder = new NotificationCompat.Builder(this, null);
    mBuilder.setContentTitle("App Name")
            .setContentText("Foreground service...")
            .setTicker("Foreground service...")
            .setSmallIcon(R.drawable.ic_menu_slideshow)
            .setLargeIcon(IconLg)
            .setPriority(Notification.PRIORITY_HIGH)
            .setVibrate(new long[] {100})
            .setVisibility(Notification.VISIBILITY_PUBLIC)
            .setOngoing(true)
            .setAutoCancel(false);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);

        // Configure the notification channel.
        notificationChannel.setDescription("Channel description");
        notificationChannel.enableLights(true);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.setVibrationPattern(new long[]{100});
        notificationChannel.enableVibration(true);
        notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        mNotifyManager.createNotificationChannel(notificationChannel);

        mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
        startForeground(1, mBuilder.build());
    }
    else
    {
        mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
        mNotifyManager.notify(1, mBuilder.build());
    }



}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    Log.d("RUNNER : ", "\n IT IS ACTIVE UNTIL NEXT BOOT....");
    return START_STICKY;
}

@Override
public void onDestroy()
{
    Log.d("RUNNER : ", "\n IT WILL BE AGAIN ACTIVE BY ANDROID OS AUTOMATICALLY, DO NOT WORRY AND DONT CODE TO START IT AGAIN !!....");
    super.onDestroy();
}


@Override
public IBinder onBind(Intent intent)
{
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("NOT_YET_IMPLEMENTED_BY_DEVELOPER");
}
}

如何开始

这取决于您在奥利奥下方或 post 奥利奥的目标 android ......我更喜欢下面的所有内容:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
    this.startForegroundService(new Intent(this, RunnerService.class));
}
else
{
    this.startService(new Intent(this, RunnerService.class));
}

从 MainActivity 或任何 ON_BOOT_RECEIVER,或者从任何你想要的地方,只要按照这里所说的启动它...

如何测试在前台

将它从最近的消息中删除...它会调用 onDestroy 但它永远不会被销毁,您将无法滑动通知。这意味着成功。

如何快速测试

使用带有 MainActivity 的示例新项目以上述方式调用服务。

下一步是什么..?

是的,你只能在这里问你的下一个任务......,我会不断更新和指导......我希望你把enqueueWork概念和你所有的概念放在一边,不要去想它。 ..

Lets go step by step and let me know the updates....

更新 2

您应该只在模拟器上尝试...如果成功,请在实际设备上尝试...这又是一个问题...

现在世界上有很多手机phone厂商,需要 stock android 来自 google,因为它是开源的,并修改它以禁用 BOOT 上的所有服务。它只保留 Google 、WhatsApp、FaceBook、Twitter 和主要市场领导者......好像他们不允许他们没有人会购买他们的设备......

示例:

  1. Vivo = FunTouchOs
  2. Oppo = ColorOs
  3. 有一个巨大的列表....

不要检查 BOOT_COMPLETE...,它不会工作,因为它们被修改了 android..

但我想在实际设备上进行测试

然后在 os 完全来自 google 并且具有 android os.

的设备上进行测试

那其他os修改自android

怎么办

有窍门...,但让我们一步步来...。一旦您成功了,我会告诉您的...!!

UPDATE : 3

由于不清楚要求是什么,我做了一些假设并写下了答案:

实现前台执行可以做的是:

  1. 按照我描述的方式实现前台服务
  2. 使用本地 broadcastmanager 广播您自己的事件。
  3. 在前台服务的 onCreate 中注册运行时接收器以接收广播
  4. 在接收广播时调用用户定义的方法 class 和前台服务的 context。并从那里执行所有任务。
  5. 从前台服务的 onDestroy 取消注册接收器。

实现后台执行可以做的是:

如果您有重复的任务并希望在 background 中执行它,即使该应用程序已从最近的应用程序中删除...那么 :

  1. 使用使用 GooglePLAYServices
  2. 的 Firebase Job Dispatcher
  3. 如果您使用 forever 那么即使系统重新启动,即使应用程序不在前台或后台或最近,该作业也会自动触发...

到目前为止,我认为 JobIntentService 没有任何必要,因此它的静态 enqueueWork 方法;解决您的问题需要更多的分辨率和细节。