Android Studio聊天APP开发:Me 8 Pro真机Long转Timestamp序列化报错
问题:Me 8 Pro真机调试Firebase聊天APP时出现类型转换错误
错误日志
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.chat, PID: 7894 java.lang.RuntimeException: Could not deserialize object. Failed to convert value of type java.lang.Long to Timestamp (found in field 'timestamp')
问题背景
开发聊天APP时,未实现通知功能的代码在模拟器上运行正常,但仅在Me 8 Pro真机调试时抛出上述错误,其他真机可正常运行。已尝试将ChatModel中的Timestamp类型改为long、Long、Date、Number,均无法解决问题。
相关代码
ChatModel.java
package com.example.chat.model; import com.google.firebase.Timestamp; import java.util.Date; public class ChatModel { String message; String user_name; String messageID; String user_image_url; String chat_image; Timestamp timestamp; public ChatModel() { } public ChatModel(String message, String user_name, String messageID, String user_image_url, String chat_image, Timestamp timestamp) { this.message = message; this.user_name = user_name; this.messageID = messageID; this.user_image_url = user_image_url; this.chat_image = chat_image; this.timestamp = timestamp; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getUser_name() { return user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } public String getMessageID() { return messageID; } public void setMessageID(String messageID) { this.messageID = messageID; } public String getUser_image_url() { return user_image_url; } public void setUser_image_url(String user_image_url) { this.user_image_url = user_image_url; } public String getChat_image() { return chat_image; } public void setChat_image(String chat_image) { this.chat_image = chat_image; } public Timestamp getTimestamp() { return timestamp; } public void setTimestamp(Timestamp timestamp) { this.timestamp = timestamp; } }
MainActivity.java
package com.example.chat; import static com.example.chat.cords.FirebaseCords.MAIN_CHAT_DATABASE; import static com.example.chat.cords.FirebaseCords.mAuth; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.example.chat.adapter.ChatAdapter; import com.example.chat.cords.FirebaseCords; import com.example.chat.model.ChatModel; import com.example.chat.model.SaveState; import com.firebase.ui.firestore.FirestoreRecyclerAdapter; import com.firebase.ui.firestore.FirestoreRecyclerOptions; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.firestore.FieldValue; import com.google.firebase.firestore.Query; import com.theartofdev.edmodo.cropper.CropImage; import com.theartofdev.edmodo.cropper.CropImageView; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; public class MainActivity extends AppCompatActivity { @Override public void onStart() { super.onStart(); FirebaseUser currentUser = mAuth.getCurrentUser(); if (currentUser == null) { startActivity(new Intent(this, LoginActivity.class)); finish(); } chatAdapter.startListening(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.chat_room, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.logout: mAuth.signOut(); startActivity((new Intent(this, LoginActivity.class))); break; } return super.onOptionsItemSelected(item); } EditText chat_box; RecyclerView chat_list; ChatAdapter chatAdapter; Uri imageUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); chat_box = findViewById(R.id.chat_box); chat_list = findViewById(R.id.chat_list); initChatList(); } private void initChatList() { chat_list.setHasFixedSize(true); chat_list.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)); Query query = MAIN_CHAT_DATABASE.orderBy("timestamp", Query.Direction.DESCENDING); FirestoreRecyclerOptions<ChatModel> option = new FirestoreRecyclerOptions.Builder<ChatModel>() .setQuery(query, ChatModel.class) .build(); chatAdapter = new ChatAdapter(option); chat_list.setAdapter(chatAdapter); chatAdapter.startListening(); } public void addMessage(View view) { String message = chat_box.getText().toString(); FirebaseUser user = mAuth.getCurrentUser(); if (!TextUtils.isEmpty(message)) { Date today = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String messageID = format.format(today); String user_image_url = ""; Uri photoUrl = user.getPhotoUrl(); String originalUrl = "s96-c/photo.jpg"; String resizeImageUrl = "s400-c/photo.jpg"; if (photoUrl!=null) { String photoPath = photoUrl.toString(); user_image_url = photoPath.replace(originalUrl,resizeImageUrl); } HashMap<String, Object> messageObj = new HashMap<>(); messageObj.put("message", message); messageObj.put("user_name", user.getDisplayName()); messageObj.put("timestamp",FieldValue.serverTimestamp()); messageObj.put("messageID", messageID); messageObj.put("chat_image",""); messageObj.put("user_image_url", user_image_url); MAIN_CHAT_DATABASE.document(messageID).set(messageObj).addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { Toast.makeText(MainActivity.this, "Message send", Toast.LENGTH_SHORT).show(); chat_box.setText(""); } else { Toast.makeText(MainActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show(); } } }); } } public void OpenExplorer(View view) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { ChoseImage(); } else { if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 20); } else { Toast.makeText(this, "Storage Permission Needed", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 20); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 20) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show(); ChoseImage(); } else { Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show(); } } } private void ChoseImage(){ CropImage.activity() .setGuidelines(CropImageView.Guidelines.ON) .start(MainActivity.this); } @Override protected void onActivityResult(int requestCode, int resultCode,@Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { CropImage.ActivityResult result = CropImage.getActivityResult(data); Log.d("TAG", "onActivityResult:"+result); if (resultCode == RESULT_OK) { imageUri = result.getUri(); Log.d("TAG", "onActivityResult:"+imageUri); startActivity(new Intent(MainActivity.this, ImageUploadPreview.class) .putExtra("image_uri", imageUri.toString())); } else if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) { Toast.makeText(this, result.getError().getMessage(),Toast.LENGTH_SHORT).show(); } } } public void Finish(View view) { long currentTime = new Date().getTime(); new SaveState(this).setClickTime(currentTime); finish(); } }
ChatAdapter.java
package com.example.chat.adapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.example.chat.R; import com.firebase.ui.firestore.FirestoreRecyclerAdapter; import com.firebase.ui.firestore.FirestoreRecyclerOptions; import com.example.chat.model.ChatModel; import de.hdodenhof.circleimageview.CircleImageView; public class ChatAdapter extends FirestoreRecyclerAdapter<ChatModel, ChatAdapter.ChatViewHolder> { public ChatAdapter (@NonNull FirestoreRecyclerOptions <ChatModel> options) { super(options); } @Override protected void onBindViewHolder (@NonNull ChatViewHolder chatViewHolder, int i, @NonNull ChatModel chatModel) { chatViewHolder.message.setText(chatModel.getMessage()); Glide.with(chatViewHolder.user_image.getContext().getApplicationContext()) .load(chatModel.getUser_image_url()) .into(chatViewHolder.user_image); if(!chatModel.getChat_image().equals("")){ Glide.with(chatViewHolder.chat_image.getContext().getApplicationContext()) .load(chatModel.getChat_image()) .into(chatViewHolder.chat_image); chatViewHolder.chat_image.setVisibility(View.VISIBLE); }else{ chatViewHolder.chat_image.setVisibility(View.GONE); } } @NonNull @Override public ChatViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_item,parent,false); return new ChatViewHolder(v); } class ChatViewHolder extends RecyclerView.ViewHolder { TextView message; CircleImageView user_image; ImageView chat_image; public ChatViewHolder(@NonNull View itemView) { super(itemView); message = itemView.findViewById(R.id.message); user_image = itemView.findViewById(R.id.user_image); chat_image = itemView.findViewById(R.id.chat_image); } } }
数据库说明
数据库中timestamp字段存储的是Firebase服务器生成的时间戳类型。
解决建议
- 更新Firebase依赖版本:Me 8 Pro可能存在系统兼容性问题,将
firebase-firestore、firebase-ui-firestore等相关依赖升级到最新稳定版,确保序列化逻辑在不同设备上一致。 - 添加自定义类型转换器:手动处理Long与Timestamp的转换,避免自动序列化出错:
在public class TimestampConverter { public static Timestamp fromLong(Long value) { return value == null ? null : new Timestamp(new Date(value)); } public static Long toLong(Timestamp timestamp) { return timestamp == null ? null : timestamp.toDate().getTime(); } }ChatModel的timestamp字段上通过Firestore的@PropertyName或自定义序列化逻辑调用转换器,或者使用Firestore的FirestoreSettings配置自定义类型适配。 - 清理数据库旧数据:检查是否存在部分旧数据的
timestamp以Long类型存储,删除或更新这些数据后重新测试。 - 同步设备系统时间:确保Me 8 Pro的系统时间与网络时间一致,避免Firestore客户端因时间偏差导致类型解析异常。
内容的提问来源于stack exchange,提问作者Mario
相关产品推荐
相关产品推荐

