如何对存储加密数据的ArrayList进行排序并在RecyclerView中展示
解决加密数据列表排序与RecyclerView点击位置问题
我来帮你梳理一下问题,你现在的核心痛点是直接对加密字段排序导致顺序不对,以及随之而来的点击位置匹配问题,我们一步步来修复:
1. 修正排序逻辑:基于解密后的明文排序
你之前的sortBy { it.PassItemApp.toString() }是对加密后的字符串排序,这当然得不到按字母顺序排列的结果——加密后的字符串和明文的字符顺序完全无关。我们需要先解密每个PassItem的App名称,再以此为依据排序。
方案一:直接在排序时解密(快速实现)
修改viewDocuments方法中的排序代码,把解密逻辑嵌入到排序条件中:
private fun viewDocuments(){ documentslist = dbHandler.getDocuments(this) // 按解密后的App名称字母排序 documentslist.sortBy { passItem -> // 复用你Adapter中的解密逻辑,获取明文 val initialisationVector = passItem.PassItemIvApp val bytesIV = Base64.decode(initialisationVector, Base64.NO_WRAP) val oisIV = ObjectInputStream(ByteArrayInputStream(bytesIV)) val initializationVectorIV = oisIV.readObject() as ByteArray val encryptedText = passItem.PassItemApp val bytesText = Base64.decode(encryptedText, Base64.NO_WRAP) val oisText = ObjectInputStream(ByteArrayInputStream(bytesText)) val textByteArray = oisText.readObject() as ByteArray val key = passItem.PassItemKeyApp val keyText = Base64.decode(key, Base64.NO_WRAP) val oisKey = ObjectInputStream(ByteArrayInputStream(keyText)) val keyToDecrypt = oisKey.readObject() as SecretKey val aesDecrypt = Decrypted(keyToDecrypt,initializationVectorIV) val decryptedByteArray = aesDecrypt.decrypt(textByteArray) decryptedByteArray.toString(charset("UTF_8")) } adapter = DocumentAdapter(this,documentslist,this) rv = findViewById(R.id.recyclerView) rv.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL,false) rv.adapter = adapter }
方案二:缓存明文(性能优化)
上面的方法每次排序都要重复解密所有项,性能不太友好。如果可以修改PassItem类,建议添加一个明文缓存字段,避免重复解密:
// 修改PassItem类,添加缓存字段 data class PassItem( // 你的原有字段... val PassItemApp: String, val PassItemIvApp: String, val PassItemKeyApp: String, // 新增明文缓存字段 var decryptedAppName: String? = null )
然后在排序前批量解密并缓存:
private fun viewDocuments(){ documentslist = dbHandler.getDocuments(this) // 先批量解密所有项并缓存明文 documentslist.forEach { passItem -> if (passItem.decryptedAppName == null) { // 解密逻辑和之前一致 val initialisationVector = passItem.PassItemIvApp val bytesIV = Base64.decode(initialisationVector, Base64.NO_WRAP) val oisIV = ObjectInputStream(ByteArrayInputStream(bytesIV)) val initializationVectorIV = oisIV.readObject() as ByteArray val encryptedText = passItem.PassItemApp val bytesText = Base64.decode(encryptedText, Base64.NO_WRAP) val oisText = ObjectInputStream(ByteArrayInputStream(bytesText)) val textByteArray = oisText.readObject() as ByteArray val key = passItem.PassItemKeyApp val keyText = Base64.decode(key, Base64.NO_WRAP) val oisKey = ObjectInputStream(ByteArrayInputStream(keyText)) val keyToDecrypt = oisKey.readObject() as SecretKey val aesDecrypt = Decrypted(keyToDecrypt,initializationVectorIV) val decryptedByteArray = aesDecrypt.decrypt(textByteArray) passItem.decryptedAppName = decryptedByteArray.toString(charset("UTF_8")) } } // 用缓存的明文排序 documentslist.sortBy { it.decryptedAppName } adapter = DocumentAdapter(this,documentslist,this) rv = findViewById(R.id.recyclerView) rv.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL,false) rv.adapter = adapter }
同时Adapter也可以直接用缓存的明文,避免重复解密:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val document : PassItem = documents[position] // 优先使用缓存的明文,兜底才重新解密 holder.txtDocApp.text = document.decryptedAppName ?: run { // 这里的解密逻辑和之前一致,解密后更新缓存 val initialisationVector = document.PassItemIvApp val bytesIV = Base64.decode(initialisationVector, Base64.NO_WRAP) val oisIV = ObjectInputStream(ByteArrayInputStream(bytesIV)) val initializationVectorIV = oisIV.readObject() as ByteArray val encryptedText = document.PassItemApp val bytesText = Base64.decode(encryptedText, Base64.NO_WRAP) val oisText = ObjectInputStream(ByteArrayInputStream(bytesText)) val textByteArray = oisText.readObject() as ByteArray val key = document.PassItemKeyApp val keyText = Base64.decode(key, Base64.NO_WRAP) val oisKey = ObjectInputStream(ByteArrayInputStream(keyText)) val keyToDecrypt = oisKey.readObject() as SecretKey val aesDecrypt = Decrypted(keyToDecrypt,initializationVectorIV) val decryptedByteArray = aesDecrypt.decrypt(textByteArray) val textAfterDecryption = decryptedByteArray.toString(charset("UTF_8")) document.decryptedAppName = textAfterDecryption textAfterDecryption } }
2. 修复点击位置问题
你当前传递position到下一个页面的方式有隐患——如果列表后续发生变化(比如新增/删除项),position对应的对象可能会出错。更可靠的方式是直接传递PassItem对象:
首先让PassItem实现Serializable接口:
data class PassItem(...) : Serializable
然后修改onItemClick方法:
override fun onItemClick(position: Int) { val clickedItem = documentslist[position] val intent = Intent(this@HomeActivity,DocActivity::class.java) intent.putExtra("clickedItem", clickedItem) startActivity(intent) }
在DocActivity中接收对象:
val clickedItem = intent.getSerializableExtra("clickedItem") as PassItem
这样不管列表怎么排序,你传递的都是正确的对象,不会因为position变化而匹配错误。
额外优化建议
- 封装解密逻辑:把解密代码抽成一个工具类(比如
EncryptionUtils),提供fun decryptAppName(passItem: PassItem): String方法,避免在Activity和Adapter中重复写相同的代码,提升可维护性。 - 后台线程执行解密:解密是耗时操作,建议用Coroutine在IO线程执行,避免主线程卡顿:
private fun viewDocuments(){ lifecycleScope.launch(Dispatchers.IO) { documentslist = dbHandler.getDocuments(this@HomeActivity) // 解密+排序逻辑放在IO线程 documentslist.forEach { /* 解密缓存 */ } documentslist.sortBy { it.decryptedAppName } // 切换回主线程更新UI withContext(Dispatchers.Main) { adapter = DocumentAdapter(this@HomeActivity,documentslist,this@HomeActivity) rv = findViewById(R.id.recyclerView) rv.layoutManager = LinearLayoutManager(this@HomeActivity, LinearLayout.VERTICAL,false) rv.adapter = adapter } } }
内容的提问来源于stack exchange,提问作者Santi
相关产品推荐
相关产品推荐

