在 Firebase 侦听器中设置 Singleton 属性值 [英] Setting Singleton property value in Firebase Listener

查看:20
本文介绍了在 Firebase 侦听器中设置 Singleton 属性值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在测试 Firebase 以及我计划在整个应用的生命周期中访问的单例模型.我现在被一些看似微不足道的事情困住了,但我一生都无法弄清楚.我有一个我使用的模型示例:Firebase 中的书签.

I'm currently testing out Firebase along with a Singleton model I plan to use to access during the lifecycle of the whole app. I'm now stuck with something that seems really trivial but I can't figure it out for the life of me. I have a sample of the model I use: Bookmarks in firebase.

public class BookSingleton {



private static BookSingleton model;

private ArrayList<BookMark> bookmarks = new ArrayList<BookMark>();


public static BookSingleton getModel()
{
    if (model == null)
    {
        throw new IllegalStateException("The model has not been initialised yet.");
    }

    return model;
}


public ArrayList<Bookmark> theBookmarkList()
{
    return this.bookmarks;
}


public void setBookmarks(ArrayList<Bookmark> bookmarks){
    this.bookmarks = bookmarks;
}


public void loadModelWithDataFromFirebase(){
    Firebase db = new Firebase(//url);
    Firebase bookmarksRef = fb.child(//access correct child);


    final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
    bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
                    //getting all properties from firebase...
                    Bookmark bookmark = new Bookmark(//properties here);
                    loadedBookmarks.add(bookmark);



                }
            }
            //bookmarks still exist here at this point
            setBookmarks(loadedBookmarks);

        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {

        }
    });
    //by now loadedBookmarks is empty
    //this is probably the issue?
    //even without this line bookmarks is still not set in mainactivity
    setBookmarks(loadedBookmarks);
}

现在,当我使用 Singleton 集的实例启动 mainActivity 时,我得到一个空错误,因为显然我编写的从 firebase 加载模型数据的函数没有设置任何内容.

Now when I start the mainActivity with the instance of the Singleton set I get a null error because clearly the function I wrote to load the model data from firebase sets nothing.

是这样的:MainActivity

public class MainActivity extends AppCompatActivity {

private BookSingleton theModel;



@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    // Load the model
    theModel = BookSingleton.getModel(this);
      //manually setting this works 
      //        ArrayList<Book> bookSamples = new ArrayList<Book>;
      //        bookSamples.add(aBookSample);

    theModel.loadModelWithSampleData(bookSamples);
    //should have set the singleton model property Bookmarks to the results from firebase

    theModel.loadModelWithDataFromFirebase();
    //returns 0
    Log.d(TAG, "" + theModel.theBookmarkList().size());


    setContentView(R.layout.activity_main);

    //......rest of code

我怎样才能做到这一点?

How can I make this work?

推荐答案

Firebase 加载和同步数据异步.所以您的 loadModelWithDataFromFirebase() 不会等待加载完成,它只是开始从数据库加载数据.当您的 loadModelWithDataFromFirebase() 函数返回时,加载尚未完成.

Firebase loads and synchronizes data asynchronously. So your loadModelWithDataFromFirebase() doesn't wait for the loading to finish, it just starts loading the data from the database. By the time your loadModelWithDataFromFirebase() function returns, the loading hasn't finished yet.

您可以使用一些位置良好的日志语句轻松地自己测试:

You can easily test this for yourself with some well-placed log statements:

public void loadModelWithDataFromFirebase(){
    Firebase db = new Firebase(//url);
    Firebase bookmarksRef = fb.child(//access correct child);

    Log.v("Async101", "Start loading bookmarks");
    final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
    bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Log.v("Async101", "Done loading bookmarks");
            //getting all properties from firebase...
            Bookmark bookmark = new Bookmark(//properties here);
            loadedBookmarks.add(bookmark);
        }

        @Override
        public void onCancelled(FirebaseError error) { throw error.toException(); }
    });
    Log.v("Async101", "Returning loaded bookmarks");
    setBookmarks(loadedBookmarks);
}

与您可能期望的相反,日志语句的顺序是:

Contrary to what you likely expect, the order of the log statements will be:

Start loading bookmarks
Returning loaded bookmarks
Done loading bookmarks

您有两种选择来处理这种加载的异步性质:

You have two choice for dealing with the asynchronous nature of this loading:

  1. 消除异步错误(通常伴随着喃喃自语之类的短语:这是一个错误,这些人不知道他们在做什么")

  1. squash the asynchronous bug (usually accompanied by muttering of phrases like: "it was a mistake, these people don't know what they're doing")

拥抱异步野兽(通常伴随相当多的几个小时的诅咒,但过了一段时间后就会平静和表现更好的应用程序)

embrace the asynchronous beast (usually accompanied by quite some hours of cursing, but after a while by peace and better behaved applications)

服用蓝色药丸 - 使异步调用同步运行

如果您想选择第一个选项,一个放置得当的同步原语可以解决问题:

Take the blue pill - make the asynchronous call behave synchronously

If you feel like picking the first option, a well placed synchronization primitive will do the trick:

public void loadModelWithDataFromFirebase() throws InterruptedException {
    Firebase db = new Firebase(//url);
    Firebase bookmarksRef = fb.child(//access correct child);

    Semaphore semaphore = new Semaphore(0);

    final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
    bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Bookmark bookmark = new Bookmark(//properties here);
            loadedBookmarks.add(bookmark);
            semaphore.release();
        }

        @Override
        public void onCancelled(FirebaseError error) { throw error.toException(); }
    });
    semaphore.acquire();
    setBookmarks(loadedBookmarks);
}

更新 (20160303):当我刚刚在 Android 上测试时,它阻止了我的应用程序.它在常规 JVM 上运行良好,但 Android 在线程方面更加挑剔.随意尝试并使其工作...或

Update (20160303): when I just tested this on Android, it blocked my app. It works on a regular JVM fine, but Android is more finicky when it comes to threading. Feel free to try and make it work... or

如果您选择采用异步编程,则应重新考虑应用程序的逻辑.

If you instead choose to embrace asynchronous programming, you should rethink your application's logic.

您当前有首先加载书签.然后加载示例数据.然后加载更多."

You currently have "First load the bookmarks. Then load the sample data. And then load even more."

对于异步加载模型,您应该这样想:无论何时加载书签,我都想加载示例数据.每当加载了示例数据时,我都想加载更多."

With an asynchronous loading model, you should think like "Whenever the bookmarks have loaded, I want to load the sample data. Whenever the sample data has loaded, I want to load even more."

这种想法的好处是,当数据可能不断变化并因此多次同步时,它也能工作:每当书签发生变化时,我也想加载示例数据.每当样本数据发生变化时,我都想加载更多."

The bonus of thinking this way is that it also works when the data may be constantly changing and thus synchronized multiple times: "Whenever the bookmarks change, I want to also load the sample data. Whenever the sample data changes, I want to load even more."

在代码中,这会导致嵌套调用或事件链:

In code, this leads to nested calls or event chains:

public void synchronizeBookmarks(){
    Firebase db = new Firebase(//url);
    Firebase bookmarksRef = fb.child(//access correct child);

    final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
    bookmarksRef.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Bookmark bookmark = new Bookmark(//properties here);
            loadedBookmarks.add(bookmark);
            setBookmarks(loadedBookmarks);
            loadSampleData();
        }

        @Override
        public void onCancelled(FirebaseError error) { throw error.toException(); }
    });
}

在上面的代码中,我们不只是等待单个值事件,而是处理所有事件.这意味着无论何时更改书签,都会执行 onDataChange 并且我们(重新)加载示例数据(或任何其他适合您的应用程序需要的操作).

In the above code we don't just wait for a single value event, we instead deal with all of them. This means that whenever the bookmarks are changed, the onDataChange is executed and we (re)load the sample data (or whatever other action fits your application's needs).

为了使代码更可重用,您可能需要定义自己的回调接口,而不是在onDataChange 中调用精确的代码.看看这个答案的一个很好的例子.

To make the code more reusable, you may want to define your own callback interface, instead of calling the precise code in onDataChange. Have a look at this answer for a good example of that.

这篇关于在 Firebase 侦听器中设置 Singleton 属性值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆