Android列表显示服务器新增项时触发本地通知的问题求助
问题解决:服务器新增事件时触发本地通知,避免启动Activity时重复弹出
问题分析
你当前的代码存在几个核心问题导致通知异常弹出:
- 循环逻辑错误:
for(int i = result.items.data.size(); i <= result.items.data.size(); i++)这个循环仅执行一次,且i > result.items.data.size()的判断永远不成立,同时你初始化了空的Event item,通知内容会为空 - 未区分新增事件和已有事件:每次启动Activity拉取数据时,没有判断哪些是服务器新增的,导致只要有数据就可能触发通知逻辑
- 通知触发条件错误:没有基于“新事件”的判断逻辑,而是错误的循环条件触发
解决方案
要实现仅在服务器新增事件时触发通知,核心是记录本地已处理过的最新事件ID,每次拉取数据后对比筛选出新增事件,再针对这些事件发送通知。
步骤1:本地存储最新事件ID
使用SharedPreferences存储最后一次处理的最大事件ID,用来区分新旧事件。
步骤2:拉取数据后筛选新增事件
在Retrofit的success回调中,对比服务器返回的事件ID和本地存储的ID,找出所有ID更大的事件(即新增事件)。
步骤3:对新增事件发送通知
仅针对新增事件构建并发送通知,同时更新本地存储的最大ID。
步骤4:修正ListView的30条限制逻辑
确保ListView最多显示30条,新增项加入时移除最早的项。
修改后的代码示例
1. EventsActivity修改
public class EventsActivity extends AppCompatActivity { @Bind(R.id.back) View back; @Bind(R.id.list) ListView list; @Bind(R.id.clearAllEvents) View clearAllEvents; @Bind(R.id.content_layout) View content_layout; @Bind(R.id.loading_layout) View loading_layout; @Bind(R.id.nodata_layout) View nodata_layout; @Bind(R.id.search) View search; String searchtext; private SharedPreferences prefs; private static final String KEY_LAST_EVENT_ID = "last_event_id"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_events); ButterKnife.bind(this); prefs = getSharedPreferences("EventPrefs", MODE_PRIVATE); final String api_key = (String) DataSaver.getInstance(EventsActivity.this).load("api_key"); final EventsAdapter adapter = new EventsAdapter(this); list.setAdapter(adapter); loading_layout.setVisibility(View.VISIBLE); API.getApiInterface(this).getEvents(api_key, getResources().getString(R.string.lang), 0, new Callback<ApiInterface.GetEventsResult>() { @Override public void success(ApiInterface.GetEventsResult result, Response response) { loading_layout.setVisibility(View.GONE); if(result.items.data.size() != 0) { content_layout.setVisibility(View.VISIBLE); // 处理ListView最多30条逻辑:保留最新的30条,移除旧项 ArrayList<Event> events = result.items.data; if(events.size() > 30) { events = new ArrayList<>(events.subList(0, 30)); } adapter.setArray(events); // 处理新增事件通知 processNewEvents(events); } else { nodata_layout.setVisibility(View.VISIBLE); } } @Override public void failure(RetrofitError retrofitError) { Toast.makeText(EventsActivity.this, R.string.errorHappened, Toast.LENGTH_SHORT).show(); } }); } private void processNewEvents(ArrayList<Event> events) { int lastProcessedId = prefs.getInt(KEY_LAST_EVENT_ID, 0); int newMaxId = lastProcessedId; // 遍历事件,筛选出新增的(ID大于本地记录的最后ID) for(Event event : events) { if(event.id > lastProcessedId) { sendEventNotification(event); // 更新最大ID if(event.id > newMaxId) { newMaxId = event.id; } } } // 更新本地存储的最新事件ID if(newMaxId != lastProcessedId) { prefs.edit().putInt(KEY_LAST_EVENT_ID, newMaxId).apply(); } } private void sendEventNotification(Event event) { String channelId = "notification_channel_1"; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 创建Android O及以上的通知渠道 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ if(notificationManager != null && notificationManager.getNotificationChannel(channelId) == null){ NotificationChannel notificationChannel = new NotificationChannel( channelId, "事件通知", NotificationManager.IMPORTANCE_HIGH ); notificationChannel.setDescription("服务器新增事件通知"); notificationChannel.enableLights(true); notificationChannel.enableVibration(true); notificationManager.createNotificationChannel(notificationChannel); } } // 构建通知跳转Intent Intent intent = new Intent(this, EventsActivity.class); intent.putExtra("event", new Gson().toJson(event)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), event.id, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); // 构建通知内容 NotificationCompat.Builder builder = new NotificationCompat.Builder( getApplicationContext(), channelId ); builder.setSmallIcon(R.drawable.ic_notification_original); builder.setDefaults(NotificationCompat.DEFAULT_ALL); builder.setContentTitle("GPSTrackr"); builder.setContentText(String.format("%s: %s", event.device_name, event.message)); builder.setContentIntent(pendingIntent); builder.setAutoCancel(true); builder.setPriority(NotificationCompat.PRIORITY_MAX); // 发送通知,用事件ID作为通知ID避免重复覆盖 if (notificationManager != null) { notificationManager.notify(event.id, builder.build()); } } }
2. EventsAdapter修改(简化数组处理逻辑)
public class EventsAdapter extends AwesomeAdapter<Event> { public EventsAdapter(Context context) { super(context); } @Override public void setArray(ArrayList<Event> array) { super.setArray(array); } @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null) convertView = getLayoutInflater().inflate(R.layout.adapter_events, null); Event item = getItem(position); TextView device_name = (TextView) convertView.findViewById(R.id.device_name); device_name.setText(item.device_name); TextView date = (TextView) convertView.findViewById(R.id.date); date.setText(item.time); TextView message = (TextView) convertView.findViewById(R.id.message); message.setText(item.message); TextView geofence_name = (TextView) convertView.findViewById(R.id.geofence_name); geofence_name.setText(item.geofence_name); return convertView; } }
关键优化点
- 用
SharedPreferences记录已处理的最大事件ID,确保仅对新增事件发送通知 - 修正通知逻辑:使用事件ID作为通知ID,避免重复通知被覆盖,同时传递正确的事件数据
- 完善ListView的30条限制逻辑:当返回数据超过30条时,只保留最新的30条
- 移除无效的循环和错误的条件判断,简化通知触发逻辑
内容的提问来源于stack exchange,提问作者Leyoussi Lauriel
相关产品推荐
相关产品推荐

