我正在尝试使用 JNI 在我的 Android Java 代码中使用一些编码 C 函数。
当我想返回编码的字符串时,使用
return (*env)->NewStringUTF(env, resultStr);
我明白了
08-15 13:36:43.787: W/dalvikvm(11302): JNI WARNING: input is not valid Modified UTF-8: illegal start byte 0x98
08-15 13:36:43.787: W/dalvikvm(11302): string: '����.y�����s��a'
08-15 13:36:43.787: W/dalvikvm(11302): in Lorg/wfmu/radio/MainActivity;.xteaBase64Encoding:(Ljava/lang/String;)[B (NewStringUTF)
我猜这是由于这个 xtea 算法。
那么如何将字符串传递给 Java,以及如何读取它呢?
爪哇代码
static {
System.loadLibrary("mylib");
}
private native String xteaBase64Encoding(String str);
...
String encodedPlaylistId = xteaBase64Encoding("somestring");
C代码
void charToUint32(char *string, uint32_t *block, unsigned int len)
{
char *blockAsChar = (char *) block;
for (int i = 0; i < len/4; ++i){
blockAsChar[i*4+3] = string[i*4];
blockAsChar[i*4+2] = string[i*4+1];
blockAsChar[i*4+1] = string[i*4+2];
blockAsChar[i*4] = string[i*4+3];
}
}
void uint32ToChar(uint32_t *block, char *string, unsigned int len)
{
char *blockAsChar = (char *) block;
for (int i = 0; i < len/4; ++i){
string[i*4] = blockAsChar[i*4+3];
string[i*4+1] = blockAsChar[i*4+2];
string[i*4+2] = blockAsChar[i*4+1];
string[i*4+3] = blockAsChar[i*4];
}
}
/*
** Translation Table as described in RFC1113
*/
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
** encodeblock
**
** encode 3 8-bit binary bytes as 4 '6-bit' characters
*/
void b64encodeblock( unsigned char in[3], unsigned char out[4], int len )
{
out[0] = cb64[ in[0] >> 2 ];
out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const k[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
void xtea(uint32_t *v, uint32_t *k, unsigned int len) {
for (int j = 0; j < len/8; ++j) {
encipher(32, &v[j*2], k);
}
}
jstring Java_***_MainActivity_xteaBase64Encoding(JNIEnv * env, jobject this, jstring javaStr)
{
const char *str = (*env)->GetStringUTFChars(env, javaStr, 0);
char *key = "abcdefghijklmnop";
int modSize = strlen(str) % 8;
int dataSize = strlen(str) + (modSize?(8 - modSize):0);
char *sourceString = malloc(dataSize);
memset(sourceString, 0, dataSize);
memcpy(sourceString, str, strlen(str));
uint32_t *dataBlock= (uint32_t *) malloc(dataSize);
memset(dataBlock,0,dataSize);
charToUint32(sourceString, dataBlock, dataSize);
free(sourceString);
(*env)->ReleaseStringUTFChars(env, javaStr, str);
uint32_t *keyData= (uint32_t *) malloc(16);
memset(keyData,0,16);
charToUint32(key, keyData, 16);
xtea(dataBlock,keyData,dataSize);
char *resultStr = malloc(dataSize+1);
memset(resultStr, 0, sizeof(dataSize+1));
uint32ToChar(dataBlock, resultStr, dataSize);
// Base64 encode the string
int base64Size = (dataSize / 3 + ((dataSize % 3)?1:0)) * 4;
char *base64Str = malloc(base64Size+1);
memset(base64Str,0, base64Size+1);
int j=0;
int i = 0;
int bytesRemaining = dataSize;
while (bytesRemaining > 0){
b64encodeblock((unsigned char *)&resultStr[j],(unsigned char *) &base64Str[i], bytesRemaining>3?3:bytesRemaining);
bytesRemaining -= 3;
j += 3;
i += 4;
}
return (*env)->NewStringUTF(env, resultStr);
}