我正在使用Firebase来保存我的数据。我正在尝试将Firebase方法和我的方法分离到活动上。例如,我创建了一个名为“FirebaseMethodsHelper”的类,我想编写所有Firebase方法。 例如,“getAllUsers”方法应该在列表中返回所有用户。 我唯一的问题是它不起作用。
我不知道我做错了什么,所以如果你们请帮助我。
片段
public class MyPlayListFragment extends Fragment {
private FirebaseDatabase refToVideos;
private FirebaseUser currentUser;
private ArrayList<Video> videosList;
private VideoViewAdapter adapter;
private RecyclerView rvVideos;
private List<Video> checkList;
public MyPlayListFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_my_play_list, container, false);
rvVideos = (RecyclerView)v.findViewById(R.id.rvVideos);
return v;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
videosList = new ArrayList<>();
refToVideos = FirebaseDatabase.getInstance();
currentUser = FirebaseAuth.getInstance().getCurrentUser();
FirebaseMethodsHelper fmh = new FirebaseMethodsHelper();
checkList = fmh.getAllVideosFromDB(currentUser);
if(checkList != null)
Log.d("checkList",checkList.toString());
FirebaseMethodHelper类
public class FirebaseMethodsHelper {
private FirebaseDatabase databaseRef;
private ArrayList<User> usersList;
private ArrayList<Video> videosList;
public List<Video> getAllVideosFromDB(FirebaseUser currentUser){
databaseRef = FirebaseDatabase.getInstance();
databaseRef.getReference(Params.VIDEOS).child(currentUser.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
videosList.add(snapshot.getValue(Video.class));
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
return videosList;
}
}
我不知道为什么,但它总是返回null。
答案 0 :(得分:2)
这是异步Web API的典型问题:您无法返回尚未加载的现在。
Firebase数据库(和大多数现代Web API)数据是异步加载的,因为它可能需要一些时间。在将数据加载到辅助线程上时,主应用程序代码将继续,而不是等待数据(这将导致用户的应用程序无响应对话框)。然后,当数据可用时,将调用onDataChange()
方法并使用该数据。
这会更改您应用的流量。最简单的方法是查看几个日志语句:
public List<Video> getAllVideosFromDB(FirebaseUser currentUser){
databaseRef = FirebaseDatabase.getInstance();
System.out.println("Before attaching listener");
databaseRef.getReference(Params.VIDEOS).child(currentUser.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Got data");
}
@Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
System.out.println("After attaching listener");
}
运行此代码将打印以下序列:
在附加监听器之前
附加监听器后
获得数据
这可能不是您所期望的,但是在您返回数组时,该数组为空的原因正是如此。
大多数开发人员的初步回应是尝试&#34;修复&#34;这种异步行为。我建议不要这样做:网络是异步的,越早接受,就越早了解如何通过现代Web API提高效率。
我发现为这种异步范例重新构建问题最容易。而不是说'#34;首先获取所有视频,然后记录它们&#34;,我将问题定格为&#34;开始获取所有视频。加载视频后,请记录下来&#34;。
这意味着任何需要视频的代码必须位于onDataChange()
内(或从内部调用)。 E.g:
public List<Video> getAllVideosFromDB(FirebaseUser currentUser){
databaseRef = FirebaseDatabase.getInstance();
databaseRef.getReference(Params.VIDEOS).child(currentUser.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
videosList.add(snapshot.getValue(Video.class));
}
if (videosList != null) {
Log.d("checkList",videosList.toString());
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
}
正如我所说,对于以前没有处理异步API的开发人员而言,这是一个常见问题。因此,已经有很多关于这个主题的问题,我建议你也检查一下:
答案 1 :(得分:1)
它返回null,因为列表videosList
尚未初始化。此外,您在方法中返回列表的方法是错误的。 firebase调用是异步的,因此您无法控制数据何时可用,因此几乎每次您的方法将返回null(如果列表尚未初始化)或空列表(如果您初始化它)。< / p>
解决方案是使用界面回调,这会根据需要将视频列表返回到您的片段:
public interface FirebaseCallback {
void listVideos(ArrayList<Video> videos);
}
此方法将从您的FirebaseMethodHelper
类调用:
public void getAllVideosFromDB(FirebaseUser currentUser, FirebaseCallback callback){
databaseRef = FirebaseDatabase.getInstance();
databaseRef.getReference(Params.VIDEOS).child(currentUser.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
ArrayList<Video> videosList = new ArrayList<>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
videosList.add(snapshot.getValue(Video.class));
}
callback.listVideos(videosList);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
从您的片段中,您实现了接口并将this
传递给方法,如下所示:
public class MyPlayListFragment extends Fragment implements FirebaseCallback {
private FirebaseDatabase refToVideos;
private FirebaseUser currentUser;
private ArrayList<Video> videosList;
private VideoViewAdapter adapter;
private RecyclerView rvVideos;
private List<Video> checkList;
public MyPlayListFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_my_play_list, container, false);
rvVideos = (RecyclerView)v.findViewById(R.id.rvVideos);
return v;
}
@Override
public void listVideos(ArrayList<Video> videos) {
//do what you want with the videos
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
videosList = new ArrayList<>();
refToVideos = FirebaseDatabase.getInstance();
currentUser = FirebaseAuth.getInstance().getCurrentUser();
FirebaseMethodsHelper fmh = new FirebaseMethodsHelper();
checkList = fmh.getAllVideosFromDB(currentUser, this);
if(checkList != null)
Log.d("checkList",checkList.toString());