2017-12-16 1 views
3

자바에 대한 많은 경험이 없습니다. 이 질문이 어리석은 것인지 확실하지 않지만 Firebase Realtime Database에서 사용자 이름을 가져 와서이 이름의 결과를이 메소드의 결과로 반환해야합니다. 그래서 ...이 값을 얻는 방법을 알았지 만이 방법으로 결과를 반환하는 방법을 이해하지 못합니다. 이 작업을 수행하는 가장 좋은 방법은 무엇입니까?메소드의 결과로 dataSnapshot 값을 반환하는 방법은 무엇입니까?

private String getUserName(String uid) { 
    databaseReference.child(String.format("users/%s/name", uid)) 
      .addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // How to return this value? 
      dataSnapshot.getValue(String.class); 
     } 

     @Override 
     public void onCancelled(DatabaseError databaseError) {} 
    }); 
} 
+3

수익을 자사의 무효 방법 당신은 당신이 내부에 추가 외부 쓴 어떤 방법 때문에 어디서든 바로 onDataChange 내에서 사용이 변수를 사용하려면 또한 비동기 sooo를, 일을 못해 .. 반환하지 않습니다 의미 onDataChange –

+1

언급 된 @PeterHaddad처럼 onDataChange 메서드는 비동기 적이므로 항상 null을 반환합니다. 나는 묻는다 : 당신은 왜 사용자 이름을 돌려 줄 필요가 있는가? 왜이 이름을 onDataChange 내에서 사용하지 않는가? –

+2

@PeterHaddad는'onDataChange()'메소드가 비동기라고 불리기 때문에 작동하지 않을 것이라고 말했다. 내 대답을 참조하십시오. –

답변

9

이것은 비동기 웹 API의 고전적인 문제입니다. 아직로드되지 않은 것을 반환 할 수 없습니다. 다른 말로하면, 글로벌 변수를 단순히 생성하여 이 될 것이므로 onDataChange() 메소드 밖에서 사용할 수 없습니다. 이것은 onDataChange() 메서드가 비동기라고 부르기 때문에 발생합니다. 연결 속도 및 상태에 따라 데이터를 사용할 수 있으려면 몇 백 밀리 초에서 몇 초가 걸릴 수 있습니다.

Firebase Realtime Database는 비동기 적으로 데이터를로드 할뿐만 아니라 거의 모든 현대 웹 API가 수행합니다. 시간이 오래 걸릴 수 있기 때문입니다. 따라서 데이터를 기다리지 않고 (사용자를 위해 응답하지 않는 응용 프로그램 대화 상자로 이어질 수 있음), 데이터가 보조 스레드에로드되는 동안 주 응용 프로그램 코드가 계속됩니다. 그런 다음 데이터를 사용할 수있게되면 onDataChange() 메서드가 호출되고 데이터를 사용할 수 있습니다. 다른 말로는, onDataChange() 메서드가 호출 될 때까지 데이터가 아직로드되지 않습니다.

은 좀 더 명확하게 무슨 일이 일어나고 있는지보고, 코드에서 몇 로그 문을 배치하여, 예를 보자. 우리 것이 코드를 실행하면

private String getUserName(String uid) { 
    Log.d("TAG", "Before attaching the listener!"); 
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // How to return this value? 
      dataSnapshot.getValue(String.class); 
      Log.d("TAG", "Inside onDataChange() method!"); 
     } 

     @Override 
     public void onCancelled(DatabaseError databaseError) {} 
    }); 
    Log.d("TAG", "After attaching the listener!"); 
} 

, 출력은 줘야 :

이 아마 청취자에게

있어 데이터

를 부착 한 후 청취자

를 장착하기 전에 당신이 기대 한 것이 아니지만, 당신의 d ata si null을 반환 할 때. 대부분의 개발자를위한

초기 반응은 시도하고 나는 개인적으로이에 대한 추천이 asynchronous behavior을 "수정"하는 것입니다. 웹은 비동기식이며, 동의하는 것이 빠르면 빠를수록 최신 웹 API로 생산성을 높일 수있는 방법을 배울 수 있습니다.

나는이 비동기 패러다임에 대한 문제를 재구성하는 것이 가장 쉬운 방법을 발견했습니다. "먼저 데이터를 가져온 다음 로그에 기록"하는 대신 "데이터를 가져 오기 시작하십시오. 데이터가로드되면 로그에 기록하십시오." 이 데이터를 필요로하는 코드가 onDataChange() 방법 내부에 또는 다음과 같이 내부가 호출해야 함을 의미합니다 :

databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { 
    @Override 
    public void onDataChange(DataSnapshot dataSnapshot) { 
     // How to return this value? 
     if(dataSnapshot != null) { 
      System.out.println(dataSnapshot.getValue(String.class)); 
     } 
    } 

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

그 외부를 사용하려는 경우, 다른 방법이있다. Firebase가 데이터를 반환하기를 기다리기 위해 자신의 콜백을 생성해야합니다. 이를 위해, 먼저이 같은 interface을 만들어야합니다

public interface MyCallback { 
    void onCallback(String value); 
} 

그런 다음 당신은 실제로 데이터베이스에서 데이터를 가져 오는하는 방법을 만들어야합니다. 이 방법은 다음과 같아야합니다

결국
public void readData(MyCallback myCallback) { 
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      String value = dataSnapshot.getValue(String.class); 
      myCallback.onCallback(value); 
     } 

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

단지 readData() 메서드를 호출하고이 같이 필요한 곳 ​​인수로 MyCallback 인터페이스의 인스턴스를 전달 :

readData(new MyCallback() { 
    @Override 
    public void onCallback(String value) { 
     Log.d("TAG", value); 
    } 
}); 

이것은이다 onDataChange() 메서드 밖에서 그 값을 사용할 수있는 유일한 방법입니다. 자세한 내용은 video을 참조하십시오. 당신은 캔트 ANW 곳

3

나는 당신이 무엇을 요구하고 있는지 이해하고 있습니다. fetch 메소드에서 "반환"(본질적으로)하고 싶다고하더라도, 가져 오기가 완료된 후에 검색된 값을 실제로 사용하기를 원한다고 말하는 것으로 충분할 수 있습니다.

  1. 이 클래스
  2. 의 상단에 (당신은 대부분 제대로 한) 당신의 값을 변수를 불러 오기 만들기
  3. 는 클래스의 공용 변수를 설정 : 그렇다면, 이것은 당신이해야 할 일이다 검색된 값과 같습니다.

가져 오기가 성공하면 변수를 사용하여 많은 작업을 수행 할 수 있습니다. 도 4a 및 4b는 몇 가지 간단한 예이다 :

4a. 편집 : 사용의 예로서 , 당신은 당신이 yourNameVariable를 사용하는 클래스에서 실행하는 데 필요한 어떤 다른 트리거 할 수 있습니다 (당신이 확신 할 수 그것을 yourNameVariable null가 아닌)

4B. 편집 : 예를 들어 버튼의 onClickListener에 의해 트리거되는 함수에서 변수를 사용할 수 있습니다.


보십시오. 당신이 당신의 클래스를 통해 원하는 곳

// 1. Create a variable at the top of your class 
private String yourNameVariable; 

// 2. Retrieve your value (which you have done mostly correctly) 
private void getUserName(String uid) { 
    databaseReference.child(String.format("users/%s/name", uid)) 
      .addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // 3. Set the public variable in your class equal to value retrieved 
      yourNameVariable = dataSnapshot.getValue(String.class); 
      // 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null. 
      sayHiToMe(); 
     } 

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

// (part of step 4a) 
public void sayHiToMe() { 
    Log.d(TAG, "hi there, " + yourNameVariable); 
} 

// 4b. use the variable in a function triggered by the onClickListener of a button. 
public void helloButtonWasPressed() { 
    if (yourNameVariable != null) { 
    Log.d(TAG, "hi there, " + yourNameVariable); 
    } 
} 

그런 다음 yourNameVariable를 사용할 수 있습니다.


참고 : 당신이 yourNameVariableonDataChange 이후를 사용하는 경우 null는 아니고, 비동기이며 다른 곳을 사용하려고 할 때 완료되지 않았을 수 있음을 확인해야합니다.

+0

이것은 잘못된 답변입니다.이것은 비동기 메서드에 관해서는 어떻게 진행되고있는 것이 아닙니다. 이 대답을 사용하면 "yourNameVariable"이라는 변수를 "수업 전반에 걸쳐 어디에서든지 사용"할 때 항상 'null'이됩니다. 당신은'onDataChange()'메소드에서만 그것을 사용할 수있다. 그게 전부 야. –

+0

@AlexMamo, 인터페이스를 정의하는 당신의 솔루션은 훌륭하게 작동하지만 코멘트의 다음 부분은 정확하지 않습니다 : "yourNameVariable"은 항상 null입니다. " 인터페이스를 정의 할 필요가없는 좀 더 가벼운 솔루션을 찾는 사람들에게 이것은 잘 작동합니다. 일단 변수가 검색되고 설정되면 결과를 필요로하는 모든 함수를 트리거 할 수 있고 변수가 'null'이 아니라는 것을 알 수 있습니다 (처음에는 firebase에 존재하는 한). 당신이 구체적인 유스 케이스를 볼 수 있도록 예제를 업데이트했다. – Rbar

+0

또한이 메서드를 호출하면 메서드의 비동기 동작과 아무 관련이 없습니다. 이 메소드를 호출하는 것은'onDataChange()'메소드 내부의'Log.d (TAG, "안녕하세요,"+ yourNameVariable); "과 같은 줄입니다. 이것은'onDataChange()'메소드가 트리거 될 때 당신이 그 메소드를 호출한다는 것을 의미합니다. –

관련 문제