【问题标题】:Value of a global variable is reset after it is initialised in ValueEventListener全局变量的值在 ValueEventListener 中初始化后重置
【发布时间】:2017-05-15 09:38:17
【问题描述】:

我有一个名为“bg”的全局变量。它在 Firebase 数据库引用“myRef”的 ValueEventListener 中初始化

但如果我尝试在 valueeventlistener 块之外使用变量的值,则该变量为空。好像被重置了一样。

我对安卓很陌生。请让我知道出了什么问题。

代码如下:

package com.example.tejasvi.donorfinder;

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;

import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.firebase.auth.FirebaseAuth;
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;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;
    private FirebaseAuth mAuth;

    public String latlng[],bg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_maps);

         mAuth = FirebaseAuth.getInstance();

         Bundle bundle = getIntent().getExtras();

         String s= bundle.getString("Phone");  

         String path="Active Requests/"+mAuth.getCurrentUser().getUid()+"/"+s+"/Blood Group";

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference myRef = database.getReference(path);   


        myRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                bg=dataSnapshot.getValue().toString();
                }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Toast.makeText(MapsActivity.this, "Error", Toast.LENGTH_LONG).show();

            }
        });

        Toast.makeText(MapsActivity.this, bg, Toast.LENGTH_SHORT).show();  //bg is empty here

        SupportMapFragment mapFragment = (SupportMapFragment)    getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        }
}

dataSnapshot.getValue() 的值为“A+ve”

JSON文件如下:

{
  "Active Requests": {
    "3R8XGIM5uSRv3sxQZ2sqqUnIjQ62": {
      "9849949799": {
        "Blood Group": "B+ve",
        "Date of Requirement": "3/1/17",
        "Direct or Replacement?": "Direct donation",
        "Donation Type": "Whole Blood",
        "For Self?": "Yes",
        "Hospital Name": "Shekhar Hospital",
        "Location": "12.945793000000002,77.56759339999999",
        "Name": "Poorna Chandra Tejasvi",
        "Units": "2"
      }
    },
    "YL5oVxmua4gktNM2TwwPavL1Tj32": {
      "123456789": {
        "Blood Group": "A+ve",
        "Date of Requirement": "1/1/17",
        "Direct or Replacement?": "Direct donation",
        "Donation Type": "Whole Blood",
        "For Self?": "No",
        "Hospital Name": "Columbia Asia Hospital - Hebbal",
        "Location": "13.0509869,77.5937892",
        "Recipient Name": "jssnxn",
        "Recipient Relation": "Married",
        "Units": "3"
      }
    }
  }
}

【问题讨论】:

  • Active Requests/<uid>/<s>/Blood Group 下的值为空,请将您的数据库导出为 JSON 并在此处发布内容。
  • 它不为空。我试过烤块内的值,它可以工作
  • 你能记录dataSnapshot.toString()的值吗?
  • 是的,我可以。这里似乎有一个更深层次的问题。控件出块后,变量神奇地被重置。
  • 在实际接收到数据之前,bg的值默认为null。发生的情况是您发出请求并尝试访问尚不可用的响应。

标签: android firebase firebase-realtime-database


【解决方案1】:

这里没有魔法。您只是看到了异步加载的效果,大多数现代网络都是在此基础上构建的。

如果您在调试器中运行代码或添加一些日志语句,最容易理解,如下所示:

FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(path);   

System.out.println("Before adding listener");
myRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("In onDataChange");
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
});

System.out.println("After adding listener");

当你像这样运行代码时,它会打印:

添加监听器之前

添加监听器后

在 OnDataChange 中

这可能不是您所期望的,但它是处理 Firebase 时的预期行为,正如大多数现代 Web API 所说的那样。由于加载数据可能需要未知的时间(毕竟它必须从 Internet 上的某个地方获取),因此代码不会等待数据返回,而是在附加听众。然后当数据可用时,它会调用您的onDataChange(),您就可以使用这些数据了。

这种行为一开始会让人非常困惑,所以如果不是很清楚也不要担心。重要的是要意识到这种行为是正常的,并且是现代 Web 编程的一部分。所以不要试图解决它(即让代码等待数据)。相反:尝试将您的问题重新构建为更适合这种异步性质的模型。

我发现从“首先加载数据,然后在 toast 中显示”到“开始加载数据,然后在加载后,在 toast 中显示”,最容易重新构建我的问题。这转换为以下代码:

FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(path);   

myRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        bg=dataSnapshot.getValue().toString();
        Toast.makeText(MapsActivity.this, bg, Toast.LENGTH_SHORT).show();  
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
});

【讨论】:

  • 谢谢!但是如果我必须在其他地方使用“bg”的值,比如 onMapReady(),我该怎么办?
  • 你不能。相反,您必须反转条件。例如。 “当数据可用时,我会在地图上绘制它”。
  • 您能告诉我如何以编程方式进行吗?我对安卓很陌生。提前致谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多