1. 程式人生 > >android JNI執行NDK編譯成的可執行檔案

android JNI執行NDK編譯成的可執行檔案

1.android環境的可執行檔案的生成

所謂的android下的可執行檔案,其實就是一般的c/c++程式碼使用NDK編譯出來的應用程式。它和linux下用gcc編譯出來的程式和windows系統下的.exe檔案是一樣的。要將程式碼編譯成可執行檔案只需要將編譯so的include $(BUILD_SHARED_LIBRARY)改成include $(BUILD_EXECUTABLE)就行。

Main.cpp

#include <iostream>
#include <android/log.h>
using namespace std;
#define LOG_TAG "JNI_LOG"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
int main(int argc, char **argv)
{
	LOGI("NativeFile Begin!");
	LOGI("This is a Test!");
	LOGI("NativeFile End!");
	return 0;
}

Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
    Main.cpp 
LOCAL_MODULE := NativeFile
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)

Application.mk

APP_PLATFORM = android-8
APP_ABI := armeabi
APP_STL := stlport_static

2.android 應用使用JNI執行NDK編譯的可執行檔案.

1) 將上面生成的NativeFile可執行檔案放在android工程的asset目錄下面。

2) 將asset中的可執行檔案拷貝到應用的data空間中。

3)使用linux的fork函式建立子程序,並使用execlp執行可執行檔案.

4)此時apk所在的程序和可執行檔案的程序分屬於二個不同的程序,擁有不同的程序id,所以就算APK檔案解除安裝了以後這個可執行檔案還是在執行狀態的,這樣我們的應用就可以在apk被解除安裝了以後仍然能做很多的事情,比如向360一樣顯示一個解除安裝調查的網頁,或者傳送應用被解除安裝的訊息給自己的伺服器等等。

下面附上程式碼.

MainActivity.java

package com.example.testnative;

import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;

public class MainActivity extends Activity {
	static 
	{
		System.loadLibrary("TestNative");
	}
	native void ExecFile(AssetManager asset,String path);
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ExecFile(getAssets(),getCacheDir().toString());
	}
	
}


TestNative.cpp

#include <jni.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <android/log.h>
#include <time.h>
#include <utime.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
extern "C"
{
#define LOG_TAG "JNI_LOG"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
char* Jstring2String(JNIEnv* env, jstring jstr)
{
	char* rtn = NULL;
	jclass clsstring = env->FindClass("java/lang/String");
	jstring strencode = env->NewStringUTF("utf-8");
	jmethodID mid = env->GetMethodID(clsstring, "getBytes",
			"(Ljava/lang/String;)[B");
	jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
	jsize alen = env->GetArrayLength(barr);
	jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
	if (alen > 0)
	{
		rtn = (char*) malloc(alen + 1);
		memcpy(rtn, ba, alen);
		rtn[alen] = 0;
	}
	env->ReleaseByteArrayElements(barr, ba, 0);
	return rtn;
}
void ExecChildProcess(const char* fileName, const char* param)
{
	LOGI("Begin ExecCmdByChildProcess!\n");
	pid_t pid = fork();
	if (pid == 0)
	{
		pid_t pid2 = fork();
		if (pid2 == 0)
		{
			if (execlp(fileName, fileName, param, NULL) < 0)
				LOGI("ExecChildProcess %s error!\n", fileName);
			exit(0);
		}
		else
		{
			exit(0);
		}
	}
	else if (pid > 0)
	{
		int status = 0;
		waitpid(pid, &status, 0);
	}
	else
	{
		LOGE("ExecChildProcess Error!\n");
	}
}
void Java_com_example_testnative_MainActivity_ExecFile(JNIEnv* env, jobject obj,
		jobject asset, jstring path)
{
	AAssetManager* asMg = AAssetManager_fromJava(env, asset);
	AAsset* as = AAssetManager_open(asMg, "NativeFile", AASSET_MODE_UNKNOWN);
	char* tmp = Jstring2String(env, path);
	char cPath[256] =
	{ 0 };
	strcpy(cPath, tmp);
	strcat(cPath, "/NativeFile");
	free(tmp);
	if (as != NULL)
	{
		int len = AAsset_getLength(as);
		LOGI("len=%d", len);
		int file = open(cPath, O_WRONLY | O_CREAT, 0755);
		char* buf = new char[1024];
		while (len > 0)
		{

			int n = AAsset_read(as, buf, 1024);
			if (n < 0)
				break;
			write(file, buf, n);
			len -= n;
		}
		delete[] buf;
		AAsset_close(as);
		close(file);
	}
	ExecChildProcess(cPath, " ");
}
}


執行APK後的結果: