【问题标题】:Worker-Thread ends prematurely工作线程提前结束
【发布时间】:2018-08-17 13:04:00
【问题描述】:

我的目标是奥利奥。如您所知,奥利奥引入了对后台任务执行时间的限制。根据谷歌的说法,解决方法是将后台任务放在前台。这就是我想要做的,但是一旦前台服务运行,它会在一段时间后被销毁。 首先手机会关闭它的屏幕,然后一旦我再次激活它,后台任务就会继续。有时会在未完成任务的情况下调用前台服务上的 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;一次只运行一个线程

【问题讨论】:

  • 投了赞成票,认为是好的努力...在下面回答...
  • @sandhyasasane 我看不到你的答案。
  • 现在我可以看到我的答案了...

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


【解决方案1】:

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

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

解决方法是 - 根据谷歌...

请节省时间和您自己...谷歌文档是最糟糕的..我给他们的文档打了 -10 分(满分 10 分)。

把后台任务放到前台。

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

这是我试图做的,但是一旦前台服务运行,它会在一段时间后被销毁......

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

首先手机关闭它的屏幕,然后一旦我再次激活它,后台任务就会继续。有时会在未完成任务的情况下调用前台服务上的 onDestroy。

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

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

也忘了这个吧...让我们先看看前台服务是什么以及它是如何创建的...


什么是前台服务

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

何时使用

根据我的观点,用户不喜欢显示消息的永久通知 ^这是在前台运行,可能会很快耗尽您的电池^,再次用户将无法将其滑开,只能强制停止或卸载应用程序停止它。因此,根据我的实现观点,^开发人员必须使用它来实现运行时接收器,因为 post-oreo 设备不欢迎通过扩展 Broadcastreceiver 并将其意图条目放在 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");
}
}

如何开始

这取决于您在 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

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

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

例子:

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

不要检查这个是否为 BOOT_COMPLETE...,因为它们被修改了 android..

但我想在实际设备上测试它

然后在操作系统完全来自谷歌并具有android操作系统的设备上进行测试。

那么对于从android修改的其他操作系统我该怎么办

有技巧...,但是让我们一步一步来...我会告诉你,一旦你成功了..!!

更新:3

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

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

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

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

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

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

到目前为止,我认为不需要 JobIntentService,因此不需要它的静态 enqueueWork 方法;解决您的问题需要更多的解决方案和详细信息。

【讨论】:

  • 虽然它似乎可以工作,但现在该服务在 ui 线程上运行并阻止它。如何让它在后台运行,同时保持“前台服务”?
  • 我希望你已经实现了前面提到的前台服务.., 1) 现在为了在后台运行你的代码,让我知道你将在后台代码中做什么?简而言之,该程序将做什么? 2)您的项目中是否需要任何要求的广播 - 接收器......?
  • 在后台播放一个音频文件和一个描述性通知然后有一个暂停和一个描述性通知然后有一个音频 sn-p 和一个描述性通知然后有一个暂停.这取决于附加到意图中的内容而重复,即暂停时间和重复时间以及通知标签的变化。然后运行我在使用 enqueuework 之前转发的下一个项目。暂停是通过“Thread.sleep”实现的。全部完成后,我希望主 ui 中的禁用按钮变为启用状态。
  • 1) 你还在考虑谷歌文档......即使我告诉它一个递归循环也不要阅读和思考......正如你再次提到的“排队工作”2)什么动作会触发播放音频文件...? 3) 为什么播放音频会暂停? 3)“取决于意图额外......”哪个意图......以及为什么......?请您用通用语言更新您的问题,您想做什么以及何时发生,没有“背景”、“排队”、“意图额外”之类的词......
  • 后台发生的事情对你来说并不重要。对你来说,这是一个黑匣子,这很好。黑盒子的输入是一个意图,主要活动初始化的整数、双精度数和布尔值。下一个要求是它不阻塞主线程。一个带有一组整数、双精度和布尔值的 bb 将运行,然后运行下一组整数、双精度和布尔值(不平行),依此类推。执行完所有 bbes 后,启用主线程上的按钮以指示队列结束。我再次使用 enqueueWork 来完成非并发执行。
猜你喜欢
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多