programing

단일 값 이벤트에 대한 Firebase 오프라인 기능 및 청취자 추가

powerit 2023. 7. 2. 21:04
반응형

단일 값 이벤트에 대한 Firebase 오프라인 기능 및 청취자 추가

사할때다마를 addListenerForSingleValueEvent와 함께setPersistenceEnabled(true)로컬 오프라인 복사본만 얻을 수 있습니다.DataSnapshot업데이트된 것이 표시됩니다.DataSnapshot서버에서.

하지만, 만약 내가 사용한다면.addValueEventListener와 함께setPersistenceEnabled(true)의 최신 복사본을 얻을 수 있습니다.DataSnapshot서버에서.

이것이 정상입니까?addListenerForSingleValueEventDataSnapshot로컬로로 하고 수신기를 성공적으로 한 후 합니다.DataSnapshot ONCE(오프라인 또는 온라인)?

업데이트(2021):원하는 동작을 구현하는 새로운 메서드 호출(getAndroid 및 iOS)이 있습니다. 먼저 서버에서 최신 값을 가져오려고 시도하고 서버에 연결할 수 없을 때만 캐시로 돌아갑니다.영구 수신기를 사용하는 권장 사항은 계속 적용되지만, 로컬 캐싱을 사용하는 경우에도 데이터를 한 번 가져올 수 있는 더 깨끗한 옵션이 있습니다.


지속성 작동 방식

Firebase 클라이언트는 사용자가 현재 듣고 있는 모든 데이터의 복사본을 메모리에 보관합니다.마지막 수신기의 연결이 끊어지면 메모리에서 데이터가 플러시됩니다.

Firebase Android 응용 프로그램에서 다음을 사용하여 디스크 지속성을 활성화하는 경우:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

Firebase 클라이언트는 앱이 최근에 청취한 모든 데이터의 로컬 복사본(디스크에)을 보관합니다.

수신기를 연결할 때 발생하는 작업

과 같은 다을가있가니다합정다고고가 해보세요.ValueEventListener:

ValueEventListener listener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        System.out.println(snapshot.getValue());
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
        // No-op
    }
};

를 할 때ValueEventListener위치:

ref.addValueEventListener(listener); 
// OR
ref.addListenerForSingleValueEvent(listener); 

캐시에 에서 " " " " " " " " " " " " " " " " " " " " " " " " 를 합니다.onDataChange()로컬 캐시의 해당 값에 대해 즉시 적용됩니다.그러면 이 값에 대한 업데이트를 요청하기 위해 서버에 대한 검사도 시작합니다.나중에 를 호출할 수 있습니다.onDataChange()캐시에 마지막으로 추가된 이후 서버의 데이터가 변경된 경우 다시 검색합니다.

사용시발작업는하를 사용하면 ?addListenerForSingleValueEvent

단일 값 이벤트 수신기를 동일한 위치에 추가하는 경우:

ref.addListenerForSingleValueEvent(listener);

는 (로) Firebase를 즉시 onDataChange()로컬 디스크 캐시의 값입니다.다음을 호출하지 않습니다.onDataChange()서버의 값이 다른 것으로 밝혀지더라도 더 이상 사용할 수 없습니다.업데이트된 데이터는 계속 요청되고 이후 요청 시 반환됩니다.

이 내용은 이전에 공유 데이터를 사용한 Firebase 동기화 작동 방식에 대해 설명했습니다.

솔루션 및 해결 방법

가장 좋은 해결책은 단일 값 이벤트 수신기 대신 를 사용하는 것입니다.일반 값 수신기는 서버에서 즉시 로컬 이벤트와 잠재적인 업데이트를 모두 가져옵니다.

두 번째 해결책은 새로운 방법(2021년 초에 도입)을 사용하는 것인데, 이 방법은 이러한 문제가 없는 동작입니다.이 메서드는 항상 먼저 서버에서 값을 가져오려고 하므로 완료하는 데 시간이 더 오래 걸립니다.값이 변경되지 않더라도 사용하는 것이 더 나을 수 있습니다.addListenerForSingleValueEvent(하지만 그 경우에는 이 페이지에 나타나지 않았을 수도 있습니다.)

해결 방법으로 단일 값 이벤트 수신기를 사용하는 위치를 호출할 수도 있습니다.이렇게 하면 데이터가 변경될 때마다 데이터가 업데이트되므로 단일 값 이벤트 수신기에서 현재 값을 볼 수 있는 가능성이 크게 향상됩니다.

그래서 저는 이것에 대한 해결책을 가지고 있습니다.필요한 경우 ValueEventListener를 사용하고 0.5초 후 수신기를 제거하여 업데이트된 데이터를 확보했는지 확인하기만 하면 됩니다.실시간 데이터베이스는 대기 시간이 매우 짧기 때문에 안전합니다.아래의 안전 코드 예제를 참조하십시오.

public class FirebaseController {

private DatabaseReference mRootRef;
private Handler mHandler = new Handler();

private FirebaseController() {
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);

    mRootRef = FirebaseDatabase.getInstance().getReference();
}

public static FirebaseController getInstance() {
    if (sInstance == null) {
        sInstance = new FirebaseController();
    }
    return sInstance;
}

그런 다음 "addListenerForSingleEvent"를 사용할 방법이 있습니다.

public void getTime(final OnTimeRetrievedListener listener) {
    DatabaseReference ref = mRootRef.child("serverTime");
    ref.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (listener != null) {
                // This can be called twice if data changed on server - SO DEAL WITH IT!
                listener.onTimeRetrieved(dataSnapshot.getValue(Long.class));
            }
            // This can be called twice if data changed on server - SO DEAL WITH IT!
            removeListenerAfter2(ref, this);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            removeListenerAfter2(ref, this);
        }
    });
}

// ValueEventListener version workaround for addListenerForSingleEvent not working.
private void removeListenerAfter2(DatabaseReference ref, ValueEventListener listener) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            HelperUtil.logE("removing listener", FirebaseController.class);
            ref.removeEventListener(listener);
        }
    }, 500);
}

// ChildEventListener version workaround for addListenerForSingleEvent not working.
private void removeListenerAfter2(DatabaseReference ref, ChildEventListener listener) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            HelperUtil.logE("removing listener", FirebaseController.class);
            ref.removeEventListener(listener);
        }
    }, 500);
}

핸들러가 실행되기 전에 앱을 닫더라도 어쨌든 제거됩니다.편집: 참조 경로를 키로 사용하고 데이터 스냅샷을 값으로 사용하여 해시 맵에서 추가 및 제거된 수신기를 추적하기 위해 추상화할 수 있습니다.fetchData 메서드는 "한 번"에 대한 부울 플래그를 사용하여 래핑할 수도 있습니다. 이 방법이 참이면 데이터를 한 번 가져오는 방법을 사용할 수 있습니다. 그렇지 않으면 정상적으로 계속됩니다.천만에요!

트랜잭션을 생성하고 중단할 수 있습니다. 온라인(nline data) 또는 오프라인(cached data) 시 onComplete가 호출됩니다.

나는 이전에 데이터베이스가 동기화할 수 있을 정도로 연결되어 있을 때만 작동하는 기능을 만들었습니다.타임아웃을 추가하여 문제를 해결했습니다.저는 이것을 작업하고 이것이 작동하는지 테스트해 보겠습니다.아마도 미래에 제가 자유 시간이 생기면 안드로이드 lib를 만들어서 출판할 것이지만, 그때쯤이면 코틀린의 코드가 될 것입니다.

/**
     * @param databaseReference reference to parent database node
     * @param callback callback with mutable list which returns list of objects and boolean if data is from cache
     * @param timeOutInMillis if not set it will wait all the time to get data online. If set - when timeout occurs it will send data from cache if exists
     */
    fun readChildrenOnlineElseLocal(databaseReference: DatabaseReference, callback: ((mutableList: MutableList<@kotlin.UnsafeVariance T>, isDataFromCache: Boolean) -> Unit), timeOutInMillis: Long? = null) {

        var countDownTimer: CountDownTimer? = null

        val transactionHandlerAbort = object : Transaction.Handler { //for cache load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, true)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.abort()
            }
        }

        val transactionHandlerSuccess = object : Transaction.Handler { //for online load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                countDownTimer?.cancel()
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, false)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.success(p0)
            }
        }

코드에서 타임아웃이 설정되면 나는 트랜잭션을 중단으로 호출할 타이머를 설정합니다.이 트랜잭션은 오프라인 상태에서도 호출되며 온라인 또는 캐시된 데이터를 제공합니다(이 기능에서는 이 데이터가 캐시된 데이터일 가능성이 매우 높습니다).그런 다음 거래를 성공적으로 부릅니다. OnComplete소방 데이터베이스에서 응답이 있을 경우에만 호출됩니다.이제 타이머(null이 아닌 경우)를 취소하고 콜백으로 데이터를 전송할 수 있습니다.

이 구현을 통해 개발자는 데이터가 캐시에서 전송되거나 온라인 데이터임을 99% 확인할 수 있습니다.

오프라인에서 속도를 높이려면(분명히 데이터베이스가 연결되어 있지 않은 상태에서 시간 초과를 무시하고 기다리지 않으려면) 위의 기능을 사용하기 전에 데이터베이스가 연결되어 있는지 확인하십시오.

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    boolean connected = snapshot.getValue(Boolean.class);
    if (connected) {
      System.out.println("connected");
    } else {
      System.out.println("not connected");
    }
  }

  @Override
  public void onCancelled(DatabaseError error) {
    System.err.println("Listener was cancelled");
  }
});

지속성이 활성화된 workinkg에서 수신자가 onDataChange()로 호출을 받은 횟수를 세어 2회 수신을 중지했습니다.나를 위해 일했고, 아마도 도움이 될 것입니다.

private int timesRead;
private ValueEventListener listener;
private DatabaseReference ref;

private void readFB() {
    timesRead = 0;
    if (ref == null) {
        ref = mFBDatabase.child("URL");
    }

    if (listener == null) {
        listener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                //process dataSnapshot

                timesRead++;
                if (timesRead == 2) {
                    ref.removeEventListener(listener);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        };
    }
    ref.removeEventListener(listener);
    ref.addValueEventListener(listener);
}

언급URL : https://stackoverflow.com/questions/34486417/firebase-offline-capabilities-and-addlistenerforsinglevalueevent

반응형