#Android#Shell 最近在做 Taskwarrior 的安卓端,根据文档的推荐决定直接运行二进制文件,而非自行解析数据。众所周知,安卓的存储根目录( 首先把要执行的二进制文件放到 然后在 随后在 至此,(理论上)你已经可以像这样运行binary了: 然而,像上面那样直接运行大概率会跳出来一堆 依赖中很有可能有 另外,如果遇到类似如下报错: 有可能是程序自动使用了错误的共享库: 手动复制一份过去就行了,这方面我也不太懂,有可能说得不太准确,欢迎指正。 java native interface - Android Q execute binary file on release build - Stack Overflow What path to put executable to run on Android 29? - Stack Overflow Android can't execute process for Android API 29 (Android 10) from ... - Stack Overflow android - Running binary files - Stack Overflow libs - How do I embed a native code library in an Android app (SDK 29+)? - Stack Overflow/storage/emulated/*
)是没有执行权限的,所以通常会将可执行文件放到应用的私有目录:/data/data/PACKAGE_NAME/files
。但是在 API 29 之后,谷歌移除了应用主目录的执行权限,虽然我经常怒喷谷歌,但是对于这个改动我觉得是比较合理的,然而谷歌只是提了一嘴说建议把可执行文件打包到安装包里面,具体怎么操作却不舍得多讲。被迫使用谷歌解决谷歌留下来的烂摊子。可执行权限
libs/对应ABI/
中,并且文件名格式必须为libxxx.so
,否则 release 中不会打包进去,这是非常没有道理的设定,谷歌也知道这不合理,但就是不改。AndroidManifest.xml
中将android:extractNativeLibs
设为true
。build.gradle
中设置一番,最后结果如下(略过了一些不重要的部分):android {
defaultConfig {
targetSdk 32
ndk {
abiFilters 'arm64-v8a', 'x86'
}
......
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["lib*.so"])
......
}
val libName = "libtask.so"
val nativeLibsDir: String = MyApplication.appContext.applicationInfo.nativeLibraryDir
val libPath = "$nativeLibsDir/$libName"
shell.run("$libPath --version") // 此处使用了 ktsh
依赖
error while loading shared libraries
,所以要用ldd
看下有哪些依赖,把它们丢进安装包里,然后设置LD_LIBRARY_PATH
。libnettle.so.8
这种文件,而libs
目录是不接受这种文件名的此时有两种选择:patchelf
手动将依赖的名字改成libnettle.so
,然后放到libs
里面,这样做就无需自行解压、判断架构了。terminating with uncaught exception of type std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>
# 报错:
$ ldd `which libtask.so`
libc++_shared.so => /system/lib64/libc++_shared.so (0x7f0b396000)
......
# 以 root 用户正常运行:
$ ldd `which libtask.so`
libc++_shared.so => /data/user/0/com.senventise.taskroid/files/libc++_shared.so (0x7450b5a000)
......
参考