Create Native Method on Android App




##Add Native Support

On Eclipse package explorer, right click on the project, and select following menu

Android Tools > Add Native Support

Here, type to a form 'yuv420spDecoder'

Now, a 'jni' directory has been created at the project root. Also auto generated Android.mk in 'jni'.

```make:jni/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := yuv420spDecoder
LOCAL_SRC_FILES := yuv420spDecoder.cpp
include $(BUILD_SHARED_LIBRARY)
```

##Create Method

- Define the native method in the class of JAVA

```java:src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
private native static final void nYuv420spToRgb(ByteBuffer oBuf, ByteBuffer iBuf, int width, int height);
```

Also ok 'public', 'non-static', 'final', etc… 

- Run 'javah' Comand

```bash:Terminal
javah -classpath bin/classes -d jni zyxw.project.cocopuri.util.Yuv420SpDecoder
```

- Generated A File

Auto generated a following file in 'jni'

```cpp:jni/zyxw_project_cocopuri_util_Yuv420SpDecoder.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class zyxw_project_cocopuri_util_Yuv420SpDecoder */

#ifndef _Included_zyxw_project_cocopuri_util_Yuv420SpDecoder
#define _Included_zyxw_project_cocopuri_util_Yuv420SpDecoder
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     zyxw_project_cocopuri_util_Yuv420SpDecoder
 * Method:    nYuv420spToRgb
 * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;II)V
 */
JNIEXPORT void JNICALL Java_zyxw_project_cocopuri_util_Yuv420SpDecoder_nYuv420spToRgb
  (JNIEnv *, jclass, jobject, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
```

- Write implementation

Write implementation code normally...

```cpp:jni/yuv420spDecoder.cpp
#include "zyxw_project_cocopuri_util_Yuv420SpDecoder.h"

#define LOG_TAG ("yuv420spDecoder")

static const int YUV_ELEMENT_MASK = 0xff;
static const int RED_ELEMENT_MASK = 0xff0000;
static const int GREEN_ELEMENT_MASK = 0xff00;
static const int BLUE_ELEMENT_MASK = 0xff;
static const int RED_ELEMENT_SHIFT = 6;
static const int GREEN_ELEMENT_SHIFT = 2;
static const int BLUE_ELEMENT_SHIFT = 10;
static const int ALPHA_ELEMENT_FULL = 0xff000000;

int max(int, int);
int min(int, int);
int getRgb(int, int, int);
int getRgbValueInRange(int);

void Java_zyxw_project_cocopuri_util_Yuv420SpDecoder_nYuv420spToRgb(JNIEnv* env, jclass thiz, jobject oBuf,
        jobject iBuf, jint width, jint height) {

	typedef char byte;
	int* rgb = reinterpret_cast<int*>(env->GetDirectBufferAddress(oBuf));
	byte* yuv = reinterpret_cast<byte*>(env->GetDirectBufferAddress(iBuf));

	const int frameSize = width * height;
	for (int i = 0; i < height; i++) {
		int uvp = frameSize + (i >> 1) * width;
		int u = 0x0, v = 0x0;
		for (int j = 0; j < width; j++) {
			int y = max(0x0, yuv[i * width + j] & YUV_ELEMENT_MASK) - 0x10;
			if ((j & 1) == 0) {
				v = (yuv[uvp++] & YUV_ELEMENT_MASK) - 0x80;
				u = (yuv[uvp++] & YUV_ELEMENT_MASK) - 0x80;
			}
			*(rgb++) = getRgb(y, u, v);
		}
	}
	return;
}

int max(int a, int b) {

	return (a > b) ? a : b;
}

int min(int a, int b) {
	return (a < b) ? a : b;
}

int getRgb(int y, int u, int v) {

	int y0x4a8 = 0x4a8 * y;
	int r = getRgbValueInRange(y0x4a8 + 0x662 * v);
	int g = getRgbValueInRange(y0x4a8 - 0x341 * v - 0x190 * u);
	int b = getRgbValueInRange(y0x4a8 + 0x812 * u);
	return ALPHA_ELEMENT_FULL | ((r << RED_ELEMENT_SHIFT) & RED_ELEMENT_MASK)
	        | ((g >> GREEN_ELEMENT_SHIFT) & GREEN_ELEMENT_MASK) | ((b >> BLUE_ELEMENT_SHIFT) & BLUE_ELEMENT_MASK);
}

int getRgbValueInRange(int value) {

	return max(0x0, min(value, 0x3ffff));
}
```

## Invoke The Native Method

- Load the native library before invoke it.

```java:src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
	static {
		System.loadLibrary("yuv420spDecoder");
	}
```

- Invoke native method from Java

```java:src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
nYuv420spToRgb(oBuf, iBuf, width, height);
```

Entire Java Code…

```java:src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
package zyxw.project.cocopuri.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;

/**
 * Don't move this class to other package or location!!
 * Using native library {@value #NLIB_NAME}.
 * 
 * @author Y
 * @category Factory
 * @since 10/08/2012
 */
public class Yuv420SpDecoder {

	private static final int RANGE_MIN = 0x0;
	private static final int RANGE_MAX = 0x3ffff;
	private static final int YUV_ELEMENT_MASK = 0xff;
	private static final int RED_ELEMENT_MASK = 0xff0000;
	private static final int GREEN_ELEMENT_MASK = 0xff00;
	private static final int BLUE_ELEMENT_MASK = 0xff;
	private static final int RED_ELEMENT_SHIFT = 6;
	private static final int GREEN_ELEMENT_SHIFT = 2;
	private static final int BLUE_ELEMENT_SHIFT = 10;
	private static final int ALPHA_ELEMENT_FULL = 0xff000000;

	private static final int INT_CAPA = Integer.SIZE / Byte.SIZE;
	private static final String NLIB_NAME = "yuv420spDecoder";

	static {
		System.loadLibrary(NLIB_NAME);
	}

	private Yuv420SpDecoder() {} // no ctor

	/**
	 * Decode yuv420sp.
	 * 
	 * @param yuv420sp
	 *            This byte array (YUV420SP data) will be converted to Bitmap.
	 * @param width
	 *            Width of the YUV420SP graphic
	 * @param height
	 *            Height of the YUV420SP graphic
	 * @param useNative
	 *            Whether to use native library {@value #NLIB_NAME}
	 * @return Decoded result.
	 */
	public static final Bitmap decodeYuv420sp(final byte[] yuv420sp, final int width, final int height, boolean useNative) {

		int[] rgb = new int[width * height];
		if (useNative) {
			ByteBuffer oBuf = ByteBuffer.allocateDirect(INT_CAPA * rgb.length).order(ByteOrder.nativeOrder());
			ByteBuffer iBuf = ByteBuffer.allocateDirect(yuv420sp.length).order(ByteOrder.nativeOrder()).put(yuv420sp);
			nYuv420spToRgb(oBuf, iBuf, width, height);
			oBuf.asIntBuffer().get(rgb);
		} else {
			yuv420spToRgb(rgb, yuv420sp, width, height);
		}
		Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		result.setPixels(rgb, 0, width, 0, 0, width, height);
		return result;
	}

	/**
	 * @param rgb
	 *            This int array will recieve the result.
	 * @param yuv420sp
	 *            This byte array (YUV420SP data) will be converted to RGB.
	 * @param width
	 *            Width of the YUV420SP graphic.
	 * @param height
	 *            Height of the YUV420SP graphic.
	 */
	private static final void yuv420spToRgb(int[] rgb, final byte[] yuv420sp, final int width, final int height) {

		final int frameSize = width * height;
		for (int i = 0; i < height; i++) {
			int uvp = frameSize + (i >> 1) * width;
			int u = 0x0, v = 0x0;
			for (int j = 0; j < width; j++) {
				int y = Math.max(0x0, yuv420sp[i * width + j] & YUV_ELEMENT_MASK) - 0x10;
				if ((j & 1) == 0) {
					v = (yuv420sp[uvp++] & YUV_ELEMENT_MASK) - 0x80;
					u = (yuv420sp[uvp++] & YUV_ELEMENT_MASK) - 0x80;
				}
				rgb[i * width + j] = getRgb(y, u, v);
			}
		}
	}

	private static int getRgb(int y, int u, int v) {

		int y0x4a8 = 0x4a8 * y;
		int r = getRgbValueInRange(y0x4a8 + 0x662 * v);
		int g = getRgbValueInRange(y0x4a8 - 0x341 * v - 0x190 * u);
		int b = getRgbValueInRange(y0x4a8 + 0x812 * u);
		return ALPHA_ELEMENT_FULL
				| ((r << RED_ELEMENT_SHIFT) & RED_ELEMENT_MASK)
				| ((g >> GREEN_ELEMENT_SHIFT) & GREEN_ELEMENT_MASK)
				| ((b >> BLUE_ELEMENT_SHIFT) & BLUE_ELEMENT_MASK);
	}

	private static int getRgbValueInRange(int value) {

		return Math.max(RANGE_MIN, Math.min(value, RANGE_MAX));
	}

	/**
	 * This method will be called in {@link #decodeYuv420sp(byte[], int, int, boolean)} when that method's boolean param
	 * is true. This use native library {@value #NLIB_NAME}.
	 */
	private native static final void nYuv420spToRgb(ByteBuffer oBuf, ByteBuffer iBuf, int width, int height);

}
```
Try to compare performances of Java and C/C++ with this code.

##About Overload

```java:src/zyxw/project/cocopuri/util/Yuv420SpDecoder.java
	private native final void toNegative(ByteBuffer buf, int w, int h);

	private native boolean toNegative(String inputFileName, String outputFileName);
```

If overload method exists, signatures would be added to native method's name. (Of course, auto generated with javah)

```cpp:jni/zyxw_project_cocopuri_util_Yuv420SpDecoder.h
/*
 * Class:     com_example_opencv_nativetest_FullscreenActivity
 * Method:    toNegative
 * Signature: (Ljava/nio/ByteBuffer;II)V
 */
JNIEXPORT void JNICALL Java_com_example_opencv_nativetest_FullscreenActivity_toNegative__Ljava_nio_ByteBuffer_2II
  (JNIEnv *, jobject, jobject, jint, jint);

/*
 * Class:     com_example_opencv_nativetest_FullscreenActivity
 * Method:    toNegative
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL Java_com_example_opencv_nativetest_FullscreenActivity_toNegative__Ljava_lang_String_2Ljava_lang_String_2
  (JNIEnv *, jobject, jstring, jstring);
```
However, don't care of them.
Write implementation code normally...

## Difference of JDK version

- Difference of JDK 6.x and 7.x

Case of java 6.x, javah command is

```bash:Terminal
javah -classpath bin/classes -d jni <Target Java class>
```
But, run same command on Java 7.x,

**Error: cannot access android.app.Activity
class file for android.app.Activity not found**


This error returned…

So, try the following command (on Java 7.x)

```bash:Terminal
javah -classpath bin/classes;<Path to Android-SDK>/platforms/android-<Target SDK version>/android.jar -d jni <Target Java class>
```

This time, **Permission denied** is returned…

- Solution

Use Java 6!

If wanna use Java 7, Install both, and run following command

```bash:Terminal
<Path to Java 6>/javah -classpath bin/classes -d jni <Target Java class>
```