问题
I'm developing an app which is basically a location tracking software. When you start it, it saves locations and sending them to a server.
The code is working for like 5 years now without any modification, without any errors.
It is implemented with a simple foreground service.
In the recent months I was getting user reported errors about the service stops randomly on Huawei devices. First I thought it is some kind of rare/new crash on newer androids but there was no error logs at all in Fabric.
I tried it in a new Huawei device and for my greatest surprise, this phenomenon does really exists. Huawei devices (with EMUI) does really kills the foreground services after a couple of minutes.
This is really really bad for my app, first of all, the users want to run this tracking app for long hours, and secondly, the recent months made Huawei be a popular choice amongs Android users. Like 10% of my user base has a Huawei device.
I'm aware of https://dontkillmyapp.com/ It is a great website for getting information about this issue.
I was tried their solution which is basically adding a wakelock with a specific tag to my service, so the Huawei's EMUI won't kill it.
I've tried this in the following way, but my Huawei test device still kills my foreground service after some minutes.
Code inside my Service:
I basically aquires a wakelock in the service's onCreate callback.
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: My service is a foreground service, with a presistent notification. It can run well for DAYS on other devices.
Please help if you can,
Adam
回答1:
It's not related to Huawei phones it's about Android OS Version.
Since releasing Android 26 (Oreo) .Google decided to have hard restrictions on permissions and Also some permissions was not being grant either.
Basically, i would like to suggest you to read more about Google Oreo and later Permissions policies .
i had your problem too. my app works perfect in below Android 26 but after that , i encountered a big disaster and currently working on it to handle it for new android version users.
finally , you have to adapt to Current Google Policy otherwise you have to make decision of SHIFT+DELETE.
Edit
Clraification: My service is a foreground service, with a presistent notification. It can run well for DAYS on other devices.
As you clarified , Your application service life cycle continues when app is closed.
Before Android.O
, You could run service by getApplicationContext.startService(YOUR_INTENT_SERVICE)
but after Android.O
as google documented here you have to use IntentService instead of Service .You should enqueueWork
Your service otherwise,the service wouldn't be started.
and also, it's not enough to start service safely , you Must schedule starting service periodically. because , When app is closed ,your service goes to blacklist and will be closed as soon as possible by OS.
After Android.O Services are in whitelist when the application is open and interact by user otherwise it goes to blacklist and will be closed to free up resources in order to be allocated by other high-priority apps.
Here is a piece of Code that would help you to overcome the problem.
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;
}
Above code is a scheduled job which will be invoked per minute and re-scheduled again .
Then checks OS version and tries to start service
or put it in enqueueWork
UPDATE 2019/29/11
Recently i've noticed that there are some new Restrictions.most importantly,
- jobscheduler periodic interval
- jobscheduler maximum active time
jobscheduler periodic interval can't be less than 15 mins and jobscheduler maximum active time can be active more than 1 min.Therefore, if you need to keep your service alive.you should have 15 jobs with persist attribute. while 1th job is running next job(2th) will be started next 1 min and finally when 15th job is started 1th job will be started next 1 min.as a result, you have a cycle-recycle which lead you have alive service.
回答2:
I have tested below code in a huawei and it worked. Maybe you can use it in your service with notification. It might not be perfect though i am still a novice on the subject.
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:
I had the a similar problem few month ago, I spent a lot of time to search a solution and finally i have found this (i don't know if works for you, but it was help me).
I implemented in an application that I developed, a service that recover the user position every minutes, this position is saved in the sqlite database of the device. I need that the application work alway without interrupt.
In the test phase I found that some devices interrupted the execution of my code (like in your case).
After several fists on my desk, I realized that the problem was related to energy saving options which are different for some manufacturers but also for Android versions.
This solution help me:
@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();
}
}
}
Basically I ask the user (I can't do it any other way) to allow my application to avoid being optimized (this code work for Build.VERSION.SDK_INT >= 23)
回答4:
I think the issue is in the way you handles the services in your app.
The major thing is, you need to keep a notification running on the status bar that indicates the service related to your app is running and end-user can terminate the service by clicking on the notification.
It is the combination of service + notification + required policies.
Hope it helps.
来源:https://stackoverflow.com/questions/56883074/huawei-device-killing-my-foreground-service-even-with-dontkillmyapp-coms-solut