在本地复制和解决Android java.lang.unsatisfiedLinkError

Phi*_*lip 23 java java-native-interface android unsatisfiedlinkerror android-app-bundle

我和一个朋友一起创建了一个Android应用来组织学校成绩。该应用程序可以在我的设备和大多数用户设备上正常运行,但是崩溃率超过3%,这主要是由于java.lang.UnsatisfiedLinkErrorAndroid 7.0、8.1和9版本所发生的。

我已经在手机和包括所有架构在内的多个仿真器上测试了该应用程序。我将应用程序作为android-app-bundle上传到应用程序商店,并怀疑这可能是问题的根源。

我在这里有点迷茫,因为我已经尝试了好几件事,但是到目前为止,我既不能减少出现的次数,也无法在任何设备上重现它。任何帮助将不胜感激。

我发现此资源指出Android有时无法解压缩外部库。因此,他们创建了一个ReLinker库,该库将尝试从压缩的应用程序中获取库:

不幸的是,这并没有减少由于导致的崩溃数量java.lang.UnsatisfiedLinkError。我继续在线研究并找到了这篇文章,这表明问题出在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)
Run Code Online (Sandbox Code Playgroud)

Wrapper.java是我们调用本机库的类。它指向的行如下所示:

import java.util.HashMap;
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

另外,这里是入口点的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);
}
Run Code Online (Sandbox Code Playgroud)

这里是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
Run Code Online (Sandbox Code Playgroud)

Ngh*_*Bui 0

您的两个本机方法是static在 Java 中声明的,但在 C++ 中,相应的函数是使用属于 type 的第二个参数声明的jobject

将类型更改为jclass应该有助于解决您的问题。