我看到了類似這樣的問題,但沒有一個解決了我的問題。 我通過AlarmManager開始後臺服務。每次在服務啓動,如果它已在SharedPreferences被禁用,如果沒有,就重新安排自身的新實例,它會檢查和推移,以下這些替代路徑:Android - 已取消的通知不斷重現
-
如果用戶想要使用GPS
- ,它等待用戶的位置並使用 調用REST端點;
- 如果用戶不想使用GPS,它將使用存儲在首選項中的位置。
HTTP調用(超時:30秒)的結果是一個JSONObject,其產生0- Ñ通知(取決於它找到多少個「近處的物體」)。
我的問題是:通知,即使被用戶取消(滑動或打開它們),通常會再次出現,就好像它們從未顯示過一樣。它永遠不會發生,因爲Web服務會收到每次更新的排除對象id列表。
下面的代碼:
ScannerService.java
package com.kiulomb.itascanner.service;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.media.RingtoneManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.kiulomb.itascanner.R;
import com.kiulomb.itascanner.network.HTTPRequestManager;
import com.kiulomb.itascanner.network.HTTPResponseListener;
import com.kiulomb.itascanner.network.URLs;
import com.kiulomb.itascanner.pref.FilterPreferencesManager;
import com.kiulomb.itascanner.pref.NotificationsHistoryManager;
import com.kiulomb.itascanner.pref.PrefConstants;
import com.kiulomb.itascanner.utils.Haversine;
import com.kiulomb.itascanner.utils.MyConfiguration;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
public class ScannerService extends Service {
private static final String TAG = ScannerService.class.getSimpleName();
private boolean locationFound = false;
private boolean withoutLocation = false;
private LocationManager mLocationManager = null;
private final Timer myTimer = new Timer();
private final long TIMEOUT = 20000;
TimerTask myTask = new TimerTask() {
public void run() {
try {
Log.i(TAG, "Timeout is over, trying to stop service (location found? " + locationFound + ")");
if (!locationFound) {
stopSelf();
}
} catch (Exception e) {
Log.e(TAG, "Could not stop service after time: " + e.getMessage());
}
}
};
private LocationListener[] mLocationListeners = new LocationListener[] {
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.NETWORK_PROVIDER)
};
private boolean alreadySearching = false;
private class LocationListener implements android.location.LocationListener {
Location mLastLocation;
LocationListener(String provider) {
Log.i(TAG, "LocationListener is " + provider);
mLastLocation = new Location(provider);
}
@Override
public void onLocationChanged(final Location location) {
Log.i(TAG, "onLocationChanged: " + location);
if (withoutLocation) {
return;
}
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
if (location != null) {
if (isConnected) {
mLastLocation.set(location);
locationFound = true;
Log.i(TAG, "already searching? " + alreadySearching);
if (!alreadySearching) {
findClosest(location.getLatitude(), location.getLongitude());
}
alreadySearching = true;
} else {
Log.e(TAG, "no connectivity, ending service");
stopSelf();
}
} else {
Log.e(TAG, "no position, ending service");
stopSelf();
}
}
@Override
public void onProviderDisabled(String provider) {
Log.i(TAG, "onProviderDisabled: " + provider);
}
@Override
public void onProviderEnabled(String provider) {
Log.i(TAG, "onProviderEnabled: " + provider);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.i(TAG, "onStatusChanged: " + provider);
}
}
private void initializeLocationManager() {
Log.d(TAG, "initializeLocationManager");
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
// super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
SharedPreferences pref = getSharedPreferences(PrefConstants.PREF_APP_FILE, MODE_PRIVATE);
if (pref.getBoolean(PrefConstants.PREF_APP_SERVICE_ENABLED, PrefConstants.PREF_APP_SERVICE_ENABLED_DEFAULT)) {
Intent intent = new Intent(ScannerService.this, ScannerService.class);
PendingIntent pintent = PendingIntent.getService(ScannerService.this, 0, intent, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() + 60000, pintent); // or setExact() // TODO custom time
// alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 60000, pintent);
if (!pref.getBoolean(PrefConstants.PREF_APP_SERVICE_CUSTOMCENTER, PrefConstants.PREF_APP_SERVICE_CUSTOMCENTER_DEFAULT)) {
// use GPS
initializeLocationManager();
try {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MyConfiguration.LOCATION_INTERVAL,
MyConfiguration.LOCATION_DISTANCE,
mLocationListeners[1]);
} catch (SecurityException ex) {
Log.e(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e(TAG, "network provider does not exist, " + ex.getMessage());
}
try {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MyConfiguration.LOCATION_INTERVAL,
MyConfiguration.LOCATION_DISTANCE,
mLocationListeners[0]);
} catch (SecurityException ex) {
Log.e(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e(TAG, "gps provider does not exist " + ex.getMessage());
}
} else {
withoutLocation = true;
// do not use GPS
String[] savedNotifCenter = pref.getString(PrefConstants.PREF_APP_SERVICE_CENTER, PrefConstants.PREF_APP_SERVICE_CENTER_DEFAULT).split(",");
double savedLat = Double.parseDouble(savedNotifCenter[0]);
double savedLng = Double.parseDouble(savedNotifCenter[1]);
locationFound = true; // prevent the service from stopping
findClosest(savedLat, savedLng);
}
} else {
stopSelf();
return;
}
/*if (isForeground(getPackageName())) {
Log.i(getClass().getSimpleName(), "application is in foreground, stopping service");
stopSelf();
return;
}*/
myTimer.schedule(myTask, TIMEOUT);
}
public boolean isForeground(String myPackage) {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfo = manager.getRunningTasks(1);
ComponentName componentInfo = runningTaskInfo.get(0).topActivity;
return componentInfo.getPackageName().equals(myPackage);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
if (mLocationManager != null) {
for (LocationListener mLocationListener : mLocationListeners) {
try {
mLocationManager.removeUpdates(mLocationListener);
} catch (SecurityException se) {
Log.e(TAG, "security exception", se);
} catch (Exception ex) {
Log.e(TAG, "fail to remove location listeners, ignore", ex);
}
}
}
}
private void findClosest(final double lat, final double lng) {
new Thread(new Runnable() {
@Override
public void run() {
String url = URLs.buildURL(URLs.NOTIFICATIONS);
url += "?lat=" + lat;
url += "&lng=" + lng;
final SharedPreferences pref = getSharedPreferences(PrefConstants.PREF_APP_FILE, MODE_PRIVATE);
if (pref.contains(PrefConstants.PREF_APP_SERVICE_RADIUS)) {
url += "&radius=" + pref.getInt(PrefConstants.PREF_APP_SERVICE_RADIUS, PrefConstants.PREF_APP_SERVICE_RADIUS_DEFAULT);
}
url += "&limit=" + PrefConstants.PREF_APP_MAP_LIMIT_DEFAULT;
if (pref.contains(PrefConstants.PREF_APP_SERVICE_IV)) {
url += "&iv=" + pref.getInt(PrefConstants.PREF_APP_SERVICE_IV, PrefConstants.PREF_APP_SERVICE_IV_DEFAULT);
}
String exclusionsNumbers = getExcludedNumbersParam();
if (exclusionsNumbers.length() > 0) {
url += "&exNum=" + exclusionsNumbers;
}
final NotificationsHistoryManager notificationsHistoryManager = new NotificationsHistoryManager(ScannerService.this);
final List<Long> excludedIds = notificationsHistoryManager.getAlreadyFoundObjects();
String exclusionsIds = getExcludedIdsParam(excludedIds);
if (exclusionsIds.length() > 0) {
url += "&exId=" + exclusionsIds;
}
/*final long lastId = pref.getLong(PrefConstants.PREF_SERVICE_LAST_ID, 0L);
url += "&li=" + lastId;*/
final Context context = ScannerService.this;
HTTPRequestManager requestManager = new HTTPRequestManager(context, url, true, null, new HTTPResponseListener() {
@Override
public void onSuccess(JSONObject response) {
try {
JSONArray responseArray = response.getJSONArray("objects");
final String foundString = getString(R.string.found);
final String inCityString = getString(R.string.in_city);
final String expiringString = getString(R.string.expiring);
final DateFormat sdf = SimpleDateFormat.getTimeInstance();
final Resources res = getResources();
final String packageName = getPackageName();
final String mapsApiKey = getString(R.string.google_maps_key);
final boolean notifClickAutoCancel = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_NOTIFCANCEL, PrefConstants.PREF_APP_SERVICE_NOTIFCANCEL_DEFAULT);
final boolean notifExpiredAutoCancel = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_NOTIFCANCELEXPIRED, PrefConstants.PREF_APP_SERVICE_NOTIFCANCELEXPIRED_DEFAULT);
final boolean mapPicture = pref.getBoolean(PrefConstants.PREF_APP_SERVICE_MAPPICTURE, PrefConstants.PREF_APP_SERVICE_MAPPICTURE_DEFAULT);
final Locale defaultLocale = Locale.getDefault();
Calendar calendar = Calendar.getInstance();
// long maxId = lastId;
for (int i = 0; i < responseArray.length(); i++) {
try {
final MyEntity p = MyEntity.fromJSONLight(responseArray.getJSONObject(i));
// it should never happen, but notifications are shown many times :/
if (!excludedIds.contains(p.getId())) {
excludedIds.add(p.getId());
// maxId = Math.max(p.getId(), maxId);
final double iv = p.getIV();
final long expirationFixed = (p.getDisappearTime() - System.currentTimeMillis() - 2000);
final Calendar expirationTime = (Calendar) calendar.clone();
// now.add(Calendar.SECOND, (int) ((p.getDisappearTime() - System.currentTimeMillis()/1000) - 2));
expirationTime.setTimeInMillis(expirationTime.getTimeInMillis() + expirationFixed);
final int distance = (int) Math.round(1000 * Haversine.distance(lat, lng, p.getLatitude(), p.getLongitude()));
String cityName = null;
Geocoder gcd = new Geocoder(context, defaultLocale);
List<Address> addresses = gcd.getFromLocation(p.getLatitude(), p.getLongitude(), 1);
if (addresses.size() > 0) {
cityName = addresses.get(0).getLocality();
}
final String cityNameParam = cityName;
new Thread(new Runnable() {
@Override
public void run() {
sendNotification((int) (p.getId()),
foundString + " " + p.getName() + (iv > 0 ? " " + iv + "%" : "") + (cityNameParam != null ? " " + inCityString + " " + cityNameParam : ""),
expiringString + " " + sdf.format(expirationTime.getTime()) + " - " + distance + "m" + (movesStringParam != null ? " (" + movesStringParam + ")" : ""),
p,
res,
packageName,
notifClickAutoCancel,
notifExpiredAutoCancel,
expirationFixed,
mapsApiKey,
mapPicture);
}
}).start();
}
} catch (Exception e) {
Log.e(TAG, "error", e);
}
}
notificationsHistoryManager.saveAlreadyFoundObjects(excludedIds);
stopSelf();
} catch (Exception e) {
Log.e(TAG, "error in reading JSONArray", e);
stopSelf();
}
}
@Override
public void onError(int errorCode) {
stopSelf();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(context);
requestQueue.add(requestManager);
}
}).start();
}
private String getExcludedNumbersParam() {
String exclusionsNumbers = "";
List<Integer> excludedNumbers = new FilterPreferencesManager(ScannerService.this).getNotificationsExcludedNumbers();
int sizeNumbers = excludedNumbers.size();
for (int i = 0; i < sizeNumbers; i++) {
exclusionsNumbers += excludedNumbers.get(i);
if (i < sizeNumbers - 1) {
exclusionsNumbers += ",";
}
}
return exclusionsNumbers;
}
private String getExcludedIdsParam(List<Long> excludedIds) {
String exclusionsIds = "";
int sizeIds = excludedIds.size();
for (int i = 0; i < sizeIds; i++) {
exclusionsIds += excludedIds.get(i);
if (i < sizeIds - 1) {
exclusionsIds += ",";
}
}
return exclusionsIds;
}
private Locale locale = Locale.getDefault();
private void sendNotification(final int notificationId,
final String title,
final String message,
final MyEntity entity,
final Resources res,
final String packageName,
final boolean autoClickCancel,
final boolean autoExpiredCancel,
final long expirationFromNow,
final String mapsApiKey,
final boolean mapPicture) {
final double entityLat = entity.getLatitude();
final double entityLng = entity.getLongitude();
Intent mapIntent = null;
try {
String urlAddress = "http://maps.google.com/maps?q=" + entityLat + "," + entityLng;
mapIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlAddress));
} catch (Exception e) {
Log.e(TAG, "error in notification intent preparation", e);
}
PendingIntent pendingIntent = PendingIntent.getActivity(ScannerService.this, 0, mapIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
int drawable = res.getIdentifier("entity" + String.format(locale, "%04d", entity.getNumber()) + "big", "drawable", packageName);
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(ScannerService.this)
.setSmallIcon(drawable)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(autoClickCancel)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_HIGH)
.setLights(ContextCompat.getColor(ScannerService.this, R.color.colorPrimary), 500, 2000);
if (mapPicture) {
String imageUrl = "https://maps.googleapis.com/maps/api/staticmap"
+ "?center=" + entityLat + "," + entityLng
+ "&zoom=14"
+ "&scale=false"
+ "&size=450x275"
+ "&maptype=roadmap"
+ "&key=" + mapsApiKey
+ "&format=jpg"
+ "&visual_refresh=true";
Log.i(getClass().getSimpleName(), "generated url for notification image: " + imageUrl);
Bitmap bmURL = getBitmapFromURL(imageUrl);
if (bmURL != null) {
notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bmURL));
}
}
if (mapIntent != null) {
notificationBuilder.setContentIntent(pendingIntent);
}
if (autoExpiredCancel) {
Log.i(getClass().getSimpleName(), "setting notification timer for expiration, id: " + notificationId + ", expiring in " + expirationFromNow + "ms");
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Log.i(getClass().getSimpleName(), "canceling notification expired, id: " + notificationId);
notificationManager.cancel(notificationId);
}
}, expirationFromNow);
}
}
// }
private Bitmap getBitmapFromURL(String strURL) {
try {
URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
通知歷史經理
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NotificationsHistoryManager {
private final static String PREF_FILE = "nh";
private final static String PREF_FOUND_KEY = "f";
private SharedPreferences pref;
public NotificationsHistoryManager(Context context) {
pref = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
}
public void saveAlreadyFoundObjects(List<Long> found) {
Set<String> idsString = new HashSet<>();
int size = found.size();
for (int i = Math.max(0, size - 200); i < size; i++) {
long f = found.get(i);
idsString.add(f + "");
}
pref.edit().putStringSet(PREF_FOUND_KEY, idsString).apply();
}
public List<Long> getAlreadyFoundObjects() {
List<Long> excluded = new ArrayList<>();
for (String id : pref.getStringSet(PREF_FOUND_KEY, new HashSet<String>())) {
try {
excluded.add(Long.parseLong(id));
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "error in parsing string '" + id + "' to long id: " + e.getMessage());
}
}
return excluded;
}
public void clean() {
pref.edit().clear().apply();
}
}
注:當MainActivity啓動時,它會檢查服務的一個實例正在運行,如果沒有,則使用AlarmManager安排一個新的。我認爲這是問題的原因,但正如你所看到的那樣,服務每次檢查已經通知的內容並跳過它。 我試着改變START_STICKY爲NOT_STICKY,使用偏好來處理重複的ID,同步操作......我不知道還有什麼要嘗試的。請幫助我:)如果你需要更多的細節,請問。
謝謝!