Java Recyclerview适配器';s NOTIFYDATASETCHANGE未更新RecyclerView
我有一个带有firebase实时数据库和本地SQLite数据库的聊天应用程序Java Recyclerview适配器';s NOTIFYDATASETCHANGE未更新RecyclerView,java,android,sql,arrays,database,Java,Android,Sql,Arrays,Database,我有一个带有firebase实时数据库和本地SQLite数据库的聊天应用程序RecyclerView。我正在将消息保存到本地数据库(SQLite),然后调用adapter.notifyDataSetChanged() 如果消息已经在数据库中(消息唯一id),则SQLite将在数据库insertOrThrow方法上返回0。我正在像这样检查可用性 if (id == 0) { Log.d(TAG,"Database Already Has Value Of This Random Id ")
RecyclerView
。我正在将消息保存到本地数据库(SQLite),然后调用adapter.notifyDataSetChanged()
如果消息已经在数据库中(消息唯一id),则SQLite将在数据库insertOrThrow
方法上返回0。我正在像这样检查可用性
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
} else {
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
adapter.notifyDataSetChanged();
}
但是,即使调用了else
语句,myRecyclerView
屏幕也不会更新,而是在后台,如果我键入或接收到它保存到本地数据库但不显示在屏幕上的任何消息
ChatRecyclerView
在以下情况下有效
public class FireBase_Messaging_Service extends FirebaseMessagingService {
public static final String TAG="###FireBase MSG###";
public static final int NOTIFICATION=5;
String UserName;
String ID;
String Msg;
Map<String,String> data;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG,"From "+remoteMessage.getFrom());
if (remoteMessage.getData().size()>0){
data = remoteMessage.getData();
Log.d(TAG,"Message Data "+remoteMessage.getData());
data = remoteMessage.getData();
UserName = data.get("name");
ID = data.get("ID");
Msg = data.get("Message");
showNotification(Msg,ID,UserName);
}
if (remoteMessage.getNotification()!=null){
Log.d(TAG,"Message Notification Body "+remoteMessage.getNotification().getBody());
// Toast.makeText(this, "Notification "+remoteMessage.getNotification().getBody(), Toast.LENGTH_LONG).show();
}
}
private void showNotification(String Message,String ID,String UserName) {
Log.d(TAG,"Show Notification "+Message+" "+ID);
Intent intent=new Intent(this, Navigation_Drawer.class);
intent.putExtra("Type","Text");
//intent.putExtra("Type",MsgType);
intent.putExtra("ID",ID);
intent.putExtra("uname",UserName);
intent.putExtra("Message",Msg);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(this,NOTIFICATION,intent,PendingIntent.FLAG_UPDATE_CURRENT);
int color = getResources().getColor(R.color.black);
String ChannelID = "Message";
notificationChannel(ChannelID,"Chat");
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),ChannelID)
.setSmallIcon(R.drawable.default_x)
.setColor(color)
.setContentTitle(UserName)
.setContentText(Message)
.setChannelId(ChannelID)
.setTicker("My App")
.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND | Notification.FLAG_SHOW_LIGHTS)
.setLights(0xff00ff00, 1000, 500) // To change Light Colors
.setStyle(new NotificationCompat.BigTextStyle().bigText(Message))//For Expandable View
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify(NOTIFICATION,builder.build());
}
@Override
public void onDeletedMessages() {
super.onDeletedMessages();
}
private void notificationChannel (String ChannelID, String channelName) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(ChannelID,channelName, NotificationManager.IMPORTANCE_DEFAULT);
channel.setLightColor(Color.GREEN);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
}
}
这就是我通常加载聊天片段的方式
getFragmentManager().beginTransaction().add(R.id.Navigation\u Drawer,chatFragment.commit()代码>
使用Asynctask
((Navigation_Drawer)context).getFragmentManager().beginTransaction().replace(R.id.Navigation_Drawer, chatFragment).commit();
这就是我从通知中启动ChatFragment的方式
public class FireBase_Messaging_Service extends FirebaseMessagingService {
public static final String TAG="###FireBase MSG###";
public static final int NOTIFICATION=5;
String UserName;
String ID;
String Msg;
Map<String,String> data;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG,"From "+remoteMessage.getFrom());
if (remoteMessage.getData().size()>0){
data = remoteMessage.getData();
Log.d(TAG,"Message Data "+remoteMessage.getData());
data = remoteMessage.getData();
UserName = data.get("name");
ID = data.get("ID");
Msg = data.get("Message");
showNotification(Msg,ID,UserName);
}
if (remoteMessage.getNotification()!=null){
Log.d(TAG,"Message Notification Body "+remoteMessage.getNotification().getBody());
// Toast.makeText(this, "Notification "+remoteMessage.getNotification().getBody(), Toast.LENGTH_LONG).show();
}
}
private void showNotification(String Message,String ID,String UserName) {
Log.d(TAG,"Show Notification "+Message+" "+ID);
Intent intent=new Intent(this, Navigation_Drawer.class);
intent.putExtra("Type","Text");
//intent.putExtra("Type",MsgType);
intent.putExtra("ID",ID);
intent.putExtra("uname",UserName);
intent.putExtra("Message",Msg);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(this,NOTIFICATION,intent,PendingIntent.FLAG_UPDATE_CURRENT);
int color = getResources().getColor(R.color.black);
String ChannelID = "Message";
notificationChannel(ChannelID,"Chat");
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),ChannelID)
.setSmallIcon(R.drawable.default_x)
.setColor(color)
.setContentTitle(UserName)
.setContentText(Message)
.setChannelId(ChannelID)
.setTicker("My App")
.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND | Notification.FLAG_SHOW_LIGHTS)
.setLights(0xff00ff00, 1000, 500) // To change Light Colors
.setStyle(new NotificationCompat.BigTextStyle().bigText(Message))//For Expandable View
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify(NOTIFICATION,builder.build());
}
@Override
public void onDeletedMessages() {
super.onDeletedMessages();
}
private void notificationChannel (String ChannelID, String channelName) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(ChannelID,channelName, NotificationManager.IMPORTANCE_DEFAULT);
channel.setLightColor(Color.GREEN);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
}
}
我认为您需要在这里实现LoaderCallbacks
,并监听数据库中所做的更改,从而相应地更新RecyclerView
。我在这里创建了一个显示数据库读、写和更新以及在数据库中进行更改的视图,以在相应的RecyclerView
中产生适当的效果,并向数据库注册内容观察者
// FETCH MESSAGE FROM DATABASE
chatDatabase();
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
整个想法是在数据库表中附加一个观察者,它将通知您表中的任何更改。当检测到更改时,将自动生成数据库读取查询,再次从表中重新加载数据时,将触发RecyclerView
update insideonLoadFinished
功能
我在我的Github项目中有一个演示实现,它显示了要在RecyclerView
中显示的用户列表,并自动更新RecyclerView
,而无需调用notifyDataSetChanged
。请再次检查控制数据显示在RecyclerView
中的适配器。这是您在案例中可能拥有的最简单的实现,避免了任何复杂性
希望有帮助
更新
在阅读了代码之后,我提出了一些您应该做的修复。很难理解为什么它不能像您预期的那样工作,因为给出的代码格式和完整性都不是很好。不过,我可以在这里向您推荐一些可能会有所帮助的关键修复
在append\u chat\u conversation
功能中,您不必每次发现新的聊天信息并将其添加到信息中时都调用notifyDataSetChanged
。我想建议您,不要将聊天信息添加到您的信息
列表中。您可以去掉以下if-else
块。只需更新聊天数据库并在其中插入新的聊天信息即可。因此,我建议您删除以下部分
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
continue;
} else {
Log.d(TAG,"Database Don't Has Value Of This Random Id ");
Log.d(TAG,"Message Size "+message.size());
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
Log.d(TAG,"Message Size "+message.size());
adapter.notifyDataSetChanged();
Log.d(TAG,"Adapter Notified Data Set "+adapter.getItemCount());
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
在此行之前调用chatDatabase
函数从数据库加载消息
// FETCH MESSAGE FROM DATABASE
chatDatabase();
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
现在修复您的chatDatabase
功能。每次调用chatDatabase
函数时,您将创建一个新的LinearLayoutManager
,并将其分配给您的RecyclerView
。我建议把它也去掉
按如下方式修改您的chatDatabase
功能
private void chatDatabase() {
//Database Init and Filling Adapter
Log.d(TAG, "Chat Database Function");
chat_database = new Chat_Database(getActivity());
chatCursor = chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist = functions.DatabaseExist(getActivity(), "CHAT_DATABASE.DB");
boolean chatItemsCounts = chatCursor.getCount() > 0;
chatCursor.moveToFirst();
Log.d(TAG, "Value At Chat Database " + checkDB_Exist + " " + chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount() > 0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)) {
Log.d(TAG, "Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID = chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID = chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName = chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType = chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message = chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp = chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath = chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom = chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo = chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG, "Value Of Database Message String = " + database_Message);
Log.d(TAG, "Row ID of Database " + database_rowID);
// Check Message Type
Log.d(TAG, "Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(text);
} while (chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
adapter.setChatMessages(message); // Add a public function in your adapter to set the chat messages
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size() - 1);
}
});
Log.d(TAG, "Value Of Database Close or Not " + value);
}
}
适配器中的setChatMessages
函数如下所示
public void setChatMessages(ArrayList<Message> messageList) {
this.message = messageList;
}
public void setChatMessages(ArrayList messageList){
this.message=messageList;
}
我通过深入研究android生命周期解决了这个问题。正如我上面提到的,只有当我从通知中输入ChatFragment时,我才面临这个问题。我是唯一的活动状态问题。代码正在运行。我所做的总是在通知点击时重新创建活动(在Firebase代码中)
在Intent
中挖掘大量代码并更改多个标志后。只有android:launchMode=“singleTask”
解决了我的问题。只有当活动不存在时才创建活动。现在,当点击通知时,它不会调用activityondestory
方法。它是从onStart
开始的活动,避免了要重新创建的活动
我将此标志添加到AndroidManifest的NavigationDrawer活动标记中
<activity
android:name=".Navigation_Drawer"
android:launchMode="singleTask"
android:theme="@style/AppTheme"/>
这类问题对开发人员来说真的很头疼,但我不知道用这一行代码我就能解决这个问题。谢谢大家的帮助。我看不到您的适配器实现,但可以安全地假设它保存了对消息列表的引用,而没有将值复制到新集合中吗?@eimmer请参见onCreateView
最后一行中的适配器实现。我看到了它的初始化位置,而不是实现位置。Chat_Adapter是您自己定义的类,还是由Firebase提供的类?@eimmer是它的自定义适配器而不是Firebase One。请您指点一下,因为它只发生在我们收到通知后进入应用程序时,否则工作正常。当它工作正常时,我提到了上述情况。你能调整一下我们的代码吗?我正在检查。带着结果回来。如果不工作,我会尽量为您提供格式良好的完整代码,这样您就可以很容易地得到它。我没有什么疑问。无论何时收到新消息,都可以获取完整的消息列表。定期调用数据库对设备性能有好处。我正在编写你的代码,我会尽快更新你。非常感谢您的帮助。由于以下原因导致继续崩溃:android.dat