华为设备杀死我的前台服务,即使使用dontkillmyapp.com的解决方案也是如此

32 人关注

我正在开发一个应用程序,它 基本上是一个位置跟踪软件 。当你启动它时, 它会保存位置并将它们发送到服务器上。

这个代码 已经工作了5年,没有任何修改,也没有任何错误。

它是通过一个 简单的前台服务 实现的

最近几个月,我收到用户报告的错误,说 华为设备上的服务随机停止。 首先,我认为这是某种罕见的/新的崩溃在较新的安卓设备上,但 在Fabric中根本没有错误记录。

我在一个新的华为设备上试了一下,让我最惊讶的是,这种现象确实存在。 华为设备(使用EMUI)确实在几分钟后杀死了前台服务。

这对我的应用程序来说真的非常糟糕,首先,用户希望长时间运行这个跟踪应用程序,其次,近几个月来,华为成为安卓用户的热门选择。我的用户群中有10%拥有华为设备。

我知道 https://dontkillmyapp.com/ ,这是一个获得关于这个问题的信息的伟大网站。

我已经 尝试了他们的解决方案--这基本上是在我的服务中添加一个带有特定标签的wakelock, 所以华为的EMUI不会杀死它。

我已经尝试了以下方式, 但我的华为测试设备 在几分钟后 仍然杀死了我的前台服务

我的服务里面的代码。

我基本上在服务的onCreate回调中获得了一个wakelock。

 private void acquireLock() {
    if (wakeLock == null) {
        PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
        if (mgr != null) {
            if (Build.MANUFACTURER.toLowerCase().equals("huawei")) {
                lockTag = "LocationManagerService";
            wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockTag);
            Log.i("MY_TAG", "tag:" + lockTag);
    if (wakeLock != null && !wakeLock.isHeld()) {
        wakeLock.acquire();
        //also tried with: wakeLock.acquire(1000*60*60*72); 3 days wakelock just in case.
        Log.i("MY_TAG", "wakeLock acquired!");
@Override
public void onCreate() {
    acquireLock();

E D I T。

Clraification:我的服务是一个前台服务,有一个持续的通知。 它可以在其他设备上良好运行数日。

如果可以的话,请帮忙。

5 个评论
M D
华为正在定制AOSP,并在电池优化和其他地方设置更多限制,以便用户不会遇到这样的问题。
我正在努力,如果你需要,我可以给你一些好的建议。
@Mr.AF很想听听他们的意见
我也在使用foregroundService,对于AOSP安卓操作系统,需要的操作是:显示前台通知,并由用户禁用打盹。对于使用OEMrom和OEM电池优化的设备,你必须遵循dontkillmyapp。Dontkillmyapp将华为列为最差的制造商,并说没有解决方案,无论是用户端还是开发者端。
你找到解决办法了吗?我已经试过awakelock,它有一点帮助,但不能保证保持服务的活力。唯一真正有帮助的是在电池设置中把我的应用程序从自动优化切换到手动,但这对我来说不是很好的解决方案,因为这取决于用户可能会偷懒。
android
android-service
huawei-mobile-services
Adam Varhegyi
Adam Varhegyi
发布于 2019-07-04
9 个回答
Mattia
Mattia
发布于 2019-07-09
0 人赞同

几个月前我也遇到了类似的问题,我花了很多时间来寻找解决方案,最后我找到了这个(我不知道是否对你有用,但它对我有帮助)。

我在自己开发的一个应用程序中实现了一个服务,每分钟恢复用户的位置,这个位置被保存在设备的sqlite数据库中。我需要这个应用程序在没有中断的情况下一直工作。

在测试阶段,我发现有些设备中断了我的代码的执行(就像你的情况)。

在我的桌子上打了几拳之后,我意识到这个问题与节能选项有关,这些选项对于一些制造商和安卓版本是不同的。

这个解决方案帮助了我。

@SuppressLint({"NewApi", "BatteryLife"})
private void checkOptimization() {
    String packageName = getApplicationContext().getPackageName();
    PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    if (pm != null) {
        if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                Intent intent = new Intent();
                intent.setAction(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + ctx.getPackageName()));
                ctx.startActivity(intent);
        } else {
            new initialize().execute();

基本上,我要求用户(我不能用其他方式)允许我的应用程序避免被优化(这段代码对Build.VERSION.SDK_INT >= 23有效)。

清单文件需要这个权限。

android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS  
    
这并不能完全防止应用程序被杀死。
fireb86
fireb86
发布于 2019-07-09
0 人赞同

不是 一个与华为有关的解决方案,但有一些有用的行动可以缓解这个问题。

在这种情况下,需要调用 startForeground START_STICKY

/** YourService **/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    startForeground(
        App.NOTIFICATION_ID_YOUR_SERVICE,
        buildNotification("Foo bar")
    return START_STICKY

这两种方法允许用户禁用打盹(Oreo>)和启用自动启动权限(一些OEM)以保留STICKY服务的生命周期。

/** YourActivity **/
fun openBatteryOptimization(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val intent = Intent()
        intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
        context.startActivity(intent)
    } else {
        //Timber.d("Battery optimization not necessary")
fun openAutostartSettings(context: Context) {
    try {
        val intent = Intent()
        val manufacturer = Build.MANUFACTURER
        if ("xiaomi".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.miui.securitycenter",
                "com.miui.permcenter.autostart.AutoStartManagementActivity"
        } else if ("oppo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.coloros.safecenter",
                "com.coloros.safecenter.permission.startup.StartupAppListActivity"
            ) //need "oppo.permission.OPPO_COMPONENT_SAFE" in the manifest
        } else if ("vivo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.vivo.permissionmanager",
                "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"
        } else if ("Letv".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.letv.android.letvsafe",
                "com.letv.android.letvsafe.AutobootManageActivity"
        } else if ("Honor".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.huawei.systemmanager",
                "com.huawei.systemmanager.optimize.process.ProtectActivity"
        } else {
            //Timber.d("Auto-start permission not necessary")
        val list = context.packageManager
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
        if (list.size > 0) {
            context.startActivity(intent)
    } catch (e: Exception) {

另外,使用部分唤醒锁也有助于缓解,但它不能保证保持服务的活力。

/** YourService **/
private val wakeLock: PowerManager.WakeLock by lazy {
    (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
        newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ServiceWakelock")
private fun acquireWakelock() {
    try {
        wakeLock.let {
            wakeLock.setReferenceCounted(false)
            if (!wakeLock.isHeld) {
                wakeLock.acquire()
    } catch (e: RuntimeException) {
private fun releaseWakelock() {
    try {
        wakeLock.let {
            if (it.isHeld) {
                it.release()
    } catch (e: RuntimeException) {
override fun onCreate() {
    super.onCreate()
    acquireWakelock()
override fun onDestroy() {
    releaseWakelock()
    super.onDestroy()

使用Binder,你可以检查服务是否在运行(并再次启动它),你可以得到服务的实例。

/** YourActivity **/
private val mConnection = object : ServiceConnection {
    override fun onServiceConnected(className: ComponentName, iBinder: IBinder) {
        // The system calls this to deliver the IBinder returned by the service's onBind() method.
        val binder = iBinder as YourService.YourBinder
        service = binder.getService()
        bound = true
    override fun onServiceDisconnected(arg0: ComponentName) {
        // The Android system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds
        bound = false
private fun bindYourService() {
    Intent(this, YourService::class.java).also { intent ->
        applicationContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
private fun unbindYourService() {
    try {
        applicationContext.unbindService(mConnection)
    } catch (e: Exception) {
        Timber.e(e)
    bound = false
/** YourService **/
private val binder = YourBinder()
inner class YourBinder: Binder() {
    fun getService(): YourService = this@YourService
override fun onBind(intent: Intent): IBinder {
    return binder
override fun onRebind(intent: Intent?) {
    super.onRebind(intent)
override fun onUnbind(intent: Intent?): Boolean {
    return super.onUnbind(intent)
    
我读了你的答案。好的材料,虽然可能不是完整的答案,但对完整的答案有帮助。
什么是 AutoStartManagementActivity
@IgorGanapolsky 小米自动启动设置
alla
回答得好,但这是华为的问题。因为,举例来说,我的应用程序在三星上运行良好,而华为在锁屏后就将其杀死。当我在设置中把该应用从电池优化中删除后,该应用即使在锁屏后也能正常工作,但一段时间后还是被操作系统杀死。我使用ForegroundService,粘性,其中有WakeLock。
有谁能用JAVA提供完整的答案?
captaink
captaink
发布于 2019-07-09
0 人赞同

华为的EMUI严格监控和管理应用程序的电池消耗。即使应用程序有二次启动、后台运行和自动启动的权限,EMUI也可能在一段时间后杀死它或它的后台进程。

EMUI通过一个列表来管理这个问题,正如我所知,如果一个应用程序有这样的问题,它有必要申请一个特殊的权限以进入该列表。

deadfish
"有必要申请一个特别许可,以列入该名单。"。请告诉我更多关于这种特殊许可的情况。
alla
alla
发布于 2019-07-09
0 人赞同

对我来说,下面的方法很有帮助(在华为设备的设置中进行修改)。

步骤1.将你的华为智能手机设置为允许特定的应用程序在后台运行

第2步。禁用相同应用程序的电池优化

https://www.digitalcitizen.life/stop-huawei-from-closing-apps-when-you-lock-screen

https://dontkillmyapp.com/

PatrickKen
PatrickKen
发布于 2019-07-09
0 人赞同

这就像应用程序开发人员和操作系统开发人员之间的博弈,前者希望保持应用程序的活力,后者希望检测并杀死不必要的进程以优化操作系统的性能。

对于华为设备,有3种优雅的方式来保持应用程序的活力,正如我所尝试的,它们都有优点和缺点。

1.1.引导用户给予应用程序在后台运行的权限。

你可以在 "设置"-"应用程序"-"应用程序启动 "中实现代码,要求用户给予所需的权限。

    private void showActivity(@NonNull String packageName, @NonNull String activityDir) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(packageName, activityDir));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    private void goSettings() {
        try {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
        } catch (Exception e) {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");

这可以直接在代码中完成,但用户必须进行设置,在某些特定的情况下(手动优化操作系统),应用程序仍然可能被杀死。

2.2. 集成华为的HMS推送工具包。

它提供了高优先级的数据信息,可以让你向目标设备发送数据信息,当信息到达时,直接启动相应的应用程序。通过这种方式,你可以通过定期发送消息或由某些活动触发来激活App,但发送高优先级的数据消息需要申请许可,而且需要几周时间。

3.申请EMUI后台活动许可。

这可能是一劳永逸的解决方案,它给予应用程序在后台操作的权限,并忽略了功耗和操作系统优化。不需要改变代码,非常直接,但申请需要一个月左右的时间来审查,而且有可能被拒绝。

hb0
唯一的问题是。在哪里申请 "EMUI后台活动许可"?
你可以联系AppGallery,我们试图申请一个,但失败了...
A Farmanbar
A Farmanbar
发布于 2019-07-09
0 人赞同

这与 华为 手机无关,而是与 安卓操作系统 版本有关。

自从发布了 安卓26 (奥利奥)之后。谷歌决定设置一些 限制和约束 ,而且一些权限也不授予。基本上,我想建议你阅读更多关于谷歌奥利奥和后来的权限安全和政策。

我有你的问题,我的应用程序在 < Android 26 ,但对于后来的版本,我遇到了一个大灾难,我正在努力为新的安卓版本用户处理它。因此,你必须适应当前的谷歌政策。否则,你必须做出决定, SHIFT + DELETE

如果一个应用程序在运行Android 8.0的设备上处于前台(API 26级)的设备上,位置更新行为与Android 8.0(API级)上的相同。 7.1.1(API级别25)和更低的版本。

因此,如果用户关闭应用程序,甚至有意或无意地启动了前台服务, 通知用户再次打开应用程序

调整你的应用程序的位置行为

  • 把你的应用程序带到前台。

  • 通过调用startForegroundService()在你的应用程序中启动一个前台服务。当这样的前台服务被激活时。
    它将作为一个持续的通知出现在通知区。

  • 使用Geofencing API的元素,如GeofencingClient,这些元素经过优化,可以最大限度地减少电力使用。

  • 使用一个被动的位置监听器,如果有前台应用要求以更快的速度进行位置更新,它可能会收到更快的位置更新。 更快的速度。

  • 你能举出这种限制吗?是否有任何可能使这种情况发生?
    @AdamVarhegyi 我不能确切地说你的问题是什么,因为你在问题中展示的东西是没有帮助的,但我可以给你举例。
    很简单,我的前台服务被华为的操作系统杀死。(EMUI)就这样了。
    @AdamVarhegyi 你的前台服务是否必须在应用关闭时继续进行? 还是在应用生命周期内工作?
    @AdamVarhegyi 我编辑了帖子,希望它对你有用,因为它对我有用。
    Blu
    Blu
    发布于 2019-07-09
    0 人赞同

    试着看一下这里-- 解释一下 ,通过设置其优先级,使用广播接收机提供一个永不停止的后台服务。 HIGH

    我正在使用它,到目前为止,它对我来说完全正常,我们没有任何使用华为设备的客户,所以不知道它在那些设备上会如何工作,但由于它在三星上工作得很好,我想它在华为上也会像预期那样工作。请看一下。

    Viraj S
    Viraj S
    发布于 2019-07-09
    0 人赞同

    我认为问题在于你处理你的应用程序中的服务的方式。
    最主要的是,你需要在状态栏上保持一个 通知 ,表明与你的应用程序相关的服务正在运行,最终用户可以通过点击通知终止该服务。
    这就是服务+通知+所需策略的组合。
    希望这对你有帮助。

    什么是 必要的政策
    Fractal
    Fractal
    发布于 2019-07-09
    0 人赞同

    我在华为手机上测试了以下代码,它起作用了。也许你可以在你的服务中使用它并发出通知。虽然我在这方面还是个新手,但它可能并不完美。

    public class SensorService extends Service {
    public int counter=0;
    Context context;
    public SensorService(Context applicationContext) {
        super();
        Log.i("HERE", "here I am!");
    static PhoneCallListener phoneListener = null;
    TelephonyManager telephonyManager;
    public SensorService() {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        String CHANNEL_ID = "yourid";
        String CHANNEL_NAME = "yourchannel";
        NotificationChannel channel = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            channel = new NotificationChannel(CHANNEL_ID,
                    CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager != null) {
                manager.createNotificationChannel(channel);
        Notification notification = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            notification = new Notification.Builder(this, CHANNEL_ID)
                    .setContentTitle("title")
                    .setContentText("text")
                    .setAutoCancel(true)
                    .build();
            this.startForeground(1,notification);
            if (telephonyManager != null) {
                phoneListener = new PhoneCallListener();
                telephonyManager.listen(phoneListener,
                        PhoneStateListener.LISTEN_CALL_STATE);
        System.out.println("SERVICE");
        return START_STICKY;
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("EXIT", "ondestroy!");
        Intent broadcastIntent = new Intent(this, SensorRestarterBroadcastReceiver.class);
        sendBroadcast(broadcastIntent);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, SensorService.class));
        } else {
            context.startService(new Intent(context, SensorService.class));
        Intent brcast = new Intent(this, AlarmReceiver.class);
        sendBroadcast(brcast);
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        context = getApplicationContext();
        return null;