标签:jni
在一种平台下编译出能够在另外一种平台下运行二进制代码
平台(1,操作系统:windows linux mac os solaris 2,cpu x86 arm mips)
源代码--->编译---->动态库(.dll,.so)-->目标平台运行
windows 源代码编译成 linux,arm下的可执行文件
ndk (native develop kits)工具链 一系列的工具组成,链接在一起,链式调用
cdt (c/c++ develop tools) c/c++源代码 高亮显示
cygwin windows平台下的linux系统模拟器
* build 一些列批处理工具
* docs 文档
* platforms 各种平台下所需要的头文件和函数库
* prebuilt 预编译工具
* samples ndk开发案例
* sources 工具源文件
* tests 测试目录
* toolchains 工具链
* ***ndk-build 编译工具
1、创建android工程
2、创建native方法
3、实现c的功能
4、在工程目录下创建jni文件夹
5、把jni.h头文件拷贝到jni目录下
6、创建c源文件
#include <jni.h>//导入头文件使用里面所需要的接口方法
char* getHelloWorld()
{
return "hello world from c";
}
//public native String helloFromC();//这是要调用的c程序里的一个方法,我们如果要调用它就需要
//在下面去遵循协议去调用该方法
//如何让java文件去调用c文件里的内容,就要按照jni.h里面规定好的协议:如下
//Java_包名_类名_方法名()
//doGet(request,response)与它类似,相当于一个桥梁,下面也是一个接通java程序和c程序的桥梁
//JNIEnv* env沟通:c程序,jobject obj沟通java程序的
jstring Java_com_itheima_helloWorld_MainActivity_helloFromC(JNIEnv* env,jobject obj )
{
// jstring (*NewStringUTF)(JNIEnv*, const char*);
jstring str = (*env)->NewStringUTF(env,getHelloWorld());
return str;
}
7、实现java中native方法的jni规范
返回类型 Java_包名_类名_方法名(JNIEnv* env,jobject obj,其他参数)
jstring Java_com_itheima_helloWorld_MainActivity_helloFromC(JNIEnv* env,jobject obj )
8、实现jni方法
{
// jstring (*NewStringUTF)(JNIEnv*, const char*);
jstring str = (*env)->NewStringUTF(env,getHelloWorld());
return str;
}
9、创建Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := java中调用的名字
LOCAL_SRC_FILES := c实现的源文件 6步
include $(BUILD_SHARED_LIBRARY)
10、进入工程目录下,执行ndk-build命令 ,生成.so文件
11、java中加载.so文件
static{
System.loadLibrary("hellolib");
}
12、调用执行
package com.itheima.helloWorld;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("hellolib");
}
//定义jni接口:native就是jni的关键字
public native String helloFromC();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
Toast.makeText(this, helloFromC(), 0).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
1、定义接口
public native int add(int a ,int b);
2、把建立jni包并把jni.h头文件拉进来
3、生成add.c文件
#include <jni.h>
int add(int a , int b)
{
return a+b;
}
//public native int add(int a ,int b);
jint Java_com_itheima_add_MainActivity_add(JNIEnv* env,jobject obj ,jint a , jint b)
{
return add(a,b);
}
4、新建Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := addlib
LOCAL_SRC_FILES := add.c
include $(BUILD_SHARED_LIBRARY)
5、生成so文件
6、设计安卓UI
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.itheima.add"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.itheima.add.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
7、实现java调用c程序方法
package com.itheima.add;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("addlib");
}
public native int add(int a ,int b);
private EditText et_one;
private EditText et_two;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_one = (EditText) findViewById(R.id.et_one);
et_two = (EditText) findViewById(R.id.et_two);
}
public void click(View view)
{
String sone = (et_one.getText()+"").trim();
String stwo = (et_two.getText()+"").trim();
int res = add(Integer.parseInt(sone),Integer.parseInt(stwo));
Toast.makeText(this, "res:"+res, 0).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
* 1. jdk1.6 进入工程目录下的bin/classes目录 用javah 包名.类名
* 2. jdk1.7 进入工程目录下的src目录 用javah 包名.类名
配置ndk工具
windows->references->android->ndk->在右边窗口输入ndk解压目录
1、创建工程 右键工程-->android tools-->add native support->输入动态库名字->点击确定
2、把cpp改为c,同时在Android.mk文件中也把cpp改成c
3、定义java中native方法
public native int mutli_p(int a, int b);
4、实现c代码
5、在c源文件中实现jni规范(native方法) javah命令参考上面
右键工程--》properties->c/c++ general->paths and symbols 选择includes 点击add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存
6、在c源文件中实现jni方法
#include <jni.h>
int multi(int a ,int b)
{
return a*b;
}
jint Java_com_itheima_multi1_MainActivity_mutli_1p
(JNIEnv * env, jobject obj, jint a , jint b)
{
return multi(a,b);
}
package com.itheima.multi1;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("multi");
}
public native int mutli_p(int a, int b);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, mutli_p(3,8), 1).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
7、java中加载.so文件
加载MainActivity.class时自动生成
8、运行
配置ndk工具 windows->references->android->ndk->在右边窗口输入ndk解压目录
1、创建工程 右键工程-->android tools-->add native support->输入动态库名字->点击确定
2、把cpp改为c,同时在Android.mk文件中也把cpp改成c
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := sort
LOCAL_SRC_FILES := sort.c
include $(BUILD_SHARED_LIBRARY)
3、定义java中native方法
public native void addarray(int datas[],int len );//为了给数组赋值
4、实现c代码
//如果要调用以下这段c程序方法,那么问题就是如何把java中的数组传到如下这段c程序中给该方法使用
void addarray(int *arr,int len )
{
int i = 0;
for(;i<len;i++)
{
arr[i]++;
}
}
5、在c源文件中实现jni规范(native方法) javah命令参考上面
右键工程--》properties->c/c++ general->paths and symbols 选择includes 点击add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include保存
6、在c源文件中实现jni方法(难点)jni精华
#include <jni.h>
//如果要调用以下这段c程序方法,那么问题就是如何把java中的数组传到如下这段c程序中给该方法使用
void addarray(int *arr,int len )
{
int i = 0;
for(;i<len;i++)
{
arr[i]++;
}
}
void Java_com_itheima_paixu_MainActivity_addarray
(JNIEnv * env, jobject obj, jintArray jia, jint len)
{
//jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
//以下这段代码就是为了把java传过来的java数据转成c的整型数组,而以上这个就是GetIntArrayElements方法的规范
jint* jintarray = (*env)->GetIntArrayElements(env,jia,0);//通过规范去把java数组转成c数组
addarray(jintarray,len);
}
7、java中加载.so文件
package com.itheima.paixu;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("sort");
}
/**
* 对int数组中的每个值加1
* @param datas
*/
public native void addarray(int datas[],int len );
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
int datas[] = {11,12,13,14};
addarray(datas,datas.length);
Toast.makeText(this, "1:"+datas[0]+"3:"+datas[2], 1).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
加载后自动生成
8,运行
1、创建工程 右键工程-->android tools-->add native support->输入动态库名字->点击确定
2、把cpp改为c,同时在Android.mk文件中也把cpp改成c
3、定义java中native方法
/**
* 对int数组中的每个值加1
* @param datas
* @param len
*/
public native void insertsortc(int datas[],int len);
4、实现c代码
void insertsort(int *datas,int len)
{
//插入排序的外层循环,插入排序的次数
int i = 0;
for (i = 1; i < len; i++){
int temp = datas[i];
int j = 0;
for (j = i - 1;j >= 0; j--){
if (datas[j] > temp){
//把j的位置放到后面
datas[j + 1] = datas[j];
} else {
datas[j + 1] = temp;
break;
}
}
if (j == -1){
datas[0] = temp;
}
}
}
5、在c源文件中实现jni规范(native方法) javah命令参考上面
右键工程--》properties->c/c++ general->paths and symbols 选择includes 点击add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存
6、在c源文件中实现jni方法
void Java_com_itheima_sort1_MainActivity_insertsortc
(JNIEnv *env, jobject obj, jintArray jia, jint len)
{
// jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
jint* jintarray = (*env)->GetIntArrayElements(env,jia,0);
insertsort(jintarray,len);
}
7、java中加载.so文件
package com.itheima.sort1;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
static{
System.loadLibrary("sort");
}
/**
* 对int数组中的每个值加1
* @param datas
* @param len
*/
public native void insertsortc(int datas[],int len);
/**
* 初始化datas数组
* @param datas
*/
public void init(int datas[]){
for (int i = 0; i < datas.length; i++) {
datas[i] = (int)(Math.random()*100);
}
}
/*
* java的选择排序的代码,用来调用比较与c的选择排序代码的效率差距
*
*/
public void insertSort(int datas[]) {
// 插入排序的外层循环,插入排序的次数
for (int i = 1; i < datas.length; i++) {
int temp = datas[i];
int j = 0;
for (j = i - 1; j >= 0; j--) {
if (datas[j] > temp) {
// 把j的位置放到后面
datas[j + 1] = datas[j];
} else {
datas[j + 1] = temp;
break;
}
}
if (j == -1) {
datas[0] = temp;
}
}
}
/**
* 打印数组
* @param datas
*/
public void print(int datas[]){
for (int i = 0; i < datas.length; i++) {
System.out.print(datas[i]+",");
}
System.out.println();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
final int datas[] = new int [30000];
init(datas);//使用方法为数组初始化随机值
new Thread(){
public void run(){
System.out.println(new Date());
insertsortc(datas, datas.length);
// insertSort(datas);
System.out.println(new Date());
};
}.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
8、运行
package com.itheima.sort1;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
static{
System.loadLibrary("sort");
}
/**
* 对int数组中的每个值加1
* @param datas
* @param len
*/
public native void insertsortc(int datas[],int len);
/**
* 初始化datas数组
* @param datas
*/
public void init(int datas[]){
for (int i = 0; i < datas.length; i++) {
datas[i] = (int)(Math.random()*100);
}
}
/*
* java的选择排序的代码,用来调用比较与c的选择排序代码的效率差距
*
*/
public void insertSort(int datas[]) {
// 插入排序的外层循环,插入排序的次数
for (int i = 1; i < datas.length; i++) {
int temp = datas[i];
int j = 0;
for (j = i - 1; j >= 0; j--) {
if (datas[j] > temp) {
// 把j的位置放到后面
datas[j + 1] = datas[j];
} else {
datas[j + 1] = temp;
break;
}
}
if (j == -1) {
datas[0] = temp;
}
}
}
/**
* 打印数组
* @param datas
*/
public void print(int datas[]){
for (int i = 0; i < datas.length; i++) {
System.out.print(datas[i]+",");
}
System.out.println();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
final int datas[] = new int [30000];
init(datas);//使用方法为数组初始化随机值
// print(datas);
new Thread(){
public void run(){
long start = System.currentTimeMillis();
insertsortc(datas, datas.length);
// insertSort(datas);
long end = System.currentTimeMillis();
System.out.println("程序运算的时间是:"+(end-start));
};
}.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Java的选择排序的算法效率
c的选择排序的算法效率
一
12-06 03:21:46.365: E/AndroidRuntime(1648): Caused by: java.lang.UnsatisfiedLinkError: Couldn‘t load mullib: ***findLibrary returned null***
1,平台不匹配
创建 Application.mk 在文件加
APP_ABI := all
生成所有平台的.so文件
2,动态库的名字写错了
二
12-06 03:27:13.910: E/AndroidRuntime(1801): Caused by: java.lang.UnsatisfiedLinkError: addarray
12-06 03:27:13.910: E/AndroidRuntime(1801): at com.itheima.sort.MainActivity.addarray(Native Method)
jni规范没有写正确
1、反编译美图秀秀的apk文件,观察美图秀秀工程里使用到的jni接口和解压apk后获得的.so文件
2、新建工程并把.so文件复制到lib文件下,按美图秀秀的jni规范新建好包把jni.java文件复制到工程下
3、定义布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="style1"
android:text="style1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="style2"
android:text="style2" />
<ImageView
android:id="@+id/iv_mtxx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
4、在主函数中调用jni接口,并加上访问sd卡的权限
package com.itheima.myheimaxx;
import com.mt.mtxx.image.JNI;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends Activity {
//即使使用别人的.so文件,这个函数库导入的过程也要添加
static{
System.loadLibrary("mtimage-jni");
}
private ImageView iv_mtxx;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_mtxx = (ImageView) findViewById(R.id.iv_mtxx);
iv_mtxx.setImageBitmap(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/Koala.jpg"));
}
public void style1(View view){
Bitmap tu = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/Koala.jpg");
int width = tu.getWidth();
int height = tu.getHeight();
int pixels[] = new int[width*height];
tu.getPixels(pixels, 0, width, 0, 0, width, height);
//实现复制过来的美图秀秀的JNI接口对象
JNI jni = new JNI();
//使用接口对象的方法
jni.StyleLomoB(pixels, width, height);
tu = Bitmap.createBitmap(pixels, width, height, tu.getConfig());
iv_mtxx.setImageBitmap(tu);
}
public void style2(View view) {
Bitmap tu = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/Koala.jpg");
int width = tu.getWidth();
int height = tu.getHeight();
int pixels[] = new int[width * height];
tu.getPixels(pixels, 0, width, 0, 0, width, height);
//实现复制过来的美图秀秀的JNI接口对象
JNI jni = new JNI();
//使用接口对象的方法
jni.StyleLomoC(pixels, width, height);
tu = Bitmap.createBitmap(pixels, width, height, tu.getConfig());
iv_mtxx.setImageBitmap(tu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
该案例的如何把java传递过来的字符串到c程序的处理暂时不解释,后面会讲解,所以读者可以跳过项目中的该部分
开发步骤
1、创建工程 右键工程-->android tools-->add native support->输入动态库名字->点击确定
2、把cpp改为c,同时在Android.mk文件中也把cpp改成c
3、定义java中native方法
/**
*
* @param name 用户名
* @param pass 密码
* @return
* 404用户名不存在 200登录成功 500密码错误
*/
public native int check(String name,String pass);
4、实现c代码
int check_login(char* name,char* pass)
{
if (strcmp(name,"andy") == 0) //字符串相等
{
if (strcmp(pass,"123") == 0 )
{
return 200;
}
else
{
return 500;
}
}
else
{
return 404;
}
}
/**
* 该段代码为了接收从java传过来的string类型,再转成c的string类型的方法
*/
char* string_java2c(JNIEnv *env,jstring name){
//1.
jclass clazz = (*env)->FindClass(env,"java/lang/String");
//2. jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID mid = (*env)->GetMethodID(env,clazz,"getBytes","(Ljava/lang/String;)[B");
//3.jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyteArray jba = (*env)->CallObjectMethod(env,name,mid,(*env)->NewStringUTF(env,"utf-8"));
//4.jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
jbyte* jb = (*env)->GetByteArrayElements(env,jba,JNI_FALSE);
//5.jsize (*GetArrayLength)(JNIEnv*, jarray);
jint len = (*env)->GetArrayLength(env,jba);
char m[10];
if (len == 0){
return "";
}
char *str = malloc(len + 1);
memcpy(str,jb,len);
str[len] = 0;
// 5.void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,jbyte*, jint)
(*env)->ReleaseByteArrayElements(env,jba,jb,0);
return str;
}
5、在c源文件中实现jni规范(native方法) javah命令参考上面
右键工程--》properties->c/c++ general->paths and symbols 选择includes 点击add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存
6、在c源文件中实现jni方法
jint Java_com_itheima_MyICBCClient_MainActivity_check
(JNIEnv *env, jobject obj, jstring name, jstring pass)
{
char *cname = string_java2c(env,name);
char *cpass = string_java2c(env,pass);
return check_login(cname,cpass);
}
7、java中加载.so文件
8、运行
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
android:id="@+id/et_name" />
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:id="@+id/et_password" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="login"
android:onClick="click"/>
</LinearLayout>
MainActivity
package com.itheima.MyICBCClient;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("ICBC");
}
private EditText et_name;
private EditText et_pass;
/**
*
* @param name 用户名
* @param pass 密码
* @return
* 404用户名不存在 200登录成功 500密码错误
*/
public native int check(String name,String pass);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) findViewById(R.id.et_name);
et_pass = (EditText) findViewById(R.id.et_password);
}
// "andy" "andy "
public void click(View view){
String name = (et_name.getText() + "").trim();
String pass = (et_pass.getText() + "").trim();
int res = check(name,pass);
switch (res) {
case 200:
Toast.makeText(this, "登陆成功", 1).show();
break;
case 404:
Toast.makeText(this, "用户名不存在", 1).show();
break;
case 500:
Toast.makeText(this, "pass error", 1).show();
break;
default:
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
我们为什么要在C程序和java程序之间互相调用?因为例如现实当中监测洪水,当洪水来临时设备会自动发送短信告知我们,但是这个步骤的实现用C来实现?累到吐血都写不出来,所以需要由控制硬件的c程序调用java程序去发送短信告知用户
而C来调用Java其实就是一种反射
复制案例七:
1、改造MainActivity中的方法,准备是用C语言来调用
package com.itheima.MyICBCClient;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("ICBC");
}
private EditText et_name;
private EditText et_pass;
private ProgressDialog pd;
/**
*
* @param name 用户名
* @param pass 密码
* @return
* 404用户名不存在 200登录成功 500密码错误
*/
public native int check(String name,String pass);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) findViewById(R.id.et_name);
et_pass = (EditText) findViewById(R.id.et_password);
}
//我们的目的是用c调用以下的方法showMessage和closeMessage
public void showMessage(final String mess){
runOnUiThread(new Runnable(){
@Override
public void run() {
if(pd!=null){
pd.dismiss();
}
pd = new ProgressDialog(MainActivity.this);
pd.setTitle("正在处理");
pd.setMessage(mess);
pd.show();
}
});
}
public void closeMessage(){
if(pd!=null){
pd.dismiss();
}
}
// "andy" "andy "
public void click(View view){
final String name = (et_name.getText() + "").trim();
final String pass = (et_pass.getText() + "").trim();
new Thread(){
public void run(){
//这里是java调用c方法,然后在这个c方法里又会调用java的showMessage方法和closeMessage方法
check(name,pass);//这里是主线程进行调用
};
}.start();
//用c来调用以下代码
// int res = check(name,pass);
// switch (res) {
// case 200:
// Toast.makeText(this, "登陆成功", 1).show();
// break;
// case 404:
// Toast.makeText(this, "用户名不存在", 1).show();
// break;
// case 500:
// Toast.makeText(this, "pass error", 1).show();
// break;
// default:
// break;
// }
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
2、改造ICBC.c文件里的native方法,使用C语言调用java程序的方法,利用反射
#include <jni.h>
int check_login(char* name,char* pass)
{
if (strcmp(name,"andy") == 0) //字符串相等
{
if (strcmp(pass,"123") == 0 )
{
return 200;
}
else
{
return 500;
}
}
else
{
return 404;
}
}
/**
* 该段代码为了接收从java传过来的string类型,再转成c的string类型的方法
*/
char* string_java2c(JNIEnv *env,jstring name){
//1.
jclass clazz = (*env)->FindClass(env,"java/lang/String");
//2. jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID mid = (*env)->GetMethodID(env,clazz,"getBytes","(Ljava/lang/String;)[B");
//3.jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyteArray jba = (*env)->CallObjectMethod(env,name,mid,(*env)->NewStringUTF(env,"utf-8"));
//4.jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
jbyte* jb = (*env)->GetByteArrayElements(env,jba,JNI_FALSE);
//5.jsize (*GetArrayLength)(JNIEnv*, jarray);
jint len = (*env)->GetArrayLength(env,jba);
char m[10];
if (len == 0){
return "";
}
char *str = malloc(len + 1);
memcpy(str,jb,len);
str[len] = 0;
// 5.void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,jbyte*, jint)
(*env)->ReleaseByteArrayElements(env,jba,jb,0);
return str;
}
//如何在C中实现显示调用打开关闭进度框
//C来调用Java其实就是一种反射,反射的原理其实就是JNI
jint Java_com_itheima_MyICBCClient_MainActivity_check
(JNIEnv *env, jobject obj, jstring name, jstring pass)
{
char *cname = string_java2c(env,name);
char *cpass = string_java2c(env,pass);
int res = check_login(cname,cpass);
char* mess = "name jia mi chuli....";//c程序中的字符串
//调用java方法的步骤被封装了
c_show(env,obj,mess);//该步骤调用java中的showMessage方法
sleep(2);//休息两秒钟
c_close(env,obj);//实现java中的closeMessage方法
return res;
}
//封装:C调用java
void c_close(JNIEnv *env, jobject obj)
{
//1,class:第一步获得class
// jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env,"com/itheima/MyICBCClient/MainActivity");
//2,method
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID mid = (*env)->GetMethodID(env,clazz,"closeMessage","()V");
//3,obj:得到obj
//4,调用:反射
// void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env,obj,mid);
}
//封装:C调用java
void c_show(JNIEnv *env, jobject obj,char* mess)
{
//反射出java的方法
//1,class
//jclass (*FindClass)(JNIEnv*, const char*);
//const char*表示类名,在这用反斜杠目录表示
jclass clazz = (*env)->FindClass(env,"com/itheima/MyICBCClient/MainActivity");
//2,method
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
//const char*, const char*分别代表:方法名和方法的签名(也就是重载方法后的不同方法)
//如何获得java方法中的方法签名,用dos进入到工程的bin目录中的classes中,使用javap命令
//命令后面接着输入主类的包名路径,那么dos就会返回这个主类中所有方法的签名了,也就是我们可以获取我们想得到的方法签名
jmethodID mid = (*env)->GetMethodID(env,clazz,"showMessage","(Ljava/lang/String;)V");
//3,obj:已经有了
//4,invoke:调用
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
//(*env)->NewStringUTF(env,mess):这是用c的字符指针转换为java的字符串
(*env)->CallVoidMethod(env,obj,mid,(*env)->NewStringUTF(env,mess));
}
1、创建工程 右键工程-->android tools-->add native support->输入动态库名字->点击确定
2、把cpp改为c,同时在Android.mk文件中也把cpp改成c
3、定义java中native方法
//该native方法的目的就是我们把我们的名字传递给C程序,然后c程序返回名字加上Hello这样一个方法
public native String getHelloFromc(String name);
4、实现c代码
#include <jni.h>
#include <string.h>
#include<stdlib.h>
//传递名字和名字的长度
char* getHello(char* name,int len)
{
//动态申请的内存:把字符拷贝进去
char* res = malloc((6 + len + 1) * sizeof(char));
char* h = "hello:";
//把字符串hcopy到res中
strcpy(res,h);
//连接字符串:即把名字和hello连接起来
strcat(res,name);//hello:xxx
/*
* 在主函数中测试
char *name = "andy";
name = getHello(name,strlen(name));
*/
return res;
}
5、在c源文件中实现jni规范(native方法) javah命令参考上面
右键工程--》properties->c/c++ general->paths and symbols 选择includes 点击add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存
#include <jni.h>
#include <string.h>
#include<stdlib.h>
//传递名字和名字的长度
char* getHello(char* name,int len)
{
//动态申请的内存:把字符拷贝进去
char* res = malloc((6 + len + 1) * sizeof(char));
char* h = "hello:";
//把字符串hcopy到res中
strcpy(res,h);
//连接字符串:即把名字和hello连接起来
strcat(res,name);//hello:xxx
/*
* 在主函数中测试
char *name = "andy";
name = getHello(name,strlen(name));
*/
return res;
}
//如何使用上面的函数
jstring Java_com_itheima_j2c_MainActivity_getHelloFromc
(JNIEnv *env, jobject obj, jstring str)
{
char* name = java2cstring(env,obj,str);
char* res = getHello(name,strlen(name));
return (*env)->NewStringUTF(env,res);
}
//如何在Java_com_itheima_j2c_MainActivity_getHelloFromc方法里实现getHello方法呢?
//接下来完成今天的核心,如何把java中的字符串转换成C中的字符串
char* java2cstring(JNIEnv *env, jobject obj, jstring str)
{
//里面需要反射,调用字符串时也是需要调用字符串中的对象
/*
* 而其实我们要在C中获得java的字符串数组
* 其实就是获得java传来的字符串的字节流,
* 也就是用C去反射java中的字符串类的一个方法:byte[] b = "andy".getBytes("utf-8");
*/
//1,jclass
jclass clazz = (*env)->FindClass(env,"java/lang/String");
//2,method
//这步就获得了.getBytes("utf-8")的方法了
jmethodID mid = (*env)->GetMethodID(env,clazz,"getBytes","(Ljava/lang/String;)[B");
//3,str(对象)
//4,invoke
//jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
//强转成jbyteArray
jbyteArray jba = (*env)->CallObjectMethod(env,str,mid,(*env)->NewStringUTF(env,"utf-8"));
//5,获取数组array
// jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
jbyte* jb = (*env)->GetByteArrayElements(env,jba,0);
//6,array len
// jsize (*GetArrayLength)(JNIEnv*, jarray);
jsize len = (*env)->GetArrayLength(env,jba);
char* cstr = malloc(len + 1);
//然后把jb字节指针里的内容拷贝到cstr中
memcpy(cstr, jb, len);
cstr[len] = ‘\0‘;
// void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
// jbyte*, jint);
//多次调用就要释放资源
(*env)->ReleaseByteArrayElements(env,jba,jb,0);
return cstr;
}
6、在c源文件中实现jni方法
7、java中加载.so文件
package com.itheima.j2c;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("getHello");
}
//该native方法的目的就是我们把我们的名字传递给C程序,然后c程序返回名字加上Hello这样一个方法
public native String getHelloFromc(String name);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
Toast.makeText(this, getHelloFromc("andy"), 1).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
8、运行
标签:jni
原文地址:http://blog.csdn.net/faith_yee/article/details/44855745