消息聊天效果不佳

Message chat doesn't work well

通过我的应用程序,我从我的智能手机(称为 PHONE1)向另一个(称为 PHONE2)发送消息和通知。消息由 PHONE2 接收。 PHONE2 向 PHONE1 发送回复消息。 PHONE1 收到通知并阅读消息。 PHONE1 向 PHONE2 发送回复消息,应用程序崩溃。

Messages.java:

public class Messages extends AppCompatActivity {

private static final String TAG = "ChatActivity";

Context context;
private ChatArrayAdapter chatArrayAdapter;
private ListView listView;
private EditText chatText;
private Button buttonSend;
private boolean side = false;
String from, to, mess;


@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_messages);


    buttonSend = (Button) findViewById(R.id.send);

    listView = (ListView) findViewById(R.id.chatMessage);

    chatArrayAdapter = new ChatArrayAdapter(getApplicationContext(), R.layout.row_messages_right);
    listView.setAdapter(chatArrayAdapter);

    chatText = (EditText) findViewById(R.id.msg);
    chatText.setOnKeyListener(new View.OnKeyListener() {
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                return sendChatMessage();
            }
            return false;
        }
    });
    buttonSend.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            sendChatMessage();
        }
    });

    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
    listView.setAdapter(chatArrayAdapter);

    //to scroll the list view to bottom on data change
    chatArrayAdapter.registerDataSetObserver(new DataSetObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            listView.setSelection(chatArrayAdapter.getCount() - 1);
        }
    });


    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            receiveChatMessage();
        }
    }, 0, 1000);



}





@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private boolean receiveChatMessage(){
        InternalDatabaseOperations DB = new InternalDatabaseOperations(this);
        Cursor CR = DB.getInformation(DB);
        CR.moveToLast();
        from = CR.getString(0);
        to = CR.getString(1);
    if(!Objects.equals(mess, CR.getString(2))){
        mess = CR.getString(2);
        chatArrayAdapter.add(new ChatMessage(!side, mess));
        return true;
    }
        return false;
}

//Send chat message
private boolean sendChatMessage() {
    mess = chatText.getText().toString();
    chatArrayAdapter.add(new ChatMessage(side,mess));
    chatText.setText("");
        InternalDatabaseOperations DB = new InternalDatabaseOperations(this);
        Cursor CR = DB.getInformation(DB);
        CR.moveToLast();
        from = CR.getString(1);
        to = CR.getString(0);
        BackgroundTaskSendingMessage sendingMessage = new BackgroundTaskSendingMessage(this);
          sendingMessage.execute(to, from, mess);
            return true;

    } }

chatArrayAdapter.java:

class ChatArrayAdapter extends ArrayAdapter<ChatMessage> {

private TextView chatText;
private List<ChatMessage> chatMessageList = new ArrayList<>();
private Context context;

@Override
public void add(ChatMessage object) {
    chatMessageList.add(object);
    super.add(object);
}

public ChatArrayAdapter(Context context, int textViewResourceId) {
    super(context, textViewResourceId);
    this.context = context;
}

public int getCount() {
    return this.chatMessageList.size();
}

//Retrieve message position
public ChatMessage getItem(int index) {
    return this.chatMessageList.get(index);
}

//Change layout inflater if necessary
public View getView(int position, View convertView, ViewGroup parent) {
    ChatMessage chatMessageObj = getItem(position);
    View row = convertView;
    LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (chatMessageObj.left) {
        row = inflater.inflate(R.layout.row_messages_right, parent, false);
    }else{
        row = inflater.inflate(R.layout.row_messages_left, parent, false);
     }
        chatText = (TextView) row.findViewById(R.id.msgr);
        chatText.setText(chatMessageObj.message);
        return row;
    }
}

消息服务通知:

    public class MyFirebaseMessagingService extends FirebaseMessagingService{


    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {

        String message = remoteMessage.getData().get("message");
        String from = remoteMessage.getData().get("From");
        String to = remoteMessage.getData().get("to");
        InternalDatabaseOperations DB = new InternalDatabaseOperations(this);
        DB.putInformation(DB, from, to, message);

        showNotification(message);
    }

    private void showNotification(String message) {

        Intent i = new Intent(this, Messages.class);
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setAutoCancel(true)
                .setContentTitle("BookStore")
                .setContentText(message)
                .setSmallIcon(R.drawable.book)
                .setContentIntent(pendingIntent);

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.notify(0, builder.build());

    }


}

logcat:

 14007-14681/gamingproject.sellmybooks E/AndroidRuntime: FATAL EXCEPTION: Timer-0
                                                                               Process: gamingproject.sellmybooks, PID: 14007
                                                                               android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                                   at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6353)
                                                                                   at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:875)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.view.View.requestLayout(View.java:17524)
                                                                                   at android.widget.AbsListView.requestLayout(AbsListView.java:2027)
                                                                                   at android.widget.AbsListView.setSelectionFromTop(AbsListView.java:7045)
                                                                                   at android.widget.ListView.setSelection(ListView.java:2018)
                                                                                   at gamingproject.sellmybooks.Messages.onChanged(Messages.java:74)
                                                                                   at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
                                                                                   at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
                                                                                   at android.widget.ArrayAdapter.notifyDataSetChanged(ArrayAdapter.java:286)
                                                                                   at android.widget.ArrayAdapter.add(ArrayAdapter.java:182)
                                                                                   at gamingproject.sellmybooks.ChatArrayAdapter.add(ChatArrayAdapter.java:22)
                                                                                   at gamingproject.sellmybooks.Messages.receiveChatMessage(Messages.java:104)
                                                                                   at gamingproject.sellmybooks.Messages.access0(Messages.java:23)
                                                                                   at gamingproject.sellmybooks.Messages.run(Messages.java:83)
                                                                                   at java.util.Timer$TimerImpl.run(Timer.java:284)

提前感谢您的帮助。

问题是您的计时器正在使用后台线程,而您正在尝试从该后台线程更新您的 ChatArrayAdapter。

来自the documentation

Corresponding to each Timer object is a single background thread that is used to execute all of the timer's tasks, sequentially.

如果您保留定时器的代码:

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        receiveChatMessage();
    }
}, 0, 1000);

看起来您只需要将向适配器添加项目的代码放在 UI 线程上:

private boolean receiveChatMessage() {
        InternalDatabaseOperations DB = new InternalDatabaseOperations(this);
        Cursor CR = DB.getInformation(DB);
        CR.moveToLast();
        from = CR.getString(0);
        to = CR.getString(1);
    if(!Objects.equals(mess, CR.getString(2))){
        mess = CR.getString(2);

        //modified:
        runOnUiThread(new Runnable() {              
          @Override
          public void run() {
            chatArrayAdapter.add(new ChatMessage(!side, mess));
          }
        });

        CR.close(); //close your cursor to avoid memory leaks!
        return true;
    }
    CR.close(); //close your cursor to avoid memory leaks!
    return false;
}