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

点击按钮从Firebase加载数据到RecyclerView时应用崩溃求助

解决你的Firebase+RecyclerView崩溃问题

嘿,我仔细看了你的代码,找出了几个导致应用崩溃的关键问题,咱们一个个来修复:

1. Firebase查询逻辑错误(最可能的崩溃元凶)

你在getUserDetails里明明创建了针对指定手机号的查询query = mUserDB.orderByChild("phone").equalTo(mContact.getPhone()),但后续却给整个user节点加了监听,这不仅会遍历所有用户数据(性能爆炸),还很容易因为数据解析问题引发空指针崩溃。

修复后的代码:

private void getUserDetails(UserObject mContact) {
    DatabaseReference mUserDB = FirebaseDatabase.getInstance().getReference().child("user");
    Query query = mUserDB.orderByChild("phone").equalTo(mContact.getPhone());
    // 把监听绑定到你创建的查询上,而不是整个user节点
    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            if (snapshot.exists()) {
                String phone = "", name = "";
                // 现在遍历的是匹配到的结果,不是全量用户
                for (DataSnapshot childSnapshot : snapshot.getChildren()) {
                    phone = childSnapshot.child("phone").getValue(String.class);
                    name = childSnapshot.child("name").getValue(String.class);
                    UserObject mUser = new UserObject(name, phone);
                    // 避免重复添加数据
                    if (!userList.contains(mUser)) {
                        userList.add(mUser);
                    }
                }
                // 把刷新操作移到循环外,避免多次触发RecyclerView刷新
                mUserListAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
            // 别忽略错误!打印日志方便排查
            Log.e("FirebaseError", "查询失败: " + error.getMessage());
        }
    });
}

2. UserObject缺少无参构造函数

Firebase在反序列化数据的时候必须要有无参构造函数,你的UserObject只有带参构造,这会直接导致数据解析失败抛出异常。

补上无参构造:

public class UserObject {
    String name,phone;

    // 新增无参构造,Firebase必须要
    public UserObject() {}

    public UserObject(String name, String phone){
        this.name = name;
        this.phone = phone;
    }

    // 原有getter和setter保持不变...
}

3. Cursor未关闭导致资源泄漏

你在getContactList里获取了联系人Cursor,但用完之后没关闭,这会导致内存泄漏,严重时会引发运行时异常。

用try-finally确保Cursor关闭:

private void getContactList(){
    String ISOPrefix = getCountryISO();
    Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null );
    try {
        while (phones.moveToNext()){
            String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            String phone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            // 用正则简化字符替换,更简洁
            phone = phone.replaceAll("[\\s\\-()]", "");
            if (!phone.startsWith("+")) 
                phone = ISOPrefix + phone;
            UserObject mContact = new UserObject(name,phone);
            // 建议把联系人先存到contactList,避免和Firebase返回的用户数据重复
            contactList.add(mContact);
            getUserDetails(mContact);
        }
    } finally {
        if (phones != null) {
            phones.close(); // 必须关闭Cursor释放资源
        }
    }
}

4. 权限缺失

你的代码读取了联系人设备和电话状态,必须在AndroidManifest.xml里声明权限:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

而且在Android 6.0(API 23)及以上,需要动态申请权限,不然会直接崩溃。给你的FindUserActivity加上权限申请逻辑:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_find_user);
    contactList = new ArrayList<>();
    userList = new ArrayList<>();

    // 先检查权限,没有就申请
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED
            || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, 
                new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE}, 
                100);
    } else {
        // 权限已获取,初始化列表和数据
        initializeRecyclerView();
        getContactList();
    }
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 100) {
        if (grantResults.length > 0 
                && grantResults[0] == PackageManager.PERMISSION_GRANTED 
                && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
            initializeRecyclerView();
            getContactList();
        } else {
            Toast.makeText(this, "需要权限才能获取联系人数据哦", Toast.LENGTH_SHORT).show();
        }
    }
}

5. RecyclerView的小优化

把setHasFixedSize(true)设为true(如果你的列表项高度固定),能提升RecyclerView的性能,另外用this代替getApplicationContext()作为LayoutManager的上下文,避免潜在的上下文问题:

private void initializeRecyclerView() {
    mUserList= findViewById(R.id.userList);
    mUserList.setNestedScrollingEnabled(false);
    mUserList.setHasFixedSize(true); // 改为true,提升性能
    mUserListLayoutManager= new LinearLayoutManager(this, LinearLayoutManager.VERTICAL,false); 
    mUserList.setLayoutManager(mUserListLayoutManager);
    mUserListAdapter= new UserListAdapter(userList);
    mUserList.setAdapter(mUserListAdapter);
}

额外小建议

  • 给UserObject重写equals和hashCode方法,这样contains方法才能正确判断对象是否存在,避免重复添加
  • 不要忽略任何回调里的错误(比如Firebase的onCancelled),打印日志能帮你快速定位问题
  • 可以考虑用DiffUtil来更新RecyclerView数据,比直接调用notifyDataSetChanged更高效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:02:39