UnsatisfiedLinkError(未find本地方法)

有一块代码在Android 4.1.2上正常工作,但不在4.0.3上,崩溃日志是4.0.3

我得到UnsatisfiedLinkErrorexception

请引导我谢谢

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(); } } 

这里是我编译和创build二进制“libsqliteX.so”文件的源代码cpp文件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 CONSOLE

 **** Build of configuration Default for project CustomSqlite **** D:\software\adt-bundle-windows-x86-20130522\adt-bundle-windows-x86-20130522\android-ndk-r9-windows-x86\android-ndk-r9\ndk-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:\software\adt-bundle-windows-x86-20130522\adt-bundle-windows-x86-20130522\android-ndk-r9-windows-x86\android-ndk-r9\ndk-build.cmd all Install : libsqliteX.so => libs/armeabi/libsqliteX.so **** Build Finished **** 

这一行产生了这个问题

 native_init(localWindow); 

请考虑这一点

什么/在哪里做错了,如何克服这个问题,我坚持了这个从3天

谢谢

Solutions Collecting From Web of "UnsatisfiedLinkError(未find本地方法)"

这意味着运行时试图find一个匹配的方法,但不成功。 一些常见的原因是:

  • 图书馆没有得到加载。 检查logcat输出以获取关于库加载的消息。
  • 由于名称或签名不匹配,未find该方法。

这是一个经常发生的错误。 你可以在这里find更多关于它的细节:

  • 为什么我得到UnsatisfiedLinkError

每当为跨平台执行本机代码时, 工具链中缺less的任何细节都将使您的应用程序崩溃。