JNI示例

JNI示例
2007年05月27日 星期日 16:15
一、JNI简介
JNI:Java Native Interface,是Java语言提供的一种通用接口,用于Java代码与本地化代码的交互。所谓本地化代码是指直接编译成的与机器相关的二进制代码,而非Java字节码之类的中间代码。Windows下面的可执行文件,DLL等,Linux下面的可执行文件和SO文件等,都是二进制代码。
JNI允许Java语言编写的程序与其他语言编写的程序库(DLL, SO)或可执行文件进行互操作,包括汇编、C、C++。JNI产生的原因在于以下几种需求:
(1). 你的应用程序需要使用系统相关的功能,而Java代码不支持或是难以办到。
(2). 已有其他语言写好的类库或程序,希望Java程序可以使用它们。
(3). 出于更高的性能要求,希望使用汇编或是C/C++语言来实现部分功能。
下图出自JNI Tutorial,展示了JNI的地位:

其他的理论的东西就不多说了,JNI Tutorial讲得很清楚。强烈建议阅读。

二、JNI的开发步骤
这里以使用C++编写本地化方法实现为例,开发一个使用JNI的Demo程序,具体步骤如下所示:
(1). 编写带有native方法的java类
(2). 使用javac命令编译所编写的java类
(3). 使用javah命令处理类文件,生成C/C++头文件
(4). 使用C/C++实现本地方法
(5). 将C/C++编写的文件生成动态连接库

三、Demo程序,Demo程序宣示了Java代码中调用C++的输出功能打印字符串,同时演示了使用C++的输入功能读取字符串,并使用System.out.println输出。

1. 编写带native方法的Java类
本示例中的源程序就一个Java类。如下所示:
// HelloJNI.java -- 简单的JNI入门示例。
// 2007-4-5 16:41:45

public class HelloJNI {
  public native void displayHello();
  public native void showTime();

  private native String getLine(String prompt);

  static {
    System.loadLibrary("hello");
  }

  public static void main(String[] args) throws Exception {
    HelloJNI hj = new HelloJNI();

    System.out.println("==> Demo 1: hello");
    hj.displayHello();
    System.out.println("==> Demo 2: time");
    hj.showTime();

    System.out.println("==> Demo 3: input");
    String input = hj.getLine("Type a line: ");
    System.out.println("User typed: " + input);
  }
}

说明:一共3个native方法,第一个简单的Hello,第二个使用<ctime>头文件中的相关函数计算当前时间,第三个读取输入。注意static语句块:
static {
        System.loadLibrary("hello");
}
在JVM载入HelloJNI类的时候加载动态库,System.loadLibrary()中的参数是我们要生成的动态库文件的名字,不包括扩展名,在Windows下面是hello.dll,Linux下面是hello.so,这个由Java自动识别。

2. 编译此类

3. javah HelloJNI 生成C/C++头文件,生成的头文件不用任何修改。如下所示:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:      HelloJNI
* Method:     displayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_displayHello
  (JNIEnv *, jobject);

/*
* Class:      HelloJNI
* Method:     showTime
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_showTime
  (JNIEnv *, jobject);

/*
* Class:      HelloJNI
* Method:     getLine
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_HelloJNI_getLine
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif



4. 编写C/C++实现文件,如下所示:
// HelloJNIImpl.cpp -- 自己编写的实现文件
// 2007-4-5 16:52:53

#include <jni.h>
#include <iostream>
#include <ctime>
#include <string>
#include <cstdio>
#include <windows.h>
#include "HelloJNI.h"

extern "C" {
  //
}

using namespace std;

JNIEXPORT void JNICALL Java_HelloJNI_displayHello(JNIEnv * env, jobject obj) {
  cout << "Hello, JNI tech. This is from C++!" << endl;
}

JNIEXPORT void JNICALL Java_HelloJNI_showTime(JNIEnv * env, jobject obj) {
  time_t sec;
  tm t;

  time_t loop;

  cout << "时间:";
  sec = time(NULL);
  t = *localtime(&sec);
  unsigned int hour = t.tm_hour;
  unsigned int mini = t.tm_min;
  unsigned int secd = t.tm_sec;
  
  if(hour < 10) {
  cout << "0" << hour;
  }
  else {
    cout << hour;
  }
  cout << ":";
  if(mini < 10) {
    cout << "0" << mini;
  }
  else {
    cout << mini;
  }
  cout << ":";
  if(secd < 10) {
    cout << "0" << secd;
  }
  else {
    cout << secd;
  }
  cout << endl;
}

JNIEXPORT jstring JNICALL Java_HelloJNI_getLine(JNIEnv * env, jobject obj, jstring prompt) {
  char buf[128] = {0};
  const char * str = (env)->GetStringUTFChars(prompt, 0);
//   printf("%s", str);
  cout << str;
  (env)->ReleaseStringUTFChars(prompt, str);
  string buffer;
  getline(cin, buffer);
//   scanf("%s", buf);
  return (env)->NewStringUTF(buffer.c_str());
}

说明:JNI Tutorial中使用的居然是像下面这样的代码,env指针的使用应该有误,敬请注意。
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
...

5. 生成本地库文件。笔者使用的VS.NET 2003中的C++编译器。VS.NET 2003命令行工具生成DLL的命令:
cl -I<头文件目录1> -I<头文件目录2> -LD <C/C++源文件名> -Fe<生成的DLL文件名>
-I选项指定头文件目录,-LD指定C++源文件,-Fe指定生成的DLL文件名。将各个部分替换成实际的情况,实际使用的命令如下:
cl -IC:/java/jdk1.5.0_10/include -IC:/java/jdk1.5.0_10/include/win32 -LD HelloJNIImpl.cpp -Fehello.dll
注:cl可以增加-EHsc参数来减少生成过程中的输出信息。如下图所示:

6. 运行结果,如下图所示:

注意自己输入的那一行!

7. 自动化。其实步骤都很简单,所以可以使用Ant来完成。下面提供一个ANT Buildfile。使用之前请根据自己的实际情况修改相关属性。
注意:Builfile编码是UTF-8的。
build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project default="help" basedir="." name="JNI_Hello">
  <property name="app.home" value="."></property>

  <!-- 设置头文件的目录,根据JDK具体的安装目录而定 -->
  <property name="h.dir1" value="C:/java/jdk1.5.0_10/include"></property>
  <property name="h.dir2" value="C:/java/jdk1.5.0_10/include/win32"></property>
  
  <!-- 设置cl工具的路径,视具体情况而定 -->
  <property name="vc.bin.dir"
    value="C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/bin">
  </property>

  <property name="cpp.name" value="HelloJNIImpl.cpp"></property>
  <property name="dll.name" value="hello.dll"></property>

  <target name="compile" description=" ==> 编译Java源文件">
    <javac srcdir="${app.home}" destdir="${app.home}">
    </javac>
  </target>

  <target name="javah" description=" ==> 生成C/C++头文件">
    <javah destdir="${app.home}" force="yes" class="HelloJNI"/>
  </target>
  
  <target name="dll" description=" ==> 调用cl命令生成相关的DLL文件">
    <exec dir="${app.home}" executable="${vc.bin.dir}/cl.exe" os="Windows XP">
      <!-- 参数-EHsc好像是VS.NET2003中cl.exe的,可以不加,参考下面的注释行 -->
      <arg line="-EHsc -I${h.dir1} -I${h.dir2} -LD ${cpp.name} -Fe${dll.name}"/>
      <!--
      <arg line="-I${h.dir1} -I${h.dir2} -LD ${cpp.name} -Fe${dll.name}"/>
      -->
    </exec>
  </target>

  <target name="run" description=" ==> 运行">
    <java classname="HelloJNI"></java>
  </target>

  <target name="all" description=" ==> 自动进行上面任务列表">
    <antcall target="compile"/>
    <antcall target="javah"/>
    <antcall target="dll"/>
    <antcall target="run"/>
  </target>

  <target name="help" description=" ==> 显示帮助信息">
    <echo>用法帮助:</echo>
    <echo>ant compile      编译Java源文件</echo>
    <echo>ant javah        生成C/C++头文件</echo>
    <echo>ant dll          调用cl命令生成相关的DLL文件</echo>
    <echo>ant run          运行</echo>
    <echo></echo>
    <echo>ant all          自动进行上面任务列表</echo>
    <echo>ant help         显示帮助信息</echo>
  </target>
</project>

Done!^_^!
jni调用的c++作的嵌入了一个浏览器:)
把代码发上来大家分享
------------------------------------------------------
//MyWindow.java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.peer.*;
import sun.awt.*;

public class MyWindow extends Canvas
{
    static
    {
        // Load the library that contains the JNI code.
        System.loadLibrary("MyWindow");
    }

    // native entry point for initializing the IE control.
    public native static void initialize(int hwnd, String strURL);

    // native entry point for resizing
    public native static void resizeControl(int hwnd, int nWidth, int nHeight);
    
    public native static int getNativeWindowHandle(Canvas canvas);
    
    public void addNotify()
    {
        super.addNotify();
        m_hWnd = getNativeWindowHandle(this);
        initialize(m_hWnd, m_strURL);
    }

    String m_strURL = "http://www.163.com";
    int    m_hWnd   = 0;

    public static void main( String[] argv )
    {
        Frame f = new Frame();
        f.setLayout(new BorderLayout());
        f.setTitle("Internet Explorer inside Java Canvas");
        
        MyWindow w = new MyWindow();
        if(argv.length>0)
            w.m_strURL = argv[0];

        String strText = "URL:" + w.m_strURL;
        f.add(w,BorderLayout.CENTER);
        f.add(new Label(strText),BorderLayout.NORTH);
        f.setBounds(300,300,500,300);
        f.setVisible(true);
    }

    public void setSize( int width, int height )
    {
        super.setSize(width,height);
        if(m_hWnd!=0)
            resizeControl(m_hWnd, width, height);
    }

    public void setSize( Dimension d )
    {
        super.setSize(d);
        if(m_hWnd!=0)
            resizeControl(m_hWnd, d.width, d.height);
    }

    public void setBounds( int x, int y, int width, int height )
    {
        super.setBounds(x,y,width,height);
        if(m_hWnd!=0)
            resizeControl(m_hWnd, width, height);
    }

    public void setBounds( Rectangle r )
    {
        super.setBounds(r);
        if(m_hWnd!=0)
            resizeControl(m_hWnd, r.width, r.height);
    }
}
将上面的文件编译得到MyWindow.class
再用javah MyWindow命令得到MyWindow.h头文件(是给c++用的)
剩下的事交给vc了
小嵩的vc是vc6.0中文版
建立一个dll的工程
文件-〉新建-〉Win32 Dynamic-Link Libiary
命名为MyWindow
建立一个.cpp文件 MyWindow.cpp
----------------------------------------------------------
//MyWindow.cpp
#include <jni.h>
#include <jawt.h>
#include <afxwin.h>
#include <windows.h>
#include "MyWindow.h"
#include "jawt_md.h"
//#include <assert.h>
#include <process.h>

// Includes for ATL
#pragma comment(lib,"atl.lib")
#include <atldef.h>
#define _ATL_DLL_IMPL
#include <atliface.h>
#include <atlbase.h>
#include <exdisp.h>

// Structure for Thread Parameters.
typedef struct {
    char szURL[1024];
    HWND hwnd;
} ThreadParam;

// Helper functions.
VOID CreateIEControl(ThreadParam *);
static void WINAPIV StartATL(LPVOID);


JNIEXPORT jint JNICALL Java_MyWindow_getNativeWindowHandle
  (JNIEnv *env, jobject jobj, jobject window)
{

JAWT awt;
awt.version = JAWT_VERSION_1_3;
jboolean result = JAWT_GetAWT(env, &awt);

if (result == JNI_FALSE)
return 0;

JAWT_DrawingSurface* ds = awt.GetDrawingSurface(env, window);

if (ds == 0)
return 0;

jint lock = ds->Lock(ds);

if ((lock & JAWT_LOCK_ERROR) != 0)
return 0;

JAWT_DrawingSurfaceInfo* dsi = ds->GetDrawingSurfaceInfo(ds);

if (dsi == 0)
return 0;

JAWT_Win32DrawingSurfaceInfo* dsiwin = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo;

jint ret = reinterpret_cast<jint>(dsiwin->hwnd);

ds->FreeDrawingSurfaceInfo(dsi);
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);

return ret;
}



// native method for initializing the control.
JNIEXPORT void JNICALL Java_MyWindow_initialize
  (JNIEnv *pEnv, jobject, jint hwndIn, jstring string)
{
    // Fill up the params.
    const char *str    = pEnv->GetStringUTFChars(string, 0);
    ThreadParam *pThreadParam = new ThreadParam;
    pThreadParam->hwnd = (HWND) hwndIn;
    strcpy(pThreadParam->szURL,str);
    pEnv->ReleaseStringUTFChars(string, str);

    // Launch the Thread.
    _beginthread(StartATL, 0, pThreadParam);
}

// Thread for creating the control
void WINAPIV StartATL(LPVOID lpVoid)
{
    ThreadParam *pThreadParam = (ThreadParam *)lpVoid;
    CreateIEControl(pThreadParam);
    delete pThreadParam;
    MSG msg;
    // Windows message loop.
    while(GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

// Creates IE control
VOID CreateIEControl(ThreadParam *pThreadParam)
{
    AtlAxWinInit();
    printf("Create AtlAxWin Begin...[0x%x][%s]/n",pThreadParam->hwnd,pThreadParam->szURL);
    // In the 2nd Param you can use ProgID or UUID of any activex control.
    HWND hwndChild = ::CreateWindow("AtlAxWin",
                                    "http://www.microsoft.com",
                                    WS_CHILD|WS_VISIBLE,
                                    0,0,0,0,
                                    pThreadParam->hwnd,NULL,
                                    ::GetModuleHandle(NULL),
                                    NULL);

    IUnknown *pUnk = NULL;
    AtlAxGetControl(hwndChild,&pUnk);
    printf("Create AtlAxWin Done...[0x%x]/n",pUnk);

    // get an interface to set the URL.
    CComPtr<IWebBrowser2> spBrowser;
    pUnk->QueryInterface(IID_IWebBrowser2, (void**)&spBrowser);
    if (spBrowser)
    {
        CComVariant ve;
        CComVariant vurl(pThreadParam->szURL);
#pragma warning(disable: 4310) // cast truncates constant value
        spBrowser->put_Visible(VARIANT_TRUE);
#pragma warning(default: 4310) // cast truncates constant value
        spBrowser->Navigate2(&vurl, &ve, &ve, &ve, &ve);
    }
}

// native method for handling resizes.
JNIEXPORT void JNICALL Java_MyWindow_resizeControl
  (JNIEnv *, jobject, jint hwndIn, jint width, jint height)
{
    HWND hwnd = (HWND) hwndIn;
    RECT rc;
    if(hwnd!=NULL)
    {
        ::GetWindowRect(hwnd,&rc);
        HWND hwndChild = GetWindow(hwnd, GW_CHILD);
        printf("got resize (0x%x,%d,%d)/n",hwndChild,width,height);
        ::SetWindowPos(hwndChild,NULL,0,0,rc.right-rc.left,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOMOVE);
    }
}
----------------------------------------------------------
在编译之前有几件事情得先做一下
1.把刚才得到的MyWindow.h文件复制到工程中
2.把C:/Program Files/Java/j2sdk1.5.0/include和
  C:/Program Files/Java/j2sdk1.5.0/include/win32下的文件复制到
  C:/Program Files/Microsoft Visual Studio/VC98/Include下面
3.把C:/Program Files/Java/j2sdk1.5.0/lib下的jawt.lib复制到
  C:/Program Files/Microsoft Visual Studio/VC98/Lib下
  并在工程-〉设置-〉Link中添加jawt.lib
一切准备就绪按F7进行编译,于是在Debug中得到MyWindow.dll文件
将它复制到你编写的java 应用程序的文件夹中
好了运行MyWindow.class看看结果吧
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值