【发布时间】:2019-10-07 10:12:05
【问题描述】:
我和朋友一起创建了一个 Android 应用来组织学校成绩。该应用程序在我的设备和大多数用户设备上运行良好,但崩溃率超过 3%,主要是因为 java.lang.UnsatisfiedLinkError 并且发生在 Android 版本 7.0、8.1 和 9 上。
我已经在我的手机和多个模拟器上测试了该应用程序,包括所有架构。我将应用作为 android-app-bundle 上传到应用商店,并怀疑这可能是问题的根源。
我在这里有点迷茫,因为我已经尝试了几件事,但到目前为止,我既无法减少出现次数,也无法在我的任何设备上重现它。任何帮助将不胜感激。
我发现this resource 指出Android 有时无法解压外部库。因此他们创建了一个ReLinker library,它将尝试从压缩的应用程序中获取库:
不幸的是,这并没有减少由于java.lang.UnsatisfiedLinkError 而导致的崩溃数量。我继续我的在线研究,发现this article,这表明问题出在64位库上。所以我删除了 64 位库(该应用程序仍然可以在所有设备上运行,因为 64 位架构也可以执行 32 位库)。但是,错误仍然像以前一样以相同的频率发生。
通过 google-play-console 我得到了以下崩溃报告:
java.lang.UnsatisfiedLinkError:
at ch.fidelisfactory.pluspoints.Core.Wrapper.callCoreEndpointJNI (Wrapper.java)
at ch.fidelisfactory.pluspoints.Core.Wrapper.a (Wrapper.java:9)
at ch.fidelisfactory.pluspoints.Model.Exam.a (Exam.java:46)
at ch.fidelisfactory.pluspoints.SubjectActivity.i (SubjectActivity.java:9)
at ch.fidelisfactory.pluspoints.SubjectActivity.onCreate (SubjectActivity.java:213)
at android.app.Activity.performCreate (Activity.java:7136)
at android.app.Activity.performCreate (Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2908)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3063)
at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1823)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loop (Looper.java:198)
at android.app.ActivityThread.main (ActivityThread.java:6729)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:876)
Wrapper.java 是调用我们原生库的类。然而,它指向的行如下所示:
import java.util.HashMap;
ch.fidelisfactory.pluspoints.Core.Wrapper.callCoreEndpointJNI 是我们原生 cpp 库的入口点。
在原生 cpp 库中,我们使用了一些外部库(curl、jsoncpp、plog-logging、sqlite 和 tinyxml2)。
2019 年 6 月 4 日编辑
根据要求,这里是Wrapper.java的代码:
package ch.fidelisfactory.pluspoints.Core;
import android.content.Context;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.util.HashMap;
import ch.fidelisfactory.pluspoints.Logging.Log;
/***
* Wrapper around the cpp pluspoints core
*/
public class Wrapper {
/**
* An AsyncCallback can be given to the executeEndpointAsync method.
* The callback method will be called with the returned json from the core.
*/
public interface AsyncCallback {
void callback(JSONObject object);
}
public static boolean setup(Context context) {
String path = context.getFilesDir().getPath();
return setupWithFolderAndLogfile(path,
path + "/output.log");
}
private static boolean setupWithFolderAndLogfile(String folderPath, String logfilePath) {
HashMap<String, Serializable> data = new HashMap<>();
data.put("folder", folderPath);
data.put("logfile", logfilePath);
JSONObject res = executeEndpoint("/initialization", data);
return !isErrorResponse(res);
}
public static JSONObject executeEndpoint(String path, HashMap<String, Serializable> data) {
JSONObject jsonData = new JSONObject(data);
String res = callCoreEndpointJNI(path, jsonData.toString());
JSONObject ret;
try {
ret = new JSONObject(res);
} catch (JSONException e) {
Log.e("Error while converting core return statement to json.");
Log.e(e.getMessage());
Log.e(e.toString());
ret = new JSONObject();
try {
ret.put("error", e.toString());
} catch (JSONException e2) {
Log.e("Error while putting the error into the return json.");
Log.e(e2.getMessage());
Log.e(e2.toString());
}
}
return ret;
}
public static void executeEndpointAsync(String path, HashMap<String, Serializable> data, AsyncCallback callback) {
// Create and start the task.
AsyncCoreTask task = new AsyncCoreTask();
task.setCallback(callback);
task.setPath(path);
task.setData(data);
task.execute();
}
public static boolean isErrorResponse(JSONObject data) {
return data.has("error");
}
public static boolean isSuccess(JSONObject data) {
String res;
try {
res = data.getString("status");
} catch (JSONException e) {
Log.w(String.format("JsonData is no status message: %s", data.toString()));
res = "no";
}
return res.equals("success");
}
public static Error errorFromResponse(JSONObject data) {
String errorDescr;
if (isErrorResponse(data)) {
try {
errorDescr = data.getString("error");
} catch (JSONException e) {
errorDescr = e.getMessage();
errorDescr = "There was an error while getting the error message: " + errorDescr;
}
} else {
errorDescr = "Data contains no error message.";
}
return new Error(errorDescr);
}
private static native String callCoreEndpointJNI(String jPath, String jData);
/**
* Log a message to the core
* @param level The level of the message. A number from 0 (DEBUG) to 5 (FATAL)
* @param message The message to log
*/
public static native void log(int level, String message);
}
另外,这里是入口点的 cpp 定义,然后调用我们的核心库:
#include <jni.h>
#include <string>
#include "pluspoints.h"
extern "C"
JNIEXPORT jstring JNICALL
Java_ch_fidelisfactory_pluspoints_Core_Wrapper_callCoreEndpointJNI(
JNIEnv* env,
jobject /* this */,
jstring jPath,
jstring jData) {
const jsize pathLen = env->GetStringUTFLength(jPath);
const char* pathChars = env->GetStringUTFChars(jPath, (jboolean *)0);
const jsize dataLen = env->GetStringUTFLength(jData);
const char* dataChars = env->GetStringUTFChars(jData, (jboolean *)0);
std::string path(pathChars, (unsigned long) pathLen);
std::string data(dataChars, (unsigned long) dataLen);
std::string result = pluspoints_execute(path.c_str(), data.c_str());
env->ReleaseStringUTFChars(jPath, pathChars);
env->ReleaseStringUTFChars(jData, dataChars);
return env->NewStringUTF(result.c_str());
}
extern "C"
JNIEXPORT void JNICALL Java_ch_fidelisfactory_pluspoints_Core_Wrapper_log(
JNIEnv* env,
jobject,
jint level,
jstring message) {
const jsize messageLen = env->GetStringUTFLength(message);
const char *messageChars = env->GetStringUTFChars(message, (jboolean *)0);
std::string cppMessage(messageChars, (unsigned long) messageLen);
pluspoints_log((PlusPointsLogLevel)level, cppMessage);
}
这里是 pluspoints.h 文件:
/**
* Copyright 2017 FidelisFactory
*/
#ifndef PLUSPOINTSCORE_PLUSPOINTS_H
#define PLUSPOINTSCORE_PLUSPOINTS_H
#include <string>
/**
* Send a request to the Pluspoints core.
* @param path The endpoint you wish to call.
* @param request The request.
* @return The return value from the executed endpoint.
*/
std::string pluspoints_execute(std::string path, std::string request);
/**
* The different log levels at which can be logged.
*/
typedef enum {
LEVEL_VERBOSE = 0,
LEVEL_DEBUG = 1,
LEVEL_INFO = 2,
LEVEL_WARNING = 3,
LEVEL_ERROR = 4,
LEVEL_FATAL = 5
} PlusPointsLogLevel;
/**
* Log a message with the info level to the core.
*
* The message will be written in the log file in the core.
* @note The core needs to be initialized before this method can be used.
* @param level The level at which to log the message.
* @param logMessage The log message
*/
void pluspoints_log(PlusPointsLogLevel level, std::string logMessage);
#endif //PLUSPOINTSCORE_PLUSPOINTS_H
【问题讨论】:
-
你能发布你的 Wrapper.java 吗?
-
@Zaartha 我刚刚编辑了我的帖子并添加了 Wrapper.java 以及入口点的 cpp 端。提前感谢您的帮助!
-
不确定这是否有帮助 developer.android.com/topic/performance/…,将其设为
android:extractNativeLibs="true"看看效果如何。 -
请把
pluspoints.h的内容也贴出来。 -
@luckingging3er 似乎没有关于 Android 版本的模式,它发生在迄今为止所有受支持的版本上:5、6、7、8 和 9。谢谢你的链接,是的,我已经看过那个了。将再次解决它,以确保我以预期的方式得到了一切。
标签: java android java-native-interface unsatisfiedlinkerror android-app-bundle