码迷,mamicode.com
首页 > 编程语言 > 详细

JNI(Java Native Interface)_02

时间:2015-04-03 17:33:48      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:jni

JNI(Java Native Interface)_02

ndk开发常用术语

观察jni.h文件的c语言细节:

技术分享

技术分享

交叉编译

在一种平台下编译出能够在另外一种平台下运行二进制代码

平台(1,操作系统:windows linux mac os solaris    2,cpu x86 arm mips)

交叉编译原理

源代码--->编译---->动态库(.dll,.so)-->目标平台运行

windows 源代码编译成 linux,arm下的可执行文件

ndk开发工具

ndk (native develop kits)工具链  一系列的工具组成,链接在一起,链式调用 
cdt (c/c++ develop tools)  c/c++源代码 高亮显示
cygwin windows平台下的linux系统模拟器

ndk工具介绍

* build 一些列批处理工具
* docs 文档
* platforms 各种平台下所需要的头文件和函数库
* prebuilt 预编译工具
* samples ndk开发案例
* sources 工具源文件
* tests 测试目录
* toolchains 工具链
* ***ndk-build 编译工具

nkd开发步骤

示例1

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;

    }
}

技术分享

案例二:02_两个数相加

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;
    }

}

技术分享

javah命令生成头文件

* 1. jdk1.6   进入工程目录下的bin/classes目录  用javah  包名.类名
* 2. jdk1.7   进入工程目录下的src目录   用javah  包名.类名

工具开发ndk步骤(快速开发)

配置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、运行

技术分享

案例四:04_排序

配置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,运行

技术分享

案例五、05排序比较Java和C的算法效率

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的选择排序的算法效率

技术分享

ndk常见开发错误

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规范没有写正确

案例六、06_itheima秀秀

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;
    }

}

技术分享

案例七、07_ICBCClient:银行账号密码管理系统

该案例的如何把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;
    }

}

技术分享

技术分享

案例八、08_ICBCClient(C调用java案例)

我们为什么要在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));
}

技术分享

案例九、09_java2cString:怎么把java中的字符串转成C中的字符串给c去调用

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(Java Native Interface)_02

标签:jni

原文地址:http://blog.csdn.net/faith_yee/article/details/44855745

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!