从C++转到JAVA需要注意的地方

C++转Java必知
本文探讨了从C++转向Java时应注意的关键区别,包括Java的虚拟机运行环境、严格的面向对象特性、本地方法调用的复杂性、无独立头文件的设计及Package命名空间的使用。
从C++转到JAVA需要注意的地方   
  
1.Java在虚拟机上运行  

Java源代码并不是被编译成为普通的机器代码。而是被翻译成为虚拟机可以执行的代码。一个Java解释器最终执行这些代码。这其中没有连接的过程;解释在需要的时候动态的加载一些类; 

2.Java是完全面向对象的  

Java是一种完全面向对象的语言。这意味着你对任何一个Java对象所做的动作都是通过一个方法实现的。第一点就是,再也没有没有主函数这样的孤立的东西了。取而代之的是,你必须开始用一个对象的看法看待一个程序,一个类的对象。大多数Java程序是通过继承Java基础类Object来实现所需要的东西,但是你可以通过创建程序基础类用于多个特性相似的应用程序来节省时间。  

严格的面向对象的规定意味着原有的C/C++代码不可以直接不加改动的使用;系统调用也是这样的。C++中,你可以通过在C++正常的命名空间外声明extern"C"来使用原有的C的过程调用,包括系统调用。  

在Java中,只有一个类似的安全回溯的方法,但是并不是十分简单的方法。你必须定义一个本地方法,其目的是为C语言提供接口,然后提供连接的介质。Java环境提供了完成这种任务的工具,但是整个过程和C++中提供的extern比微不足道,完成使用C++类的过程则更加复杂,因为这样会引入对C的借口和C函数和C++成员函数的问题。  

3.Java中没有独立的头文件  

在Java中,关于类的一切东西都被放到一个单独的文件中。方法的位置只可能在一个地方出现,一个方法的实现必须在它的定义过程中同时进行。这样做得优点是在实现程序的时候不容易因为文件的非同步错误而失败,或者获取到一个没有实现的声明。类的声明可以被Java解释器利用甚至是从一个编译过的单元中获取,所以不再需要有头文件,只要有编译过的文件。  

这样做的缺点与我们编程的过程有关。许多C++程序员喜欢用头文件来代替文档。要看一个成员函数的接口参数,只需要看头文件中的声明即可。你可以经常的看头文件即可了解怎样去使用这个类。在Java中,没有这样的总结。因为实现类方法的代码必须在方法定义的时候出现,而且,对于一个单独的函数的代码来说就经常占据了一整页乃至更多。这样,很难通过看Java的代码就初步了解类是怎样使用的。你必须为你需要的类准备足够多的文档。不言而喻,再处理非商业类库的时候文档是极度缺乏的。所以sun公司充分考虑到了这一缺点,在当前的Java环境中提供了两个工具来补偿这些,javap来打印类标识,javadoc为嵌入式程序提供HTML文档。  

4.用Package来分解Java命名空间  

在大的C++工程中经常遇到的一个问题是命名空间--怎样保证工程的一些程序员不会创建和另一些程序员一样名字的类?更糟糕的是,供应商可能会提供一个包含和你的类一样名字的类的库。有许多方法可以解决这一问题,但是很可能在问题发现之前工程已经启动,改正错误是需要付出许多痛苦的。  

Java通过"Package"这个概念解决了这个问题,Package有效地通过通过集合类划分了命名空间。在不同包内的两个同名的类仍然是不同的。关键问题就变成了类是否放置到相应的包中。  

5.异常是Java的重要特性  

在C++中,异常和异常处理是十分深奥的事情;许多C++程序员从没有处理过它们甚至不知道它们是何物。异常是在正常的过程中出现的未预料的错误,因此,它们不会从方法中返回,或者作为参数传入;但是,它们不能被忽略!这里的一个例子是计算一个数的方根的方法。正常的接口形式是将一个正数作为参数传入方法,然后方法会返回一个正实数作为结果,方法可以检验这些并且在异常产生的时候抛出异常。在大多数系统中,程序员并不是必须这样做,这样,一个没有考虑到的异常可以使程序不正常的退出。
<think>好的,我现在需要回答用户关于在Android中使用C++创建服务并与Java应用通信的问题,要求使用NDK和JNI,并提供示例教程。首先,我需要回忆一下相关的知识点,并结合用户提供的引用内容来构建回答。 首先,用户提到了几个引用,其中引用[1]说明NDK是用于开发C/C++动态库,并打包到APK中。引用[2]提到JNI的作用是处理JavaC++之间的交互,比如类型换和方法调用。引用[3]和[4]则涉及Log输出和JNI调用Java类的示例。这些信息都是构建答案的基础。 接下来,我需要明确如何在C++中创建服务。通常,在Android中,服务(Service)是Java层的组件,但用户希望用C++实现服务,这可能指的是在Native层运行的后台进程,并通过JNI与Java交互。不过,Android中服务通常还是用Java/Kotlin编写,但可以通过JNI调用Native代码处理复杂任务。所以可能需要解释如何创建Java服务,然后在Native层执行逻辑,并通过JNI通信。 步骤可能包括: 1. 创建Java端的Service类,声明Native方法。 2. 使用JNI在C++中实现这些Native方法。 3. 建立通信机制,比如回调函数或共享内存。 4. 处理线程问题,因为Native代码可能在独立线程运行。 5. 示例代码展示JavaC++的交互。 需要注意的要点: - JNI函数的命名规则(Java_package_Class_method)。 - 使用JNIEnv正确调用Java方法。 - 处理字符串、对象等数据类型的换。 - 可能用到的同步机制,如互斥锁,如果涉及多线程。 根据引用[4],生成Java对象需要获取类引用和方法ID,然后在C++中构造对象。例如,创建Student对象并设置字段值。这可能用于从C++返回数据到Java。 另外,引用[3]提到在C++中使用Log输出,可能需要使用__android_log_print,需要链接log库。 现在,我需要将这些步骤组织成清晰的教程,分步骤说明,并提供代码示例。同时,要确保LaTeX格式正确,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能公式不多,不过需要注意数学表达式部分。不过当前问题主要涉及代码和步骤,可能数学公式较少,但需检查是否有需要的地方。 然后,根据用户要求生成相关问题。问题需要围绕NDK、JNI、服务通信等方面,可能涉及优化、性能分析、应用场景等。 最后,需要引用用户提供的引用内容,在适当的位置添加引用标识。例如,在解释JNI的作用时引用[2],在提到NDK打包时引用[1],在Log输出时引用[3],在创建Java对象时引用[4]。 现在,编写回答的结构: 1. 概述:说明使用NDK和JNI实现C++服务与Java通信的基本思路。 2. 步骤一:创建Java服务类,声明Native方法。 3. 步骤二:生成JNI头文件,实现C++代码。 4. 步骤三:建立通信机制(如回调)。 5. 示例代码:JavaC++部分的代码。 6. 注意事项:线程、数据类型换等。 7. 相关问题。 在代码示例中,需要展示Java的Service类,Native方法声明,C++的JNI函数实现,以及可能的回调机制。例如,Java层调用Native方法启动服务,C++处理完成后通过JNI调用Java方法返回结果。 需要注意,在C++中启动线程处理任务时,需要正确获取JNIEnv,因为每个线程的JNIEnv可能不同。可能需要使用JavaVM的AttachCurrentThread来获取。 例如,在C++中保存JavaVM的实例,当在Native线程中需要回调Java时,先Attach线程,获取JNIEnv,然后调用Java方法。 这部分可能比较复杂,但用户需要一个示例教程,因此需要简化,展示关键步骤。 现在,整合所有内容,确保符合用户的要求,包括引用和格式要求。</think>### 如何在Android中使用C++创建服务并与Java应用通信 以下是基于NDK/JNI实现C++服务与Java通信的完整流程: --- #### **1. 创建Java服务框架** 在Java层定义`Service`类并声明Native方法: ```java public class NativeService extends Service { static { System.loadLibrary("native-lib"); // 加载C++动态库 } // Native方法:启动C++服务 public native void startNativeService(); // Native方法:向C++发送数据 public native void sendDataToNative(String data); // 定义回调接口 public interface NativeCallback { void onDataReceived(String result); } private NativeCallback callback; public void setCallback(NativeCallback cb) { this.callback = cb; } // 供C++调用的Java方法(需通过JNI触发) public void onNativeMessage(String msg) { if (callback != null) callback.onDataReceived(msg); } } ``` --- #### **2. 生成JNI头文件并实现C++逻辑** 通过`javac -h`生成头文件后,在C++中实现Native方法: ```cpp #include <jni.h> #include <string> #include <android/log.h> // 全局变量存储JavaVM和回调引用 JavaVM* gJavaVM; jobject gCallbackObj; extern "C" JNIEXPORT void JNICALL Java_com_example_NativeService_startNativeService(JNIEnv* env, jobject thiz) { // 保存JavaVM实例 env->GetJavaVM(&gJavaVM); // 创建全局引用避免被GC回收 gCallbackObj = env->NewGlobalRef(thiz); } extern "C" JNIEXPORT void JNICALL Java_com_example_NativeService_sendDataToNative(JNIEnv* env, jobject, jstring data) { const char* cstr = env->GetStringUTFChars(data, nullptr); __android_log_print(ANDROID_LOG_INFO, "JNI", "Received: %s", cstr); // 使用NDK Log[^3] // 示例:处理数据后回调Java层 jclass cls = env->GetObjectClass(gCallbackObj); jmethodID method = env->GetMethodID(cls, "onNativeMessage", "(Ljava/lang/String;)V"); env->CallVoidMethod(gCallbackObj, method, env->NewStringUTF("Processed data")); env->ReleaseStringUTFChars(data, cstr); } ``` --- #### **3. 建立双向通信机制** 在C++线程中回调Java方法时,需处理JNIEnv的线程关联性: ```cpp void native_thread_function() { JNIEnv* env; gJavaVM->AttachCurrentThread(&env, nullptr); // 绑定当前线程到JVM jclass cls = env->GetObjectClass(gCallbackObj); jmethodID method = env->GetMethodID(cls, "onNativeMessage", "(Ljava/lang/String;)V"); env->CallVoidMethod(gCallbackObj, method, env->NewStringUTF("From Native Thread")); gJavaVM->DetachCurrentThread(); // 解绑线程 } ``` --- #### **4. 配置NDK编译** 在`CMakeLists.txt`中添加Log库支持: ```cmake find_library(log-lib log) target_link_libraries(native-lib ${log-lib}) ``` --- #### **关键注意事项** 1. **数据类型换**:JNI提供`GetStringUTFChars`等方法实现JavaC++类型换[^2]; 2. **对象生命周期**:使用`NewGlobalRef`确保对象不被GC回收[^4]; 3. **线程安全**:跨线程调用JNI需通过`JavaVM`重新获取`JNIEnv`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值