为啥我的构造函数没有被调用?

Posted

技术标签:

【中文标题】为啥我的构造函数没有被调用?【英文标题】:Why is my constructor not getting called?为什么我的构造函数没有被调用? 【发布时间】:2014-04-12 00:00:48 【问题描述】:

我正在尝试向 SQLite 表添加一些记录,但 LogCat 告诉我该表不存在。并且 DDMS 显示,是的,该表没有/尚未创建。

但我确实在 SQLiteOpenHelper 类中创建了表:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper 
    . . .
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) 
        String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
                TABLE_DELIVERYITEMS + "("
                + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
                + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
                + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
                //+ COLUMN_COST + " REAL,"  + COLUMN_MARGIN + " REAL," + COLUMN_LISTPRICE + " REAL,"
                + COLUMN_COST + " REAL DEFAULT 0,"  + COLUMN_MARGIN + " REAL DEFAULT 0," + 

COLUMN_LISTPRICE + " REAL DEFAULT 0,"
                + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
                + ")";
        sqLiteDatabase.execSQL(CREATE_DELIVERYITEMS_TABLE);
    

我调用类的方法来添加记录:

SQLiteHandlerDeliveryItem sqliteHandler = new SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);
sqliteHandler.addDeliveryItem(delItem);

这应该调用 SQLiteHandlerDeliveryItem 的构造函数(当 sqliteHandler 被实例化时),但它没有!我在 onCreate() 方法中有一个断点,果然 - 它从未到达过。

为什么?以及如何强制调用构造函数,以便创建表?

奇怪[est] 的事情是我还在另一个(工作的)SQLiteOpenHelper 类中设置了一个断点,并且它没有达到......什么?!?它至少工作了一次,因为该表确实存在/是从该代码创建的。

所以我的挥杆明显有个洞;我有什么误解或做错了什么?

更新

我将答案标记为太快了。

至于:

“1. 您必须在某个时候调用 getWritableDatabase() 或 getReadableDatabase()。”

在每个创建或读取记录的方法中调用getWritableDatabase(),如下所示:

public long addDeliveryItem(DeliveryItem delItem) 
    long IdAdded = 0;
    ContentValues values = new ContentValues();
    values.put(COLUMN_INVOICENUM, delItem.get_invoiceNumber());
    . . .
    values.put(COLUMN_QTY, delItem.get_quantity());

    SQLiteDatabase db = this.getWritableDatabase(); <= Rot Cheer

    if (db != null) 
        IdAdded = db.insert(TABLE_DELIVERYITEMS, null, values);
    
    . . .

...关于:

"2. 在 SQLiteHandlerDeliveryItem 的构造函数中,您必须调用 super(...)。"

在扩展SQLiteOpenHelper的类中调用super:

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)     

    super(context, DATABASE_NAME, factory, DATABASE_VERSION);

所以...我仍然不知道问题/解决方案是什么...

更新 2

所以我在 LogCat 中尝试向该表中插入记录时看到的是:

E/SQLiteLog:(1)没有这样的表:deliveryitems

然而,我尝试添加记录的代码实例化了扩展 SQLiteOpenHelper 的相应/适当的类,如下所示:

else if ("Delivery Items".equals(tableName)) 
    try 
        JSONArray jsonArr = new JSONArray(result);
        for (int i = 0; i < jsonArr.length(); i++) 
            JSONObject jsonObj = jsonArr.getJSONObject(i);
            String invNum = jsonObj.getString("invoiceNumber");
        . . .                        
        // Prepare for writing to db
        DeliveryItem delItem = new DeliveryItem();
        delItem.set_invoiceNumber(invNum);
        . . .                        
        SQLiteHandlerDeliveryItem sqliteHandler = new  
            SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);
        sqliteHandler.addDeliveryItem(delItem);
    

...并且该类具有创建表的代码:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper 
        . . .
public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)   

    super(context, DATABASE_NAME, factory, DATABASE_VERSION);


@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) 
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
            + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
            + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
            + COLUMN_COST + " INTEGER,"  + COLUMN_MARGIN + " INTEGER," + COLUMN_LISTPRICE + " INTEGER,"
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
            + ")";
    sqLiteDatabase.execSQL(CREATE_DELIVERYITEMS_TABLE);

那么...我做错了什么,还是没有做对?

更新 3

确实没有创建表。 LogCat 中的 err msg 表明情况确实如此(它是“没有这样的表:delivertitems”)。

我的构造函数如下所示:

@Override
public void onCreate(SQLiteDatabase db) 
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
            + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
            + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
            + COLUMN_COST + " INTEGER,"  + COLUMN_MARGIN + " INTEGER," + COLUMN_LISTPRICE + " INTEGER,"
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
            + ")";
    db.execSQL(CREATE_DELIVERYITEMS_TABLE);

此代码确实不会被输入。那么我必须跳过什么圈才能调用构造函数呢?

我认为当我实例化类时会发生这种情况:

SQLiteHandlerDeliveryItem sqliteHandler = new 
    SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);

即使调用它,构造函数如何知道 arg (SQLiteDatabase db) 是什么——它从哪里得到这个值?

我确实有一个数据库,只有一张表。它只是拒绝添加第二个表。

正如某处有人推荐的那样,我正在添加一个单独的类,该类为我要添加到数据库中的每个表扩展 SQLiteOpenHelper。

当我到达那条线时:

SQLiteHandlerDeliveryItem sqliteHandler = new 
    SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);

...然后按 F7 进入它,我到达类构造函数:

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)  

    super(context, DATABASE_NAME, factory, DATABASE_VERSION);

...但不是 onCreate 事件。

所以“难怪”我没有 deliveryitems 表,因为创建该表的代码永远不会到达;但为什么没有达到 - 我必须做什么才能达到?

【问题讨论】:

为什么每个表都有单独的SQLiteOpenHelper? SQLiteOpenHelper 处理一个数据库(即 android 文件系统上的一个 .db 文件),一个数据库可以包含多个表。 正如我所说,这是我读到的一些建议(但不记得在哪里) 即使是这种情况(假设我将所有 DB 创建代码移到一个类中),也没有调用“原始”类的 onCreate() 事件......似乎应该有一种方法可以告诉应用您有一个要处理的新表... 您需要完全卸载该应用,然后重试。 【参考方案1】:

    您必须在某个时候致电getWritableDatabase()getReadableDatabase()

    SQLiteHandlerDeliveryItem 的构造函数中,您必须调用 super(...)。

【讨论】:

我确实在创建或读取记录的每个方法中调用 getWritableDatabase(),如下所示: SQLiteDatabase db = this.getWritableDatabase(); ...但是你是说在构造函数中也需要这样的调用吗?【参考方案2】:

首先,方法SQLiteOpenHelper.onCreate() 不是构造函数。我不确定,但看起来您在问题中混淆了这两个概念。


现在开始尝试解决问题...

您的表似乎没有被创建。来自online help 为SQLiteOpenHelper.onCreate()

public abstract void onCreate (SQLiteDatabase db)

Called when the database is created for the first time. This is where the creation of tables and the initial population of the tables should happen.

所以对于典型的用例,这个方法只会被调用一次——当应用程序第一次安装时(第一次使用数据库)。此时,会调用onCreate() 来创建实际的表。但不是每次运行应用程序时都会调用它。

我的建议是检查这是否是问题 - 卸载应用程序,然后重试。在这个方法中放置一个断点以确保它确实可以运行。

有时会发生的情况是我们在应用程序上有一个数据库,然后更新内容以添加一个表,然后忘记卸载。另一种正确的做法是使用onUpgrade() 方法添加新表。

如果我误解了这个问题,请告诉我结果如何。

【讨论】:

“卸载应用程序”是指在模拟器上吗?如果是这样,我不知道这是怎么做到的......? 是的,如果你在模拟器上开发。我发现卸载的最简单方法是从命令行,假设模拟器连接到 adb:adb uninstall my.app.package.in.manifest(使用清单中声明的​​真实应用程序的真实包名称) 通过卸载删除数据库。下次运行需要数据库时,系统会找不到数据库,所以会为你调用onCreate() 它会调用所有的 onCreate() 吗?我有一个为每个表扩展 SQLiteOpenHelper 的类;我应该只有一个,其中所有的表都是在 onCreate() 中创建的吗? 嗯,有趣的问题。据我了解,对getWriteableDatabase() 的调用是触发onCreate()onUpgrade() 回调的原因。因此,通过将您的实现拆分为各种帮助程序,这可能是问题的原因。重新阅读我链接到的文档后,我认为最好将您的各种帮助程序类组合成一个,或者让它们都从创建所有表的公共基础继承。【参考方案3】:

正如我所希望的,它实际上是一个简单的解决方法:只需增加数据库版本的值。我在 p 上读到了这个。 262 O'Reilly 的“Programming Android”:

DATABASE_VERSION ...如果机器上的数据库版本低于DATABASE_VERSION,系统会运行你的onUpgrade方法将数据库升级到当前级别。

因此,您需要做的就是增加该数字:

    private static final int DATABASE_VERSION = 2; 
    // I changed it from "1" to "2", but you could change it to anything you want, I reckon (42, or 1776, or whatever).

增加版本值会导致 onUpgrade 运行,它会删除旧版本的数据库,然后调用 onCreate:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_DELIVERYITEMS);
    onCreate(db);

然后 onCreate() 事件就是这样做的——添加 DDL 来创建表。

这是在上下文中扩展 SQLiteOpenHelper 的类中的相关代码:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper 

private static final int DATABASE_VERSION = 2; 
private static final String DATABASE_NAME = "HHS.db";
private static final String TABLE_DELIVERYITEMS = "deliveryitems";

private static final String COLUMN_ID = "_id";
. . .
private static final String COLUMN_QTY = "quantity";

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)  

    super(context, DATABASE_NAME, factory, DATABASE_VERSION);


@Override
public void onCreate(SQLiteDatabase db) 
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
. . .
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " 
TEXT"
            + ")";
    db.execSQL(CREATE_DELIVERYITEMS_TABLE);


@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_DELIVERYITEMS);
    onCreate(db);

. . .

所以在我看来,拥有多个扩展 SQLiteOpenHelper 的类是一件好事,因为您可以将代码分开,而不是拥有一对巨大/巨大的 onUpgrade/onCreate spaghetti。

【讨论】:

以上是关于为啥我的构造函数没有被调用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C++ 中调用原始类型的构造函数是合法的?

为啥析构函数被调用两次而构造函数只被调用一次?

为啥构造函数被调用两次

为啥在我的代码中调用复制构造函数而不是移动构造函数?

为啥自动对象的析构函数被调用两次?

C++:调用无参数的构造函数为啥不加括号