“我的应用”正在使用Android 8中引入的新的Foreground服务设计。
在执行服务期间,取消系统托盘中显示的notification时遇到问题。即使服务未运行,某些服务仍会在通知栏上导致与服务冻结有关的通知。
我想问一些建议,以正确的方式进行操作,因为在这种情况下Android文档尚不清楚。
以下提到我执行服务和显示/取消通知的方法。欢迎任何建议。
注意:我向UserManualCheckService添加了超时方法,以强制调用 stopSelf()方法。
1)使用Worker Manager作为Worker的实例启动服务:
List<OneTimeWorkRequest> workRequestList = new ArrayList<>();
OneTimeWorkRequest workRequestUserManual = new OneTimeWorkRequest.Builder(UserManualWorker.class).build();
workRequestList.add(workRequestUserManual);
mWorkManagerInstance.enqueue(workRequestList);
工作人员示例
public class UserManualWorker extends Worker {
private Context context;
public UserManualWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.context = context;
}
@NonNull
@Override
public Result doWork() {
Intent i = new Intent(context, UserManualCheckService.class);
CommonHelper.runService(context, i);
return Result.SUCCESS;
}
}
2)服务示例
服务正在使用HTTP请求下载一些数据。通过使用 stopSelf()方法结束下载的错误和成功状态,该方法应触发父 BaseIntentService 服务中的 onDestroy()事件。
public class UserManualCheckService extends BaseIntentService implements HttpResponseListener {
private Context context = null;
@Override
public void onCreate() {
super.onCreate();
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public UserManualCheckService() {
super(UserManualCheckService.class.getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try { context = this;
//IF DEVICE IS ONLINE FETCH MESSAGES FROM REMOTE SERVER
if (CommonHelper.isConnectedToInternet(context)) {
Logger.i("UserManualCheckService started");
CommonHelper.showServiceNotification(this);
this.getRemoteData();
this.startTimeoutForRequest();
} else {
Logger.i("No need to sync UserManualCheckService now");
stopSelf();
}
} catch (Exception e) {
TrackingExceptionHelper.logException(e);
stopSelf();
}
return START_STICKY_COMPATIBILITY;
}
@Override
protected void onHandleIntent(Intent intent) {
}
private void getRemoteData() throws Exception {
JsonRequest request = HttpHelper.createRequest(Constants.Request.JSON_OBJECT, Request.Method.GET,
Constants.URL.API_URL_BASE_SCHEME, Constants.URL.API_URL_MEDIA_CHECK_MANUAL,
null, this, Request.Priority.HIGH);
HttpHelper.makeRequest(request, true);
}
@Override
public void onError(VolleyError error) {
TrackingExceptionHelper.logException(error);
stopSelf();
}
@Override
public void onResponse(Object response) throws JSONException {
Logger.d("onResponse");
if (response instanceof JSONObject) {
final JSONObject resp = (JSONObject) response;
new Thread(new Runnable() {
@Override
public void run() {
try {
checkLastSavedVersion(resp);
} catch (Exception e) {
TrackingExceptionHelper.logException(e);
}
}
}).start();
stopSelf();
} else {
Logger.e("Parsing Response data as JSON object is not implemented");
}
}
private void checkLastSavedVersion(JSONObject mediaResource) {
try {
Integer serverManualSize = mediaResource.getInt(Constants.Global.KEY_MANUAL_FILE_SIZE);
String localManualSizeAsString = SharedPrefsHelper.getInstance(context).readString(Constants.Global.KEY_MANUAL_FILE_SIZE);
Integer localManualSize = localManualSizeAsString == null ? 0 : Integer.parseInt(localManualSizeAsString);
if(!serverManualSize.equals(localManualSize)) {
new DownloadUserManualAsyncTask(serverManualSize, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
Logger.i("User manual already downloaded and up-to date");
}
} catch (Exception e) {
TrackingExceptionHelper.logException(e);
} finally {
stopSelf();
}
}
private void startTimeoutForRequest() {
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
stopSelf();
}
},
10000);
}
}
BaseIntentService
所有后台服务的父服务。在子级上调用 stopSelf()会传递给父级,并在 onDestroy()中被捕获,在该处服务停止,并且每次都应取消通知。
public abstract class BaseIntentService extends IntentService {
Context context = null;
@Override
public void onCreate() {
super.onCreate();
this.context = this;
}
public BaseIntentService(String name) {
super(name);
}
@Override
public void onDestroy() {
super.onDestroy();
Logger.d("service done, hiding system tray notification");
CommonHelper.stopService(context, this);
NotificationHelper.cancelNotification(Constants.Notification.SERVICE_NOTIFICATION_ID, context);
}
}
使用帮助程序类开始执行Foreground Service:
public static void runService(Context context, Intent i) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(context, i);
} else {
context.startService(i);
}
}
显示通知:
public static void addServiceNotification(Service context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
setupNotificationChannelsLowImportance(notificationManager);
}
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context, ANDROID_CHANNEL_ID_SYNC);
Notification notification = mBuilder
.setOngoing(false) //Always true in start foreground
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_sync)
.setContentTitle(context.getClass().getSimpleName())
//.setContentTitle(context.getString(R.string.background_sync_is_active))
.setPriority(NotificationManager.IMPORTANCE_LOW)
.setVisibility(Notification.VISIBILITY_PRIVATE)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
notification.flags |=Notification.FLAG_AUTO_CANCEL;
context.startForeground(Constants.Notification.SERVICE_NOTIFICATION_ID, notification);
if(notificationManager != null) {
//notificationManager.notify(Constants.Notification.SERVICE_NOTIFICATION_ID, notification);
}
}
}
停止服务是通过以下方式完成的:
public static void stopService(Context context, Service s) {
Intent i = new Intent(context, s.getClass());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
s.stopForeground(Service.STOP_FOREGROUND_DETACH);
s.stopForeground(Service.STOP_FOREGROUND_REMOVE);
} else {
s.stopForeground(true);
}
context.stopService(i);
}
从BaseIntentService onDestroy()调用的取消通知方法
public static void cancelNotification(int notificationId, Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(notificationId);
}
}
AndroidManifest.xml中的服务注释
<!-- [START USER MANUAL CHECK SERVICE] -->
<service
android:name=".service.UserManualCheckService"
android:enabled="true"
android:exported="false"/>
<!-- [END USER MANUAL SERVICE] -->
答案 0 :(得分:1)
两次stopForeground(int)
的单独调用导致了此现象。 stopForeground(int)
采用标志的按位组合,因此不应连续调用两次(因为第一次调用将使您的服务停止成为前台服务,这意味着不再适合使用stopForeground()
与之沟通)。我什至不知道在这种情况下记录的行为是什么。
解决方案
无论操作系统版本如何,只需调用stopForeground(true)
。
答案 1 :(得分:0)
在WorkManager中运行服务没有任何意义。