Android中调用Database构造器的getWritableDatabase()致App崩溃
问题描述
我的Android应用在Database类中调用getWritableDatabase()方法时持续崩溃。移除getWritableDatabase()和getReadableDatabase()后应用可正常运行,但用户表因onCreate方法未执行而无法创建,该如何解决?
相关代码
MainActivity.java
package com.google.assignment_lab; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import com.google.assignment_lab.database.UserTable; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ Button mainRegisterButton, mainLoginButton; UserTable userTable = new UserTable(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mainRegisterButton = findViewById(R.id.mainRegisterButton); mainLoginButton = findViewById(R.id.mainLoginButton); mainRegisterButton.setOnClickListener(this); mainLoginButton.setOnClickListener(this); } @Override public void onClick(View view) { Intent intent; if(view == mainRegisterButton){ intent = new Intent(this, RegisterActivity.class); startActivity(intent); } if(view == mainLoginButton){ intent = new Intent(this, LoginActivity.class); startActivity(intent); } } }
UserTable.java
package com.google.assignment_lab.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import androidx.annotation.Nullable; public class UserTable extends SQLiteOpenHelper { public static String DATABASE_NAME = "MyData"; public static int DATABASE_VERSION = 1; public static String TABLE_NAME = "Users"; Context context; SQLiteDatabase database; public UserTable(@Nullable Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); this.context = context; database = getWritableDatabase(); database = getReadableDatabase(); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { String query = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (username TEXT, password TEXT, email TEXT, phone TEXT)"; sqLiteDatabase.execSQL(query); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
错误日志
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.google.assignment_lab, PID: 8732 java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.google.assignment_lab/com.google.assignment_lab.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File android.content.Context.getDatabasePath(java.lang.String)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3551) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3782) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File android.content.Context.getDatabasePath(java.lang.String)' on a null object reference at android.content.ContextWrapper.getDatabasePath(ContextWrapper.java:380) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:370) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316) at com.google.assignment_lab.database.UserTable.<init>(UserTable.java:20) at com.google.assignment_lab.MainActivity.<init>(MainActivity.java:16) at java.lang.Class.newInstance(Native Method) at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95) at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45) at android.app.Instrumentation.newActivity(Instrumentation.java:1339) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3538) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3782) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
解决方案
问题根源
- Context为空导致崩溃:在
MainActivity中,直接在成员变量位置初始化UserTable userTable = new UserTable(this);时,Activity还未完成初始化,this引用的Context无效(为空),调用getWritableDatabase()触发空指针异常。 - 初始化时机错误:
SQLiteOpenHelper的getWritableDatabase()/getReadableDatabase()不需要在构造方法中调用,首次访问数据库时系统会自动调用这些方法,同时触发onCreate()创建表。
修改步骤
1. 调整MainActivity中UserTable的初始化时机
将UserTable的初始化移到onCreate()方法内部,此时Activity的Context已就绪:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ Button mainRegisterButton, mainLoginButton; UserTable userTable; // 先声明,不初始化 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); userTable = new UserTable(this); // 在这里初始化,Context已有效 mainRegisterButton = findViewById(R.id.mainRegisterButton); mainLoginButton = findViewById(R.id.mainLoginButton); mainRegisterButton.setOnClickListener(this); mainLoginButton.setOnClickListener(this); } // ... 其他代码不变 }
2. 修正UserTable的构造方法
移除构造方法中对getWritableDatabase()和getReadableDatabase()的调用,改为在需要操作数据库时获取实例:
public class UserTable extends SQLiteOpenHelper { public static String DATABASE_NAME = "MyData"; public static int DATABASE_VERSION = 1; public static String TABLE_NAME = "Users"; Context context; SQLiteDatabase database; public UserTable(@Nullable Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); this.context = context; // 移除这里的getWritableDatabase()和getReadableDatabase()调用 } // 添加获取数据库实例的方法,在需要时调用 public SQLiteDatabase getDatabase() { if (database == null || !database.isOpen()) { database = getWritableDatabase(); } return database; } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { String query = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (username TEXT, password TEXT, email TEXT, phone TEXT)"; sqLiteDatabase.execSQL(query); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { // 数据库升级时的处理逻辑,比如删除旧表重建 sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(sqLiteDatabase); } }
3. 触发表创建
当需要操作数据库(比如注册用户)时,调用getDatabase()方法,系统会自动检查数据库是否存在,不存在则创建并执行onCreate():
// 示例:在RegisterActivity中插入数据 UserTable userTable = new UserTable(this); SQLiteDatabase db = userTable.getDatabase(); ContentValues values = new ContentValues(); values.put("username", "test"); values.put("password", "123456"); db.insert(UserTable.TABLE_NAME, null, values);
额外建议
- 添加主键:给
Users表增加主键字段(如id INTEGER PRIMARY KEY AUTOINCREMENT),避免数据重复,提升查询效率。 - 使用ContentValues:插入/更新数据时用
ContentValues替代直接拼接SQL,避免SQL注入风险。
内容的提问来源于stack exchange,提问作者iBRabbit
相关产品推荐
相关产品推荐

