HelloWorld功能简单,假设这个led灯(硬件)只有我们一个HelloWorld app会去 操作它,其他app永远不去操作,那么 我们就不使用硬件访问服务(system server)了,直接使用JNI的方式操作底下硬件。

        JNI不是Android特有,有兴趣可以查阅java jni的官方接口规范文档。

        我们先写java端,接着上次创建的HelloWorld工程,在HelloWorld/app/src/main/java/com/demo/下增加一个文件夹ledctrl,里面创建一个LedCtrl.java文件,用于提供java native接口:

package com.demo.ledctrl;public class LedCtrl {    public static native int LedInit();    public static native int LedCtrl(boolean blIsOn);    public static native void LedRelease();    static {        try {            System.loadLibrary("ledctrl");        } catch (Exception e) {            e.printStackTrace();        }    }}

LedCtrl.java总共就暴露了三个接口:LedInit、LedCtrl和LedRelease,分别用于打开一个led灯资源、控制led灯亮灭以及释放一个led灯资源。而System.loadLibrary("ledctrl");则用于加载一个so动态链接库,说明这个C/C++库名字叫“libledctrl.so”。


       下面则是一个最简单的JNI模板:

#include #include #include #include #include #include #include #define PRINT_DEBUG(x,...) __android_log_print(ANDROID_LOG_DEBUG, "MyLog", ("|DBG|<%s:%d>[%s] " x),basename(__FILE__), __LINE__,__FUNCTION__,##__VA_ARGS__)#define PRINT_ERROR(x,...) __android_log_print(ANDROID_LOG_ERROR, "MyLog", ("|ERR|<%s:%d>[%s] " x),basename(__FILE__), __LINE__,__FUNCTION__,##__VA_ARGS__)jint LedInit(JNIEnv *env, jobject cls){PRINT_DEBUG("Entry LedInit ...");return 0;}jint LedCtrl(JNIEnv *env, jobject cls, jboolean blIsOn){PRINT_DEBUG("Entry LedCtrl : blIsOn = %d", blIsOn);return 0;}void LedRelease(JNIEnv *env, jobject cls){PRINT_DEBUG("Entry LedRelease ...");}static const JNINativeMethod methods[] = {{"LedInit", "()I", (void *)LedInit},{"LedCtrl", "(Z)I", (void *)LedCtrl},{"LedRelease", "()V", (void *)LedRelease},};JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved){JNIEnv *env;jclass cls;if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6)) {PRINT_ERROR("GetEnv error!");return JNI_ERR;}cls = (*env)->FindClass(env, "com/demo/ledctrl/LedCtrl");if (cls == NULL) {PRINT_ERROR("FindClass error!");return JNI_ERR;}if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0) {PRINT_ERROR("RegisterNatives error!");return JNI_ERR;}return JNI_VERSION_1_6;}

       因为在jni里使用printf是不能在Android Studio的logcat上看到打印的,要使用google封装好的__android_log_print函数才行。我这里进一步封装成PRINT_DEBUG和PRINT_ERROR宏。

        值得关注的是cls = (*env)->FindClass(env, "com/demo/ledctrl/LedCtrl");这句话,这里是要根据路径找到对应的java类,而我们的LedCtrl类是定义在com/demo/ledctrl/LedCtrl.java的。

        static const JNINativeMethod methods[] = {...};则是声明了对应于java上的native接口,LedInit、LedCtrl和LedRelease,它们没有具体实现硬件相关的代码,只是加入打印而已。

        好了,我们要使用gcc编译出libledctrl.so:

aarch64-linux-gcc -fPIC -shared ledctrl.c -o libledctrl.so -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux -nostdlib ../rk3399-android-8.1/prebuilts/ndk/r11/platforms/android-24/arch-arm64/usr/lib/libc.so -I ../rk3399-android-8.1/prebuilts/ndk/r11/platforms/android-24/arch-arm64/usr/include ../rk3399-android-8.1/prebuilts/ndk/r11/platforms/android-24/arch-arm64/usr/lib/liblog.so

         这里没有使用Makefile,也没有创建NDK工程,而是直接使用gcc命令进行编译。需要关注的是,要指定正确的库路径和头文件路径。使用-nostdlib编译选项是因为我的开发板nanopc-t4只有/system/lib64/libc.so,而没有标准c库中的libc.so.6。aarch64-linux-gcc是rk3399的交叉编译工具链,在android8.1源码中用于编译uboot、linux内核、framework等相关模块。

      编译出来libledctrl.so应该放在AS工程的什么地方呢?在AS创建工程时我们看到有个“libs”的空文件夹,我们可以把libledctrl.so放进该目录,但需要用“arm64-v8a”文件夹包裹起来(即HelloWorld/app/libs/arm64-v8a/下),否则打开app后会出现类似:

....... is not accessible for the namespace "classloader-namespace"之类的错误log。当然了,如果还要依赖其他第三方库的话,还要把库push到/system/lib64/,同时修改/system/etc/public.libraries.txt文件。

        最后还要在HelloWorld/app/build.gradle中指定库的路径:

 
android {......    sourceSets {        main {            jniLibs.srcDirs = ['libs']        }    }......

       最后在按键点击事件回调中加入LED的控制的函数调用:

......import com.demo.ledctrl.*;public class MainActivity extends AppCompatActivity {    private boolean isLedOn = true;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        LedCtrl m_LedCtrl = new LedCtrl();        m_LedCtrl.LedInit();        final Button button = findViewById(R.id.Led);        button.setOnClickListener(new View.OnClickListener() {            public void onClick(View v) {                // Code here executes on main thread after user presses button                if(isLedOn == false) {                    LedCtrl.LedCtrl(false);                    button.setText("Led off");                    Toast.makeText(getApplicationContext(), "Led off", Toast.LENGTH_SHORT).show();                }                else {                    LedCtrl.LedCtrl(true);                    button.setText("Led on");                    Toast.makeText(getApplicationContext(), "Led on", Toast.LENGTH_SHORT).show();                }                isLedOn = !isLedOn;            }        });    }}

       OK,这样HelloWorld就完成了C库的调用。点击屏幕上的按钮,在logcat上还会有打印。

更多相关文章

  1. Android 匿名共享内存C接口分析
  2. android在build中配置资源路径的方式
  3. Android PinyinIME 源码笔记 -- 1. 底层服务接口简介
  4. Android 从硬件到应用:一步一步向上爬 4 -- 使用 JNI 方法调硬件
  5. windows修改Android AVD路径
  6. 读取指定路径数据库的方法
  7. android 调用系统图片浏览器并返回图片路径
  8. Android App怎样调用 Frameworks Bluetooth接口
  9. Android Uri转换成真实File路径

随机推荐

  1. Android(安卓)UI开发第十二篇——动画效
  2. Android将胜过Windows Mobile五大原因
  3. Android之NDK开发详解
  4. 在电脑上pc端运行android安卓模拟器图...
  5. android之调用webservice 实现图片上传下
  6. Android(安卓)消息推送
  7. android eclipse 真机调试
  8. 【摘录】Google Android操作系统内核编译
  9. Android漫游记(1)---内存映射镜像(memory
  10. Android构建面试知识