工作线程过早结束

时间:2018-08-17 13:04:01

标签: android android-broadcastreceiver android-8.0-oreo foreground-service foregroundnotification

我的目标是奥利奥。如您所知,oreo引入了对后台任务执行时间的限制。根据Google的说法,解决方法是将后台任务放在前台。这是我正在尝试执行的操作,但是一旦前台服务运行,它就会在一段时间后被销毁。 首先,手机关闭屏幕,然后再次激活手机,则后台任务继续。有时,前台服务上的onDestroy会在任务没有完成的情况下被调用。

我的目标是让enqueueWork设置所有任务,以便在不调用ondestroy的情况下执行所有任务,并且无需通过电话睡眠模式来中断它。

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;一次只能运行一个线程

1 个答案:

答案 0 :(得分:2)

  

我的目标是奥利奥。如您所知,oreo引入了对后台任务执行时间的限制。

是的,确实如此。我能理解你,因为google首先使事情变得非常奇怪和复杂……然后又变得复杂……然后又……然后再次……现在像我和你这样的开发人员,以及你的问题和问题,表示结果/结果/证明。

  

解决方法是-根据google ...

也请节省时间和您自己……Google文档是最糟糕的。.我给出了十分之十的文档。

  

将后台任务置于前台。

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

  

这是我正在尝试做的,但是一旦前台服务运行,它就会在一段时间后被破坏...

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

  

首先,手机关闭屏幕,然后再次激活手机,则后台任务将继续。有时,前台服务上的onDestroy会在任务没有完成的情况下被调用。

它与前台服务完全无关。...忘记了。

  

我的目标是让enqueueWork设置所有任务,以便在不调用ondestroy的情况下执行所有任务,并且无需通过电话睡眠模式来中断它。

也忘记了这一点...首先让我们看看前台服务是什么以及如何创建它...


什么是前台服务

  • 一项仍处于活动状态的服务(这并不意味着...持续 像永无止境的do-while循环一样运行)
  • 在下次启动/重新启动之前一直保持活动状态
  • 即使用户从最近的应用程序中删除了该应用程序,它仍然保持不变
  • 但是下次启动后它不会保持活动状态
  • 需要重新打开应用程序,或者通过ON_BOOT_COMPLETE的广播接收器,或者通过AlarmManager或JobScedular重新启动它

何时使用

根据我的观点,用户不喜欢显示消息^的永久通知,该消息正在前台运行,并且可能很快会耗尽电池电量^,用户将再次无法将其滑开,只能强制停止或卸载应用程序才能停止它。因此,按照我的实现观点,开发人员必须使用它来实现运行时接收器,因为后扩展设备不欢迎通过扩展Broadcastreceiver并将其intent条目放入manifest.xml文件实现的静态接收器。即使开发人员尝试执行此操作,也永远不会在后奥利奥设备上调用接收器...,是的,它会在奥利奥设备下调用。因此,仅实现一个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");
}
}

如何启动

这取决于您要在oreo或post oreo以下定位的是哪个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概念和所有概念都放在一边,不要再考虑了...

  

让我们一步一步,让我知道最新消息。...

更新2

您应该仅在模拟器上尝试...如果成功,则在实际设备上尝试...再次出现问题...

现在世界上有很多手机制造商,这需要 从Google购买库存Android,因为它是开源的,并对其进行了修改以禁用BOOT上的所有服务。它只会保留Google,WhatsApp,FaceBook,Twitter和主要的市场领导者...好像他们不允许他们那样,没人会购买他们的设备...

示例:

  1. Vivo = FunTouchOs
  2. Oppo = ColorOs
  3. 有一个庞大的清单。...

请勿检查BOOT_COMPLETE ...,因为将其修改为android,因此无法正常工作。

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

然后在操作系统完全来自Google且具有Android操作系统的设备上进行测试。

然后我该如何处理从android修改的其他操作系统

有把戏……,但是让我们一步一步吧。……一旦你成功了,我会告诉你的。。

  

UPDATE:3

由于尚不清楚我在做些假设并写出答案的要求是什么?

您可以实现前台执行:

  1. 实施我所描述的前台服务
  2. 使用本地broadcastmanager广播您自己的事件。
  3. 在前台服务注册运行时接收器的onCreate中,以接收广播
  4. 在接收广播后,将使用前景服务的context调用用户定义的类的方法。然后从那里执行所有任务。
  5. 从前台服务的onDestroy注销接收器。

您可以实现后台执行:

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

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

截至目前,我还没有看到对JobIntentService的任何需求,因此看不到它的静态enqueueWork方法;解决问题需要更多的分辨率和详细信息。