web-dev-qa-db-ja.com

Huaweiデバイスがdontkillmyapp.comのソリューションを使用してもフォアグラウンドサービスを終了させる

私は基本的に位置追跡ソフトウェアであるアプリを開発しています。起動すると、場所が保存され、サーバーに送信されます。

コードは現在、変更なしで、エラーなしで5年間動作しています

シンプルなフォアグラウンドサービスで実装されています。

最近数か月間、ユーザーから報告されたサービスに関するエラーがHuaweiデバイスでランダムに停止していました。最初に、ある種のまれな/新しいクラッシュが発生したと思いました新しいandroidですが、Fabricにエラーログはまったくありませんでした。

新しいHuaweiデバイスで試してみましたが、驚いたことに、この現象は実際に存在しています。 Huaweiデバイス(EMUIを使用)は、数分後にフォアグラウンドサービスを強制終了します。

これは私のアプリにとって本当に悪いことです。まず第一に、ユーザーはこの追跡アプリを長時間実行したいと考えています。次に、ここ数か月間、Huaweiはユーザーの間で人気のある選択肢となりましたAndroid。私のユーザーベースの10%がHuaweiデバイスを持っているように。

私は https://dontkillmyapp.com/ を知っています。これは、この問題に関する情報を入手するための優れたWebサイトです。

私はそれらの解決策を試しました-これは基本的に特定のタグを持つウェイクロックを私のサービスに追加していますので、ファーウェイのEMUIはそれを殺しません。

私は次の方法でこれを試しましたが、Huaweiテストデバイスはまだ数分後に私のフォアグラウンドサービスを終了します。

私のサービス内のコード:

基本的に、サービスの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:

説明:私のサービスはフォアグラウンドサービスであり、通知が持続します。他のデバイスでDAYSの間正常に実行できます。

できれば助けてください、

アダム

16
Adam Varhegyi

これはnot Huawei関連のソリューションですが、問題を軽減するための便利なアクションがあります。

この場合、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
}

これらの2つの方法により、ユーザーはDozeを無効にし(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) {
    }
}

また、部分的なwake lockを使用すると軽減できますが、サービスの維持を保証するものではありません。

/** 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)
}
1
fireb86

私はhuaweiで以下のコードをテストしましたが、うまくいきました。多分あなたはあなたのサービスでそれを通知で使うことができます。まだ初心者ですが、完璧ではないかもしれません。

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);
    }






    telephonyManager = (TelephonyManager) this
            .getSystemService(Context.TELEPHONY_SERVICE);



    if(phoneListener == null) {

        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;
}


}
0
Fractal

同様の問題があり、フォアグラウンドサービスも実行しています。

現在の回避策:Huaweiを使用しているユーザーに、

 Settings --> Battery --> App launch 

アプリの起動を手動に変更します。

残念ながら、私はこれをプログラムで行う方法をまだ見つけていません。ユーザーが行う必要があります。この「アプリの起動」メニューはHuaweiからのもののようです。それは彼ら自身の「PowerGenie」アプリに関連していると思います。

これは実際の解決策ではありませんが、少なくともこのバッテリーセーバーのホワイトリストに登録されているため、アプリが強制終了されることはほとんどありません。

0
romulus

ここを一度試してみてください- 説明 n コード 優先度をHIGHに設定することで、ブロードキャストレシーバーを使用した、終わりのないバックグラウンドサービス

私はそれを使用していて、今までは完全に問題なく動作します。Huaweiデバイスを使用しているクライアントはありません。そのため、IDKはそのデバイスでどのように機能しますか? Huaweiでも期待どおりに動作します。見てください。

0
Blu