Firebase 캐시를 바이패스하여(Android 앱에서) 데이터를 새로 고치는 방법은 무엇입니까?
대부분의 시간을 오프라인에서 작동해야 하는 Android 응용 프로그램에서 온라인일 때 다음과 같은 동기화 작업을 수행해야 합니다.
User myUser = MyclientFacade.getUser();
If (myUser.getScore > 10) {
DoSomething()
}
여기서 사용자는 Firebase로 채워진 POJO;
이 문제는 Firebase 캐시가 활성화될 때 발생합니다.
Firebase.getDefaultConfig().setPersistenceEnabled(true);
사용자는 이미 캐시에 있으며 데이터는 타사(또는 다른 장치)에 의해 Firebase DB에서 업데이트됩니다.실제로 사용자를 가져오기 위해 Firebase에 쿼리할 때 먼저 캐시에서 데이터를 얻고 나중에 Firebase 서버에서 최신 데이터로 두 번째 변경 이벤트를 얻지만 너무 늦었습니다!
동기 메서드 MyclientFacade.getUser()를 살펴보겠습니다.
Public User getUser() {
Firebase ref = myFireBaseroot.child("User").child(uid);
ref.keepSynced(true);
/* try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
final CountDownLatch signal = new CountDownLatch(1);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
//ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
this.user = dataSnapshot.getValue(User.class);
signal.countDown();
}
@Override
public void onCancelled(FirebaseError firebaseError) {
signal.countDown();
}
});
signal.await(5, TimeUnit.SECONDS);
ref.keepSynced(false);
return this.user;
}
를 사용하면 동일한 동작이 발생합니다.addValueEventListener
또는addListenerForSingleValueEvent
ref.keepSynced
:
내 사용자의 캐시 점수 값이 5이고 Firebase DB의 점수 값이 11이라고 가정해 보겠습니다.
가 내가전때를 부를 때.getUser
cache first)을이기 때문에 5점(Firebase ask cache first)을 입니다. 그래서 저는 전화를 하지 않을 것입니다.doSomething()
방법.
내가 댓글을 달면Thread.sleep()
내 예에서 나온 코드, Firebase 캐시는 업데이트하기에 충분한 시간이 있을 것이고 나의getUser
올바른 점수 값(11)을 반환합니다.
그렇다면 서버 측에서 직접 최신 값을 묻고 캐시를 우회하려면 어떻게 해야 합니까?
이것은 저의 지원서에도 많은 스트레스를 주는 문제였습니다.
부터 시작해서 것을 했습니다..addListenerForSingleValueEvent()
.addValueEventListener()
하려고 노력하다.keepSynced()
더해을사위지기하용연더)을합니다.Thread.sleep()
위에서 설명한 방법) 및 실제로 일관되게 작동한 것은 없습니다(심지어는Thread.sleep()
운영 앱에서 실제로 허용되지 않는 방법)은 일관된 결과를 제공하지 않았습니다.
그래서 제가 한 것은 이것이었습니다: 쿼리 개체를 만들고 호출한 후에..keepSynced()
그리고 쿼리하는 노드에 mock/token 객체를 작성한 후 해당 작업의 완료 수신기에서 mock 객체를 삭제한 후 원하는 데이터 검색을 수행합니다.
다음과 같은 것:
MockObject mock = new MockObject();
mock.setIdentifier("delete!");
final Query query = firebase.child("node1").child("node2");
query.keepSynced(true);
firebase.child("node1").child("node2").child("refreshMock")
.setValue(mock, new CompletionListener() {
@Override
public void onComplete(FirebaseError error, Firebase afb) {
query.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot data) {
// get identifier and recognise that this data
// shouldn't be used
// it's also probably a good idea to remove the
// mock object
// using the removeValue() method in its
// speficic node so
// that the database won't be saddled with a new
// one in every
// read operation
}
public void onCancelled(FirebaseError error) {
}
});
}
});
}
이것은 지금까지 저에게 꾸준히 효과가 있었습니다! (글쎄요, 하루 정도 동안은, 그러니 이것을 소금에 절여서 드세요.읽기 전에 쓰기 작업을 수행하는 것이 캐시를 우회하는 것처럼 보이는데, 이는 타당합니다.따라서 데이터가 신선하게 반환됩니다.
유일한 단점은 읽기 작업 전에 추가 쓰기 작업을 수행하는 것으로, 약간의 지연이 발생할 수 있습니다(분명히 작은 개체 사용). 하지만 항상 새로 생성되는 데이터에 대한 가격이라면, 저는 그것을 받아들이겠습니다!
이것이 도움이 되길 바랍니다!
발견한 방법은 Firebase의 방법은해결의견발 Firebase한을 입니다.runTransaction()
.이것은 항상 서버에서 데이터를 검색하는 것으로 나타납니다.
String firebaseUrl = "/some/user/datapath/";
final Firebase firebaseClient = new Firebase(firebaseUrl);
// Use runTransaction to bypass cached DataSnapshot
firebaseClient.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
// Return passed in data
return Transaction.success(mutableData);
}
@Override
public void onComplete(FirebaseError firebaseError, boolean success, DataSnapshot dataSnapshot) {
if (firebaseError != null || !success || dataSnapshot == null) {
System.out.println("Failed to get DataSnapshot");
} else {
System.out.println("Successfully get DataSnapshot");
//handle data here
}
}
});
제 솔루션은 로드업 시 Database.database().isPersistenceEnabled = true를 호출한 다음 새로 고침이 필요한 노드에서 .keepSynced(true)를 호출하는 것이었습니다.
여기서 문제는 .keepSynced(true) 직후에 노드를 쿼리하면 새 데이터가 아닌 캐시를 얻을 가능성이 높다는 것입니다.약간 어설프지만 기능적인 해결 방법: Firebase가 새로운 데이터를 얻을 수 있는 시간을 주기 위해 노드에 대한 쿼리를 1초 정도 지연시킵니다.사용자가 오프라인 상태인 경우 대신 캐시를 받을 수 있습니다.
아, 그리고 백그라운드에서 항상 최신 상태로 유지하고 싶지 않은 노드라면 작업이 끝나면 .keepSynced(false)를 호출하는 것을 기억하십시오.
이 를 이코를추오시십하에 .onCreate
메서드를 선택할 수 있습니다.(데이터베이스 참조 변경)
예:
public class MyApplication extendes Application{
@Override
public void onCreate() {
super.onCreate();
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);
}
}
저한테는 잘 맞습니다.
참조: https://firebase.google.com/docs/database/android/offline-capabilities
저는 수락된 해결책과 거래를 모두 시도했습니다.트랜잭션 솔루션이 더 깨끗하고 좋지만 success OnComplete는 db가 온라인 상태일 때만 호출되므로 캐시에서 로드할 수 없습니다.그러나 트랜잭션을 중단할 수 있으며 오프라인(캐시된 데이터 포함)에서도 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)
removeListener()
}
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)
removeListener()
}
override fun doTransaction(p0: MutableData?): Transaction.Result {
return Transaction.success(p0)
}
}
오프라인에서 속도를 높이려면(분명히 데이터베이스가 연결되어 있지 않은 상태에서 시간 초과를 무시하고 기다리지 않으려면) 위의 기능을 사용하기 전에 데이터베이스가 연결되어 있는지 확인하십시오.
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");
}
});
이 줄 사용
FirebaseDatabase.getInstance().purgeOutstandwrites();
Flutter에서 실시간 Db에서 최신 스냅샷을 강제로 가져오는 방법은 다음과 같습니다.
Future<DataSnapshot> _latestRtdbSnapshot(DatabaseReference dbRef) async {
// ! **NASTY HACK**: Write/delete a child of the query to force an update.
await dbRef.keepSynced(true);
await dbRef.child('refreshMock').remove();
final res = await dbRef.once();
dbRef.keepSynced(false);
return res;
}
늦은 건 알지만 같은 문제에 직면한 누군가를 위해 마침내 완벽한 해결책을 찾았습니다.다음 방법을 사용하여 캐시된 메모리에서 데이터가 전송되는지 확인할 수 있습니다.
if (documentSnapShot.metadata.isFromCache){
// data is coming from the cached memory
} else {
// data is coming from the server
}
다음은 전체 코드 조각입니다.Kotlin
:
gameRoomRef.addSnapshotListener { documentSnapshot, fireStoreException ->
fireStoreException?.let {
return@addSnapshotListener
}
documentSnapshot?.let {
if (it.metadata.isFromCache)
// data is from cached memory
else
// data is from server
}
언급URL : https://stackoverflow.com/questions/35454652/how-to-bypass-the-firebase-cache-to-refresh-data-in-android-app
'programing' 카테고리의 다른 글
입력이 C의 정수 유형인지 확인합니다. (0) | 2023.06.14 |
---|---|
Firestore에서 모든 하위 컬렉션과 중첩된 하위 컬렉션이 있는 문서 삭제 (0) | 2023.06.14 |
마이그레이션 변경 열을 FK로 후속 처리한 후 실패 취소 (0) | 2023.06.14 |
기본 쿼리에서 다중 위치 절이 작동하지 않는 이유 (0) | 2023.06.14 |
URL에서 마지막 슬래시 이후의 모든 항목을 가져오는 방법은 무엇입니까? (0) | 2023.06.14 |