使用Android NDK和Java测试Linux驱动

简介:

在Android模拟器和Ubuntu上测试Linux驱动

三、使用AndroidNDK测试Linux驱动

     在 Android系统中Linux驱动主要的使用者是APK程序。因此,Linux驱动做完后必须要用APK程序进行测试才能说明Linux驱动可以正常使 用。由于上一节在Android虚拟机上使用C语言编写的可执行程序测试了Linux驱动,因此很容易想到可以利用Android NDK来测试Linux驱动,

由于Android NDK也使用C/C++来编写程序,因此可以利用上一节的C语言代码,当然,还得加上一些AndroidNDK特有的代码。在使用AndroidNDK测试Linux驱动之前需要做如下两件事。

1. 由于Linux驱动模块不会随Android系统启动而装载,因此必须执行build.sh脚本文件安装word_count驱动。

2. 不能使用默认方式启动Android模拟器,而要使用我们自己编译的Linux内核启动Android模拟器,启动模拟器的命令如下:

#  emulator-avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage

为了方便,读者也可以在随书光盘带的Ubuntu Linux虚拟环境中直接执行如下的命令来异步启动Android模拟器。其中emulator.sh文件在/root/drivers目录中。

# sh emulator.sh &

本节的例子已经包含在随书光盘和虚拟环境中,路径如下:

随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_ndk

虚拟环境:/root/drivers/ch06/word_count/word_count_ndk

word_count_ndk工程的代码部分由WordCountNDKTestMain.java和ndk_test_word_count.c文件组成。工程结构如图6-17所示。

       ndk_test_word_count.c文件用于访问word_count驱动。该文件包含两个供Java访问的函数,分别用来读取/dev /wordcount设备文件中的单词数和向/dev/wordcount设备文件写入字符串。下面先看看ndk_test_word_count.c文 件的完整代码。

#include <string.h> 
#include <jni.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <stdlib.h> 
//  JNI函数:readWordCountFromDev 
//  用于从/dev/wordcount设备文件读取单词数 
jint Java_mobile_android_word_count_ndk_WordCountNDKTestMain_readWordCountFromDev( 
         JNIEnv* env, jobject thiz) 
   
     int  dev;                //  open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 
     jint wordcount = 0;     //  单词数 
     unsigned char  buf[4];   //  以4个字节形式存储的单词数 
   
         //  以只读方式打开/dev/wordcount设备文件 
     dev = open( "/dev/wordcount" , O_RDONLY); 
   
     //  从dev/wordcount设备文件中读取单词数 
     read(dev, buf, 4); 
   
     int  n = 0;              //  存储单词数的int类型变量 
   
     //  将由4个字节表示的单词数转换成int类型的值 
     n = (( int ) buf[0]) << 24 | (( int ) buf[1]) << 16 | (( int ) buf[2]) << 8 | (( int ) buf[3]); 
          //  将int类型的单词数转换成jint类型的单词数 
     wordcount = (jint) n; 
     //  关闭/dev/wordcount设备文件 
     close(dev); 
     //  返回单词数 
     return  wordcount; 
//  将jstring类型的值转换成char *类型的值 
char * jstring_to_pchar(JNIEnv* env, jstring str) 
     char * pstr = NULL; 
     //  下面的代码会调用Java中的String.getBytes方法获取字符串的字节数 
   
     //  获取java.lang.String类 
     jclass clsstring = (*env)->FindClass(env, "java/lang/String" ); 
     //  将字符串“utf-8”转换成jstring类型的值 
     jstring strencode = (*env)->NewStringUTF(env, "utf-8" ); 
     //  获取java.lang.String.getBytes方法 
     jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes" , "(Ljava/lang/String;)[B" ); 
     //  调用String.getBytes方法将str变量的值转换成jbytearray类型的值 
     jbyteArray byteArray = (jbyteArray)( (*env)->CallObjectMethod(env, str, mid, strencode)); 
     //  获取字节长度 
     jsize size = (*env)->GetArrayLength(env, byteArray); 
     //  将jbytearray类型的值转换成jbyte*类型的值 
     jbyte* pbyte = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE); 
     if  (size > 0) 
    
         //  为char*类型变量pstr分配空间 
         pstr = ( char *) malloc (size); 
         //  将pbyte变量中的值复制到pstr变量中 
         memcpy (pstr, pbyte, size); 
    
     //  返回转换后的值 
     return  pstr; 
//  JNI函数:writeStringToDev 
//  用于向/dev/wordcount设备文件写入字符串 
void  Java_mobile_android_word_count_ndk_WordCountNDKTestMain_writeStringToDev( 
         JNIEnv* env, jobject thiz, jstring str) 
   
     int  dev;   //  open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 
     //  以只写方式打开/dev/wordcount设备文件 
     dev = open( "/dev/wordcount" , O_WRONLY); 
     //  将jstring类型字符串转换成char* 类型的值 
     char * pstr = jstring_to_pchar(env, str); 
     if  (pstr != NULL) 
    
         //  向/dev/wordcount设备文件写入字符串 
         write(dev,pstr, strlen (pstr)); 
    
     //  关闭/dev/wordcount设备文件 
     close(dev); 

 编写上面的代码有一个重点就是jstring_to_pchar函数。该函数可以将jstring类型的数据转换成char*类型的数据。转换的基 本思想就是调用Java方法String.getBytes,获取字符串对应的字节数组(jbyteArray)。由于write函数需要的是char *类型的数据,因此,还必须将jbyteArray类型的数据转换成char *类型的数据。采用的方法是先将jbyteArray类型的数据转换成jbyte类型的数据,然后调用memcpy函数将jbyte类型的数据复制到使用 malloc函数分配的char *指针空间中。在jstring_to_pchar函数中有如下的一行代码。

jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"];

     看到getMethodID方法最后一个参数的值是"(Ljava/lang/String;)[B",可能Android NDK初学者会对此感到困惑,以为是写错了。实际上这是JNI(Android NDK程序实际上就是遵循JNI规则的程序)对方法参数和返回类型的描述。在JNI程序中为了方便描述Java数据类型,将简单类型使用了一个大写英文字 母表示,如表6-1所示。

    除了表6-1所示的Java简单类型外,还有一些数据类型需要在JNI代码中与其对应。表6-2是这些数据类型在JNI中的描述符。

     从表6-2所示的数据类型对照关系很容易想到本例中的"(Ljava/lang/String;)[B"是什么意思。jstring_to_pchar函数调用的是如下的getBytes方法的重载形式。

public byte[] getBytes(String charsetName) throwsUnsupportedEncodingException

在JNI中调用Java方法需要指定方法参数和返回值的数据类型。在JNI中的格式如下:

"(参数类型)返回值类型"

     getBytes方法的参数类型是String,根据表6-2的描述,String类型中JNI在的描述符是"Ljava/lang/String; "。getBytes方法的返回值类型是byte[]。这里就涉及到一个数组的表示法。在JNI中数组使用左中括号([]表示,后面是数组中元素的类型。 每一维需要使用一个“[”。byte[]是一维字节数组,所以使用"[B"表示。如果是byte[][][],应使用"[[[B"表示。如果Java方法 未返回任何值(返回值类型是void),则用V表示。如void mymethod(int value)的参数和返回值类型可表示为"(I)V"。

Android NDK程序还需要一个Android.mk文件,代码如下:

LOCAL_PATH := $(call my-dir) 
   
include $(CLEAR_VARS) 
   
LOCAL_MODULE    := ndk_test_word_count 
LOCAL_SRC_FILES := ndk_test_word_count.c 
   
include $(BUILD_SHARED_LIBRARY) 

 

在编写Java代码调用JNI函数之前,先看一下本例的界面,如图6-18所示。

       读者需要先在PC上运行build.sh脚本文件安装word_count驱动。然后单击“从/dev/wordcount读取单词数”按钮,会在按钮下 方输出当前/dev/wordcount设备文件中统计出的单词数。读者也可以在输入框中输入一个由空格分隔的字符串,然后单击“向/dev /wordcount写入字符串”按钮,再单击“从/dev/wordcount读取单词数”按钮,就会统计出字符串中包含的单词数,效果如图6-19所 示。

下面看一下本例中Java部分(WordCountNDKTestMain.java)的完整代码。

package  mobile.android.word.count.ndk; 
   
import  android.app.Activity; 
import  android.os.Bundle; 
import  android.view.View; 
import  android.widget.EditText; 
import  android.widget.TextView; 
import  android.widget.Toast; 
   
public  class  WordCountNDKTestMain extends  Activity 
     private  TextView tvWordCount; 
     private  EditText etString; 
   
     @Override 
     public  void  onCreate(Bundle savedInstanceState) 
    
         super .onCreate(savedInstanceState); 
         setContentView(R.layout.main); 
         tvWordCount = (TextView) findViewById(R.id.textview_wordcount); 
         etString = (EditText) findViewById(R.id.edittext_string); 
    
     //  “从/dev/wordcount读取单词数”按钮的执行代码 
     public  void  onClick_ReadWordCountFromDev(View view) 
    
         //  显示单词数 
         tvWordCount.setText( "单词数:"  + String.valueOf(readWordCountFromDev())); 
    
// “向/dev/wordcount写入字符串”按钮的执行代码 
     public  void  onClick_WriteStringToDev(View view) 
    
         //  向/dev/wordcount设备文件写入字符串 
         writeStringToDev(etString.getText().toString()); 
         Toast.makeText( this , "已向/dev/wordcount写入字符串" , Toast.LENGTH_LONG).show(); 
    
   
     // native方法 
     public  native  int  readWordCountFromDev(); 
     public  native  void  writeStringToDev(String str); 
     static 
    
         System.loadLibrary( "ndk_test_word_count" ); 
    

 

WordCountNDKTestMain.java中的代码只是简单地调用了JNI函数来操作/dev/wordcount文件。其他的代码都是常规的Android应用级别的代码。如果读者对这部分不熟悉,可以参阅笔者所著的《Android开发权威指南》。

四、使用Java代码直接操作设备文件来测试Linux驱动

      如果Android拥有root权限,完全可以直接使用Java代码操作/dev/wordcount设备文件(没有root权限,Linux驱动模块是 无法安装的)。本节将介绍如何使用Java代码来测试Linux驱动(测试程序不使用一行C/C++代码)。本节示例的路径如下:

随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_java

虚拟环境:/root/drivers/ch06/word_count/word_count_java

word_count_java工程中只有一个源代码文件WordCountJavaTestMain.java。该文件的内容如下:

package  mobile.android.word.count.java; 
   
import  java.io.FileInputStream; 
import  java.io.FileOutputStream; 
import  android.app.Activity; 
import  android.os.Bundle; 
import  android.view.View; 
import  android.widget.EditText; 
import  android.widget.TextView; 
import  android.widget.Toast; 
   
public  class  WordCountJavaTestMain extends  Activity 
     private  TextView tvWordCount; 
     private  EditText etString; 
     @Override 
     public  void  onCreate(Bundle savedInstanceState) 
    
         super .onCreate(savedInstanceState); 
         setContentView(R.layout.main); 
         tvWordCount = (TextView) findViewById(R.id.textview_wordcount); 
         etString = (EditText) findViewById(R.id.edittext_string); 
    
     //  “从/dev/wordcount读取单词数”按钮的执行代码 
     public  void  onClick_ReadWordCountFromDev(View view) 
    
         //  显示单词数 
         tvWordCount.setText( "单词数:"  + String.valueOf(readWordCountFromDev())); 
    
// “向/dev/wordcount写入字符串”按钮的执行代码 
     public  void  onClick_WriteStringToDev(View view) 
    
         //  向/dev/wordcount设备文件写入字符串 
         writeStringToDev(etString.getText().toString()); 
         Toast.makeText( this , "已向/dev/wordcount写入字符串" , Toast.LENGTH_LONG).show(); 
    
     //  下面是用Java实现的操作/dev/wordcount设备文件的代码 
   
     //  读取/dev/wordcount设备文件中的单词数 
     private  int  readWordCountFromDev() 
    
         int  n = 0
         byte [] buffer = new  byte [ 4 ]; 
         try 
        
             //  打开/dev/wordcount设备文件 
             FileInputStream fis = new  FileInputStream( "/dev/wordcount" ); 
             //  从设备文件中读取4个字节  
             fis.read(buffer); 
             //  将4个字节转换成int类型的值 
             n = (( int ) buffer[ 0 ]) << 24  | (( int ) buffer[ 1 ]) << 16 
                     | (( int ) buffer[ 2 ]) << 8  | (( int ) buffer[ 3 ]);  
             fis.close(); 
        
         catch  (Exception e) 
        
        
         return  n; 
    
    //  向/dev/wordcount设备文件中写入字符串 
     private  void  writeStringToDev(String str) 
    
         try 
        
             //  打开/dev/wordcount设备文件 
             FileOutputStream fos = new  FileOutputStream( "/dev/wordcount" ); 
             //  写入字符串 
             fos.write(str.getBytes( "iso-8859-1" )); 
             fos.close(); 
        
         catch  (Exception e) 
        
        
    

本例的运行效果和使用方法与上一节的例子类似。读者可以运行随书光盘或虚拟环境中的例子与上一节的例子进行比较。


本文节选至《Android深度探索(卷1):HAL与驱动开发》, 接下来几篇文章将详细阐述如何开发ARM架构的Linux驱动,并分别利用android程序、NDK、可执行文件测试Linux驱动。可在ubuntu Linux、Android模拟器和S3C6410开发板(可以选购OK6410-A开发板,需要刷Android)

本文转自银河使者博客园博客,原文链接http://www.cnblogs.com/nokiaguy/archive/2013/03/18/2966996.html如需转载请自行联系原作者


银河使者

相关文章
|
29天前
|
算法 Java Linux
java制作海报七:java Graphics2D 合成图片 在 linux下中文不显示,echarts图上的中文也不显示问题
这篇文章讨论了在Linux环境下使用Java Graphics2D合成图片时遇到的中文显示问题,并提供了解决方案,包括如何在Linux系统中添加中文字体库。
31 1
java制作海报七:java Graphics2D 合成图片 在 linux下中文不显示,echarts图上的中文也不显示问题
|
1月前
|
Java Linux
java读取linux服务器下某文档的内容
java读取linux服务器下某文档的内容
35 3
java读取linux服务器下某文档的内容
|
27天前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
33 1
|
1月前
|
XML Java Maven
在 Cucumber 测试中自动将 Cucumber 数据表映射到 Java 对象
在 Cucumber 测试中自动将 Cucumber 数据表映射到 Java 对象
46 7
|
29天前
|
算法 Java Linux
java制作海报五:java 后端整合 echarts 画出 折线图,项目放在linux上,echarts图上不显示中文,显示方框口口口
这篇文章介绍了如何在Java后端整合ECharts库来绘制折线图,并讨论了在Linux环境下ECharts图表中文显示问题。
36 1
|
1月前
|
编译器 Android开发
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
|
2月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
39 5
|
2月前
|
Java Linux Python
Linux环境下 代码java调用python出错
Linux环境下 代码java调用python出错
46 3
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
114 1
下一篇
无影云桌面