星期四, 3月 08, 2012

_id、_ID 傻傻分不清楚

最近在寫 Android 的 Database 程式的時候,發現點擊 ExpandableListView 的第一層要讓他展開的時候,總是會出現以下的 IllegalStateException:

E/AndroidRuntime(12706): FATAL EXCEPTION: main E/AndroidRuntime(12706): java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it. E/AndroidRuntime(12706): at android.database.CursorWindow.nativeGetLong(Native Method) E/AndroidRuntime(12706): at android.database.CursorWindow.getLong(CursorWindow.java:515) E/AndroidRuntime(12706): at android.database.AbstractWindowedCursor.getLong(AbstractWindowedCursor.java:75) E/AndroidRuntime(12706): at android.database.CursorWrapper.getLong(CursorWrapper.java:106) E/AndroidRuntime(12706): at android.widget.CursorTreeAdapter$MyCursorHelper.getId(CursorTreeAdapter.java:436) E/AndroidRuntime(12706): at android.widget.CursorTreeAdapter.getGroupId(CursorTreeAdapter.java:191) E/AndroidRuntime(12706): at android.widget.ExpandableListConnector.getItemId(ExpandableListConnector.java:421) E/AndroidRuntime(12706): at android.widget.AdapterView.getItemIdAtPosition(AdapterView.java:756) E/AndroidRuntime(12706): at android.widget.AdapterView.setSelectedPositionInt(AdapterView.java:1128) E/AndroidRuntime(12706): at android.widget.AbsListView.onTouchEvent(AbsListView.java:3147) E/AndroidRuntime(12706): at android.view.View.dispatchTouchEvent(View.java:5541) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1951) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1712) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957) E/AndroidRuntime(12706): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726) E/AndroidRuntime(12706): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1912) E/AndroidRuntime(12706): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1371) E/AndroidRuntime(12706): at android.app.Activity.dispatchTouchEvent(Activity.java:2364) E/AndroidRuntime(12706): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1860) E/AndroidRuntime(12706): at android.view.View.dispatchPointerEvent(View.java:5721) E/AndroidRuntime(12706): at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:2890) E/AndroidRuntime(12706): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2466) E/AndroidRuntime(12706): at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:845) E/AndroidRuntime(12706): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2475) E/AndroidRuntime(12706): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime(12706): at android.os.Looper.loop(Looper.java:137) E/AndroidRuntime(12706): at android.app.ActivityThread.main(ActivityThread.java:4424) E/AndroidRuntime(12706): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime(12706): at java.lang.reflect.Method.invoke(Method.java:511) E/AndroidRuntime(12706): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) E/AndroidRuntime(12706): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) E/AndroidRuntime(12706): at dalvik.system.NativeStart.main(Native Method)

他唯一比較算是「錯誤訊息」的是「Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.」這個,是表示說程式試圖存取某個 column,可是 query 中沒有這個欄位,所以會回傳 -1。於是乎,程式就這麼爆炸了。

可是... 他只告訴我「col -1」,沒告訴我他到底想查哪個欄位啊... = = 所以只好去追程式碼,發現主要是這個東西「android.widget.CursorTreeAdapter.getGroupId()」會試著抓「_id」這欄位。

SQLite 在 create table 的時候,會自動幫你建立叫做「_id」的欄位作為索引。但是如果你的 create table statement 中有包含某個 integer 型態的 primary key,他就會改用這個欄位來索引(就是不會再多建一個「_id』)了。

Android 的 Cursor 會拿這個欄位作為內部索引,會試圖存取它。當找不到這個欄位的時候,就會丟出 IllegalStateException。

可是呢,我很犯賤的在 create table 的時候用了 _ID 作為 primary key,於是即使 query 是「SELECT `_id` FROM `table_name`;」,他回傳的資料中還是會以 _ID 作為欄位名稱...

結論就是... create table 的時候得用「_id」,用「_Id」、「_iD」、「_ID」統統不行... XD~