私は同様の問題について他のいくつかの答えを見ましたが、なぜ私のものが機能しないのか理解できません。
Firebaseからコマンドを読み取り、ドローンを動かすようにアプリを取得しようとしています。 firebaseのコマンドは、別のソフトウェアから提供されます。このアプリは、Parrot Drone SDKサンプルコードの上に構築されています。
コマンドオブジェクトからテキストを取得してテキストビューに追加できるようですが、新しい子が追加されるとクラッシュします。新しい子が追加されると、このエラーが発生します。
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.parrot.sdksample, PID: 10592
com.google.firebase.database.DatabaseException: Can't convert object of type Java.lang.String to type com.parrot.sdksample.activity.CommandObject
at com.google.Android.gms.internal.zg.zzb(Unknown Source)
at com.google.Android.gms.internal.zg.zza(Unknown Source)
at com.google.firebase.database.DataSnapshot.getValue(Unknown Source)
at com.parrot.sdksample.activity.MiniDroneActivity$13.onChildAdded(MiniDroneActivity.Java:383)
at com.google.Android.gms.internal.px.zza(Unknown Source)
at com.google.Android.gms.internal.vj.zzHX(Unknown Source)
at com.google.Android.gms.internal.vp.run(Unknown Source)
at Android.os.Handler.handleCallback(Handler.Java:739)
at Android.os.Handler.dispatchMessage(Handler.Java:95)
at Android.os.Looper.loop(Looper.Java:148)
at Android.app.ActivityThread.main(ActivityThread.Java:5417)
at Java.lang.reflect.Method.invoke(Native Method)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:726)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:616)
Firebaseでのデータ構造のサンプルは次のとおりです。
{
"drones" : {
"commands" : {
"-L-n9HwaQktOdI2VEVlH" : {
"executed" : false,
"text" : "TAKE_OFF",
"timestamp" : 1.512686825309134E9
},
"-L-nAuK5Ifde7Cdnan8K" : {
"executed" : false,
"text" : "LAND",
"timestamp" : 1.512687248764272E9
}
}
}
}
Firebaseからデータを取得するアクティビティの関数は次のようになります。
private void initFirebase(){
mCommandTextView = (TextView) findViewById(R.id.commandTextView);
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference commandsRef = database.getReference("drones/commands");
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
CommandObject command = dataSnapshot.getValue(CommandObject.class);
mCommandTextView.append(command.text + "\n");
// I've tried commenting out the if statements below this, but it doesn't seem to make a difference.
if ("TAKE_OFF".equals(command.text)) {
mMiniDrone.takeOff();
} else if ("LAND".equals(command.text)) {
mMiniDrone.land();
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName){
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName){
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
commandsRef.addChildEventListener(childEventListener);
}
私のCommandObjectクラスは次のようになります。
public class CommandObject {
public String text;
public float timestamp;
public boolean executed;
public CommandObject() {
}
public CommandObject(boolean executed, String text, float timestamp){
this.executed = executed;
this.text = text;
this.timestamp = timestamp;
}
}
代わりに値イベントリスナーを使用してみましたが、同じ問題が発生しました。
このエラーが発生しています:
Can't convert object of type Java.lang.String to type com.parrot.sdksample.activity.CommandObject
String
型のCommandObject
型のデータを読み取ろうとしているため、このエラーが発生する理由です。
これらの値を取得するより簡単な方法は、次のようにString
クラスを使用することです。
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference commandsRef = rootRef.child("drones").child("commands");
ValueEventListener eventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
boolean executed = ds.child("executed").getValue(Boolean.class);
String text = ds.child("text").getValue(String.class);
double timestamp = ds.child("timestamp").getValue(Double.class);
Log.d("TAG", executed + " / " + text + " / " + timestamp);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {}
};
commandsRef.addListenerForSingleValueEvent(eventListener);
そして、これはCommandObject
クラスのオブジェクトを使用したアプローチ:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference commandsRef = rootRef.child("drones").child("commands");
ValueEventListener eventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
CommandObject commandObject = ds.getValue(CommandObject.class);
Log.d("TAG", commandObject.getExecuted() + " / " +
commandObject.getText() + " / " +
commandObject.getTimestamp());
}
}
@Override
public void onCancelled(DatabaseError databaseError) {}
};
commandsRef.addListenerForSingleValueEvent(eventListener);
どちらの場合も、出力は次のようになります。
false / TAKE_OFF / 1.512686825309134E9
false / LAND / 1.512687248764272E9
Alexが提供するソリューションは機能しましたが、オブジェクトにデータを保存できない理由にはまったく答えませんでした。また、子供がリスナーを追加している間、アプリはFirebaseに既に存在するが新しいときにデータを読み取ることができます子が追加され、値は空です。
onChildAdded:DataSnapshot { key = -L0JjGo-3QMYDsuTMQcN, value = }
これを引き起こしている可能性のあるものを見つけるためにもう少し掘り下げてみたところ、値がすべて同時に書き込まれていなかったことが原因である可能性が高いことがわかりました。また、firebaseを見ると、最初にキーが追加され、次に値が追加されているようです。これが、エラーが存在しないために変換できないという理由です。 Python Firebase Admin SDKを使用してデータをFirebaseに追加しているため、これが理由であるかどうかはわかりません。
そこで、問題を解決するために、コードをonChildChanged関数に移動し、必要なデータがすべて存在する場合にのみコードが実行されるようにチェックを追加しました。これにより、オブジェクトに保存されている値をすぐに取得できます。
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName){
if (dataSnapshot.child("text").exists() &&
dataSnapshot.child("executed").exists() &&
dataSnapshot.child("timestamp").exists()){
Log.d(TAG, "onChildChanged:" + dataSnapshot.toString());
CommandObject command = dataSnapshot.getValue(CommandObject.class);
if ("TAKE_OFF".equals(command.text)) {
mMiniDrone.takeOff();
} else if ("LAND".equals(command.text)) {
mMiniDrone.land();
}
}
}
アレックスは私に適切なソリューションを投稿しましたが、firebase-uiとkotlinを使用していたため、使用されるコードは異なります。詳細 こちら
val options = FirebaseRecyclerOptions.Builder<ChatMessage>()
.setQuery(query, ChatMessage::class.Java)
.build()
// equivalent options object with manual parsing, use it to debug which field gives error
val options = FirebaseRecyclerOptions.Builder<ChatMessage>()
.setQuery(query, object : SnapshotParser<ChatMessage> {
override fun parseSnapshot(snapshot: DataSnapshot): ChatMessage {
val senderId = snapshot.child("senderId").getValue(String::class.Java)
val receiverId = snapshot.child("receiverId").getValue(String::class.Java)
val senderName = snapshot.child("senderName").getValue(String::class.Java)
val text = snapshot.child("text").getValue(String::class.Java)
val timestamp = snapshot.child("timestamp").getValue(Long::class.Java)
return ChatMessage(senderId, receiverId, senderName, text, timestamp)
}
})
.build()
adapterMessages = object : FirebaseRecyclerAdapter<ChatMessage, ChatHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatHolder {
val rootView = LayoutInflater.from(parent.context).inflate(R.layout.chat_message_item, parent, false)
return ChatHolder(rootView)
}
override fun onBindViewHolder(holder: ChatHolder, position: Int, model: ChatMessage) {
holder.populateItem(model)
}
}
データベースからデータを取得できなかった同様の問題に直面しました。メッセージにDatabaseException
エラーがある
このタイプからそのタイプに変換できません。
間違い私が作っていたのは、データベースに間違って書き込んでいた。私は次のようなものを書きました...
mdatabaseReference.setValue(myObject)
の代わりに
mdatabaseReference.child(myObject.getId()).setValue(myObject)
したがって、基本的に私が提案している答えは、データベースから読み取るときにこの種のエラーが発生しない場合は、JSONツリーの適切なノードでデータベースにデータが正しく書き込まれるようにする必要があります。
私の場合、問題は、Kotlinクラスを使用していて、次のようなデフォルトのコンストラクターがなかったことです。
constructor() : this(0, "")
通常、コンストラクターoopは次のように使用できます。
それは同じでエラーはもうありません
CommandObject command = new CommandObject(ds.child("").getValue()....
それを記入し、firebaseデータの参照が空でないことを確認してください