一直以为在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)