Android Push notifications not working when app is closed

孤街浪徒 提交于 2019-12-22 08:09:24

问题


I'm using OkSse to subscribe to my Server Sent Events.

Whenever a new message is sent by the server, a notification should appear whether the app is in foreground,minimized or fully closed.

The notifications work as expected when minimized or in foreground but when fully closed, this only works on some device brands.

Researching a bit, I found that:

this issue is noticed only on phones by manufacturers like Xiaomi, Oppo, One Plus, Vivo, Lenovo, Huawei, Samsung, and a few others.

How does WhatsApp, Facebook, Slack, Gmail, etc.. display notifications even if app is closed?

I don't use FCM, just subscribe to Sse.

I also have in manifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver android:name="com.goparty.communication.NotificationsAlarmReceiver">
    <intent-filter>
        <action android:name="android.media.action.DISPLAY_NOTIFICATION" />
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

How can I achieve this?

Update

I managed to make it work by using an IntentService and BroadcastReceiver but this crashes for Oreo versions.

After more reading I found that JobIntentService is the replacement of IntentService when targeting Android O and above.

The transition to JobIntentService was pretty straight forward but again, my solution is not working when I close the app.

The whole purpose of notifications are to work even when the app is closed.

Using IntentService worked in the background by sending a broadcast with a new intent of my service.

I'm doing the same with JobIntentService with no luck.

Here's my code:

public class NotificationService extends JobIntentService {

    private static final int JOB_ID = 1;
    private NotificationManager notificationManager;

    public static void enqueueWork(Context context, Intent intent) {
        enqueueWork(context, NotificationService.class, JOB_ID, intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Logger.logGoParty(getClass().getSimpleName() + "#onCreate");
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Logger.logGoParty("Running in background.");
        sendBroadcast(new Intent(getApplicationContext(), NotificationService.class));
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        Logger.logGoParty(getClass().getSimpleName() + "#onHandleWork");
        Logger.logGoParty("Sse initialized");

        Request request = new Request.Builder().url("the sse url").build();
        OkSse okSse = new OkSse();

        ServerSentEvent sse = okSse.newServerSentEvent(request, new ServerSentEvent.Listener() {
            @Override
            public void onOpen(ServerSentEvent sse, Response response) {
                Logger.logGoParty(response.toString());
            }

            @Override
            public void onMessage(ServerSentEvent sse, String id, String event, String message) {
                Logger.logGoParty("Sse#onMessage: " + message);
                try {
                    JSONObject promoJson = new JSONObject(message);

                    sendNotification(...);

                } catch (JSONException e) {
                    Logger.logGoParty("JSONException: " + e.toString());
                }
            }
                // ...
        });
    }

    private void sendNotification(String promoImageUrl, String notificationContent, String title) {

        try {
            URL url = new URL(promoImageUrl);
            Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());


            NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
            style.bigPicture(bitmap);

            Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

            Intent intent = new Intent(this, MapsActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            intent.putExtra("page-to-open", "promotions");
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);


            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, GoParty.PROMO_CHANNEL_ID)
                    .setSmallIcon(R.mipmap.ic_launcher_round)
                    .setContentTitle(title)
                    .setAutoCancel(true)
                    .setSound(defaultSound)
                    .setContentText(notificationContent)
                    .setContentIntent(pendingIntent)
                    .setStyle(style)
                    .setLargeIcon(bitmap)
                    .setWhen(System.currentTimeMillis())
                    .setPriority(NotificationCompat.PRIORITY_HIGH)
                    .setCategory(NotificationCompat.CATEGORY_MESSAGE);

            notificationManager.notify(0, notificationBuilder.build());

            Log.i("GoParty", "Notification sent ----- ");

        } catch (MalformedURLException e) {
            Logger.logGoParty(e.toString());
        } catch (IOException e) {
            Logger.logGoParty(e.toString());
        }
    }
}

Notification Receiver:

public class NotificationReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        Intent notificationIntent = new Intent(context, NotificationService.class);
        NotificationService.enqueueWork(context, notificationIntent);
    }
}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.goparty">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />


    <application
        android:name=".scopes.application.GoParty"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps"
            android:screenOrientation="portrait">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.goparty.LoginActivity" />
        </activity>

        <service
            android:name=".jobs.NotificationService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

        <receiver android:name=".receivers.NotificationReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

回答1:


Main Reason behind this is AutoLaunch and Battery Optimization(MIUI) devices try to change don't optimize option for your app in Battery setting

 private void initOPPO() {
        try {

            Intent i = new Intent(Intent.ACTION_MAIN);
            i.setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.floatwindow.FloatWindowListActivity"));
            startActivity(i);
        } catch (Exception e) {
            e.printStackTrace();
            try {

                Intent intent = new Intent("action.coloros.safecenter.FloatWindowListActivity");
                intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.floatwindow.FloatWindowListActivity"));
                startActivity(intent);
            } catch (Exception ee) {

                ee.printStackTrace();
                try {

                    Intent i = new Intent("com.coloros.safecenter");
                    i.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity"));
                    startActivity(i);
                } catch (Exception e1) {

                    e1.printStackTrace();
                }
            }

        }
    }

    private static void autoLaunchVivo(Context context) {
        try {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.iqoo.secure",
                    "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity"));
            context.startActivity(intent);
        } catch (Exception e) {
            try {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.vivo.permissionmanager",
                        "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
                context.startActivity(intent);
            } catch (Exception ex) {
                try {
                    Intent intent = new Intent();
                    intent.setClassName("com.iqoo.secure",
                            "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager");
                    context.startActivity(intent);
                } catch (Exception exx) {
                    ex.printStackTrace();
                }
            }
        }
    }

Usage

if (Build.MANUFACTURER.equalsIgnoreCase("oppo")) {
                                initOPPO();
                            } else if (Build.MANUFACTURER.equalsIgnoreCase("vivo")) {
                                autoLaunchVivo(BaseSettingActivity.this);
                            } else if (Build.MANUFACTURER.equalsIgnoreCase("xiaomi")) {
                                try {
                                    Intent intent = new Intent();
                                    intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
                                    startActivity(intent);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }



回答2:


Your Approach: Using OkSse for notification means the need of ever running background service.

Problem 1: Android background service restrictions link

JobIntentService internally will use JobService and it will not be a continues running service. It will not run indifferently as execution time limit will apply. It will stop and rescheduled to continue its execution later. So in your case, it will not work.

Problem 2: Devices Xiaomi, Oppo, One Plus, Vivo, Lenovo, Huawei, Samsung have Battery Optimization check which lets JobIntentService to stop.

Solution:

1) Run Foreground service which is ugly and not recommended

2) Firebase High Priority Notification link HIGHLY RECOMMENDED

I have implemented it on the above mention devices, even if the app is set to battery optimization mode you will receive a notification.



来源:https://stackoverflow.com/questions/55719666/android-push-notifications-not-working-when-app-is-closed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!