问题
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