You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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服务器生成的时间戳类型。


解决建议

  1. 更新Firebase依赖版本:Me 8 Pro可能存在系统兼容性问题,将firebase-firestore、firebase-ui-firestore等相关依赖升级到最新稳定版,确保序列化逻辑在不同设备上一致。
  2. 添加自定义类型转换器:手动处理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配置自定义类型适配。
  3. 清理数据库旧数据:检查是否存在部分旧数据的timestamp以Long类型存储,删除或更新这些数据后重新测试。
  4. 同步设备系统时间:确保Me 8 Pro的系统时间与网络时间一致,避免Firestore客户端因时间偏差导致类型解析异常。

内容的提问来源于stack exchange,提问作者Mario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 14:22:00