我正在Android中实现即时消息应用程序(顺便说一句,Firebase Udacity课程的一部分,FriendlyChat)。
它保存并显示每条消息的书面消息或图像。这些保存的项目的类称为FriendlyMessage,其中包含消息的作者,然后是文本或图像。

我已经在onAuthStateChanged中设置了ChildEventListener,该代码在onCreate上编码。这就是说,仅在onResume(将其附加/添加到DatabaseReference)和onPause(取消/删除)上调用AuthListener。
发送按钮的onClickListener也在onCreate中设置。 EditText中的文本传递给String,创建FriendlyMessage实例,然后使用.push()。setValue(object)方法保存到数据库。
每当执行此操作时,都会调用onChildAdded,在其中可以将最新的FriendlyMessage实例添加到适配器,并且书面消息将显示在ListView中。

对于图像,我有一个打开图像文件的按钮,然后选择一个,然后调用onActivityResult。
在这里,我能够检索图像,将其存储在FirebaseStorage中,甚至将其保存到数据库中(也可以使用push()。setValue(object)方法)。
但是,在我编写的一些日志的帮助下,我注意到没有调用onChildAdded,因此,同时由于该应用程序可能已暂停,我的ListView仍然为空白( onPause)。旋转手机或重新启动应用程序时,将显示所有内容,包括上载的图像。但是,当然,如您所知,在用户上传图片后立即显示图片是很重要的。

我究竟做错了什么?或者我该怎么做,以便在将图像FriendlyMessage实例保存到数据库时调用onChildAdded(知道现在正在onActivityResult上调用它)?

MainActivity.xml:

package com.google.firebase.udacity.friendlychat;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.fragment.app.Fragment;

import com.firebase.ui.auth.AuthUI;
import com.google.android.gms.tasks.Continuation;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    public static final String ANONYMOUS = "anonymous";
    public static final int DEFAULT_MSG_LENGTH_LIMIT = 1000;
    public static final int RC_SIGN_IN = 1;
    public static final int RC_PHOTO_PICKER = 2;

    private ListView mMessageListView;
    private MessageAdapter mMessageAdapter;
    private ProgressBar mProgressBar;
    private ImageButton mPhotoPickerButton;
    private EditText mMessageEditText;
    private Button mSendButton;
    private TextView mDisplayNameTextView;

    private String mUsername;

    private int countChildEventCalls; //for testing ChildEventListener calls

    //Firebase variables
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference mDatabaseReference;
    private FirebaseAuth mFirebaseAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private ChildEventListener mChildEventListener;
    private FirebaseStorage mFirebaseStorage;
    private StorageReference mChatPhotosStorageReference;

    private List<AuthUI.IdpConfig> mAuthProviders = Arrays.asList(
            new AuthUI.IdpConfig.EmailBuilder().build(),
            new AuthUI.IdpConfig.GoogleBuilder().build());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mUsername = ANONYMOUS;
        countChildEventCalls = 0;

        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mFirebaseStorage = FirebaseStorage.getInstance();
        mFirebaseAuth = FirebaseAuth.getInstance();

        mDatabaseReference = mFirebaseDatabase.getReference().child("messages");
        mChatPhotosStorageReference = mFirebaseStorage.getReference().child("chat_photos");

        // Initialize references to views
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mMessageListView = (ListView) findViewById(R.id.messageListView);
        mPhotoPickerButton = (ImageButton) findViewById(R.id.photoPickerButton);
        mMessageEditText = (EditText) findViewById(R.id.messageEditText);
        mSendButton = (Button) findViewById(R.id.sendButton);
        mDisplayNameTextView = (TextView) findViewById(R.id.display_name);

        // Initialize message ListView and its adapter
        List<FriendlyMessage> friendlyMessages = new ArrayList<>();
        mMessageAdapter = new MessageAdapter(this, R.layout.item_message, friendlyMessages);
        mMessageListView.setAdapter(mMessageAdapter);

        // Initialize progress bar
        mProgressBar.setVisibility(ProgressBar.INVISIBLE);

        // Enable Send button when there's text to send
        mMessageEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (charSequence.toString().trim().length() > 0) {
                    mSendButton.setEnabled(true);
                } else {
                    mSendButton.setEnabled(false);
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {
            }
        });

        mMessageEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(DEFAULT_MSG_LENGTH_LIMIT)});

        // Send button sends a message and clears the EditText
        mSendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // TODO: Send messages on click
                FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, null);

                mDatabaseReference.push().setValue(friendlyMessage);

                // Clear input box
                mMessageEditText.setText("");
            }
        });

        // ImagePickerButton shows an image picker to upload a image for a message
        mPhotoPickerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("image/jpeg");
                intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
                startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);
              }
        });

        mAuthListener = new FirebaseAuth.AuthStateListener() {

            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();

                if(user != null) { //the user is logged in
                    onSignedInInitialize(user.getDisplayName());
                }

                else { //the user isn't logged in yet. Gotta launch FirebaseAuthUI
                    onSignedOutCleanup();
                    startActivityForResult(AuthUI.getInstance()
                        .createSignInIntentBuilder()
                        .setIsSmartLockEnabled(false)
                        .setAvailableProviders(mAuthProviders)
                        .build(),
                            RC_SIGN_IN);
                }
            }
        };
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
            case R.id.sign_out_menu:
                AuthUI.getInstance().signOut(this);
                return true;

            default: return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onResume(){
        super.onResume();
        mFirebaseAuth.addAuthStateListener(mAuthListener);
    }

    @Override
    protected void onPause(){
        super.onPause();
        if(mAuthListener != null) {
            mFirebaseAuth.removeAuthStateListener(mAuthListener);
        }
        detachDatabaseReadListener();
        mMessageAdapter.clear();
    }

    public void onSignedInInitialize(String displayName){
        mUsername = displayName;
        mDisplayNameTextView.setText(mUsername);

        if(mChildEventListener == null){
            attachDatabaseReadListener();
        }
    }

    public void attachDatabaseReadListener(){
        mChildEventListener = new ChildEventListener() {

            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                countChildEventCalls++;
                Log.v(TAG, "Could be Text or Photos: calling ChildEventListener");
                FriendlyMessage message = dataSnapshot.getValue(FriendlyMessage.class);
                mMessageAdapter.add(message);
                Log.v(TAG, "Text or Photos: message added to Adapter. Calls: " + countChildEventCalls);
            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                Log.v(TAG, "Text or Photos: calling onChildChange!");
            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Log.v(TAG, "Text or Photos: onCancelled called.");
            }
        };

        mDatabaseReference.addChildEventListener(mChildEventListener);
    }

    public void onSignedOutCleanup(){
        mUsername = ANONYMOUS;
        mMessageAdapter.clear();
        detachDatabaseReadListener();
    }

    public void detachDatabaseReadListener(){
        if(mChildEventListener != null){
            mDatabaseReference.removeEventListener(mChildEventListener);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data){
        super.onActivityResult(requestCode, resultCode, data);

        Log.v(TAG, "Photos: Started activitiyForResult");
        Log.v(TAG, "for Photos purpose, requestCode = " + requestCode + ", resultCode = " + resultCode);

        if(requestCode == RC_SIGN_IN){
            if(resultCode == RESULT_OK){
                Toast.makeText(MainActivity.this, "You're logged in! Type with your friends", Toast.LENGTH_LONG).show();
            }
            else if (resultCode == RESULT_CANCELED){
                Toast.makeText(MainActivity.this, "Sign-in cancelled", Toast.LENGTH_LONG).show();
                finish();
            }
        }
        else if (requestCode == RC_PHOTO_PICKER && resultCode == RESULT_OK) {
            uploadImageToChat(data);
        }

    }

    public void uploadImageToChat(Intent dataIntent){
        Log.v(TAG, "Photos: Reached photo picker RC request");
        Uri imageUri = dataIntent.getData();
        final StorageReference photoRef = mChatPhotosStorageReference.child(imageUri.getLastPathSegment());
        UploadTask uploadTask = photoRef.putFile(imageUri);

        uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                    @Override
                    public void onSuccess(Uri uri) {
                        String stringUrl = uri.toString();
                        Log.v(TAG, "Photos, uploaded, path: " + stringUrl);

                        FriendlyMessage imageMessage = new FriendlyMessage(null, mUsername, stringUrl);
                        mDatabaseReference.push().setValue(imageMessage);
                        Log.v(TAG, "Photos: Added Message to database");
                    }
                });

            }
        });
        //mMessageAdapter.notifyDataSetChanged();
        /*
            Task<Uri> taskUri = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
                @Override
                public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
                    if (!task.isSuccessful()) {
                        throw task.getException();
                    }

                    // Continue with the task to get the download URL
                    return photoRef.getDownloadUrl();
                }
            }).addOnCompleteListener(new OnCompleteListener<Uri>() {
                @Override
                public void onComplete(@NonNull Task<Uri> task) {
                    if (task.isSuccessful()) {
                        Log.v(TAG, "Photos: upload successful");
                        Uri downloadUri = task.getResult();
                        String stringUrl = task.toString();
                        FriendlyMessage imageMessage = new FriendlyMessage(null, mUsername, stringUrl);
                    } else {
                        // Handle failures
                        // ...
                        Log.v(TAG, "Photos: wasn't able to upload photo");
                    }
                }
            }); */


    }

}


MessageAdapter.xml:

package com.google.firebase.udacity.friendlychat;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;

public class MessageAdapter extends ArrayAdapter<FriendlyMessage> {
    public MessageAdapter(Context context, int resource, List<FriendlyMessage> objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item_message, parent, false);
        }

        ImageView photoImageView = (ImageView) convertView.findViewById(R.id.photoImageView);
        TextView messageTextView = (TextView) convertView.findViewById(R.id.messageTextView);
        TextView authorTextView = (TextView) convertView.findViewById(R.id.nameTextView);

        FriendlyMessage message = getItem(position);

        boolean isPhoto = message.getPhotoUrl() != null;
        if (isPhoto) {
            messageTextView.setVisibility(View.GONE);
            photoImageView.setVisibility(View.VISIBLE);
            Glide.with(photoImageView.getContext())
                    .load(message.getPhotoUrl())
                    .into(photoImageView);
        } else {
            messageTextView.setVisibility(View.VISIBLE);
            photoImageView.setVisibility(View.GONE);
            messageTextView.setText(message.getText());
        }
        authorTextView.setText(message.getName());

        return convertView;
    }
}

最佳答案

同时,我想我找到了解决方案。

在我的onSignedInInitialize方法上,我曾说过,如果ChildEventListener为null,则只有将我的ChildEventListener添加到数据库引用中。因为我要在onPause上删除此侦听器,并且当我返回到mainActivity(在onActivityResult上)时它可能不为null,所以我认为我没有将事件侦听器附加到数据库引用,因此之后没有显示任何内容正在上传的图片。

所以我删除了提到的条件,并在回到onResume时就将ChildEventListener实例添加到数据库引用中。有效。

还是谢谢你!
最好的祝福,

07-27 19:39