142 lines
4.0 KiB
Markdown
142 lines
4.0 KiB
Markdown
|
|
# JNI函数名不匹配问题修复说明
|
|||
|
|
|
|||
|
|
## 问题描述
|
|||
|
|
|
|||
|
|
在代码重构过程中,将原生方法从`MainActivity`移动到`DataManager`类后,出现了以下错误:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Cannot resolve corresponding JNI function Java_com_example_cmake_1project_1test_DataManager_createStreamParser
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 问题原因
|
|||
|
|
|
|||
|
|
### JNI函数命名规则
|
|||
|
|
|
|||
|
|
JNI函数名遵循以下格式:
|
|||
|
|
```
|
|||
|
|
Java_{包名}_{类名}_{方法名}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
其中:
|
|||
|
|
- 包名中的点(.)用下划线(_)替换
|
|||
|
|
- 类名中的下划线用`_1`替换
|
|||
|
|
|
|||
|
|
### 具体变化
|
|||
|
|
|
|||
|
|
**重构前的JNI函数名**(在MainActivity中):
|
|||
|
|
```
|
|||
|
|
Java_com_example_cmake_1project_1test_MainActivity_createStreamParser
|
|||
|
|
Java_com_example_cmake_1project_1test_MainActivity_destroyStreamParser
|
|||
|
|
Java_com_example_cmake_1project_1test_MainActivity_streamParserAppend
|
|||
|
|
Java_com_example_cmake_1project_1test_MainActivity_streamParserDrainPackets
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重构后的JNI函数名**(在DataManager中):
|
|||
|
|
```
|
|||
|
|
Java_com_example_cmake_1project_1test_DataManager_createStreamParser
|
|||
|
|
Java_com_example_cmake_1project_1test_DataManager_destroyStreamParser
|
|||
|
|
Java_com_example_cmake_1project_1test_DataManager_streamParserAppend
|
|||
|
|
Java_com_example_cmake_1project_1test_DataManager_streamParserDrainPackets
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 解决方案
|
|||
|
|
|
|||
|
|
### 方案1:回调接口模式(已实现)
|
|||
|
|
|
|||
|
|
通过创建回调接口,让`DataManager`通过`MainActivity`来调用原生方法,保持原有的JNI函数名。
|
|||
|
|
|
|||
|
|
#### 实现步骤
|
|||
|
|
|
|||
|
|
1. **创建回调接口**
|
|||
|
|
```kotlin
|
|||
|
|
interface NativeMethodCallback {
|
|||
|
|
fun createStreamParser(): Long
|
|||
|
|
fun destroyStreamParser(handle: Long)
|
|||
|
|
fun streamParserAppend(handle: Long, chunk: ByteArray)
|
|||
|
|
fun streamParserDrainPackets(handle: Long): List<SensorData>?
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **修改DataManager构造函数**
|
|||
|
|
```kotlin
|
|||
|
|
class DataManager(private val nativeCallback: NativeMethodCallback)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **MainActivity实现接口**
|
|||
|
|
```kotlin
|
|||
|
|
class MainActivity : AppCompatActivity(), DataManager.NativeMethodCallback
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **保持原生方法在MainActivity中**
|
|||
|
|
```kotlin
|
|||
|
|
// 原生方法声明 - 保持原来的JNI函数名
|
|||
|
|
external fun createStreamParser(): Long
|
|||
|
|
external fun destroyStreamParser(handle: Long)
|
|||
|
|
external fun streamParserAppend(handle: Long, chunk: ByteArray)
|
|||
|
|
external fun streamParserDrainPackets(handle: Long): List<SensorData>?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 优势
|
|||
|
|
- 保持原有的JNI函数名,无需修改C++代码
|
|||
|
|
- 维持了代码重构的架构优势
|
|||
|
|
- 清晰的职责分离
|
|||
|
|
|
|||
|
|
#### 劣势
|
|||
|
|
- 增加了接口依赖
|
|||
|
|
- 稍微增加了代码复杂度
|
|||
|
|
|
|||
|
|
### 方案2:修改C++代码中的JNI函数名
|
|||
|
|
|
|||
|
|
如果您有权限修改C++代码,可以将C++中的JNI函数名改为新的名称。
|
|||
|
|
|
|||
|
|
#### 需要修改的C++函数名
|
|||
|
|
```cpp
|
|||
|
|
// 原来的函数名
|
|||
|
|
JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_MainActivity_createStreamParser
|
|||
|
|
|
|||
|
|
// 改为
|
|||
|
|
JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_DataManager_createStreamParser
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 优势
|
|||
|
|
- 完全符合重构后的架构
|
|||
|
|
- 无需额外的接口层
|
|||
|
|
|
|||
|
|
#### 劣势
|
|||
|
|
- 需要修改C++代码
|
|||
|
|
- 可能影响其他依赖项目
|
|||
|
|
|
|||
|
|
## 推荐方案
|
|||
|
|
|
|||
|
|
**推荐使用方案1(回调接口模式)**,原因如下:
|
|||
|
|
|
|||
|
|
1. **无需修改C++代码**:保持现有C++代码的稳定性
|
|||
|
|
2. **维持重构优势**:仍然保持了代码的职责分离
|
|||
|
|
3. **向后兼容**:如果将来需要,可以轻松切换到方案2
|
|||
|
|
4. **风险较低**:不会引入新的编译或链接问题
|
|||
|
|
|
|||
|
|
## 修复后的架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
MainActivity (实现NativeMethodCallback)
|
|||
|
|
↓
|
|||
|
|
DataManager (通过回调调用原生方法)
|
|||
|
|
↓
|
|||
|
|
原生C++库 (保持原有JNI函数名)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **JNI函数名一致性**:确保Kotlin中的`external`方法名与C++中的JNI函数名完全匹配
|
|||
|
|
2. **库加载**:确保在`MainActivity`的`companion object`中正确加载原生库
|
|||
|
|
3. **接口实现**:MainActivity必须实现`NativeMethodCallback`接口的所有方法
|
|||
|
|
4. **依赖注入**:DataManager的构造函数现在需要传入回调接口实例
|
|||
|
|
|
|||
|
|
## 验证修复
|
|||
|
|
|
|||
|
|
修复完成后,您应该能够:
|
|||
|
|
1. 成功编译项目
|
|||
|
|
2. 不再看到JNI函数名不匹配的错误
|
|||
|
|
3. 保持原有的功能正常运行
|
|||
|
|
4. 享受重构后的代码结构优势
|