Чат Android - Firebase, проблемы с задержкой синхронизации - PullRequest
0 голосов
/ 29 февраля 2020

В течение нескольких месяцев я создавал чат для системы Android, используя Firebase в качестве базы данных. Я следовал этому руководству здесь ( ссылка ), и в начале все было хорошо, чат был хорош и не имел никаких задержек. Затем я начал добавлять другие особенности, такие как отображение или нет сообщения и статус участников (онлайн и офлайн), и с этого момента стали проявляться, в частности, три проблемы:

1), когда я измените активность чата на go на другую, а затем вернитесь в чат, макет будет пустым без сообщений, и если вы попытаетесь изменить активность, приложение закроется само. Я обнаружил, что получаю эту ошибку:

E / RecyclerView: адаптер не подключен; пропуск макета

Это файлы, которые составляют часть, относящуюся к чату:

MessageChat. java

public class MessageChat {

private String sender;
private String receiver;
private String msg;
private String currenttime;
private boolean isseen;

public MessageChat(String sender, String receiver, String msg, String currenttime, boolean isseen){
    this.sender = sender;
    this.receiver = receiver;
    this.msg = msg;
    this.currenttime = currenttime;
    this.isseen = isseen;
}

public MessageChat(){}

public String getSender() {
    return sender;
}

public void setSender(String sender) {
    this.sender = sender;
}

public String getReceiver() {return receiver;}

public void setReceiver(String receiver) { this.receiver = receiver;}

public String getMsg() {
    return msg;
}

public void setMsg(String msg) {
    this.msg = msg;
}

public String getCurrenttime() {
    return currenttime;
}

public void setCurrenttime(String currenttime) {
    this.currenttime = currenttime;
}

public boolean isIsseen() {return isseen;}

public void setIsseen(boolean isseen) {this.isseen = isseen;}
}

msgAdapter. java

public class msgAdapter extends RecyclerView.Adapter<msgAdapter.MsgViewHolder> {

public static final int INT_TYPE_LEFT = 0;
public static final int INT_TYPE_RIGHT = 1;

private static List<MessageChat> mChat;
private static Context context;

private FirebaseUser fuser;

public msgAdapter(List<MessageChat> msg, Context context) {
    this.mChat = msg;
    this.context = context;
}

@Override
public msgAdapter.MsgViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == INT_TYPE_RIGHT) {
        View view = LayoutInflater.from(context).inflate(R.layout.right_message, null);
        msgAdapter.MsgViewHolder msgViewHolder = new msgAdapter.MsgViewHolder(view);
        return msgViewHolder;
    }else{
        View view = LayoutInflater.from(context).inflate(R.layout.left_message, null);
        msgAdapter.MsgViewHolder msgViewHolder = new msgAdapter.MsgViewHolder(view);
        return msgViewHolder;
    }
}

@Override
public void onBindViewHolder(final msgAdapter.MsgViewHolder holder, final int position) {
    MessageChat msg = mChat.get(position);
    holder.show_msg.setText(msg.getMsg());

    if ((position == mChat.size()-1) && msg.getSender().equals(fuser.getUid())){
        if (msg.isIsseen()){
            holder.tv_seen.setText(" Seen ");
            holder.tv_seen.setVisibility(View.VISIBLE);
        }else {
            holder.tv_seen.setText(" Sent ");
            holder.tv_seen.setVisibility(View.VISIBLE);
        }
    }else{
        holder.tv_seen.setVisibility(View.GONE);
    }
}

@Override
public int getItemCount() {
    return mChat.size();
}

public static class MsgViewHolder extends RecyclerView.ViewHolder {
    TextView username, show_msg, tv_seen;
    public MsgViewHolder(final View itemView) {
        super(itemView);
        show_msg = (TextView) itemView.findViewById(R.id.show_msg);
        tv_seen = (TextView) itemView.findViewById(R.id.tv_seen);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // item clicked
                MessageChat msg = mChat.get(getAdapterPosition());
                String ctime = msg.getCurrenttime();
                TastyToast.makeText(context, ctime, TastyToast.LENGTH_LONG, TastyToast.INFO);
            }
        });
    }
}

@Override
public int getItemViewType(int position) {
    fuser = FirebaseAuth.getInstance().getCurrentUser();
    if (mChat.get(position).getSender().equals(fuser.getUid())){
        return INT_TYPE_RIGHT;
    }else{
        return INT_TYPE_LEFT;
    }
}
}

На inte rnet Я нашел много ответов, которые решают эту проблему следующим образом, заменив это:

View view = LayoutInflater.from (context) .inflate (R.layout.right_message, null);

к этому:

View view = LayoutInflater.from (context) .inflate (R.layout.right_message, parent, false);

Но в моем случае я получил только пропущенный чат с пустыми пробелами между одним comi c и другим.

Homefragment. java

public class HomeFragment extends Fragment {

private HomeViewModel homeViewModel;

private View v;
private ImageButton btn_send;
private EditText et_send_mex;

private DatabaseReference reference;

private msgAdapter mAdapter;
private List<MessageChat> mChat;
private RecyclerView recyclerView;

private FirebaseUser user;
private String Uid, Oid;

private static final String sId = "xyz1";
private static final String pId = "xyz2";


ValueEventListener seenListener;

@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    homeViewModel =
            ViewModelProviders.of(this).get(HomeViewModel.class);

    // Inflate the layout for this fragment
    v = inflater.inflate(R.layout.fragment_home, container, false);
    btn_send = v.findViewById(R.id.btn_send);
    et_send_mex = v.findViewById(R.id.et_send_mex);

    recyclerView = v.findViewById(R.id.rv_mex);
    recyclerView.setHasFixedSize(true);
    LinearLayoutManager llManager = new LinearLayoutManager(getContext());
    llManager.setStackFromEnd(true);
    recyclerView.setLayoutManager(llManager);

    user = FirebaseAuth.getInstance().getCurrentUser();
    Uid = user.getUid();
    btn_send.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            // notify = true;
            String m = et_send_mex.getText().toString();
            if (!m.equals("") || !m.equals("\n")){
                if (Uid.equals(pId)){
                    sendMessage(Uid,sId,m);
                }else if (Uid.equals(sId)){
                    sendMessage(Uid,pId,m);
                }
            }
        }
    });
    readMessage();
    return v;
}


private void seenMessage(final String senderId){
    reference = FirebaseDatabase.getInstance().getReference("chats");
    seenListener = reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                MessageChat msgchat = snapshot.getValue(MessageChat.class);
                if (msgchat.getReceiver().equals(user.getUid()) && msgchat.getSender().equals(senderId)){
                    HashMap<String, Object> hashMap = new HashMap<>();
                    hashMap.put("isseen", true);
                    snapshot.getRef().updateChildren(hashMap);
                }
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

private void sendMessage(String sender, String receiver, String message){

    reference = FirebaseDatabase.getInstance().getReference();
    HashMap<String, Object> hashMap = new HashMap<>();
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    month = month+1;
    int day = c.get(Calendar.DAY_OF_MONTH);
    int hour = c.get(Calendar.HOUR_OF_DAY);
    int sec = c.get(Calendar.MINUTE);
    String dt = day+" - "+month+" - "+year+", "+hour+":"+sec;
    hashMap.put("currenttime", dt);
    hashMap.put("sender", sender);
    hashMap.put("receiver", receiver);
    hashMap.put("msg", message);
    hashMap.put("isseen", false);
    reference.child("chats").push().setValue(hashMap);
    et_send_mex.setText("");

}

private void readMessage (){

    mChat = new ArrayList<>();
    mAdapter = new msgAdapter(mChat,getContext());
    recyclerView.setAdapter(mAdapter);
    reference = FirebaseDatabase.getInstance().getReference("chats");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            mChat.clear();
            for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                MessageChat chat = snapshot.getValue(MessageChat.class);
                mChat.add(chat);
                if (!chat.getSender().equals(Uid)){
                    Oid = chat.getSender();
                    seenMessage(Oid);
                }
            }
            mAdapter.notifyDataSetChanged();
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

// user status
private void status(String status){
    user = FirebaseAuth.getInstance().getCurrentUser();
    reference = FirebaseDatabase.getInstance().getReference("Users").child(user.getUid());

    HashMap<String, Object> hashMap = new HashMap<>();
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    month = month+1;
    int day = c.get(Calendar.DAY_OF_MONTH);
    int hour = c.get(Calendar.HOUR_OF_DAY);
    int sec = c.get(Calendar.MINUTE);
    String dt = day+" - "+month+" - "+year+", "+hour+":"+sec;
    hashMap.put("lastaccess", dt);
    hashMap.put("status", status);
    reference.updateChildren(hashMap);
}

@Override
public void onResume() {
    super.onResume();
    status("online");
    recyclerView.setAdapter(mAdapter);
}

@Override
public void onPause() {
    super.onPause();
    reference.removeEventListener(seenListener);
    status("offline");
}

}

Мне также посоветовали добавить сюда:

@Override
public void onResume() {
  super.onResume();
  status("online");
}

Следующий код:

recyclerView.setAdapter (mAdapter);

Но с плохими результатами ( возможно, вы можете подсказать мне, правильно ли добавлять эту часть здесь или нет, большое спасибо ) * 105 3 *

2) Когда я отправляю сообщение, часто требуется много времени, чтобы добраться до базы данных, скажем, что Firebase на самом деле не база данных в реальном времени. Здесь я не встречал ни одного типа ошибки, она просто очень медленная.

3) Позже я также добавил изменение состояния, просто когда приложение закрыто или в фоновом режиме (onPause ()), состояние в базе данных отключен, в противном случае он подключен. Но часто это не работает должным образом, возможно, сбои приложения или задержки влияют на это.

Я остаюсь доступным для любой другой части кода или для пояснения. Я заранее благодарю вас за вашу помощь и извиняюсь за мой плохой Engli sh, я работал над этим приложением с прошлой весны и до сих пор не смог решить эти проблемы.

1 Ответ

1 голос
/ 29 февраля 2020

Первое, что вы хотите сделать, это удалить часть в вашем onCreate.

reference = FirebaseDatabase.getInstance().getReference("chats");
reference.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        readMessage();
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {

    }
});

Здесь вы в основном помещаете ссылку на базу данных (чаты) и каждый раз, когда она изменяется, она будет вызывать вашу функцию readMessage, которое всегда будет переопределять ваши сообщения и помещать другого слушателя в то же место. Убедитесь, что вы понимаете, что addValueEventListener запускается каждый раз, когда дерево / путь (чаты) в firebase обновляются!

Вы можете просто заменить код сверху на:

readMessage();

, который будет уже убедитесь, что он слушает вашу БД. Я полагаю, что база данных не медленная, но два слушателя могут переопределить свой результат.

Что касается следующей части, я не на 100%, но я считаю, что вы можете удалить

        mAdapter = new msgAdapter(mChat,getContext());
        recyclerView.setAdapter(mAdapter);

из onDataChange (в readMessage) и поместите его прямо под той частью, где вы создаете ArrayList. Вот так:

mChat = new ArrayList<>();
mAdapter = new msgAdapter(mChat,getContext());
recyclerView.setAdapter(mAdapter);
reference = FirebaseDatabase.getInstance().getReference("chats");

Надеюсь, я понял вашу проблему, и мой ответ даст вам больше ясности:)

Привет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...