修复Android 8+上的“后台服务”问题

5

我正在运行一个服务端代码。当我们将文本复制到手机上时它就会运行。 这个代码在Android 8以下的版本上运行正常, 但是当我在Android 8及以上版本上运行应用时出现了问题。

经过搜索,我意识到我需要使用FOREGROUND_SERVICE并为项目提供特定的访问权限。

你有什么解决方案建议吗?

服务类:

public class AutoDownloadService extends Service {

    private ClipboardManager mClipboardManager;
    public static final String CHANNEL_ID = "ForegroundServiceChannel";

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

        mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        mClipboardManager.addPrimaryClipChangedListener(mOnPrimaryClipChangedListener);
    }

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

        String input = intent.getStringExtra("inputExtra");
        createNotificationChannel();
        Intent notificationIntent = new Intent(this, SettingsActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Foreground Service")
                .setContentText(input)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentIntent(pendingIntent)
                .build();

        startForeground(1, notification);
       //  stopSelf();
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mClipboardManager != null) {
            mClipboardManager.removePrimaryClipChangedListener(mOnPrimaryClipChangedListener);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    CHANNEL_ID,
                    "Foreground Service Channel",
                    NotificationManager.IMPORTANCE_DEFAULT
            );

            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChannel);
        }
    }

    private ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
            new ClipboardManager.OnPrimaryClipChangedListener() {
                @Override
                public void onPrimaryClipChanged() {
                    ClipData clip = mClipboardManager.getPrimaryClip();
                    String textClipBoard = clip.getItemAt(0).getText().toString();
                    Toast.makeText(AutoDownloadService.this, textClipBoard, Toast.LENGTH_SHORT).show();
                }
            };
}

清单

<service
      android:name=".services.AutoDownloadService"
      android:exported="false"
      android:permission="android.permission.BIND_JOB_SERVICE" />
  • 并添加最终使用权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1
在Android 8及以上版本上发生了什么? - Maveňツ
@Maveňツ 在 Android +8 上不起作用。 - Amirhf
1
使用者意图服务(intentService)而非服务(service)。 - Atif AbbAsi
1
是的,它会起作用的,只要试一下就知道了。 - Atif AbbAsi
@AtifAbbAsi 它运行得很好,但问题在于,在中国手机或中国Rom上,当从多任务中关闭程序时,服务将关闭。 - Amirhf
显示剩余3条评论
7个回答

4
我认为你应该使用Intent Service而不是Service。你可以使用AlarmManager在一段时间后再次触发服务,以防止系统关闭服务。

2
根据文档所述,应用在前台时可以自由创建和运行前台服务和后台服务。当应用进入后台时,有几分钟的时间窗口仍允许它创建和使用服务。在此之后,应用被视为处于空闲状态。此时,系统会停止应用的后台服务,就像应用调用了这些服务的Service.stopSelf()方法一样。
因此,解决方案是在Android >=8.0上运行前台服务,并执行类似以下代码的操作。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(Intent(this, AutoDownloadService.class))
        } else {
            startService(Intent(this, AutoDownloadService.class))
        }
}

现在这不是我的问题 :( 但是关键是,在中国手机或中文版本的Rom上,当从多任务中关闭程序时,服务将关闭。 - Amirhf
我的问题是startForegroundService是否在25个API级别以上可用。但是,WhatsApp、Facebook和Instagram如何在每个API级别中使用它呢? - user13146129

1

首先,你不应该这样做。 在后台监视剪贴板不是正确的做法。

Android 8增加了一些保护措施,因此你应该作为前台服务运行,让最终用户意识到你的应用正在监视剪贴板。

无论如何,在Android 10中,只有默认IME才能访问剪贴板。因此,你的应用程序将无法在Android 10中工作。


0

当你从最近使用的应用中滑动该应用时,一些中国OEM会强制停止该应用程序。 一旦应用程序被强制停止,您将无法发布通知、启动服务、接收广播等。

唯一的解决方法是:您的应用程序应该被列入白名单,添加到自动启动列表中。像WhatsApp、Facebook等应用程序已经被OEM添加到列表中。

本博客讨论了类似的问题,即在应用程序被强制停止时无法接收通知。 https://hackernoon.com/notifications-in-android-are-horribly-broken-b8dbec63f48a

您可以采取类似的方法,在“故障排除”部分中,您可以向用户介绍如何将您的应用程序添加到自动启动列表中。

如果您的应用程序非常受欢迎,您可以联系中国制造商,并要求他们将您的应用程序列入白名单,但他们只为非常受欢迎的应用程序这样做。例如,在我的经验中,Microsoft和Hike Messenger已经为他们的应用程序完成了此操作。


WhatsApp、Telegram等消息应用在所有手机上都能正常工作,包括中国手机。这是如何实现的?我们是否需要为每个手机定义特定的访问权限? - Amirhf
就像我之前提到的WhatsApp、Facebook、Messenger等应用程序已经自动添加到白名单中一样。除了这些应用程序之外,还有其他应用程序,例如Hike Messenger、Microsoft Teams等,它们与这些中国制造商联系并将自己添加到白名单中,因为这些应用程序非常受欢迎。 - Arpit Ratan
正如Arpit Ratan所说的那样,事实上他们在2017年禁止了安卓应用商店,因此您需要联系所有的半安卓应用商店分销商,以便将您的应用程序列入白名单。请查看这里:https://medium.com/swlh/how-to-develop-a-successful-app-for-chinese-market-2c5e80414198。 - Dressy Fiddle
看起来他们在这方面确实做得非常出色...这个世界真是有趣... https://www.saporedicina.com/english/apps-blocked-china-access/ - Dressy Fiddle

0

我不太明白你是在弄文件下载还是其他的事情。但我猜你可能没有走对路。所以这里是我可以分享的。

https://developer.android.com/training/scheduling/wakelock:

  • 如果你的应用程序执行长时间的HTTP下载,请考虑使用DownloadManager
  • 如果你的应用程序从外部服务器同步数据,请考虑创建一个sync adapter
  • 如果你的应用程序依赖于后台服务,请考虑使用JobSchedulerFirebase Cloud Messaging 在特定间隔触发这些服务。

请注意,如果您只是需要经常执行的任务,请使用JobIntentService。它与Oreo及其以下版本兼容:

用于处理已排队的作业/服务的辅助程序。 在Android O或更高版本上运行时,将通过JobScheduler.enqueue作业进行调度。在旧版平台上运行时,它将使用Context.startService

在Oreo及以上版本中,有限制帮助设备节省资源(电池、内存等...),即使使用JobIntentService,您也必须考虑它们;否则,您的应用可能会被识别为耗电应用

如果您要做的工作很重要,并且足以显示在通知栏中,请使用ForegroundService。这样它将被Android系统更认真地对待,并且被杀死的机会会更少。


0

尝试使用WorkManager,它是JetPack库。

优点:

  • 确保任务执行,即使应用程序或设备重新启动(保证执行
  • 您不需要编写设备逻辑来确定设备具有哪些功能并选择适当的API;相反,您可以将任务交给WorkManager,让它选择最佳选项。它是所有上述概念的包装器。
  • 在具有API 23+的设备上使用JobScheduler
  • 在具有API 14-22的设备上使用BroadcastReceiver + AlarmManager的组合

WorkManager Flow

参考文献:WorkManager 文档
参考文献:Medium 文章
参考文献:Medium 文章(1)

[更新] - 稳定版本已发布 WorkManager


0

我的代码示例目前运行良好,但在中国的手机上出现了问题。

已在以下手机上测试通过:小米7

public class AutoDownloadService extends IntentService {

    private ClipboardManager mClipboardManager;
    public AutoDownloadService() {
        super("AutoDownloadService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        mClipboardManager.addPrimaryClipChangedListener(mOnPrimaryClipChangedListener);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Toast.makeText(this, " service started.", Toast.LENGTH_SHORT).show();
    }
    private final ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
            new ClipboardManager.OnPrimaryClipChangedListener() {
                @Override
                public void onPrimaryClipChanged() {
                    ClipData clip = mClipboardManager.getPrimaryClip();
                    String textClipBoard = clip.getItemAt(0).getText().toString();

                    if (textClipBoard.startsWith("https://www.instagram.com/")) {
                        Toast.makeText(AutoDownloadService.this, textClipBoard, Toast.LENGTH_SHORT).show();
                    }
                }
            };
}

而在文件 Manifast.xml 中,它是这样定义的

 <service android:name="com.amirhf.inatasave.services.AutoDownloadService" />

尝试调试一下,也许那些中国手机不支持ClipboardManager? - Atif AbbAsi
@AtifAbbAsi 中国手机 => 是的,当应用程序打开时我进行了测试,但是当我从多任务中关闭应用程序时它可以正常工作。 但在其他手机上,如三星、HTC等,该应用程序运行良好。 - Amirhf
小米红米7,Android版本9 | API级别28 | 构建ID:PKQ1.18.1021.001 - Amirhf
请问,如果您关闭应用程序,其他手机会发生什么?以及您在小米红米手机上遇到的问题是什么? - Atif AbbAsi
每次打开Instagram触发器或重新启动服务时,请尝试添加广播。 - Atif AbbAsi
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接