这回用Android的NDK技术写一个计算器练练手,啥是Android的NDK开发?看看Google的介绍--传送门

为啥要用NDK开发呢,NDK是用C++ 写的,运行效率特别高,在一些需要复杂计算的地方(例如游戏开发),还有就是Java代码反编译比较简单,很容易被反编译暴露源码,C++的反编译难度较大,涉及到加密的代码可以用C++来写。当然用NDK开发也有缺点,C++代码上手比Java难,而且用C++后调试难度增加,所以是否要引入C++要权衡之后再决定。
我在暑假用C++写了一个能四则运算的小程序,现在想在Android端也写一个支持四则运算的计算器,要是再用Java重写一遍就太麻烦了,正好可以用这个练一练Android的NDK开发。
工欲善其事必先利其器,还么有配置好NDK开发环境的可以看我上一篇文章--传送门;
环境配好了,话不多说,开始撸代码吧。新建一个项目,

新建项目.png
一定要把 Include C++ support 选中,然后 next,然后到选中Android版本,选择Activity的地方,都默认即可,一路 next

到这还是默认就行,然后 finish,等项目创建成功后会看到熟悉的界面

和我们普通项目有点区别的地方是在 ActivityonCreate方法中会多出一段代码和一个方法

static {        System.loadLibrary("native-lib");    }
public native String stringFromJNI();

这段代码是用来加载C++生成的动态库,方法是一个本地方法,我理解为调用C++函数的方法。
先介绍到这,然后开始计算器界面的编写,这和普通的界面编写没有区别,就不多说了,上一个效果图吧

效果图
接下来就是Activity中的各种逻辑的编写,点击事件的处理,代码有点多,先不贴了,最后我会分享。
注意了,到这重点来了,要新建C++代码的源文件和头文件。在CPP文件夹上右键 New-> C++class,填写文件名-> OK
你会发现在CPP目录下没有刚才新建的文件,我靠,AS出BUG了吧。不要急,在导航栏的最下边找到CMakeLists.text,点开,在图上标注的地方添加代码
CMakeLists.txt
src/main/cpp/native-lib.cpp 下面添加 src/main/cpp/×××××.cpp
××××是你的C++源文件的名。点击右上角的 Sync Now,等待同步完成。
这时你会发现CPP目录下有多了两个文件
CPP
这时回到 MainActivity 中,把 public native String stringFromJNI();方法修改一下。改成这样 public native double stringFromC(String s);。为啥要改成这样呢,因为我们要做一个计算器,所有的按键逻辑和显示工作由Java代码完成,而计算工作由C++ 代码去做,Java层要传递一个字符串给C++层,然后C++层把计算结果返回给Java层,Java层来显示计算结果。所以要加一个 String类型的形参,把返回值改成double。这时会报错,然后按 Alt+回车 键AS会自动修改。现在来到 native-lib.cpp文件,把刚开始AS生产的函数删掉,只保留这一个函数

JNIEXPORT jdouble JNICALLJava_com_bruce_counter_MainActivity_stringFromC(JNIEnv *env, jobject instance, jstring s_) {    const char *s = env->GetStringUTFChars(s_, 0);    // TODO //   env->ReleaseStringUTFChars(s_, s);}

NIEXPORT jdouble JNICALL 是在 jni.h中定义的返回值类型,不清楚jni类型的可以看一下
如果有env->ReleaseStringUTFChars(s_, s);代码,把他删掉,用不到。
完成的native-lib.cpp代码

#include #include #include "Counter.h"using std::string;extern "C"JNIEXPORT jdouble JNICALLJava_com_bruce_counter_MainActivity_stringFromC(JNIEnv *env, jobject instance, jstring s_) {    const char *s = env->GetStringUTFChars(s_, 0);    // TODO//    env->ReleaseStringUTFChars(s_, s);    string str=s;    delete s;    Counter *c=new Counter();    double d=c->coutn(str);    delete c;    return d;}

指针 ‘s’ 指向的就是我们从Java层传过来的字符串,我们把他保存下来,然后delete s,听说不delete会内存泄露,还没求证,就先这样写吧。Counter类是负责计算的,调用Counter的countn函数,就能得到结果。
为啥要在native-lib.cpp中调用这些方法呢?我的理解native-lib相当于main函数,Java调用C++会先调用这个。好了,整个C++层代码基本完成了。
到Java中去调用吧。

displayStr=displayStr.replace("×","*").replace("÷","/");screen.setText(stringFromC(displayStr)+"");//计算结束更新屏幕

先说一个遇到的坑,在Java中乘号用的'×',除号用的'÷',Java用的unicode编码,没有问题,但是C++默认用的是ASCII编码,不能使用’ב’÷‘,我们使用‘*’和‘/’代替,所以在传入字符串时要替换一下displayStr=displayStr.replace("×","*").replace("÷","/");
到这基本就好了,可以运行一下看看了。
发现写博客比写代码累多了,就先写到这吧,我已经把完整的demo上传到码云,需要的同学可以去下载。如果还有什么问题就贴在在评论区吧,一起学习。

更多相关文章

  1. Android(安卓)Developers:指定在线程中运行的代码
  2. Android中的动态加载机制
  3. Android通过scroller实现缓慢移动
  4. Android(安卓)7.0,6.0系统相机调用问题解决
  5. Android(安卓)Frame动画概述及示例
  6. android桌面添加快捷方式之判断是否存在无效
  7. Android之BaseAdapter的优雅实现
  8. Android(安卓)Studio---断点调试和高级调试
  9. Android(安卓)反编译技术流程

随机推荐

  1. Android系统java层次service实现
  2. Android音频系统学习笔记
  3. 判断是否获取了悬浮窗权限
  4. Android中抛出android.app.Fragment$Inst
  5. (4.1.1)Android(安卓)ActionBar完全解析,使
  6. 三、数据库入门和listview的基本使用
  7. 自定义Spinner下拉列表
  8. 布局中@null的代码实现方式
  9. [译] Android(安卓)开发最佳实践
  10. Android---把数据保存到数据库中(一)