文档章节

Android NDK开发起步Hello Jni

拉风的道长
 拉风的道长
发布于 2013/11/17 23:50
字数 1278
阅读 10394
收藏 13
/*

http://my.oschina.net/lifj/blog/177087

*/


上一篇讲了怎么配置NDK的开发环境。这次记录一下写一个Hello Jni的最简单的Android程序。

一。新建Android程序工程。

这里,我们需要做一下几步:
1.Layout中添加TextView的id。
2.MainActivity中声明native方法。
3.MainActivity中调用native方法。

我们这里先不loadLibrary。先load总感觉我的库还没有呢,先load做什么?有点莫名其妙。当然,你也可以先loadLibrary。看个人习惯了。

下面详细说明:
1.新建Android,名字,包名什么的随便取。一路下一步,生成之后,在默认生成的Activity的layout的TextView上加一个id。(这个都会吧)android:id="@+id/hello_jni"。此时的layout文件内容如下:

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/hello_jni"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>


2.在MainActivity.java中声明native方法。

public native String getHelloJniString();
3.在OnCreate()方法中获取TextView,并设置text。
((TextView)findViewById(R.id.hello_jni)).setText(getHelloJniString());
经过2、3两步之后的MainActivity.java文件内容如下:

package com.example.hellojni;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity
{

	public native String getHelloJniString();
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		((TextView)findViewById(R.id.hello_jni)).setText(getHelloJniString());

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}


二。生成.h头文件,编写.c文件。

这一步,你需要C/C++的语言基础。(这个简单的例子只需要简单的。)
按照大多数博客,会告诉你:编译工程之后,进入工程目录文件夹下,在控制台运行
javah -classpath bin/classes -d jni com.example.hellojni.MainAcitivity 
,然后就会在工程目录下生成一个jni文件夹,里面就会有.h的头文件了。

实际上,你会遇到一个错误:


错误: 无法访问android.app.Activity
  找不到android.app.Activity的类文件

是的,大多数人都会遇到这个问题,我对此表示疑问:那些写博客的人都自己试过没有?

怎么办呢?
1. 有人提出方法,在工程目录下运行命令:
javah -classpath src -d jni com.example.hellojni.MainAcitivity 
就OK了。是的,这个是OK的。(这个命令和上面一个命令的区别就是 -classpath 不同了。)

2.还有一种方法:在工程目录下,上述命令中加上android.jar的classpath。也是可行的。也就是运行如下命令:javah -classpath F:\JNI\env\platforms\android-17\android.jar;bin/classes -d jni com.example.hellojni.MainActivity
这个命令,就是把android.jar的路径加了进去。这样,就能找到Activity类了。




以上两种可行的得到.h头文件的方法,生成的.h文件的内容是不一样的,你可以试试看。至于哪一种比较好,我目前还不清楚。因为看谷歌NDK的samples里面(samples这个文件夹,就是NDK的示例程序目录,对我们来说,就是教程了)的头文件,似乎是第一种方式。第二种方式倒是没有见到过。
我个人还是倾向于用第二种方式,虽然命令比较烦。。

头文件生成好了,刷新一下工程,就会看到如下的目录结构:

多了一个jni文件夹和里面的一长串名字的头文件。名字太长,我们改短一点,改成HelloJni.h吧。省的麻烦。

接下来编写.c的实现文件。在jni文件夹右击,选择file,命名文件名字为HelloJni.c。确定。也就是如下:

然后打开新建的HelloJni.c文件,输入如下语句并保存:

#include <stdio.h>
#include "HelloJni.h"

jstring Java_com_example_hellojni_MainActivity_getHelloJniString
  (JNIEnv * env, jobject clazz)
{
	return (*env)->NewStringUTF(env, "Hello Jni!");
}


三。编写mk文件,使用Cygwin编译出so文件。

下面开始编写mk文件。在jni目录下创建一个Android.mk的文件(A要大写哦!!不然编译会出错:

Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk /home/aditya/android-ndk-r9/build/core/add-application.mk:176: *** Android NDK: Aborting... .


)。加入如下代码并保存(里面的一些命令先不了解也是可以的。):

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

这里,主要注意两个地方:
LOCAL_MODULE    := HelloJni 。这里的HelloJni就是你的库的名字,也就是你loadLibrary的字符串。生成出来的库是libHelloJni.so。
LOCAL_SRC_FILES := HelloJni.c 。这里的HelloJni.c就是c文件的名字。

下面打开Cygwin,进入到工程目录下,输入如下命令,并回车:

$NDK/ndk-build
出现如下画面,就说明ok了。

然后我们刷新工程,会发现,多了一个obj文件夹,和libs文件夹里面多了armeabi文件夹和里面的libHelloJni.so库。obj文件夹是没有用的,可以删掉了。

四。加载so文件,运行程序。

最后一步,我们再次进入MainActivity,加载库文件。加入后的代码如下:

package com.example.hellojni;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity
{

	static {
		System.loadLibrary("HelloJni");
	}
	
	public native String getHelloJniString();
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		((TextView)findViewById(R.id.hello_jni)).setText(getHelloJniString());

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

 

System.loadLibrary("HelloJni"); 这个字符串就是我们在mk文件中写的LOCAL_MODULE的值。

好了,至此,我们编码ok了。运行起来吧。

 

 

© 著作权归作者所有

拉风的道长
粉丝 55
博文 110
码字总数 55629
作品 0
昌平
程序员
私信 提问
加载中

评论(9)

拉风的道长
拉风的道长 博主

引用来自“叫我老国”的评论

你好,请问下F:\JNI\env\platforms\android-17\android.jar;这个JNI\env是什么呀?是哪个路径?
就是你的NDK目录
叫我老国
叫我老国
你好,请问下F:\JNI\env\platforms\android-17\android.jar;这个JNI\env是什么呀?是哪个路径?
7分04秒
7分04秒
写的不错,赞一个。
左言右午
左言右午
看错了,C程序的确是可以这么写的。 Sorry
左言右午
左言右午

引用来自“拉风的道长”的评论

引用来自“左言右午”的评论

引用来自“拉风的道长”的评论

引用来自“左言右午”的评论

HelloJni.c 文件中的 return (*env)->NewStringUTF(env, "Hello Jni!"); 写错了, 应该是 evn->NewStringUTF("Hello Jni");

are you sure?

Sure

我怎么写的没问题的。。。一切ok啊。你觉得是哪里出问题了呢?

打开 jni.h 文件,可以看到 JNIEnv 是一个 struct _JNIEnv, 本身不是一个指针,而在 HelloJni.c 中的 JNIEnv * env, env 是一个指向 JNIEnv 的指针,如果 env需要访问成员函数的话,需要 env-> 这种方式,而 *env 是对指针进行了解引用,访问成员函数的话就是 (*env).NewStringUTF("Hello Jni");
拉风的道长
拉风的道长 博主

引用来自“左言右午”的评论

引用来自“拉风的道长”的评论

引用来自“左言右午”的评论

HelloJni.c 文件中的 return (*env)->NewStringUTF(env, "Hello Jni!"); 写错了, 应该是 evn->NewStringUTF("Hello Jni");

are you sure?

Sure

我怎么写的没问题的。。。一切ok啊。你觉得是哪里出问题了呢?
左言右午
左言右午

引用来自“拉风的道长”的评论

引用来自“左言右午”的评论

HelloJni.c 文件中的 return (*env)->NewStringUTF(env, "Hello Jni!"); 写错了, 应该是 evn->NewStringUTF("Hello Jni");

are you sure?

Sure
拉风的道长
拉风的道长 博主

引用来自“左言右午”的评论

HelloJni.c 文件中的 return (*env)->NewStringUTF(env, "Hello Jni!"); 写错了, 应该是 evn->NewStringUTF("Hello Jni");

are you sure?
左言右午
左言右午
HelloJni.c 文件中的 return (*env)->NewStringUTF(env, "Hello Jni!"); 写错了, 应该是 evn->NewStringUTF("Hello Jni");
ndk编译opencl出现问题,大家来讨论一下

D:/utils/android-ndk-r9d/toolchains/x86-4.6/prebuilt/windows-x86_64/bin/../lib/gcc/i686-linux-android/4.6/../../../../i686-linux-android/bin/ld.exe: warning: skipping incompatib......

冰冻流星
2016/08/10
595
0
Android: NDK编程入门笔记

为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。 2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C...

gongweixin
2013/04/23
517
3
Android: NDK编程入门笔记

为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。 2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C...

xubohui
2012/08/20
292
0
Android: NDK编程入门笔记

为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。 2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C...

JORDANSG
2012/08/14
204
0
Android NDK Hello

Android应用程序访问android的根文件系统中的文件时,由于应用程序的权限限制,无法访问这些文件,怎么办? 这时就要用到NDK编程了,既用C/C++代码实现访问系统文件,并将其生成本地库,供a...

长平狐
2013/01/06
145
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx学习之模块

1、 stub_status模块: 用于展示nginx处理连接时的状态。 配置语法如下: Syntax:stub_status;Default:默认没有配置Context:server、location 可以编辑default.conf,加上如下配置: ...

码农实战
45分钟前
4
0
MySQL,必须掌握的6个知识点

目录 一、索引B+ Tree 原理 MySQL 索引 索引优化 索引的优点 索引的使用条件 二、查询性能优化使用 Explain 进行分析 优化数据访问 重构查询方式 三、存储引擎InnoDB MyISAM 比较 四、数据类...

李红欧巴
49分钟前
4
0
堆”和“栈

C++作为一款C语言的升级版本,具有非常强大的功能。它不但能够支持各种程序设计风格,而且还具有C语言的所有功能。我们在这里为大家介绍的是其中一个比较重要的内容,C++内存区域的基本介绍。...

SibylY
今天
4
0
总结:Https

一、介绍 简单理解,https即在http协议的基础上,增加了SSL协议,保障数据传输的安全性。 它由以前的http—–>tcp,改为http——>SSL—–>tcp;https采用了共享密钥加密+公开密钥加密的方式 ...

浮躁的码农
今天
6
0
数据库表与表之间的一对一、一对多、多对多关系

表1 foreign key 表2 多对一:表 1 的多条记录对应表 2 的一条记录 利用foreign key的原理我们可以制作两张表的多对多,一对一关系 多对多: 表1的多条记录可以对应表2的一条记录 表2的多条记...

Garphy
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部