综述
最近在学安卓,遇到过一些诡异的错误。因此开一贴,用来记录错误和错误的原因。
1、Execution failed for task ‘:app:kaptDebugKotlin’.
一般是使用room的时候造成的错误。具体可能是@annotation错误或者data class错误。
我这次遇到的是后一种:
@Entity(tableName = "scale_node_table")
data class ScaleNodeInfo(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "Device ID") val devId : Int,
@ColumnInfo(name="Bus ID") val busId : Int,
@ColumnInfo(name="Product Name") val name:String,
@ColumnInfo(name="Product Price") val price:BigDecimal,
@ColumnInfo(name="Scale Factor") val factor:BigDecimal,
@ColumnInfo(name="Scale Base") val base:Int
)
上面的代码在编译的时候不会提示任何错误。但是实际上Room不能直接存储BigDecimal格式数据。
建议更改成String格式,在使用的时候将其转换成BigDecimal。
@Entity(tableName = "scale_node_table")
data class ScaleNodeInfo(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "Device ID") val devId : Int,
@ColumnInfo(name="Bus ID") val busId : Int,
@ColumnInfo(name="Product Name") val name:String,
@ColumnInfo(name="Product Price") val price:String,
@ColumnInfo(name="Scale Factor") val factor:String,
@ColumnInfo(name="Scale Base") val base:Int
)
这个原因很难找。
2、@dimen错误
昨天遇到一个诡异的错误,navigation到一个页面却怎么也打不开。检查了源码和layout的xml文件。感觉没什么问题。后来发现,把margin用@dimen/设置的时候.dimen的一个取值本应该是“16dp”,错误写成了“16”.这当然有错误。但没有错误提示。
这个错误很隐蔽,有时候匪夷所思。其实类似的xml资源导致的错误还有很多。尤其是开启dataBinding之后。
3、ByteArray转String问题
下面是一段kotlin的小程序,可以在线测试
fun main() {
val a:String = "John Henry "
val a_BA = a.toByteArray()
val aStr = a_BA.toString()
val aStr2 = String(a_BA)
println("a is:$aStr or $aStr2")
}
运行结果如下:
a is:[B@7adf9f5f or John Henry
这个错误其实最开始发生在我写的一段RSA加解密程序。核心代码如下:
package com.example.loginwithrsa.ui.navi
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import java.security.KeyPairGenerator
import javax.crypto.Cipher
class PswSecurity {
private val keyPairGenerator : KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore")
private val cipherEncrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256andMGF1Padding")
private val cipherDecrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256andMGF1Padding")
private val charset = Charsets.UTF_8
suspend fun createKeyPair(){
/*try to find keystore*/
/*create*/
val keyDecrypt = KeyGenParameterSpec.Builder(
"user_psw_key",
KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.build()
keyPairGenerator.initialize(keyDecrypt)
val keyPair = keyPairGenerator.generateKeyPair()
cipherEncrypt.init(Cipher.ENCRYPT_MODE, keyPair.public)
cipherDecrypt.init(Cipher.DECRYPT_MODE,keyPair.private)
}
suspend fun encrypt(msg:String): String {
return try{
val iv = cipherEncrypt.iv
val secret = cipherEncrypt.doFinal(msg.toByteArray(charset))
Base64.encodeToString(secret,Base64.DEFAULT)
}catch (e:Throwable){
throw Throwable("can not encrypt", e)
}
}
suspend fun decrypt(secret:String): String {
return try {
val msg = cipherDecrypt.doFinal(Base64.decode(secret,Base64.DEFAULT))
String(msg)
//msg.toString()
}catch(e :Throwable){
throw Throwable("Can not decrypt", e)
}
}
}
请注意上面的公钥和私钥暂时没有保存起来,所以每次运行是会变的。
可以看到编码的时候可以直接将string用toByteArray转过去,但是这个过程是不可逆的。
在解码之后byteArray却不能直接用toString转变,而是用String(byteArray[])的形式来实现。
如果不这么做得到的结果是乱码。
那里错了?
看下面的纠正:
fun main() {
val a:String = "John Henry "
val a_BA = a.toByteArray()
val aStr = a_BA.toString(Charsets.UTF_8)
val aStr2 = String(a_BA)
println("a is:$aStr or $aStr2")
}
再次运行就对了
a is:John Henry or John Henry
所以问题看似复杂,其实说起来也很简单。ByteArray到String相当于一对多。String有很多编码形式。
但反过来String内部带有自己的编码格式,所以知道反过来怎么编码。