Android: 一个简单的app – 创建native进程并重定向stdout & stderr

一直以为在Android app中是无法使用fork()创建进程的。因为将native app拷贝到sdcard中,再通过adb shell无法执行此命令。并且android.os.Process类中也没有提供创建进程的相关方法。抱着试试看的心里,在JNI中fork一个进程出来,没相到成功了。无意中发现在java.lang.Process类中有创建进程的相关方法,难怪在android.os.Process中没有创建进程的相关方法。今天就看看在JNI中创建native进程,并将native进程中的stdout与stderr重定向到logcat中。

$ adb push assets/bin/armeabi-v7a/hello-native /sdcard/
874 KB/s (13604 bytes in 0.015s)
$ adb shell /sdcard/hello-native
/system/bin/sh: /sdcard/hello-native: can't execute: Permission denied
  • 环境

Android版本: android-5.1.1_r15

代码在项目中编译。

  • 代码

1. 一个简单的Hello, world程序,从stdout输出”Hello, world!\n”, 从stderr输出”A quick brown fox jumps over the lazy dog.\n”

int err(void)
{
    return fprintf(stderr, "A quick brown fox jumps over the lazy dog.\n");
}

int main(int argc, char *argv[])
{
    printf("Hello, world!\n");
    return err();
}

将编译成hello-native程序。

2. JNI: fork & exec hello-native, 并通过logwrapper将hello-native的stdout & stderr重定向到logcat中:

extern "C" void Java_com_brobwind_brontv_v1_NativeApplication_nSayHello(JNIEnv *env, jobject, jstring binDir)
{
	pid_t pid;
	char wrap[PATH_MAX], path[PATH_MAX];

	ScopedUtfChars _binDir(env, binDir);

	sprintf(wrap, "%s/logwrapper", _binDir.c_str());
	sprintf(path, "%s/hello-native", _binDir.c_str());

	pid = fork();
	if (pid == 0) { // Child process
		exit(execl(wrap, wrap, path, NULL));
	}
}

3. JAVA: 将assets中的bin/armeabi-v7a/{logwrapper,hello-native}文件copy到/data/data/com.brobwind.brontv.v1/bin/中:

public class NativeApplication extends Application {
    // ...
    public void onCreate() {
        final File binDir = new File(getApplicationInfo().dataDir, "/bin");
        binDir.mkdir();
        binDir.setReadable(true, false);

        try {
            File binName = new File(binDir, "logwrapper");
//          binName.delete();
            if (binName.exists() != true) {
                AssetManager am = getAssets();
                InputStream is = am.open("bin/armeabi-v7a/" + binName.getName());
                FileUtils.copyToFile(is, binName);
                FileUtils.setPermissions(binName, FileUtils.S_IRUSR | FileUtils.S_IXUSR, -1, -1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            File binName = new File(binDir, "hello-native");
//          binName.delete();
            if (binName.exists() != true) {
                AssetManager am = getAssets();
                InputStream is = am.open("bin/armeabi-v7a/" + binName.getName());
                FileUtils.copyToFile(is, binName);
                FileUtils.setPermissions(binName, FileUtils.S_IRUSR | FileUtils.S_IXUSR, -1, -1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        nSayHello(binDir.getPath());
    }
    // ...
}

4. Android.mk: 将编译出来logwrapepr, hello-native考贝到assets/bin/armeabi-v7a/中:

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := BroNativeV1

ifneq ($(ONE_SHOT_MAKEFILE),)
TARGET_BUILD_APPS := $(LOCAL_PACKAGE_NAME)
endif

LOCAL_JNI_SHARED_LIBRARIES := \
    libnative-jni
LOCAL_DIST_BUNDLED_BINARIES := true

# Only compile source java files in this apk.
LOCAL_SRC_FILES := \
    $(call all-java-files-under, src) \

LOCAL_ASSET_DIR := \
    $(LOCAL_PATH)/assets

define my-add-executable
my_libs_$(2) := assets/bin/armeabi-v7a/$(2)

$$(LOCAL_PATH)/$$(my_libs_$(2)): $$(call intermediates-dir-for,EXECUTABLES,$(2))/$(2)
    $$(copy-file-to-target)

$$(call intermediates-dir-for,SHARED_LIBRARIES,$(1))/LINKED/$(1).so: $$(LOCAL_PATH)/$$(my_libs_$(2))
endef

$(eval $(call my-add-executable,libnative-jni,logwrapper))
$(eval $(call my-add-executable,libnative-jni,hello-native))

include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))

NOTE: 需要指定LOCAL_ASSET_DIR,不然logwrapper, hello-native不会被编译到APK中。

5. repo

相关的代码可以从这里下载:

https://github.com/brobwind/BroNativeV1
  • 安装运行

运行此APK, 用logcat可以看到如下log:

I/ActivityManager(  558): Start proc 6494:com.brobwind.brontv.v1/u0a58 for activity com.brobwind.brontv.v1/.NativeActivity
--------- beginning of main
I/hello-native( 6516): Hello, world!
I/hello-native( 6516): A quick brown fox jump over the lazy dog.
I/hello-native( 6516): hello-native terminated by exit(42)

发表评论

电子邮件地址不会被公开。 必填项已用*标注