首页 > 解决方案 > 应用程序处于睡眠模式时推送通知的延迟

问题描述

尝试轮询 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;
    }
}

标签: javaandroid

解决方案


推荐阅读