只填充从数据库检索的消息viewHolder? [英] Only populate viewHolder with messages retrieved from database?

查看:97
本文介绍了只填充从数据库检索的消息viewHolder?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在关注Android Firebase Codelab。



以下是我试图修改的Friendly Chat应用程序的项目: https://github.com/firebase/friendlychat-android/tree/master/android



从数据库中检索消息后,我希望MessageViewHolder仅填充 。不幸的是,现在,MessageViewHolder也被本地填充(在用户按下发送之后)。我不希望后者发生。这意味着查看者从数据库中获取两次(本地一次,从数据库中获取一次),效率低下。

Side-node:此外,当用户按下发送时,用户可能会认为他在线,并看到他的MessageViewHolder中填充了他的消息。这是不需要的!



请转到 MainActivity 查看 populateViewHolder 方法。

在过去的几个月里,我一直试图回避这个问题,但是现在我真的需要解决问题才能继续工作。
我需要做些什么才能实现我想要的功能?

解决方案

一点点。我提供了两种解决方案。第二个解决方案是最简单的。



它更新您的本地 mMessageRecyclerView 重新离线的是,Firebase具有离线功能。 Firebase推/拉通过单独的工作线程发生。一旦你再次上线,这个工作者线程就开始同步 - 你可能需要考虑持久化存储工作线程的内容。一旦你重新启动你的应用程序,所有本地写入消失。请注意,您已经在主线程中设置了 mFirebaseAdapter 如下:

  mFirebaseAdapter = new FirebaseRecyclerAdapter< FriendlyMessage,MessageViewHolder>(FriendlyMessage.class,R.layout.item_message,
MessageViewHolder.class,
mFirebaseDatabaseReference.child(MESSAGES_CHILD)){
/ * more stuffs here * /}

它表示 FirebaseRecyclerAdapter
$ p $ FriendlyMessage.class,
R.layout.item_message,
MessageViewHolder.class ,
mFirebaseDatabaseReference.child(MESSAGES_CHILD)

观察者在 mFirebaseAdapter 立即感觉到这个变化,立即更新你的 mMessageRecyclerView RecyclerView。因此,您必须保证在成功更新Firebase之前不要更改这些参数。

另请注意, mFirebaseDatabaseReference.child(MESSAGES_CHILD).push()。setValue(friendlyMessage); ,实际的推送网络操作 - 发生在一个单独的工作线程中,正如你所知道的,网络操作不能在主线程中发生,但是在下面的一行 mMessageEditText.setText(); 发生在主线程中。所以,偶数线程正在执行(成功或不成功),主线程已经更新了你的GUI。

所以,可能的解决方案是:1)复杂的解决方案 Github: https://github.com/uddhavgautam/UddhavFriendlyChat
(您必须保证您不会更改 mFirebaseAdapter 的任何4个以上的参数,直到您成功更新Firebase更新为止 - 所以这样做很复杂,但仍然可以完美工作):在这里你创建 FriendlyMessage2 ,它与 FriendlyMessage 完全相似,(而不是 FriendlyMessage FriendlyMessage2 ),并且使用 FriendlyMessage onCompletionListener
如下:

在此解决方案中,不是使用 setValue() mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push()触发器,它在本地更新您的 RecyclerView ,这就是为什么使用 setValue()在这里不起作用。 onCompletionListener()稍后发生,所以你可以在 onSuccess()中实现逻辑。使用REST写入,您的RecyclerViewAdapter基于Firebase数据进行更新。此外,我们需要在使用Okhttp客户端通过单独的工作线程写入之前序列化FriendlyMessage2。所以,你必须修改你的build.gradle作为



更新你的build.gradle

  compile'c​​om.squareup.okhttp3:okhttp:3.5.0'
compile'c​​om.google.code.gson:gson:2.8.0'

您的setOnClickListener方法实现

  mSendButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
/ *
new FirebaseRecyclerAdapter< FriendlyMessage,MessageViewHolder>(
FriendlyMessage.class,
R.layout.item_message,
MessageViewHolder.class,
mFirebaseDatabaseReference.child(MESSAGES_CHILD))
* /

// if(OnLineTracker.isOnline(getApplicationContext())){
friendlyMessage2 = new FriendlyMessage2(mMessageEditText.getText()。toString(),mUsername,mPhotoUrl,null);


JSON = MediaType.parse(application / json; charset = utf-8);
Gson gson = new GsonBuilder()。setPrettyPrinting()。setDateFormat(yyyy-MM-dd HH:mm:ss)。

myJson = gson.toJson(friendlyMessage2);
client = new OkHttpClient();
$ b $ new Thread(new Runnable(){
@Override
public void run(){
try {
if(post(mFirebaseDatabaseReference.child我们调用主线程* /
runOnUiThread(new Runnable(){
@Override $()
mfirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT,null);
}
});

}
} catch(JSONException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();



$ b $ $ $ $ $ $ $ $ $ $ RequestBody body = RequestBody.create(JSON,String JSON,OkHttpClient客户端,MediaType JSON)抛出JSONException, ,新的JSONObject(json).toString());
Request request = new Request.Builder()
.url(url +。json)
.post(body)
.build();

响应响应= client.newCall(request).execute();
if(response.isSuccessful()){
return true;
}
else else false;
}
})。start();





$ b // mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push()。setValue(friendlyMessage2).addOnCompleteListener(new OnCompleteListener< (@NonNull Task< Void>任务){
// FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText() .toString(),mUsername,mPhotoUrl,null);
//
// mMessageEditText.setText();
// mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT,null);
//}
//});

//}
}
});

FriendlyMessage2.java - 您必须创建这个,因为您的RecyclerView适配器依赖于FriendlyMessage。使用FriendlyMessage,RecyclerView适配器中的观察者会感觉到这一点,并因此更新您的视图。

  package com.google.firebase.codelab.friendlychat; 

/ **
*由8/17/17由uddhav创建。
* /

public class FriendlyMessage2 {

private String id;
私人字符串文本;
私人字符串名称;
私人字符串photoUrl;
private String imageUrl;
$ b $ public FriendlyMessage2(){
}
$ b $ public FriendlyMessage2(String text,String name,String photoUrl,String imageUrl){
this.text =文本;
this.name = name;
this.photoUrl = photoUrl;
this.imageUrl = imageUrl;
}

public String getId(){
return id;
}

public void setId(String id){
this.id = id;
}

public String getText(){
return text;
}

public void setText(String text){
this.text = text;
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public String getPhotoUrl(){
return photoUrl;
}

public void setPhotoUrl(String photoUrl){
this.photoUrl = photoUrl;
}

public String getImageUrl(){
return imageUrl;


public void setImageUrl(String imageUrl){
this.imageUrl = imageUrl;






$ p我希望你能理解所有的东西。



从这里简单的解决方案:

2)简单的解决方案:您只需使用OnLineTracker就在你点击按钮之后。我更喜欢第二种方法。

它解决了:
1)MessageViewHolder不会在离线时填充。
2)仅当Firebase数据库写入发生时,才会填充MessageViewHolder。

  mSendButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View ();
if(OnLineTracker.isOnline(getApplicationContext())){
FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText()。toString(),mUsername,mPhotoUrl,null);
mFirebaseDatabaseReference.child(MESSAGES_CHILD).push()。setValue(friendlyMessage);
mMessageEditText.setText();
mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT,null);
}
}
});

OnLineTracker.java

  public class OnLineTracker {

public static boolean isOnline(Context ctx){
ConnectivityManager cm =(ConnectivityManager)ctx.getSystemService(Context。 CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
返回netInfo!= null&& netInfo.isConnectedOrConnecting();
}
}


I'm following the Android Firebase Codelab.

Here's the project of the Friendly Chat app I'm attempting to modify: https://github.com/firebase/friendlychat-android/tree/master/android

I want the MessageViewHolder to be populated only after retrieving a message from the database. Unfortunately, right now, MessageViewHolder also gets populated locally (after the user presses 'SEND'). I don't want the latter to happen. That means the view holder gets updated twice (once locally and once after retrieval from the database) which is inefficient.

Side-node: Also, the user might think he's online when he presses 'SEND' and sees his MessageViewHolder get populated with his message. That's unwanted!

Please go to line 177 of MainActivity to see populateViewHolder method.

I've been trying to dodge the issue for the last couple of months, but now I really need the problem to be solved to continue working. What changes do I have to make to achieve what I want?

解决方案

I have explained the scenario little bit. I provided two solutions in your case. The second solution is the easiest one.

The reason it updates your local mMessageRecyclerView when you're offline is that Firebase has off-line capabilities. Firebase push/pull happens via separate worker thread. This worker thread starts synchronizing once you go online again -- you might need to think about persistence stororing the contents of worker threads. Once you restart your app, all local write goes off. Please note you have setup your mFirebaseAdapter in the main thread as below:

mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>(FriendlyMessage.class, R.layout.item_message,
                MessageViewHolder.class,
                mFirebaseDatabaseReference.child(MESSAGES_CHILD)) { 
/* more stuffs here */ }

It means any below 4 parameters of FirebaseRecyclerAdapter changes:

FriendlyMessage.class,
                R.layout.item_message,
                MessageViewHolder.class,
                mFirebaseDatabaseReference.child(MESSAGES_CHILD)

the observer inside mFirebaseAdapter immediately senses that change and updates your mMessageRecyclerView RecyclerView immediately. So, you have to guarantee you don't change any of these parameters until you update Firebase successfully.

Also note that, mFirebaseDatabaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage);, here the actual push -- network operation -- happens in a separate worker thread, as you know, network operation can't happen in Main Thread, but the below line mMessageEditText.setText(""); happens in Main Thread. So, the even worker thread is executing (successful or unsuccessful), the Main Thread already updated your GUI.

So, possible solutions are:

1) Complex solution Github: https://github.com/uddhavgautam/UddhavFriendlyChat (you must guarantee that you don't change any above 4 parameters of your mFirebaseAdapter until you successfully update your Firebase update -- so this is kind of complex but still works perfectly): Here you create FriendlyMessage2, which is exactly similar to FriendlyMessage, (only instead of FriendlyMessage there is FriendlyMessage2), and use that only use FriendlyMessage inside onCompletionListener as below:

In this solution, instead of updating Firebase database using setValue(), we use REST write from OkHttp client. This is because mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push() triggers, which locally updates your RecyclerView, that's why using setValue() doesn't work here. The onCompletionListener() happens later, so you can implement the logic inside onSuccess(). Using REST write, your RecyclerViewAdapter updates based on firebase data. Also, we need to serialize the FriendlyMessage2 before we do write using Okhttp client via separate worker thread. So, you have to modify your build.gradle as

update your build.gradle

    compile 'com.squareup.okhttp3:okhttp:3.5.0'
    compile 'com.google.code.gson:gson:2.8.0'

Your setOnClickListener method implementation

    mSendButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    /*
                    new FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>(
                    FriendlyMessage.class,
                    R.layout.item_message,
                    MessageViewHolder.class,
                    mFirebaseDatabaseReference.child(MESSAGES_CHILD))
                     */

    //                if (OnLineTracker.isOnline(getApplicationContext())) {
                        friendlyMessage2 = new FriendlyMessage2(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null);


                    JSON = MediaType.parse("application/json; charset=utf-8");
                    Gson gson = new GsonBuilder().setPrettyPrinting().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

                    myJson = gson.toJson(friendlyMessage2);
                    client = new OkHttpClient();

                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                if(post(mFirebaseDatabaseReference.child(MESSAGES_CHILD).toString(), myJson, client, JSON)) {
/* again for GUI update, we call main thread */
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            mMessageEditText.setText("");
                                            mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null);
                                        }
                                    });

                                }
                            } catch (JSONException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                            boolean post(String url, String json, OkHttpClient client, MediaType JSON) throws JSONException, IOException {
                                RequestBody body = RequestBody.create(JSON, new JSONObject(json).toString());
                                Request request = new Request.Builder()
                                        .url(url+".json")
                                        .post(body)
                                        .build();

                                Response response = client.newCall(request).execute();
                                if(response.isSuccessful()) {
                                    return true;
                                }
                                else return false;
                        }
                    }).start();






    //                mFirebaseDatabaseReference1.child(MESSAGES_CHILD).push().setValue(friendlyMessage2).addOnCompleteListener(new OnCompleteListener<Void>() {
    //                        @Override
    //                        public void onComplete(@NonNull Task<Void> task) {
    //                            FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null);
    //
    //                            mMessageEditText.setText("");
    //                            mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null);
    //                        }
    //                    });

    //                }
                }
            });

FriendlyMessage2.java -- you have to create this because your RecyclerView adapter is dependent on FriendlyMessage. Using FriendlyMessage, the observers inside RecyclerView adapter sense that and hence update your view.

package com.google.firebase.codelab.friendlychat;

/**
 * Created by uddhav on 8/17/17.
 */

public class FriendlyMessage2 {

    private String id;
    private String text;
    private String name;
    private String photoUrl;
    private String imageUrl;

    public FriendlyMessage2() {
    }

    public FriendlyMessage2(String text, String name, String photoUrl, String imageUrl) {
        this.text = text;
        this.name = name;
        this.photoUrl = photoUrl;
        this.imageUrl = imageUrl;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhotoUrl() {
        return photoUrl;
    }

    public void setPhotoUrl(String photoUrl) {
        this.photoUrl = photoUrl;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

I hope, you understood everything.

Simple solution from here:

2) Easy solution: You simply use OnLineTracker just after you click on Button as below. I prefer 2nd method.

It solves: 1) MessageViewHolder doesn't get populated on offline. 2) MessageViewHolder gets populated only when Firebase Database write happens.

mSendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (OnLineTracker.isOnline(getApplicationContext())) {
                    FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, mPhotoUrl, null);
                    mFirebaseDatabaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage);
                    mMessageEditText.setText("");
                    mFirebaseAnalytics.logEvent(MESSAGE_SENT_EVENT, null);
                }
            }
        });

OnLineTracker.java

    public class OnLineTracker {

    public static boolean isOnline(Context ctx) {
        ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        return netInfo != null && netInfo.isConnectedOrConnecting();
    }
}

这篇关于只填充从数据库检索的消息viewHolder?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆