点击按钮从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
相关产品推荐
相关产品推荐

