UnsatisfiedLinkError(未找到本机方法)
有一段代码在 android 4.1.2 上运行良好,但在 4.0.3 上却不行,崩溃日志是 4.0.3
There is a block of code which is working fine on android 4.1.2 but not on 4.0.3, the crash log is of 4.0.3
我收到 UnsatisfiedLinkError 异常
I am getting UnsatisfiedLinkError Exception
请指导我,谢谢
02-22 12:57:09.319: D/dalvikvm(1312): Trying to load lib /data/data/com.kosh.android/lib/libsqliteX.so 0x41056f48
02-22 12:57:09.319: D/dalvikvm(1312): Added shared lib /data/data/com.kosh.android/lib/libsqliteX.so 0x41056f48
02-22 12:58:02.060: W/dalvikvm(1312): No implementation found for native Lcom/shabdkosh/android/sqlite/CursorWindow;.native_init (Z)V
02-22 12:58:07.624: D/AndroidRuntime(1312): Shutting down VM
02-22 12:58:07.624: W/dalvikvm(1312): threadid=1: thread exiting with uncaught exception (group=0x409961f8)
02-22 12:58:07.767: E/AndroidRuntime(1312): FATAL EXCEPTION: main
02-22 12:58:07.767: E/AndroidRuntime(1312): java.lang.UnsatisfiedLinkError: native_init
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.kosh.android.sqlite.CursorWindow.native_init(Native Method)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.kosh.android.sqlite.CursorWindow.<init>(CursorWindow.java:35)
02-22 12:58:07.767: E/AndroidRuntime(1312): at org.sqlite.database.sqlite.SQLiteCursor.awc_clearOrCreateWindow(SQLiteCursor.java:139)
02-22 12:58:07.767: E/AndroidRuntime(1312): at org.sqlite.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:150)
02-22 12:58:07.767: E/AndroidRuntime(1312): at org.sqlite.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:125)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:158)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:235)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.kosh.android.offline.Handler.newdb1_index0_list(Handler.java:99)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.kosh.android.offline.SearchDataDB.getAutoSuggestion(SearchDataDB.java:29)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.kosh.android.MainActivity.onQueryTextChange(MainActivity.java:885)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.SearchView.onTextChanged(SearchView.java:1081)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.SearchView.access$1900(SearchView.java:90)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.SearchView$10.onTextChanged(SearchView.java:1536)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.TextView.sendOnTextChanged(TextView.java:7577)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.TextView.handleTextChanged(TextView.java:7617)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:7990)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.sendTextChange(SpannableStringBuilder.java:881)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.change(SpannableStringBuilder.java:352)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.change(SpannableStringBuilder.java:266)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:443)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:420)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:29)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.method.QwertyKeyListener.onKeyDown(QwertyKeyListener.java:218)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.text.method.TextKeyListener.onKeyDown(TextKeyListener.java:132)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.TextView.doKeyDown(TextView.java:5386)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.TextView.onKeyDown(TextView.java:5206)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.widget.AutoCompleteTextView.onKeyDown(AutoCompleteTextView.java:682)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.KeyEvent.dispatch(KeyEvent.java:2533)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.View.dispatchKeyEvent(View.java:5446)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1248)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1859)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1361)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.app.Activity.dispatchKeyEvent(Activity.java:2324)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1806)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3300)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewRootImpl.handleFinishedEvent(ViewRootImpl.java:3273)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2436)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.os.Handler.dispatchMessage(Handler.java:99)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.os.Looper.loop(Looper.java:137)
02-22 12:58:07.767: E/AndroidRuntime(1312): at android.app.ActivityThread.main(ActivityThread.java:4340)
02-22 12:58:07.767: E/AndroidRuntime(1312): at java.lang.reflect.Method.invokeNative(Native Method)
02-22 12:58:07.767: E/AndroidRuntime(1312): at java.lang.reflect.Method.invoke(Method.java:511)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-22 12:58:07.767: E/AndroidRuntime(1312): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-22 12:58:07.767: E/AndroidRuntime(1312): at dalvik.system.NativeStart.main(Native Method)
这是我的课
package com.kosh.android.sqlite;
import android.database.CharArrayBuffer;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A buffer containing multiple cursor rows.
*/
public class CursorWindow extends android.database.CursorWindow implements Parcelable {
/** The pointer to the native window class */
/** The pointer to the native window class. set by the native methods in
* android_database_CursorWindow.cpp
*/
private int nWindow;
private int mStartPos;
/**
* Creates a new empty window.
*
* @param localWindow true if this window will be used in this process only
*/
public CursorWindow(boolean localWindow) {
super(localWindow);
mStartPos = 0;
native_init(localWindow);
}
/**
* Returns the starting position of this window within the entire
* Cursor's result set.
*
* @return the starting position of this window within the entire
* Cursor's result set.
*/
public int getStartPosition() {
return mStartPos;
}
/**
* Set the start position of cursor window
* @param pos
*/
public void setStartPosition(int pos) {
mStartPos = pos;
}
/**
* Returns the number of rows in this window.
*
* @return the number of rows in this window.
*/
public int getNumRows() {
acquireReference();
try {
return getNumRows_native();
} finally {
releaseReference();
}
}
private native int getNumRows_native();
/**
* Set number of Columns
* @param columnNum
* @return true if success
*/
public boolean setNumColumns(int columnNum) {
acquireReference();
try {
return setNumColumns_native(columnNum);
} finally {
releaseReference();
}
}
private native boolean setNumColumns_native(int columnNum);
/**
* Allocate a row in cursor window
* @return false if cursor window is out of memory
*/
public boolean allocRow(){
acquireReference();
try {
return allocRow_native();
} finally {
releaseReference();
}
}
private native boolean allocRow_native();
/**
* Free the last row
*/
public void freeLastRow(){
acquireReference();
try {
freeLastRow_native();
} finally {
releaseReference();
}
}
private native void freeLastRow_native();
/**
* copy byte array to cursor window
* @param value
* @param row
* @param col
* @return false if fail to copy
*/
public boolean putBlob(byte[] value, int row, int col) {
acquireReference();
try {
return putBlob_native(value, row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean putBlob_native(byte[] value, int row, int col);
/**
* Copy String to cursor window
* @param value
* @param row
* @param col
* @return false if fail to copy
*/
public boolean putString(String value, int row, int col) {
acquireReference();
try {
return putString_native(value, row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean putString_native(String value, int row, int col);
/**
* Copy integer to cursor window
* @param value
* @param row
* @param col
* @return false if fail to copy
*/
public boolean putLong(long value, int row, int col) {
acquireReference();
try {
return putLong_native(value, row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean putLong_native(long value, int row, int col);
/**
* Copy double to cursor window
* @param value
* @param row
* @param col
* @return false if fail to copy
*/
public boolean putDouble(double value, int row, int col) {
acquireReference();
try {
return putDouble_native(value, row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean putDouble_native(double value, int row, int col);
/**
* Set the [row, col] value to NULL
* @param row
* @param col
* @return false if fail to copy
*/
public boolean putNull(int row, int col) {
acquireReference();
try {
return putNull_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean putNull_native(int row, int col);
/**
* Returns {@code true} if given field is {@code NULL}.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL}
*/
public boolean isNull(int row, int col) {
acquireReference();
try {
return isNull_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean isNull_native(int row, int col);
/**
* Returns a byte array for the given field.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a String value for the given field
*/
public byte[] getBlob(int row, int col) {
acquireReference();
try {
return getBlob_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>byte</code> array.
*
* <p>If the value is null, then <code>null</code> is returned. If the
* type of column <code>col</code> is a string type, then the result
* is the array of bytes that make up the internal representation of the
* string value. If the type of column <code>col</code> is integral or floating-point,
* then an {@link SQLiteException} is thrown.
*/
private native byte[] getBlob_native(int row, int col);
/**
* Returns data type of the given column's value.
*<p>
* Returned column types are
* <ul>
* <li>{@link Cursor#FIELD_TYPE_NULL}</li>
* <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
* <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
* <li>{@link Cursor#FIELD_TYPE_STRING}</li>
* <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
*</ul>
*</p>
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return the value type
*/
public int getType(int row, int col) {
acquireReference();
try {
return getType_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Checks if a field contains either a blob or is null.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL} or a blob
* @deprecated use {@link #getType(int, int)} instead
*/
public boolean isBlob(int row, int col) {
acquireReference();
try {
return isBlob_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Checks if a field contains a long
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is a long
* @deprecated use {@link #getType(int, int)} instead
*/
public boolean isLong(int row, int col) {
acquireReference();
try {
return isInteger_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Checks if a field contains a float.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is a float
* @deprecated use {@link #getType(int, int)} instead
*/
public boolean isFloat(int row, int col) {
acquireReference();
try {
return isFloat_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Checks if a field contains either a String or is null.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL} or a String
* @deprecated use {@link #getType(int, int)} instead
*/
public boolean isString(int row, int col) {
acquireReference();
try {
return isString_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
private native boolean isBlob_native(int row, int col);
private native boolean isString_native(int row, int col);
private native boolean isInteger_native(int row, int col);
private native boolean isFloat_native(int row, int col);
private native int getType_native(int row, int col);
/**
* Returns a String for the given field.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a String value for the given field
*/
public String getString(int row, int col) {
acquireReference();
try {
return getString_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>String</code>.
*
* <p>If the value is null, then <code>null</code> is returned. If the
* type of column <code>col</code> is integral, then the result is the string
* that is obtained by formatting the integer value with the <code>printf</code>
* family of functions using format specifier <code>%lld</code>. If the
* type of column <code>col</code> is floating-point, then the result is the string
* that is obtained by formatting the floating-point value with the
* <code>printf</code> family of functions using format specifier <code>%g</code>.
* If the type of column <code>col</code> is a blob type, then an
* {@link SQLiteException} is thrown.
*/
private native String getString_native(int row, int col);
/**
* copy the text for the given field in the provided char array.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @param buffer the CharArrayBuffer to copy the text into,
* If the requested string is larger than the buffer
* a new char buffer will be created to hold the string. and assigne to
* CharArrayBuffer.data
*/
public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) {
if (buffer == null) {
throw new IllegalArgumentException("CharArrayBuffer should not be null");
}
if (buffer.data == null) {
buffer.data = new char[64];
}
acquireReference();
try {
char[] newbuf = copyStringToBuffer_native(
row - mStartPos, col, buffer.data.length, buffer);
if (newbuf != null) {
buffer.data = newbuf;
}
} finally {
releaseReference();
}
}
private native char[] copyStringToBuffer_native(
int row, int col, int bufferSize, CharArrayBuffer buffer);
/**
* Returns a long for the given field.
* row is 0 based
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a long value for the given field
*/
public long getLong(int row, int col) {
acquireReference();
try {
return getLong_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>long</code>.
*
* <p>If the value is null, then <code>0L</code> is returned. If the
* type of column <code>col</code> is a string type, then the result
* is the <code>long</code> that is obtained by parsing the string value with
* <code>strtoll</code>. If the type of column <code>col</code> is
* floating-point, then the result is the floating-point value casted to a <code>long</code>.
* If the type of column <code>col</code> is a blob type, then an
* {@link SQLiteException} is thrown.
*/
private native long getLong_native(int row, int col);
/**
* Returns a double for the given field.
* row is 0 based
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a double value for the given field
*/
public double getDouble(int row, int col) {
acquireReference();
try {
return getDouble_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>double</code>.
*
* <p>If the value is null, then <code>0.0</code> is returned. If the
* type of column <code>col</code> is a string type, then the result
* is the <code>double</code> that is obtained by parsing the string value with
* <code>strtod</code>. If the type of column <code>col</code> is
* integral, then the result is the integer value casted to a <code>double</code>.
* If the type of column <code>col</code> is a blob type, then an
* {@link SQLiteException} is thrown.
*/
private native double getDouble_native(int row, int col);
/**
* Returns a short for the given field.
* row is 0 based
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a short value for the given field
*/
public short getShort(int row, int col) {
acquireReference();
try {
return (short) getLong_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns an int for the given field.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return an int value for the given field
*/
public int getInt(int row, int col) {
acquireReference();
try {
return (int) getLong_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Returns a float for the given field.
* row is 0 based
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a float value for the given field
*/
public float getFloat(int row, int col) {
acquireReference();
try {
return (float) getDouble_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
/**
* Clears out the existing contents of the window, making it safe to reuse
* for new data. Note that the number of columns in the window may NOT
* change across a call to clear().
*/
public void clear() {
acquireReference();
try {
mStartPos = 0;
native_clear();
} finally {
releaseReference();
}
}
/** Clears out the native side of things */
private native void native_clear();
/**
* Cleans up the native resources associated with the window.
*/
public void close() {
releaseReference();
}
private native void close_native();
@Override
protected void finalize() {
// Just in case someone forgot to call close...
if (nWindow == 0) {
return;
}
close_native();
}
public static final Parcelable.Creator<CursorWindow> CREATOR
= new Parcelable.Creator<CursorWindow>() {
public CursorWindow createFromParcel(Parcel source) {
return new CursorWindow(source,0);
}
public CursorWindow[] newArray(int size) {
return new CursorWindow[size];
}
};
public static CursorWindow newFromParcel(Parcel p) {
return CREATOR.createFromParcel(p);
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(native_getBinder());
dest.writeInt(mStartPos);
}
public CursorWindow(Parcel source,int foo) {
super(true);
IBinder nativeBinder = source.readStrongBinder();
mStartPos = source.readInt();
native_init(nativeBinder);
}
/** Get the binder for the native side of the window */
private native IBinder native_getBinder();
/** Does the native side initialization for an empty window */
private native void native_init(boolean localOnly);
/** Does the native side initialization with an existing binder from another process */
private native void native_init(IBinder nativeBinder);
@Override
protected void onAllReferencesReleased() {
close_native();
super.onAllReferencesReleased();
}
}
这是我编译并创建二进制libsqliteX.so"文件的源 cpp 文件 http://www.sqlite.org/android/tree?ci=trunk&re=jni|src/org/sqlite/data&expand
Here is the source cpp files from where i have compiled and created the binary "libsqliteX.so" file http://www.sqlite.org/android/tree?ci=trunk&re=jni|src/org/sqlite/data&expand
我的 .so 文件 https://www.dropbox.com/s/d0u5pyhke54tcd6/libsqliteX.so一个>
这里是 CDT GLOBAL BUILD 控制台
Here is CDT GLOBAL BUILD CONSOLE
**** Build of configuration Default for project CustomSqlite ****
D:softwareadt-bundle-windows-x86-20130522adt-bundle-windows-x86-20130522android-ndk-r9-windows-x86android-ndk-r9
dk-build.cmd all
"Compile++ thumb : sqliteX <= android_database_SQLiteCommon.cpp
"Compile++ thumb : sqliteX <= android_database_SQLiteConnection.cpp
"Compile++ thumb : sqliteX <= android_database_SQLiteGlobal.cpp
"Compile++ thumb : sqliteX <= android_database_SQLiteDebug.cpp
"Compile++ thumb : sqliteX <= JNIHelp.cpp
"Compile++ thumb : sqliteX <= JniConstants.cpp
"Compile thumb : sqliteX <= sqlite3.c
SharedLibrary : libsqliteX.so
Install : libsqliteX.so => libs/armeabi/libsqliteX.so
**** Build Finished ****
**** Build of configuration Default for project CustomSqlite ****
D:softwareadt-bundle-windows-x86-20130522adt-bundle-windows-x86-20130522android-ndk-r9-windows-x86android-ndk-r9
dk-build.cmd all
Install : libsqliteX.so => libs/armeabi/libsqliteX.so
**** Build Finished ****
这一行造成了问题
native_init(localWindow);
请考虑一下
我做错了什么/在哪里以及如何克服这个问题我从 3 天开始就被这个问题困扰
What/where i m doing wrong and how to overcome this problem i am stuck with this from 3 days
谢谢
推荐答案
这意味着运行时试图找到匹配的方法但不成功.造成这种情况的一些常见原因是:
This means that the runtime tried to find a matching method but was unsuccessful. Some common reasons for this are:
- 库未加载.检查 logcat 输出以获取有关库加载的消息.
- 由于名称或签名不匹配,找不到该方法.
这是一个经常发生的错误.您可以在以下位置找到有关它的更多详细信息:
This is an error that happens quite often. You can find more details about it on:
- 为什么我会收到 UnsatisfiedLinkError
每当为跨平台编写本机代码时,工具链 会让你的应用崩溃.
Whenever doing native code for cross-platform, any detail missing from your toolchain will make your application to crash.
相关文章