点击按钮后插入数据

3
当我点击按钮时,我的应用程序为什么会崩溃,并且如何修复它?我需要在点击按钮时向我的 SQLite 数据库中插入一行。它包含两个值,这应该很容易。一个是使用 simpledateformatter 格式化的日期。另一个只是来自 ArrayList 的位置。我的代码中没有红线,但是当我按下按钮以插入行时,我的应用程序崩溃了。下面是三个 Java 文件之间交互的代码。我有一个预感问题出在 Model.java 中,但也可能不止这些。如果需要更多代码,请询问。
MainActivity.java
public class MainActivity extends Activity {
final String TAG = "*** DEBUG ***";

public static final String SMOKIN_DATA_FILE = "smokin.dat";
public static final int EDIT_ACTIVITY = 1;

public static Model model = null;
public static MySmokinDatabase mySmokinDatabase;
public static Cursor cursor;
public static SimpleCursorAdapter adapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    restoreModel();

    if (mySmokinDatabase == null)
        mySmokinDatabase = new MySmokinDatabase(this);

    refreshView();
}

@Override
protected void onResume() {
    super.onResume();

    if (model == null)
        restoreModel();

    refreshView();
}

protected void onPause() {
    super.onPause();

    saveModel();
}

private void refreshView() {
    Spinner spinner = (Spinner) findViewById(R.id.location_spinner);

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
             android.R.layout.simple_spinner_item, model.getLocationsArray());

    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    spinner.setAdapter(adapter);

    SimpleDateFormat sdf = new SimpleDateFormat("E, MMM dd");

    TextView dateText = (TextView) findViewById(R.id.date);
    TextView countText = (TextView) findViewById(R.id.count);
    TextView daysText = (TextView) findViewById(R.id.days);
    TextView totalText = (TextView) findViewById(R.id.total);
    TextView aveText = (TextView) findViewById(R.id.ave);

    GregorianCalendar now = new GregorianCalendar();
    dateText.setText(sdf.format(now.getTime()));
    //get today's count from data in the SQLite table - count entries with today's date
    countText.setText("" + "");
    //mySmokinDatabase.getTodaysCount());

    // Table data
    daysText.setText("" + String.format("%10d", model.getDays()));
    totalText.setText("" + "get total count from data in SQLite table - count total rows");

    if (model.getDays() > 0)
        aveText.setText("calc average from SQLite and model info");

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)  { 
    super.onActivityResult(requestCode, resultCode, data); 
    if (resultCode == RESULT_OK && requestCode == EDIT_ACTIVITY) { 
        saveModel();
    }
}

public void smokedHandler(View view) {
    Spinner spinner = (Spinner) findViewById(R.id.location_spinner);

    String s = spinner.getSelectedItem().toString();
    String d = model.getDates();

    mySmokinDatabase.insertSmokinValues(new Model(s,d));

    cursor = mySmokinDatabase.getAllSmokinCursor();
    adapter.changeCursor(cursor);   

    refreshView();
}

public void restoreModel() {
    // Restore from disk, or start with an empty model
    try {
        ObjectInputStream ois = new ObjectInputStream(
                openFileInput(SMOKIN_DATA_FILE));

        model = (Model) ois.readObject();
        ois.close();
    }
    catch (Exception e){
        Log.v("*** DEBUG ***", "Error writing to file: " + e);
        model = new Model();
    }
}

public void saveModel() {
    Log.v("*** AJ ***", "In onPause");

    try {
        ObjectOutputStream oos = new ObjectOutputStream(
                openFileOutput(SMOKIN_DATA_FILE, Context.MODE_PRIVATE));

        oos.writeObject(model);
        oos.close();
    }
    catch (Exception e){
        Log.v("*** MatchIt ***", "Error writing to file: " + e);
    }
}

DB文件

public long insertSmokinValues(Model model) {
    ContentValues newSmokinValues = new ContentValues();
    newSmokinValues.put(KEY_DATE, model.getDates());
    newSmokinValues.put(KEY_LOCATION, model.getLocations());        
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    return db.insert(SmokinDBOpenHelper.INCIDENTS_TABLE, null, newSmokinValues);
}

public Cursor getAllSmokinCursor() {
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    return db.query(SmokinDBOpenHelper.INCIDENTS_TABLE, new String[] 
            {KEY_ID, KEY_LOCATION, KEY_DATE}, null, null, null, null, null);        
}

模型文件

private ArrayList<String> locations = new ArrayList<String>();
public String [] defaultLocations = {"Home", "Work", "Commuting", "School", "Bar", 
"Restaurant", "Social Gathering", "Other"};

public Model(GregorianCalendar date){
    startDate = date;

    for (String s : this.defaultLocations)
        locations.add(s);
}

public Model(String s, String d) {
    place = s;
    dates = d;      
}

public String getDates() {
    GregorianCalendar gc = new GregorianCalendar();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    dates = sdf.format(gc.getTime());       

    return dates;
}   

public String getLocations() {
    for (String s : this.locations)
        place = s;
    return place;
}       

如所请求,以下是日志记录:

 03-18 15:52:42.397: W/dalvikvm(676): threadid=1: thread exiting with uncaught  
 exception
 (group=0x40a13300)
 03-18 15:52:42.457: E/AndroidRuntime(676): FATAL EXCEPTION: main
03-18 15:52:42.457: E/AndroidRuntime(676): java.lang.IllegalStateException: Could not    
execute method of the activity
03-18 15:52:42.457: E/AndroidRuntime(676):  at    
android.view.View$1.onClick(View.java:3591)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.view.View.performClick(View.java:4084)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.view.View$PerformClick.run(View.java:16966)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.os.Handler.handleCallback(Handler.java:615)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.os.Handler.dispatchMessage(Handler.java:92)
03-18 15:52:42.457: E/AndroidRuntime(676):  at android.os.Looper.loop(Looper.java:137)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.app.ActivityThread.main(ActivityThread.java:4745)
03-18 15:52:42.457: E/AndroidRuntime(676):  at    
java.lang.reflect.Method.invokeNative(Native Method)
03-18 15:52:42.457: E/AndroidRuntime(676):  at 
java.lang.reflect.Method.invoke(Method.java:511)
03-18 15:52:42.457: E/AndroidRuntime(676):  at 
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
03-18 15:52:42.457: E/AndroidRuntime(676):  at    
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
03-18 15:52:42.457: E/AndroidRuntime(676):  at dalvik.system.NativeStart.main(Native   
Method)
03-18 15:52:42.457: E/AndroidRuntime(676): Caused by: 
java.lang.reflect.InvocationTargetException
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
java.lang.reflect.Method.invokeNative(Native Method)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
java.lang.reflect.Method.invoke(Method.java:511)
03-18 15:52:42.457: E/AndroidRuntime(676):  at   
android.view.View$1.onClick(View.java:3586)
03-18 15:52:42.457: E/AndroidRuntime(676):  ... 11 more
03-18 15:52:42.457: E/AndroidRuntime(676): Caused by: java.lang.NullPointerException
03-18 15:52:42.457: E/AndroidRuntime(676):  at     
com.example.smokin4ThomasSullivan.MainActivity.smokedHandler(MainActivity.java:119)
03-18 15:52:42.457: E/AndroidRuntime(676):  ... 14 more

第119行:adapter.changeCursor(cursor); 所涉及的方法在我的数据库文件中发布

一如既往地感谢您的帮助!


2
请在您的logcat中发布堆栈跟踪。 - Gabe Sechan
1
没有 logcat,我们什么也做不了。 - Simon Dorociak
你在smokedHandler()方法中遇到了NPE。那里有一些空值。 - Simon Dorociak
似乎您的光标为空。 - Pragnani
直到我点击这个按钮之前,数据库中没有任何值。因此返回“所有值”的光标返回的是空的,因为数据库中没有任何内容。 - IrishWhiskey
显示剩余3条评论
3个回答

3

日志说明了一切:您试图在此行中使用nulladapter

adapter.changeCursor(cursor); 

编辑:

正如我所想,你在这里声明了你的adapter字段:

public static SimpleCursorAdapter adapter;

但你没有对它进行初始化,而在Java中,如果你声明一个未初始化的字段,它就会被设置为null。这就是你遇到错误的原因。
我个人没有使用SimpleCursorAdapter类,但你或许需要查看一下它的构造函数,它在Android文档中有描述: http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html 所以基本上,在onCreate()方法中,你应该这样做:
adapter = new SimpleCursorAdapter( this, 
                                   R.layout.some_layout,
                                   cursor,
                                   columns,
                                   to,
                                   0 );

之前我发布的链接中有文档描述了使用构造函数的参数。

您可能还想检查一下使用SimpleCursorAdapter的代码:http://www.mysamplecode.com/2012/07/android-listview-cursoradapter-sqlite.html


直到我点击这个按钮之前,数据库中没有任何值。该数据库包含日期和位置信息。此按钮将添加这些条目。因此,返回“所有值”的光标未返回任何内容,因为数据库中没有任何内容。有没有什么方法可以解决这个问题? - IrishWhiskey
日志在哪里显示“adapter”为空?没有行号,该方法中的任何内容都可能是。 - Tushar
哈哈,你可能是对的。我可能需要离开电脑一会儿,因为这个错误对我来说太基础了。我在上面发布了一个更全面的MainActivity,请看一下。我要休息5分钟然后回来。 - IrishWhiskey
@IrishWhiskey 我编辑了我的答案,看看吧。 :-) 如果你还需要帮助,明天我可能会更有帮助,因为现在我没有时间更好地解释它。无论如何,我希望这足够了。 - Piotr Chojnacki
太棒了,它起作用了。答案被接受,因为它是最直接和高效的。我会在完成后发布我的代码,以供其他可能会发现它有用的人使用。 - IrishWhiskey
显示剩余5条评论

1

编辑: 就像其他人所说的一样。确实适配器adapter负责NPE

但是,采用正确的方式处理Sqlitecursor也很重要。

以下操作很重要

替换

public Cursor getAllSmokinCursor() {
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    return db.query(SmokinDBOpenHelper.INCIDENTS_TABLE, new String[] 
            {KEY_ID, KEY_LOCATION, KEY_DATE}, null, null, null, null, null);        
}

with

public List<Model> getAllSmokinCursor() {
   List<Model> models= new ArrayList<Model>();
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    Cursor cursor= db.query(SmokinDBOpenHelper.INCIDENTS_TABLE, new String[] 
            {KEY_ID, KEY_LOCATION, KEY_DATE}, null, null, null, null, null);        

 cursor.moveToFirst();
      while (!cursor.isAfterLast()) {
         Model device = fromCursorToObject(cursor);

         models.add(device);
         cursor.moveToNext();
      }
      // Make sure to close the cursor
      cursor.close();
      return models;

}

我在这里使用列表,因为你的请求没有任何限制public static Device

fromCursorToObject(Cursor cursor) {
      Model model = new Model();
      int i = 0;
      model.setId(cursor.getLong(i++));
      model.setLocation(cursor.getString(i++));
      model.setDate(cursor.getString(i++));
      return model;
   }

我认为你需要在你的Model类中添加getter/setter方法,还要添加一个ID字段(private Long id)。


现在正在检查解决方案,如果您不介意,能否向我解释一下while-loop?不确定我完全理解它在那里做什么。 - IrishWhiskey
@IrishWhiskey 如果您的查询没有任何限制,它可以返回一个列表,这就是为什么我使用 while 循环的原因。 - Festus Tamakloe
如果他在日志所提到的位置遇到了“NullPointerException”,那么问题不是出在“cursor”上,而是出在“adapter”上。 - Piotr Chojnacki
看起来这个可能可行,但我太新了,无法完全理解这里的代码在做什么。我在 Model device = Method_to_transform_cursor_to_model(cursor); 这一行迷失了,它看起来像伪代码。我会研究一下,但可能需要一点时间。 - IrishWhiskey
@IrishWhiskey,我刚刚更新了我的帖子,添加了一种将游标转换为模型对象的方法。 - Festus Tamakloe
@FestusTamakloe,这似乎是一个相当长的解决方案。这并不是说它是错误的,只是看起来为了一个相当简单的解决方案而言,代码有点多。当我学完游标/适配器并且我的代码正常运行时,我会更详细地研究你的解决方案。非常感谢你详细的解释和你的时间。 - IrishWhiskey

1

首先,在这些函数中应该关闭数据库:

public long insertSmokinValues(Model model) {
    ContentValues newSmokinValues = new ContentValues();
    newSmokinValues.put(KEY_DATE, model.getDates());
    newSmokinValues.put(KEY_LOCATION, model.getLocations());        
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    return db.insert(SmokinDBOpenHelper.INCIDENTS_TABLE, null, newSmokinValues);
}

public Cursor getAllSmokinCursor() {
    SQLiteDatabase db = smokinDBOpenHelper.getWritableDatabase();

    return db.query(SmokinDBOpenHelper.INCIDENTS_TABLE, new String[] 
            {KEY_ID, KEY_LOCATION, KEY_DATE}, null, null, null, null, null);        
}

如果您不想在适配器中更改光标,当gestSmokinCursor没有结果时,请使用moveToFirst检查光标是否包含结果,如果没有,则抛出自定义异常EmptyCursorException,您可以在获取光标时处理它。
此外,您需要考虑如何关闭游标,因为我不确定您的适配器是否在更改它们和销毁活动时关闭游标,但这也是您的责任。
如果你的NullPointerException出现在这一行:
adapter.changeCursor(cursor);   

那么你的适配器只是null,需要初始化,这与数据库无关,但您仍然应该解决代码中存在的许多问题,否则当人们开始使用应用程序时,您将遇到错误和泄漏。


你可能是100%正确的。但由于这是作业,我认为教授有意想要教我们一些东西。他经常在截止日期后回顾作业,并发布适当的规范和更现实的解决方案。仅仅因为程序可以运行,并不意味着它被正确编程了。我理解这一点。 - IrishWhiskey
@IrishWhiskey 我明白你只是想让它运行起来,我指出的问题将帮助你解决在你解决了NullPointerException后会遇到的异常。即使适配器不为null,你编写的代码也会抛出异常。 - Emil Davtyan
谢谢你的帮助。我需要更多地学习关于“游标”和“适配器”的知识。尽管如此,你肯定已经指引了我正确的方向,等我学完之后,我相信你的帖子会变得非常清晰易懂。 - IrishWhiskey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接