Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Recyclerview适配器';s NOTIFYDATASETCHANGE未更新RecyclerView_Java_Android_Sql_Arrays_Database - Fatal编程技术网

Java Recyclerview适配器';s NOTIFYDATASETCHANGE未更新RecyclerView

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 ")

我有一个带有firebase实时数据库和本地SQLite数据库的聊天应用程序
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
语句,my
RecyclerView
屏幕也不会更新,而是在后台,如果我键入或接收到它保存到本地数据库但不显示在屏幕上的任何消息

Chat
RecyclerView
在以下情况下有效

  • 当我从最近的应用程序中清除应用程序时
  • 停止从本地数据库提取数据
  • 首次推出聊天屏幕
  • 当我从通知中直接进入聊天屏幕时,我面临着这个问题

    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 inside
    onLoadFinished
    功能

    我在我的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”
    解决了我的问题。只有当活动不存在时才创建活动。现在,当点击通知时,它不会调用activity
    ondestory
    方法。它是从
    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