Android 服务上下文不能用作ApplicationContext
我有一个WakefulBroadcastReceiver,它可以捕获Google云消息并启动IntentService来处理传入的消息。在IntentService中,我需要访问一个单例SQLite数据库。通常,我使用getApplicationContext()从活动中打开数据库,但从IntentService打开时,有时会抛出错误并给出NPE。如果应用程序已经后台运行了很长时间,当消息到达并且意图服务需要访问数据库时,它就会崩溃。如果应用程序最近刚刚被背景化,则DB是可访问的,并且 一切正常 我的问题是: 我应该传递什么上下文来确保数据库被正确打开?由于IntentService是Context的一个子类,我尝试只传入“this”,传入BaseContext()甚至ApplicationContext() 唤醒广播接收机 意向服务Android 服务上下文不能用作ApplicationContext,android,Android,我有一个WakefulBroadcastReceiver,它可以捕获Google云消息并启动IntentService来处理传入的消息。在IntentService中,我需要访问一个单例SQLite数据库。通常,我使用getApplicationContext()从活动中打开数据库,但从IntentService打开时,有时会抛出错误并给出NPE。如果应用程序已经后台运行了很长时间,当消息到达并且意图服务需要访问数据库时,它就会崩溃。如果应用程序最近刚刚被背景化,则DB是可访问的,并且 一切正常
公共类gcminentservice扩展了IntentService{
公共静态最终字符串TAG=“gcminentservice”;
公共静态最终整数通知_ID=1;
私人通知经理通知经理;
通知建筑商;
专用静态triDbAdapter mDbHelper;
私有最终静态字符串GAME_ID=“GAME_ID=”;
私有静态OnActiveGameNotifyListener侦听器=null;
私有静态int currentUserId;
私有静态字符串currentUserName;
公共GCMinentService(){
超级(“GCMinentService”);
mDbHelper=newTridBAdapter(this.getBaseContext());
mDbHelper.open();//您会遇到什么错误?您是否在任何地方关闭数据库?“如果应用程序已被后台关闭很长时间,当消息到达并且意图服务需要访问数据库时,它会崩溃”——很可能,您的进程在“很长时间”内终止。“我应该传递什么上下文以确保数据库被正确打开?”--我会使用getApplicationContext()
,以防任何SQLite*
类保留上下文,这样您就不会泄漏它。除此之外,我会指出“此处失败”“很好,我们真的需要堆栈跟踪。事实证明,我在哪里工作会有所不同。当我将数据库打开的内容放入构造函数时,如果应用程序不再运行且在“最近”中不可见,则在收到消息时失败。然而,如果我在onHandleIntent()中打开数据库并取出我需要的内容,然后一切都很好。我可以关闭后台应用程序,当接收者收到新的消息时,OnHandleContent似乎对上下文有不同的想法。知道为什么会这样吗?
@Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
Log.i("Receiver", "Getting Data: "+intent.toString());
setResultCode(Activity.RESULT_OK);
}
public class GcmIntentService extends IntentService {
public static final String TAG = "GcmIntentService";
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
private static triDbAdapter mDbHelper;
private final static String GAME_ID = "game_id=";
private static OnActiveGameNotifyListener listener = null;
private static int currentUserId;
private static String currentUserName;
public GcmIntentService() {
super("GcmIntentService");
mDbHelper = new triDbAdapter(this.getBaseContext());
mDbHelper.open(); // <-- Fails here
// depending on what the Intent contains, I need to send notifications that include
// various items from thte database when the notification is created. I need to know
// the opponent's name, the score, etc. so the notification can say "Joe scored..."
currentUserId = mDbHelper.getUserId();
currentUserName = mDbHelper.getName();
}
@Override
protected void onHandleIntent(Intent intent) {
<snip>
}
public static class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
public static DatabaseHelper getInstance(Context ctx) {
// Use the application context, which will ensure that you
// don't accidentally leak an Activity's context.
// See this article for more information: http://bit.ly/6LRzfx
if (mInstance == null) {
mInstance = new DatabaseHelper(ctx.getApplicationContext()); // <-- fail here
}
return mInstance;
}
/**
* Constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context ctx) {
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* Constructor - takes the context to allow the database to be
* opened/created
*
* @param ctx the Context within which to work
*/
public triDbAdapter(Context ctx) {
this.mCtx = ctx;
}
public triDbAdapter open() throws SQLException {
mDbHelper = DatabaseHelper.getInstance(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
12-17 12:26:28.254 6888-6888/com.ulsanonline.triominoes I/Receiver﹕ Getting Data: Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10 pkg=com.ulsanonline.triominoes cmp=com.ulsanonline.triominoes/.GcmIntentService (has extras) }
12-17 12:26:28.264 6888-6888/com.ulsanonline.triominoes E/GcmIntentService﹕ STACKTRACE: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:267)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:223)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
at com.ulsanonline.triominoes.triDbAdapter.open(triDbAdapter.java:430)
at com.ulsanonline.triominoes.GcmIntentService.<init>(GcmIntentService.java:57)
at java.lang.reflect.Constructor.newInstance(Native Method)
at java.lang.Class.newInstance(Class.java:1572)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2713)
at android.app.ActivityThread.access$1800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1361)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)