基于 Android NDK 的学习之旅-----数据传输一(基本数据类型和数组传输)(附源码)

标签: android ndk 学习 | 发表时间:2011-08-19 03:26 | 作者:duicky UnderSn0w
出处:http://www.cnblogs.com/

基于 Android NDK 的学习之旅-----数据传输(基本数据类型和数组传输)

 

       之前的一些文章都有涉及到上层和中间层的数据传输,简单来说,也就是参数和返回值的使用。因为中间层要做的最多的也就是数据传输与转换,下面来介绍下这方面的知识。

       数据传输可分为 基本数据类型传输 引用数据类型的传输 因为数组传输也比较特别(其实数组也是引用类型),所以这里也专门分出来讲讲。

 

1、主要流程

1、  基本数据类型的传输

a)         上层定义一个native的方法,需要一个int 参数 ,返回一个int

b)        JNI 对应 上层的方法 打印出  上层 传输下来的 int数据,并返回 int数据

c)         上层 收到 native 方法 返回的 值,在UI中显示出来

 

 

2、  数组的传输

a)         上层定义一个native的方法,需要一个int数组,返回一个int数组

b)        JNI 对应上层的方法,取出上层传递数组中的数据处理和打印出来,并存入新数组中,最后把该数组返回给 Java

c)         上层 收到 native返回的 数组,加工成字符串,在UI中显示出来

 

 

2设计实现

1、  界面设计如下:


老老样子,很搓,嘿嘿

代码不在这贴出了,有需要的兄弟直接到文章结束部分下载。

2、  关键代码说明

 

Java 上层:

   

 public native int getDoubleNumber(int num);

public native int[] getArrayDoubleNumber(int[] nums);

  MainActivity.java

  

package com.duicky;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/**
 * 数据传输
 * 
 * @author luxiaofeng <454162034@qq.com>
 *
 */
public class MainActivity extends Activity {
	
	//也就是你mk配置文件中的  LOCAL_MODULE    := NDK_06
	private static final String libSoName = "NDK_06";
	private Context mContext = null;
	
	private int num = 0;
	private int[] nums;
	
	private Button btnCalculate = null;
	private Button btnCalculateArray = null;
	private EditText etNum = null;
	private EditText etArrayNum = null;
	private TextView tvDoubleNum = null;
	private TextView tvArrayDoubleNum = null;
	
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mContext = this;
        initViews();
    }
    
    /**
     * 初始化控件
     */
    private void initViews() {
		btnCalculate = (Button) findViewById(R.id.btn_calculate);
		btnCalculateArray = (Button) findViewById(R.id.btn_calculate_array);
		etNum = (EditText) findViewById(R.id.et_num);
		etArrayNum = (EditText) findViewById(R.id.et_array_num);
		tvDoubleNum = (TextView) findViewById(R.id.tv_double_num);
		tvArrayDoubleNum = (TextView) findViewById(R.id.tv_array_double_num);
		btnCalculate.setOnClickListener(new MyOnClickListener());
		btnCalculateArray.setOnClickListener(new MyOnClickListener());
	}

    private void calculateArray() {
    	if(getArrayNums()) {
    		setArrayNums();
    	}
	}

	private void calculate() {
		if(getNum()){
			setNum();
		}
	}
	
	private boolean getNum(){
		try{
			num = Integer.valueOf(etNum.getText().toString());
		} catch(NumberFormatException e) {
			etNum.setText("");
			tvDoubleNum.setText("");
			LogUtils.toastMessage(mContext, "输入有误,请重新输入数字");
			return false;
		}
		return true;
	}
	
	private void setNum() {
		int doubleNum = getDoubleNumber(num);
		LogUtils.printWithLogCat("JNIMsg", "C JNI -- >  Java: num = "+doubleNum);
		tvDoubleNum.setText(String.valueOf(doubleNum));
	}
	
	private boolean getArrayNums() {
		String str = etArrayNum.getText().toString();
		if(str.length() <= 0) {
			etArrayNum.setText("");
			tvArrayDoubleNum.setText("");
			LogUtils.toastMessage(mContext, "请按照格式输入");
			return false;
		}
		System.out.println(str);
		if(str.endsWith(".")){
			str = str.substring(0, str.length()-2);
		}
		System.out.println(str);
		String[] strArray = str.split(",");
		int len = strArray.length;
		nums = new int[len];
		for (int i = 0; i < len; i++) {
			try {
				nums[i] = Integer.valueOf(strArray[i]);
				System.out.println(nums[i]);
			} catch(NumberFormatException e) {
				etArrayNum.setText("");
				tvArrayDoubleNum.setText("");
				LogUtils.toastMessage(mContext, "输入有误,请重新输入数组");
				return false;
			}
		}
		return true;
	}
    
	private void setArrayNums() {
		int[] doubleArrayNums = getArrayDoubleNumber(nums);
		
		if(doubleArrayNums == null || doubleArrayNums.length <= 0) {
			LogUtils.toastMessage(mContext, "未转化成功");
			return ;
		}
		
		String str = "";
		for (int i = 0; i < doubleArrayNums.length; i++) {
			str += doubleArrayNums[i] +".";
		}
		str = str.substring(0,str.length()-1);
		tvArrayDoubleNum.setText(str);
	}
	
   class MyOnClickListener implements OnClickListener{

		@Override
		public void onClick(View v) {
			switch(v.getId()) {
				case R.id.btn_calculate:
					calculate();
					break;
				case R.id.btn_calculate_array:
					calculateArray();
					break;
			}
		}
    }

	/**
     * 计算2倍的数字
     * 
     * @param num
     * 
     * @return
     */
    public native int getDoubleNumber(int num);
    
    
    /**
     * 计算2倍的数组值
     * 
     * @param num
     * 
     * @return
     */
    public native int[] getArrayDoubleNumber(int[] nums);
    
    /**
     * 载入JNI生成的so库文件
     */
    static {
        System.loadLibrary(libSoName);
    }
}

  

       定义两个native方法, 第一个是 用来 测试传输 基本数据类型的,第二个是用来测试 传输数组的。

      

       Android.mk 文件

      

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

LOCAL_MODULE    := NDK_06

LOCAL_SRC_FILES := \

Transmission.c

include $(BUILD_SHARED_LIBRARY)

  

老样子,不说了,你懂的。 如果不懂,嘎嘎,那就请点击Android.mk 文件 简介咯

 

       JNI 中间层

      

       Transmission.c

      

#include <string.h>
#include <jni.h>
#include <android/log.h>

JNIEnv* jniEnv;


jint
Java_com_duicky_MainActivity_getDoubleNumber( JNIEnv* env,jobject thiz,jint num )
{
	if(jniEnv == NULL) {
		jniEnv = env;
	}
	//获取 Java 传递下来 数字
	__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Java -- > C JNI : num = %d",num);
	//返回 2 倍 的数字给 Java
	return num*2;
}

jintArray
Java_com_duicky_MainActivity_getArrayDoubleNumber( JNIEnv* env,jobject thiz,jintArray nums )
{
	if(jniEnv == NULL) {
		jniEnv = env;
	}

	if(nums == NULL){
		return NULL;
	}

	//获取 Java 传递下来 数组 的 长度
	jsize len = (*jniEnv)->GetArrayLength(jniEnv, nums);

	__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Java -- > C JNI : len = %d",len);

	if(len <= 0) {
		return NULL;
	}

	//新建一个长度为len的jintArray数组
	jintArray array = (*jniEnv)-> NewIntArray(jniEnv, len);

	if(array == NULL) {
		return NULL;
	}

	// 把 Java 传递下来的数组 用 jint* 存起来
	jint *body = (*env)->GetIntArrayElements(env, nums, 0);

	jint i = 0;
	jint num[len];
	for (; i < len; i++) {
		num[i] = body[i] * 2;
		__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "Java -- > C JNI : nums[%d] = %d",i,num[i]);
	}

	if(num == NULL){
		return NULL;
	}

	//给 需要返回的数组赋值
	(*jniEnv)->SetIntArrayRegion(jniEnv,array, 0, len, num);

	return array;
}

  

3、运行结果

测试 基本数据类型传输: 输入 22 点击 计算 得出结果 44


 

查看 打印 信息 :看到 上层输出 结果


 

测试 引用数据类型传输:输入11,22,33,44,55  ( 逗号是在英文状态下半角输入) ,点击生成, 输出 22,44,66,88,100


查看 打印信息  看到JNI层输出 结果


 

 

以上就是 Java --- JNI  基本数据类型 数组 传输的  小例子 , 其他 基本数据类型和数组 都可以仿照上面的做法传输。

 

 

 

4、注意点

你必须知道的是:

1)  添加参数在(JNIEnv* env,jobject thiz) 后面添加 如:(JNIEnv* env,jobject thiz,jintArray nums )

2)  获取数组的长度 jsize len = (*jniEnv)->GetArrayLength(jniEnv, nums);

3)  新建数组 jintArray array = (*jniEnv)-> NewIntArray(jniEnv, len); 如果是新建别的数组,NewIntArray 要做相对应的改变

4)  获取 数组里面的元素:

1.         (*env)->GetIntArrayElements(env, nums, isCopy) , 返回 所有数据。If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy is made; if no copy is made, it is set to JNI_FALSE.

2.          (*env)->GetIntArrayRegion(env,array,start,len,buffer) , start开始复制长度为len 的数据到buffer

5)  设置 数组里面的元素

1.         (*env)->SetIntArrayRegion(env, array,start,len,buffer) , start开始复制长度为len 的数据 buffer array

 

 

有不理解的兄弟请留言,个人技术有限,有讲错的地方请大牛们指出,讲的不够全面的请多多包涵,谢谢,

 

点击下载源码 数据的输一

 

本文出自 duicky 博客  转载请注明出处 http://www.cnblogs.com/luxiaofeng54/archive/2011/08/19/2145486.html

 

作者: duicky 发表于 2011-08-19 11:26 原文链接

评论: 0 查看评论 发表评论


最新新闻:
· comScore:7月谷歌独立访问用户1.823亿 居第一(2011-08-20 09:24)
· Steven Levy:“Google 特立独行”(2011-08-20 09:22)
· 我们在做俯卧撑(2011-08-20 09:20)
· 微软诉摩托罗拉侵权案听证会下周一开庭(2011-08-20 09:09)
· 谷歌地图将提供全球天气预报和云覆盖区信息(2011-08-20 09:08)

编辑推荐:挥手作别,HP 写下 webOS 的休止符

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [android ndk 学习] 推荐:

android NDK的学习

- - CSDN博客推荐文章
NDK是基于系统原生的C/C++的开发,但是它不是一种主流,而是Android SDK开发的有益补充,因为NDK没有提供界面,也没有提供生命周期管理这一类环境. NDK是一系列的工具包,使用这些工具包能够让我们很方便的进行JNI的开发. Java native interface,JNI就是java和C/C++相互调用的接口.

基于 Android NDK 的学习之旅-----数据传输一(基本数据类型和数组传输)(附源码)

- UnderSn0w - 博客园-首页原创精华区
基于 Android NDK 的学习之旅-----数据传输(基本数据类型和数组传输).        之前的一些文章都有涉及到上层和中间层的数据传输,简单来说,也就是参数和返回值的使用. 因为中间层要做的最多的也就是数据传输与转换,下面来介绍下这方面的知识.        数据传输可分为 基本数据类型传输 和 引用数据类型的传输 , 因为数组传输也比较特别(其实数组也是引用类型),所以这里也专门分出来讲讲.

Android NDK开发Crash错误定位

- - 极客521 | 极客521
在Android开发中,程序Crash分三种情况:未捕获的异常、ANR(Application Not Responding)和闪退(NDK引发错误). 其中 未捕获的异常根据logcat打印的堆栈信息很容易定位错误. ANR错误也好查,Android规定,应用与用户进行交互时,如果5秒内没有响应用户的操作,则会引发ANR错误,并弹出一个系统提示框,让用户选择继续等待或立即关闭程序.

Android 之 JNI 开发 详解 - NDK从入门到精通

- - CSDN博客推荐文章
-- 第一个JNI示例程序下载 :  GitHub - https://github.com/han1202012/NDKHelloworld.git . JNI概念 : Java本地接口, Java Native Interface, 它是一个 协议, 该协议用来沟通Java代码和外部的本地C/C++代码, 通过该协议 Java代码可以调用外部的本地代码, 外部的C/C++ 代码可以调用Java代码;.

android学习路线图

- Enlizh - Starming星光社最新更新
      首先表扬一下iteye,本来想把这些文章发表在CSDN(虽然都是一家的). 但发现CSDN博客功能用户体验做的不是那么友好,图片压缩的太厉害. 好好的图片上传之后已经面目全非. 只好又再iteye注册了个相同的博客,当然内容也相同. 做android开发有一段时间了,一直想挤出点时间,写点什么.

Android学习之路——7.Service

- - ITeye博客
这两天又学习了Android四大组件之一的Service. (1)Service不是一个单独的Process,除非特别指派了,也不是一个Thread,但也不是运行在Main Thread中. (3)Service的生命周期:. 调用的Context.startService()   oncreate() --> onStartCommand ()--> Service is running --> The service is stopped by its or a client--> onDestroy() --> Service is shut down .

Android学习笔记(六)SQLite

- - 博客园_首页
SQLite是一个极轻量型的数据库. 它在提供了和大型数据库相当的功能,还具有轻便、跨平台等优点,SQLite使用非常方便,并不需要我们像常规数据库(SQLServer,Mysql等)那样进行安装,在Android的JDK中,其实是已经包含了SQLite这个数据库的核心. 当然我们必须要强调一点,SQLite并不是只针对Android的,其实它还可以用在别的很多地方.

Android Native 代码开发学习笔记

- iDesperadO - WindStorm
本文提供排版更佳的PDF版本下载. JNI,全称Java Native Interface,是用于让运行在JVM中的Java代码和运行在JVM外的Native代码(主要是C或者C++)沟通的桥梁. 代码编写者即可以使用JNI从Java的程序中调用Native代码,又可以从Native程序中调用Java代码.

Android学习笔记之Gallery(2)

- - CSDN博客推荐文章
Toast.makeText(GalleryActivity.this, "点击了第"+arg2+"张图片", Toast.LENGTH_LONG).show();. // 给ImageView设置资源. // 设置布局 图片120*80. 创建一个新的XML文件在 res/values/目录下 attrs.xml命名.

Android学习笔记之布局

- - CSDN博客推荐文章
我们对Android应用程序运行原理及布局文件可谓有了比较深刻的认识和理解,并且用“Hello World. 在继续深入 Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍. View的布局显示方式有下面几种: 线性布局(Linear Layout)、 相对布局(Relative Layout)、 表格布局(Table Layout)、 网格视图(Grid View)、 标签布局(Tab Layout)、 列表视图(List View)、 绝对布局(AbsoluteLayout).