[英]Android - Firebase - Getting Child of Child Data
Aim 目标
To prompt the data of the current user such as name , address , neighbourhood by acquiring them through the Data Tree within Firebase Database 通过Firebase数据库中的数据树获取当前用户的数据,例如姓名 , 地址 , 邻居等
Picture of the Data Tree 数据树的图片
Desrciption 宗旨
The "London" and "Washington" child within the Data Tree are added in by the Admin account, which means that they are not fixed and that an additional child such as "New York" can be added. 数据树中的“伦敦”和“华盛顿”子级是通过Admin帐户添加的,这意味着它们不是固定的,可以添加其他子级,例如“纽约”。
Which would mean that the newly edited Data Tree will have under "Users", there will be "London", "Washington", and "New York" 这意味着新编辑的数据树将位于“用户”下,其中将有“伦敦”,“华盛顿”和“纽约”
The other childs, such as those shown below are fixed. 其他孩子,例如下面显示的孩子是固定的。
Guards 守卫
Police 警察
Resident 居民
address 地址
image 图片
name 名称
neighbourhood 邻里
position 位置
status 状态
Problem 问题
I receive a "java.lang.NullPointerException" after attempting to prompt the data of the current user who is logged in. 尝试提示登录的当前用户的数据后,我收到“ java.lang.NullPointerException”。
The error is showing at the code String stgUserHomeName = dataSnapshot.child("Users").getValue().toString();
错误显示在代码
String stgUserHomeName = dataSnapshot.child("Users").getValue().toString();
. 。
I was able to acquire my desired data such as "name", "address" and etc. before but when I had a problem when I added in a Not-Fixed parent such as "London" and "Washington". 我之前可以获取所需的数据,例如“名称”,“地址”等,但是当我添加“伦敦”和“华盛顿”等非固定父母时遇到问题时,我可以获取这些数据。
SettingsActivity Class 设置活动类
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();
}
}
});
}
});
}
}
}
Code Explanation 代码说明
The jSettingsHomeName
and stgUserHomeName
are meant to be the "London", "Washington", and etc. jSettingsHomeName
和stgUserHomeName
分别是“伦敦”,“华盛顿”等。
Solution to almost Similar Problem 解决几乎类似的问题
In the link: How to get child of child value from firebase in android? 在链接中: 如何从android中的Firebase获取子级的子级? , it shows how a programmer can get the child of a child but the reason as to why I am unable to follow the is because my "London" and "Washington" child tier isn't fixed
,它显示了程序员如何获取孩子的孩子,但之所以我无法遵循,是因为我的“伦敦”和“华盛顿”孩子级别不固定
Update: Trying Solution given by Ewald B. 更新: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) {
}
});
Error 错误
java.lang.NullPointerException at com.example.task.app.SettingsActivity$1.onDataChange(SettingsActivity.java:91) com.example.task.app.SettingsActivity $ 1.onDataChange(SettingsActivity.java:91)上的java.lang.NullPointerException
Line 91 points to String stgUserName = node.child("Resident").child(settingsUserID).child("name").getValue().toString();
第91行指向
String stgUserName = node.child("Resident").child(settingsUserID).child("name").getValue().toString();
According to the data model and looking at this statement 根据数据模型并查看此语句
jSettingsDatabase = FirebaseDatabase.getInstance().getReference().child("Resident").child(settingsUserID);
there's no node User
underneath /Resident/userId
. 在
/Resident/userId
下没有节点User
。 The query needs to start at the database's root which, I assume, is User
. 该查询需要从数据库的根目录开始,我假设该根目录是
User
。
In order to get London, Washington, etc.
you need to adapt the code to: 为了获得
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);
...
}
....
}
});
There's no user ID in your tree that is needed for this query. 您的树中没有此查询所需的用户ID。 But it might be necessary depending on the DB's access rules.
但是根据数据库的访问规则,可能有必要。 Obviously the other queries need to be adapted as well.
显然,其他查询也需要进行调整。 The city names are also not values but a key (must be unique) so what is important to call
DataSnapshot.getKey()
method. 城市名称也不是值,而是键(必须唯一),因此调用
DataSnapshot.getKey()
方法很重要。
In your case the whole database from the User downwards will be fetched and on the client all cities that are not needed will be thrown away. 在您的情况下,将从用户向下的整个数据库中获取数据,并在客户端上丢弃所有不需要的城市。 That's a waste of resources.
那是浪费资源。
for testing purposes only set your Firebase Realtime Database rule to allow anyone read and write 出于测试目的,仅将您的Firebase实时数据库规则设置为允许任何人读写
eg 例如
{
"rules": {
".write": "true",
".read": "true"
}
}
Check out Firebase Rules documentation https://firebase.google.com/docs/database/security/ 查看Firebase Rules文档https://firebase.google.com/docs/database/security/
As soon as you have multiple children at the node, it's needed to scan them at the loop. 一旦在节点上有多个子代,就需要在循环中对其进行扫描。 The event indicates that "some" child was changed.
该事件表明“某些”孩子已更改。 Also, try to change the event to onChildAdded.
另外,尝试将事件更改为onChildAdded。 Like below:
如下所示:
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot.getChildrenCount() > 0) {
for (DataSnapshot ds1 : dataSnapshot.getChildren()) {
String stgUserHomeName = ds1.getValue.toString();
.....
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.