【问题标题】:Android - Firebase - Getting Child of Child DataAndroid - Firebase - 获取子数据的子级
【发布时间】:2017-09-24 05:03:51
【问题描述】:

瞄准

通过内的Data Tree获取nameaddressneighbourhood等当前用户的数据Firebase 数据库

数据树图片

说明

数据树中的“London”和“Washington”子项由管理员帐户添加,这意味着它们不是固定的,可以添加额外的子项,例如“New York”。

这意味着新编辑的数据树将在“用户”下有“伦敦”、“华盛顿”和“纽约”

其他子节点,如下图所示是固定的。

  • 守卫

    • 姓名
    • 社区
  • 警察

    • 地址
    • 姓名
    • 社区
  • 居民

    • 地址

    • 图片

    • 姓名

    • 社区

    • 位置

    • 状态

问题

我在尝试提示当前登录用户的数据后收到“java.lang.NullPointerException”。

错误显示在代码String stgUserHomeName = dataSnapshot.child("Users").getValue().toString();

之前我能够获取我想要的数据,例如“姓名”、“地址”等,但是当我添加 Not-Fixed 父级(例如“London”)时遇到问题”和“华盛顿”。

SettingsActivity 类

import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
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.database.ValueEventListener;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import com.squareup.picasso.Callback;
import com.squareup.picasso.NetworkPolicy;
import com.squareup.picasso.Picasso;

import java.io.File;


public class SettingsActivity extends AppCompatActivity {

    private DatabaseReference jSettingsDatabase;
    private FirebaseUser jFirebaseCurrentUser;

    private Toolbar jSettingsToolbar;

    private ImageView jSettingsImageView;
    private TextView jSettingsDisplayName;
    private TextView jSettingsStatus;
    private TextView jSettingsAddress;
    private TextView jSettingsHomeName;

    private Button jSettingsDetailsBtn;
    private Button jSettingsImageBtn;

    private static final int jSettingsGallerySelect = 1;

    private StorageReference jSettingsStorageReference;

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

        jSettingsStorageReference = FirebaseStorage.getInstance().getReference();

        jFirebaseCurrentUser = FirebaseAuth.getInstance().getCurrentUser();

        String settingsUserID = jFirebaseCurrentUser.getUid();

        jSettingsDatabase = FirebaseDatabase.getInstance().getReference().child("Resident").child(settingsUserID);
        jSettingsDatabase.keepSynced(true);

        jSettingsImageView = (ImageView) findViewById(R.id.settingUserImg);

        jSettingsToolbar = (Toolbar) findViewById(R.id.settingsToolBar);
        setSupportActionBar(jSettingsToolbar);
        getSupportActionBar().setTitle("Settings");
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        jSettingsDisplayName = (TextView) findViewById(R.id.settingUserNameTxt);
        jSettingsStatus = (TextView) findViewById(R.id.settingUserStatusTxt);
        jSettingsAddress = (TextView) findViewById(R.id.settingUserAddressTxt);
        jSettingsHomeName = (TextView) findViewById(R.id.settingsUserHomeTxt);

        jSettingsDetailsBtn = (Button) findViewById(R.id.settingChangeDetailsBtn);
        jSettingsImageBtn = (Button) findViewById(R.id.settingChangeImageBtn);

        jSettingsDatabase.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                String stgUserHomeName = dataSnapshot.child("Users").getValue().toString();
                String stgUserName = dataSnapshot.child("Users").child(stgUserHomeName).child("name").getValue().toString();
                String stgUserStatus = dataSnapshot.child("Users").child(stgUserHomeName).child("status").getValue().toString();
                String stgUserHomeAddress = dataSnapshot.child("Users").child(stgUserHomeName).child("address").getValue().toString();
                final String stgUserImage = dataSnapshot.child("Users").child(stgUserHomeName).child("image").getValue().toString();

                jSettingsDisplayName.setText(stgUserName);
                jSettingsStatus.setText(stgUserStatus);
                jSettingsAddress.setText(stgUserHomeAddress);

                if(!stgUserImage.equals("default")){
                    Picasso.with(SettingsActivity.this).load(stgUserImage).networkPolicy(NetworkPolicy.OFFLINE)
                            .placeholder(R.drawable.avataricon).into(jSettingsImageView, new Callback() {
                        @Override
                        public void onSuccess() {

                        }

                        @Override
                        public void onError() {
                            Picasso.with(SettingsActivity.this).load(stgUserImage).placeholder(R.drawable.avataricon).into(jSettingsImageView);
                        }
                    });
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

        jSettingsDetailsBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String stgUserStatusValue = jSettingsStatus.getText().toString();
                String stgUserAddressValue = jSettingsAddress.getText().toString();
                String stgUserNameValue = jSettingsDisplayName.getText().toString();
                Intent intentDetails = new Intent(SettingsActivity.this, DetailsActivity.class);
                intentDetails.putExtra("stgUserNameValue" , stgUserNameValue);
                intentDetails.putExtra("stgUserStatusValue", stgUserStatusValue);
                intentDetails.putExtra("stgUserAddressValue", stgUserAddressValue);
                startActivity(intentDetails);
            }
        });

        jSettingsImageBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intentGallery = new Intent();
                intentGallery.setType("image/*");
                intentGallery.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intentGallery, "SELECT IMAGE"), jSettingsGallerySelect);
            }
        });
    }

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

        if(requestCode == jSettingsGallerySelect && resultCode == RESULT_OK){

            Uri imageUri = data.getData();

            String currentUserID = jFirebaseCurrentUser.getUid();

            StorageReference imageFilePath = jSettingsStorageReference.child("profileImages").child(currentUserID+".jpg");

            imageFilePath.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>(){
                @Override
                public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
                    @SuppressWarnings("VisibleForTests")
                    String downloadImageUrl = task.getResult().getDownloadUrl().toString();

                    jSettingsDatabase.child("image").setValue(downloadImageUrl).addOnCompleteListener(new OnCompleteListener<Void>() {
                        @Override
                        public void onComplete(@NonNull Task<Void> task) {
                            if(task.isSuccessful()){
                                Toast.makeText(SettingsActivity.this, "Upload Successful", Toast.LENGTH_SHORT).show();
                            }else{
                                Toast.makeText(SettingsActivity.this, "Upload Failed", Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
                }
            });
        }
    }
}

代码说明

jSettingsHomeNamestgUserHomeName 是指“伦敦”、“华盛顿”等。

几乎相似问题的解决方案

在链接中:How to get child of child value from firebase in android?,它显示了程序员如何获得孩子的孩子,但我无法遵循的原因是因为我的“伦敦”和“华盛顿”孩子层 不固定

更新:Ewald B 给出的尝试解决方案。

jSettingsDatabase.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot node : dataSnapshot.getChildren()) {
            String stgUserHomeName = node.getKey();
            String stgUserName = node.child("Resident").child(settingsUserID).child("name").getValue().toString();
            String stgUserStatus = node.child("Resident").child(settingsUserID).child("status").getValue().toString();
            String stgUserHomeAddress = node.child("Resident").child(settingsUserID).child("address").getValue().toString();
            final String stgUserImage = node.child("Resident").child(settingsUserID).child("image").getValue().toString();

            jSettingsHomeName.setText(stgUserHomeName);
            jSettingsDisplayName.setText(stgUserName);
            jSettingsStatus.setText(stgUserStatus);
            jSettingsAddress.setText(stgUserHomeAddress);

            if(!stgUserImage.equals("default")){
                Picasso.with(SettingsActivity.this).load(stgUserImage).networkPolicy(NetworkPolicy.OFFLINE)
                        .placeholder(R.drawable.avataricon).into(jSettingsImageView, new Callback() {
                    @Override
                    public void onSuccess() {

                    }

                    @Override
                    public void onError() {
                        Picasso.with(SettingsActivity.this).load(stgUserImage).placeholder(R.drawable.avataricon).into(jSettingsImageView);
                    }
                });
            }
        }

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
});

错误

com.example.task.app.SettingsActivity$1.onDataChange(SettingsActivity.java:91) 处的 java.lang.NullPointerException

第 91 行指向String stgUserName = node.child("Resident").child(settingsUserID).child("name").getValue().toString();

【问题讨论】:

    标签: java android firebase firebase-realtime-database parent-child


    【解决方案1】:

    根据数据模型看这个说法

    jSettingsDatabase = FirebaseDatabase.getInstance().getReference().child("Resident").child(settingsUserID);
    

    /Resident/userId 下面没有节点 User。查询需要从数据库的根目录开始,我假设它是User

    为了获得London, Washington, etc.,您需要将代码修改为:

    jSettingsDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
    ...
    jSettingsDatabase.addValueEventListener(new ValueEventListener() {
       @Override
       public void onDataChange(DataSnapshot dataSnapshot) {
          for(DataSnapshot node : dataSnapshot.getChildren()) {
             // you will get all cities
             String stgUserHomeName = node.getKey();
             if(!"Washington".equals(stgUserHomeName)) // or whatever city you need
                continue;
             // add some more conditional logic to cope with the distinct subtrees that don't have the same properties
             // London's Resident has more properties than Washington --> exception is thrown then
             // to get the resident's data
             node.child("Resident").child(userId).child("address")...
             node.child("Resident").child(userId).child("image")...
             // or
             node.child("Resident").child(userId).getValue(Resident.class);
             ...
          }
          ....
       }
    });
    

    您的树中没有此查询所需的用户 ID。但根据数据库的访问规则,这可能是必要的。显然,其他查询也需要调整。城市名称也不是值,而是一个键(必须是唯一的),所以调用DataSnapshot.getKey() 方法很重要。

    在您的情况下,将获取从用户向下的整个数据库,并且在客户端上,所有不需要的城市都将被丢弃。这是对资源的浪费。

    【讨论】:

    • 我目前正在尝试您的解决方案,但如果我试图获取“居民的数据,例如姓名、地址等”,我只是想获取“城市”一个TextView。我尝试完成后再发表评论。感谢您的帮助。
    • @TheStudent123 我已经根据问题编辑了我的答案
    • 据说“foreach 不适用于类型 'com.google.firebase.database.DataSnapshot'”。很抱歉没有早点说这个,但我想我可以解决它。
    • 抱歉忘记了DataSnapshot.getChildren方法。
    • @TheStudent123 不客气!不过,我建议您看一下video,了解如何构建数据。我认为您的数据模型可能不适合您要完成的工作。
    【解决方案2】:

    仅用于测试目的将您的 Firebase 实时数据库规则设置为允许任何人读写

    例如

    {
      "rules": {
         ".write": "true",
         ".read": "true"
        }
    }
    

    查看 Firebase 规则文档 https://firebase.google.com/docs/database/security/

    【讨论】:

    • 好的,我编辑了。我阅读了 Firebase 文档,它现在允许用户在我的数据库中读取和写入数据。我把它从“auth != null”改了。
    • 当您附加一个 ValueEventListener 时,您会通过将其转换为您的模型类来获取您的数据,并且您不能在其上调用引用。例如London london = dataSnapshot.getValue(London.class);
    • City city = dataSnapshot.getValue(City.class); 从那里您可以获得所有其他嵌套类 - city.getPolice(); city.getGuards(); , city.getResident();
    • 我正在尝试发布的解决方案和其他解决方案。感谢您的帮助。完成后我会再次发表评论。
    【解决方案3】:

    一旦节点上有多个子节点,就需要在循环中扫描它们。该事件表明“一些”孩子被改变了。另外,尝试将事件更改为 onChildAdded。如下:

    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            if (dataSnapshot.getChildrenCount() > 0) {
                for (DataSnapshot ds1 : dataSnapshot.getChildren()) {
                    String stgUserHomeName = ds1.getValue.toString(); 
                     .....
    

    【讨论】:

    • 我目前正在尝试解决方案。但我目前正在尝试提示居民的详细信息以及他们被分配到的城市。循环还会起作用吗?感谢您的帮助。完成后我会再次发表评论。
    • 要检索居民的详细信息,我建议将它们作为类对象(而不是分隔字段)保存在 firebase 数据库中。比如说,定义类 TheResident,因此您可以检索它(关于上面的代码),例如:TheResident myResident = tds1.getValue(Theresident.class)。当然,插入数据库也应该由这个类的对象来完成。必须在类、setter/getter 和空构造函数中定义所有必需的字段。在文档中阅读更多内容。
    • 感谢您的评论。我将尝试编辑在 Firebase DB 中保存数据的方式。 =D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-15
    • 2018-03-17
    • 2015-01-25
    • 2015-08-14
    相关资源
    最近更新 更多