我正在开发一个Android应用程序,需要加载一个自编译的sqlite插件(libsimple)。编译后的插件是一个.so文件。
sqlite-android可以加载此类文件作为插件。但我是 Android 开发新手,不知道如何正确加载文件。
目前,我将编译好的插件放入 jniLibs 中。
app/
├─ src/
│ ├─ main/
│ │ ├─ jniLibs/
│ │ │ ├─ x86_64/
│ │ │ │ ├─libsimple.so
│ │ │ ├─ arm64_v8a/
│ │ │ │ ├─libsimple.so
构建的 apk 确实包含这些文件。
apk-debug.apk/
├─ lib/
│ ├─ x86_64/
│ │ ├─libsimple.so
│ ├─ arm64_v8a/
│ │ ├─libsimple.so
代码中定义的路径如下:
private val libsimplePath = "${context.applicationInfo.nativeLibraryDir}/libsimple.so"
但是,当使用 API 35 在 Android studio 中运行应用程序时,返回的路径如下
data/app/~~W0ZleGXEDPvGfIKN22O5Ow==/com.hal9000com.picfinder-eadN36R50XdAo86g8fxllN==/lib/x86_64/libsimple.so
下面的测试代码将返回 false。插件加载也会失败。
fun isPathReadable(path: String): Boolean {
val file = File(path)
return file.canRead()
}
但是当我上传 .so 文件并使用文件选择器选择它时,它将获得如下路径
/data/user/0/com.hal9000com.picfinder/cache/libsimple.so
并且插件将会工作。
质疑 ChatGPT 没有返回任何好处,我尝试添加
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
更改为 AndroidManifest.xml,但没有任何更改。
扩展加载部分代码如下。
import io.requery.android.database.sqlite.SQLiteDatabase
import io.requery.android.database.sqlite.SQLiteDatabaseConfiguration
import io.requery.android.database.sqlite.SQLiteCustomExtension
private val libsimplePath = "${context.applicationInfo.nativeLibraryDir}/libsimple.so"
private val cfg = SQLiteDatabaseConfiguration(":memory:", SQLiteDatabase.OPEN_READWRITE)
val ext = SQLiteCustomExtension(libsimplePath, null)
cfg.customExtensions.add(ext)
private val db = SQLiteDatabase.openDatabase(cfg, null, null)
需要 .so 文件的路径,如 sqlite-android 源代码中所定义。
public final class SQLiteCustomExtension {
public final String path;
public final String entryPoint;
/**
* Creates a SQLite extension description.
*
* @param path path to the loadable extension shared library
* e.g. "/data/data/(package)/lib/libSqliteICU.so"
* @param entryPoint extension entry point (optional)
* e.g. "sqlite3_icu_init"
*/
public SQLiteCustomExtension(String path, String entryPoint) {
if (path == null) {
throw new IllegalArgumentException("null path");
}
this.path = path;
this.entryPoint = entryPoint;
}
}
找到了解决方案,.so文件应该放在assets文件夹中,并在应用程序启动时复制。
fun copyAssetToInternalStorage(context: Context, assetFileName: String,saveName:String): String? {
val file = File(context.filesDir, saveName)
try {
context.assets.open(assetFileName).use { inputStream ->
FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
} catch (e: IOException) {
e.printStackTrace()
return null
}
return file.absolutePath
}
然后就可以访问该路径了。