我正在开发一个基本上是位置跟踪软件的应用。启动时,它会保存位置并将其发送到服务器。
该代码已经工作了5年,没有任何修改,没有任何错误。
它通过简单的前台服务实现。
最近几个月,我收到用户报告的关于服务在华为设备上随机停止的错误。首先,我认为这是较新的Android设备上的罕见/新崩溃,但有 Fabric中根本没有错误日志。
我在新的华为设备中尝试过,令我惊讶的是,这种现象确实存在。 几分钟后,华为设备(带有EMUI)确实杀死了前台服务。
这真的对我的应用程序不利,首先,用户希望长时间运行此跟踪应用程序,其次,近几个月来,华为已成为Android用户中的热门选择。大概有10%的用户拥有华为设备。
我知道https://dontkillmyapp.com/是个获取有关此问题的信息的好网站。
我尝试过他们的解决方案,该解决方案基本上是在服务中添加带有特定标签的唤醒锁,因此华为的EMUI不会杀死它。
我已经通过以下方式进行了尝试,但我的华为测试设备在几分钟后仍会终止我的前台服务。
我的服务中的代码:
我基本上在服务的onCreate回调中获取了唤醒锁。
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上正常运行。
如果可以,请帮助
亚当
答案 0 :(得分:3)
这不是与华为相关的解决方案,但有一些有用的措施可以缓解该问题。
在这种情况下,需要致电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
}
这两种方法允许用户禁用打ze(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)
}
答案 1 :(得分:2)
与华为手机无关,与Android OS版本有关。
自发布Android 26(Oreo)以来。Google决定对权限进行严格限制,并且也未授予某些权限。
基本上,我想建议您阅读有关Google Oreo和更高版本权限政策的更多信息。
我也遇到了你的问题。我的应用程序可以在Android 26以下的环境下完美运行,但此后,我遇到了一场大灾难,目前正在为新的Android版本用户处理它。
最后,您必须适应当前的Google政策,否则必须做出SHIFT + DELETE的决定。
修改
说明:我的服务是前台服务,具有持久性 通知。它可以在其他设备上正常运行DAYS。
如您所言,关闭应用程序后,您的应用程序服务生命周期将继续。
在之前Android.O
,您可以在getApplicationContext.startService(YOUR_INTENT_SERVICE)
之前运行服务,但在Google记录here的情况下在之后 Android.O
必须使用 IntentService 而不是 Service 。您应该enqueueWork
使用服务,否则将无法启动该服务。
此外,仅安全地启动服务还不够,您必须定期安排启动服务。因为,当应用关闭时,您的服务将进入黑名单,并且将由 OS 尽快关闭。
当应用程序打开并由用户交互时,Android.O服务进入白名单后,否则将进入黑名单并关闭以释放资源,以便由其他高优先级应用分配。
这是一段代码,可以帮助您解决问题。
public class job extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!isMyServiceRunning(this, YourService.class)) {
YourService.enqueueWork(this, YourService.class, 1000, new Intent());
}
} else {
if (!isMyServiceRunning(this, YourService.class)) {
Intent service = new Intent(this, YourService.class);
this.startService(service);
}
}
ScheduledWork.scheduleJob(this);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
jobFinished(params, true);
return false;
}
上面的代码是预定的工作,它将每分钟调用一次,然后再次重新预定。
然后检查操作系统版本并尝试start service
或将其放入enqueueWork
答案 2 :(得分: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);
}
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;
}
}
答案 3 :(得分:0)
几个月前,我遇到了类似的问题,我花了很多时间来寻找解决方案,最后我发现了这个问题(我不知道是否对您有用,但这对我有帮助)。
我在自己开发的应用程序中实现了该服务,该服务每分钟恢复一次用户位置,该位置保存在设备的sqlite数据库中。我需要应用程序始终运行而不会中断。
在测试阶段,我发现某些设备中断了我的代码的执行(就像您的情况一样)。
在我的桌子上握了几拳之后,我意识到这个问题与节能选项有关,这对于某些制造商来说是不同的,但对于Android版本而言,是不同的。
此解决方案可以帮助我
@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)
答案 4 :(得分:0)
华为的EMUI严格监控和管理应用程序的电池消耗。甚至应用程序也具有“二次启动”,“在后台运行”和“自动启动”的权限,一段时间后EMUI可能会杀死它或它的后台进程。
EMUI通过列表进行管理,并且我知道应用程序是否存在此类问题,因此有必要申请特殊许可才能显示在该列表中。
答案 5 :(得分:0)
对我而言,以下帮助(更改了华为设备的设置):
步骤1.将您的华为智能手机设置为允许特定应用在后台运行
第2步。为相同的应用程序禁用电池优化
来源:
https://www.digitalcitizen.life/stop-huawei-from-closing-apps-when-you-lock-screen
答案 6 :(得分:0)
这就像是要保持应用程序存活的应用程序开发人员与想要检测并杀死不必要的进程以优化操作系统性能的操作系统开发人员之间的游戏。
对于华为设备,有3种优雅的方法可以像我尝试过的那样保持该应用程序的运行,而且它们各有利弊。
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。集成华为的HMS Push Kit。
它提供了高优先级的数据消息,可让您将数据消息发送到目标设备,并在消息到达时直接启动相应的应用程序。这样,您可以通过定期发送消息或由某些活动触发消息来激活应用程序,但是发送高优先级数据消息需要申请许可,并且需要花费数周的时间。
3。申请EMUI后台活动权限。
这可能是一劳永逸的解决方案,它授予该应用程序在后台运行的权限,而忽略了功耗和操作系统优化。无需更改代码,非常简单,但是应用程序大约需要一个月的时间来审核,并且有可能被拒绝。
答案 7 :(得分:-1)
我认为问题在于您处理应用程序中的服务的方式。
最主要的是,您需要在状态栏上保持运行通知,以指示与您的应用相关的服务正在运行,最终用户可以通过单击通知来终止该服务。
它是服务+通知+所需策略的组合。
希望对您有所帮助。
答案 8 :(得分:-1)
尝试在这里一次-explanation n code使用广播接收器,将其优先级设置为HIGH
我正在使用它,到目前为止,它对我来说完全可以正常工作,我们没有任何使用华为设备的客户端,所以IDK它将如何在该设备上工作,但是由于它在三星上能很好地工作,我猜猜它也可以在华为上正常工作。看看吧。