当尝试轮询HTTP端点时,与手机处于活动状态相比,在手机处于活动状态时生成的通知明显更慢(活动时间为2-3秒,平均为10秒,但锁定时偶尔长达一分半钟)。它已被锁定。
应用程序要求在Android M +上不对其进行优化。这不会减少响应时间。
编辑: 尝试在服务中使用WakeLock。行为没有改变。 问题似乎出在手机处于空闲模式时,连接持续超时。
编辑2: 请求之间的间隔时间为每2秒。
主要活动
package uk.co.xxx;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import org.qtproject.qt5.android.bindings.QtActivity;
import dagger.android.AndroidInjection;
public class NotificationActivity extends QtActivity {
public static final String NURSE_CALL_CHANNEL_ID = "NURSE_CALL_CHANNEL";
public static final String HIGH_PRIORITY_CHANNEL_ID = "HIGH_PRIORITY_CHANNEL";
@Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel nurseCallChannel = createNotificationChannel();
NotificationChannel highPriorityChannel = createHighPriorityChannel();
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(nurseCallChannel);
notificationManager.createNotificationChannel(highPriorityChannel);
}
}
//Ask permission to ignore battery optimisations, but only on API 23+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!manager.isIgnoringBatteryOptimizations(getPackageName())) {
Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(intent);
}
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
private NotificationChannel createNotificationChannel() {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(NURSE_CALL_CHANNEL_ID, name, importance);
channel.setDescription(description);
String audioPath = ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + getPackageName() + "/" + R.raw.call;
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.build();
channel.setSound(Uri.parse(audioPath), attributes);
channel.enableLights(true);
channel.setLightColor(Color.WHITE);
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {0, 200, 200, 200});
channel.setShowBadge(true);
return channel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private NotificationChannel createHighPriorityChannel() {
CharSequence name = getString(R.string.high_priority_channel_name);
String description = getString(R.string.high_priority_channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(HIGH_PRIORITY_CHANNEL_ID, name, importance);
channel.setDescription(description);
String audioPath = ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + getPackageName() + "/" + R.raw.emergency;
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.build();
channel.setSound(Uri.parse(audioPath), attributes);
channel.enableLights(true);
channel.setLightColor(Color.RED);
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {0, 300});
channel.setShowBadge(true);
return channel;
}
public void startLiveCallService() {
Intent serviceIntent = new Intent(this, LiveCallService.class);
startService(serviceIntent);
}
}
服务等级
package uk.co.xxx;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.text.DateFormat;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import retrofit2.Call;
import retrofit2.Response;
import uk.co.xxx.api.CallItem;
import uk.co.xxx.api.HostSelectionInterceptor;
import uk.co.xxx.api.LiveCallResponse;
import uk.co.xxx.api.NurseCallApiInterface;
import uk.co.xxx.db.dao.ConfigurationDao;
import uk.co.xxx.db.dao.LiveCallDao;
import uk.co.xxx.db.entities.Configuration;
import uk.co.xxx.db.entities.LiveCall;
public class LiveCallService extends Service {
@Inject
public NurseCallApiInterface nurseCallApiInterface;
@Inject
public HostSelectionInterceptor hostSelectionInterceptor;
@Inject
public LiveCallDao liveCallDao;
@Inject
public ConfigurationDao configurationDao;
private Looper looper;
private Handler handler;
private final class NurseCallRunnable implements Runnable {
private static final int RUNNABLE_DELAY = 2000;
@Override
public void run() {
Log.i("LIVECALL", "Handling call intent!");
String ipAddress = configurationDao.getIpAddress();
Log.i("LIVECALL", "IP ADDRESS:" + ipAddress);
hostSelectionInterceptor.setHost(ipAddress);
Call<LiveCallResponse> call = nurseCallApiInterface.getLiveCalls();
Response<LiveCallResponse> response = null;
try {
response = call.execute();
} catch (IOException e) {
Log.e("LIVECALL", "IOException occurred when attempting to retrieve live calls", e);
}
boolean retrievalSuccess = response != null;
Configuration activeNetworkConfiguration = new Configuration(
"IS_ACTIVE_NETWORK",
retrievalSuccess ? "1" : "0"
);
configurationDao.update(activeNetworkConfiguration);
if (!retrievalSuccess) {
handler.postDelayed(this, RUNNABLE_DELAY);
return;
}
LiveCallResponse liveCallResponse = response.body();
if (liveCallResponse == null) {
handler.postDelayed(this, RUNNABLE_DELAY);
return;
}
Log.i("LIVECALL", "response:" + liveCallResponse.toString());
boolean isNew;
boolean isUpdate;
SparseArray<LiveCall> pendingRemovalCalls = new SparseArray<>();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(LiveCallService.this);
Intent activityIntent = new Intent(LiveCallService.this, NotificationActivity.class);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(LiveCallService.this, 0, activityIntent, 0);
for (LiveCall existingLiveCall: liveCallDao.getAll()) {
pendingRemovalCalls.put(existingLiveCall.getCallerTagId(), existingLiveCall);
}
if (liveCallResponse.getCallItems() != null) {
for (CallItem callItem : liveCallResponse.getCallItems()) {
isNew = true;
isUpdate = false;
LiveCall existingCall = liveCallDao.getBySender(Integer.toString(callItem.getCallpointId()));
//Without a sender GUID, we are using caller ID as a substitute
LiveCall liveCall = new LiveCall.Builder(Integer.toString(callItem.getCallpointId()))
.callerId(callItem.getCallpointId())
.callTypeId(callItem.getCallTypeId())
.callerName(callItem.getCalledBy())
.zoneId(callItem.getZoneId())
.priority(callItem.getPriority())
.date(callItem.getReceivedTime())
.forwardingBleRuName("")
.build();
pendingRemovalCalls.remove(liveCall.getCallerTagId());
if (existingCall != null) {
isNew = false;
int existingTypeId = existingCall.getCallTypeId();
int newTypeId = callItem.getCallTypeId();
if (existingTypeId != newTypeId || !liveCall.getForwardingBleRuName().equals("")) {
isUpdate = true;
}
}
if (isNew ^ isUpdate) {
Log.i("LIVECALL", "Database to be updated!");
if (isNew) {
liveCallDao.insert(liveCall);
} else {
liveCallDao.update(liveCall);
}
notificationManager.notify(liveCall.getCallerTagId(), createNotification(pendingIntent, callItem));
}
}
}
for (int i = 0, existingLiveCallSize = pendingRemovalCalls.size(); i < existingLiveCallSize; i++) {
LiveCall liveCall = pendingRemovalCalls.valueAt(i);
liveCallDao.delete(liveCall);
notificationManager.cancel(liveCall.getCallerTagId());
}
handler.postDelayed(this, RUNNABLE_DELAY);
}
}
@Override
public void onCreate() {
AndroidInjection.inject(this);
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
looper = thread.getLooper();
handler = new Handler(looper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Runnable runnable = new LiveCallService.NurseCallRunnable();
handler.post(runnable);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
looper.quit();
}
private Notification createNotification(PendingIntent intent, @NonNull CallItem callItem) {
String shortContent = callItem.getCalledBy();
String bigContent = callItem.getCallType() + "\n" + DateFormat.getDateTimeInstance().format(callItem.getReceivedTime());
String channelId;
//High priority call
if (callItem.getPriority() == 1) {
channelId = NotificationActivity.NURSE_CALL_CHANNEL_ID;
} else {
channelId = NotificationActivity.HIGH_PRIORITY_CHANNEL_ID;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(shortContent)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(bigContent))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(intent);
if (callItem.getColour() != null && !callItem.getColour().isEmpty()) {
builder.setColorized(true);
builder.setColor(callItem.getColourCode());
}
//Sounds, light colour & vibration pattern are handled by Notification Channel on Oreo and up
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
long[] vibrationPattern;
int colour;
//High priority call
if (callItem.getPriority() == 1) {
vibrationPattern = new long[] {0, 300};
colour = Color.RED;
} else {
vibrationPattern = new long[] {0, 200, 200, 200};
colour = Color.WHITE;
}
builder.setVibrate(vibrationPattern)
.setLights(colour, 500, 200);
if (callItem.getSound() != null && !callItem.getSound().isEmpty()) {
builder.setSound(callItem.getSoundUri());
}
}
Notification notification = builder.build();
notification.flags |= Notification.FLAG_ONGOING_EVENT;
if (callItem.getPriority() == 1) {
notification.flags |= Notification.FLAG_INSISTENT;
}
return notification;
}
}