电商APP基于FCM推送:如何存储通知并在RecyclerView中展示
实现FCM通知本地存储与RecyclerView展示方案
1. 定义通知数据模型
先创建结构化的通知实体类,包含展示和存储所需的核心字段:
// Kotlin示例,Java可直接转换为对应实体类 data class NotificationModel( @PrimaryKey(autoGenerate = true) val id: Int = 0, val title: String, val content: String, val timestamp: Long, val isRead: Boolean = false )
2. 本地持久化存储(Room数据库)
推荐用Room处理结构化通知数据,比SharedPreferences更适合多数据的查询、排序和更新操作:
2.1 创建Dao接口
@Dao interface NotificationDao { @Insert suspend fun insertNotification(notification: NotificationModel) @Query("SELECT * FROM NotificationModel ORDER BY timestamp DESC") fun getAllNotifications(): Flow<List<NotificationModel>> // 用Flow监听数据变化,自动更新列表 @Update suspend fun updateNotification(notification: NotificationModel) }
2.2 构建Room数据库实例
@Database(entities = [NotificationModel::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun notificationDao(): NotificationDao companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "app_notification_db" ).build() INSTANCE = instance instance } } } }
3. 处理FCM消息接收与存储
在自定义的FirebaseMessagingService中捕获通知,并存入Room:
class MyFirebaseMessagingService : FirebaseMessagingService() { private lateinit var notificationDao: NotificationDao override fun onCreate() { super.onCreate() val db = AppDatabase.getDatabase(this) notificationDao = db.notificationDao() } override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) remoteMessage.notification?.let { fcmNotification -> val title = fcmNotification.title ?: "促销通知" val content = fcmNotification.body ?: "" val timestamp = System.currentTimeMillis() // 用协程异步插入,避免阻塞主线程 CoroutineScope(Dispatchers.IO).launch { notificationDao.insertNotification( NotificationModel( title = title, content = content, timestamp = timestamp ) ) } // 可选:弹出系统通知,点击跳转至通知列表页 showSystemNotification(title, content) } } private fun showSystemNotification(title: String, content: String) { val channelId = "promotion_notification_channel" val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Android O及以上需创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "促销通知", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val intent = Intent(this, NotificationListActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val builder = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(content) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setAutoCancel(true) notificationManager.notify(System.currentTimeMillis().toInt(), builder.build()) } }
别忘了在AndroidManifest.xml中注册该Service:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
4. 用RecyclerView展示通知列表
4.1 创建列表Adapter
class NotificationAdapter( private val notifications: List<NotificationModel>, private val onItemClick: (NotificationModel) -> Unit ) : RecyclerView.Adapter<NotificationAdapter.NotificationViewHolder>() { inner class NotificationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvTitle: TextView = itemView.findViewById(R.id.tv_notification_title) val tvContent: TextView = itemView.findViewById(R.id.tv_notification_content) val tvTime: TextView = itemView.findViewById(R.id.tv_notification_time) val unreadDot: View = itemView.findViewById(R.id.view_unread_dot) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_notification, parent, false) return NotificationViewHolder(view) } override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) { val notification = notifications[position] holder.tvTitle.text = notification.title holder.tvContent.text = notification.content holder.tvTime.text = formatTimestamp(notification.timestamp) // 未读显示红点,已读隐藏 holder.unreadDot.visibility = if (notification.isRead) View.GONE else View.VISIBLE holder.itemView.setOnClickListener { onItemClick(notification) } } override fun getItemCount() = notifications.size // 格式化时间显示 private fun formatTimestamp(timestamp: Long): String { val now = System.currentTimeMillis() val diff = now - timestamp val minutes = diff / (1000 * 60) return when { minutes < 1 -> "刚刚" minutes < 60 -> "${minutes}分钟前" minutes < 1440 -> "${minutes/60}小时前" else -> SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(timestamp)) } } }
4.2 列表项布局(item_notification.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="12dp" android:gravity="center_vertical"> <View android:id="@+id/view_unread_dot" android:layout_width="8dp" android:layout_height="8dp" android:background="@drawable/circle_red" android:layout_marginEnd="12dp"/> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_notification_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16sp" android:textStyle="bold"/> <TextView android:id="@+id/tv_notification_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="@color/gray_600" android:layout_marginTop="4dp"/> <TextView android:id="@+id/tv_notification_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="@color/gray_400" android:layout_marginTop="4dp" android:layout_gravity="end"/> </LinearLayout> </LinearLayout>
4.3 通知列表页逻辑
class NotificationListActivity : AppCompatActivity() { private lateinit var notificationDao: NotificationDao override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_notification_list) val db = AppDatabase.getDatabase(this) notificationDao = db.notificationDao() val recyclerView = findViewById<RecyclerView>(R.id.recycler_view_notifications) recyclerView.layoutManager = LinearLayoutManager(this) // 监听数据库变化,自动更新列表 lifecycleScope.launch { notificationDao.getAllNotifications().collect { notifications -> val adapter = NotificationAdapter(notifications) { notification -> // 点击后标记为已读 lifecycleScope.launch(Dispatchers.IO) { notificationDao.updateNotification(notification.copy(isRead = true)) } } recyclerView.adapter = adapter } } } }
内容的提问来源于stack exchange,提问作者Tayyba Rani
相关产品推荐
相关产品推荐

