好的,所以我创建了这个从游戏的AlarmManager启动的服务类。目的是以一定的时间间隔启动此服务,并检查游戏的远程玩家是否已经玩过。它唤醒,打开一个数据库并查询游戏列表,然后检查服务器的每个游戏的状态,如果一个游戏进入状态栏有一些好东西。这一切都很好,花花公子,但这个小小的服务不断抛出一个例外 “从未在数据库上显式调用Sqlite close()” 但我想我在下面的代码中关闭了查询和数据库。 我错过了什么?我已经阅读了几十个stackoverflow线程,所有人都说我应该关闭数据库,我在onDestroy()和unBind()中关闭它,但我仍然得到这个异常。
public class WakeCheck extends Service {
private triDbAdapter mDbHelper;
@Override
public void onCreate() {
// TODO Auto-generated method stub
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mDbHelper.close();
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
//Toast.makeText(this, "MyAlarmService.onStart()", Toast.LENGTH_LONG).show();
Cursor c;
triDbAdapter mDbHelper = new triDbAdapter(this.getApplicationContext());
mDbHelper.open();
ServerCommunication sComm = new ServerCommunication(this.getApplicationContext());
c = mDbHelper.fetchAllGames();
if (c.getCount() > 0){
c.moveToFirst();
do {
int game_id = c.getInt(c.getColumnIndex(mDbHelper.KEY_ID));
try {
if ( sComm.checkplay(game_id, 0) ) {
//notify user - communicate that there has been a new play
String ns = this.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
int icon = R.drawable.status_icon;
CharSequence tickerText = "TriOminoes!";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
Context context = getApplicationContext();
CharSequence contentTitle = "TriOminoes";
String name = c.getString(c.getColumnIndex(mDbHelper.KEY_OPPNM));
int tscore = c.getInt(c.getColumnIndex(mDbHelper.KEY_TSCORE));
CharSequence contentText = name + " played for " + tscore +
" points. It's Your Turn!";
Intent notificationIntent = new Intent(this, SelectGames.class);
if (Settings.AlarmVibrate) {
notification.flags |= Notification.DEFAULT_VIBRATE;
}
if (Settings.AlarmSound) {
notification.flags |= Notification.DEFAULT_SOUND;
}
notification.flags |= Notification.FLAG_AUTO_CANCEL;
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotificationManager.notify(1, notification);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
c.close();
mDbHelper.close();
Log.d("WAKE", "Something went wrong in Communications");
}
} while (c.moveToNext());
}
c.close();
mDbHelper.close();
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
mDbHelper.close();
return super.onUnbind(intent);
}
}
package com.ulsanonline.triominoes;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class triDbAdapter {
/**
* Database creation sql statement
*/
private static final String DATABASE_CREATE1 =
"CREATE TABLE player (" +
"_id INTEGER ," +
"username TEXT not null," +
"alarmtime INTEGER not null," +
"alarmvibrate INTEGER not null," +
"alarmsound INTEGER not null," +
"showboard INTEGER not null," +
"showtouch INTEGER not null," +
"showcenters INTEGER not null );" ;
private static final String DATABASE_CREATE2 =
"CREATE TABLE games (" +
"_id INTEGER, " +
"myScore INTEGER, " +
"username TEXT, " +
"myId INTEGER, " +
"opponentId INTEGER, " +
"opponentName TEXT, " +
"opponentScore INTEGER, " +
"last_tile INTEGER, " +
"last_tile_name TEXT, " +
"row INTEGER, " +
"col INTEGER, " + // grid location & rotation, face tells
"rotate INTEGER, " + // all scoring information
"face_dn INTEGER, " + // 1=face down, 1=face pointing up
"who INTEGER, " + // who played it, user id
"tscore INTEGER, " + // just score per that turn
"remain INTEGER, " + // tiles remain(1) or not (0)
"complete INTEGER, " + // game over(1) or keep playing(0)
"last_played INTEGER); " ;
private static final String DATABASE_CREATE3 =
"CREATE TABLE tilesPlayed (" +
"_id INTEGER REFERENCES games(_id) on UPDATE CASCADE," +
"row INTEGER, " +
"col INTEGER, " + // grid location & rotation, face tells
"rotate INTEGER, " + // all scoring information
"face_dn INTEGER, " + // 1=face down, 0=face pointing up
"tile INTEGER); " ;
private static final String DATABASE_CREATE4 =
"CREATE TABLE myTiles (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"game_id INTEGER REFERENCES games(_id) on UPDATE CASCADE," +
"tile INTEGER); " ;
private static final String DATABASE_NAME = "triominoes.db";
private static final String PLAYER_TABLE = "player";
private static final String GAMES_TABLE = "games";
private static final String TILES_TABLE = "tilesPlayed";
private static final String MYTILES_TABLE = "myTiles";
private static final int DATABASE_VERSION = 1;
public static final String KEY_NAME = "username";
public static final String KEY_ID = "_id";
public static final String KEY_GAME = "game_id";
public static final String KEY_USERID = "myId";
public static final String KEY_TILE = "tile";
public static final String KEY_LASTT = "last_tile";
public static final String KEY_LASTN = "last_tile_name";
public static final String KEY_SCORE = "myScore";
public static final String KEY_OPP = "opponentId";
public static final String KEY_OPPSC = "opponentScore";
public static final String KEY_OPPNM = "opponentName";
public static final String KEY_DATE = "last_played";
public static final String KEY_ROW = "row";
public static final String KEY_COL = "col";
public static final String KEY_ROT = "rotate";
public static final String KEY_FACEDN = "face_dn";
public static final String KEY_TSCORE = "tscore";
public static final String KEY_WHO = "who";
public static final String KEY_REMAIN = "remain";
public static final String KEY_COMPLETE = "complete";
public static final String KEY_ATIME = "alarmtime";
public static final String KEY_AVIBE = "alarmvibrate";
public static final String KEY_ASOUND = "alarmsound";
public static final String KEY_SHOWBOARD = "showboard";
public static final String KEY_SHOWTOUCH = "showtouch";
public static final String KEY_SHOWCENTERS = "showcenters";
private static final String TAG = "dbAdapter";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context mCtx;
private static final String DATE_FORMAT_NOW = "yyyy-MM-dd hh:mm";
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE1);
db.execSQL(DATABASE_CREATE2);
db.execSQL(DATABASE_CREATE3);
db.execSQL(DATABASE_CREATE4);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ". ALL data will be destroyed");
db.execSQL("DROP TABLE IF EXISTS user");
db.execSQL("DROP TABLE IF EXISTS games");
db.execSQL("DROP TABLE IF EXISTS tilesPlayed");
onCreate(db);
}
}
/**
* 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;
}
/**
* Open the student database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
public triDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
/**
* createUserProfile inserts the local username and server-generated userid, _ID , into the database.
* @param username the name of the local user
* @param _id - index in player table
* @return
*/
public long createUserProfile(String username, int _id) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, username);
initialValues.put(KEY_ID, _id);
initialValues.put(KEY_ATIME,Settings.AlarmInterval);
initialValues.put(KEY_AVIBE,Settings.AlarmVibrate?1:0);
initialValues.put(KEY_ASOUND,Settings.AlarmSound?1:0);
initialValues.put(KEY_SHOWBOARD,Settings.ShowBoard?1:0);
initialValues.put(KEY_SHOWTOUCH, Settings.ShowTouch?1:0);
initialValues.put(KEY_SHOWCENTERS,Settings.ShowCenters?1:0);
return mDb.insertOrThrow(PLAYER_TABLE, null, initialValues);
}
/* initially upon startup, get what's in the dB */
public void getSettings() {
Log.w(TAG, "getting settings");
String sql = "SELECT * FROM player";
Cursor c = mDb.rawQuery(sql, null);
c.moveToLast();
Settings.AlarmInterval = c.getInt(c.getColumnIndex(KEY_ATIME));
Settings.AlarmVibrate = (c.getInt(c.getColumnIndex(KEY_AVIBE)) == 1);
Settings.AlarmSound = (c.getInt(c.getColumnIndex(KEY_ASOUND)) == 1);
Settings.ShowBoard = (c.getInt(c.getColumnIndex(KEY_SHOWBOARD)) == 1);
Settings.ShowTouch = (c.getInt(c.getColumnIndex(KEY_SHOWTOUCH)) == 1);
Settings.ShowCenters = (c.getInt(c.getColumnIndex(KEY_SHOWCENTERS)) == 1);
c.close();
}
/**
* updateLastPlayed - updates the game with the last tile played
* @param game_id
* @param user_id - local user's id
* @param who - index to which player made the move
* @param score - score to update
* @param tile - tile number
* @param name - name of the tile, used for rebuilding faces
* @param row - row in the grid the tile was placed
* @param col - col in the grid the tile was placed
* @param rot - current rotation of the tile when placed
* @param facedn - orientation of the tile as placed
* @param tScore - score for just this turn
* @param remain - 0 = no remaining tiles, 1 = there are tiles left.
* @param complete - 0 = not complete, keep playing; 1=game over
* @return
*/
public void UpdateLastPlayed(int game_id, int user_id, int who, int score, int tile, String name,
int row, int col, int rot, Boolean facedn, int tScore, int remain, int complete) {
ContentValues args = new ContentValues();
ContentValues InitialValues = new ContentValues();
int newscore;
args.put(KEY_ID, game_id);
args.put(KEY_WHO, who);
args.put(KEY_LASTT, tile);
args.put(KEY_LASTN, name);
args.put(KEY_ROW, row);
args.put(KEY_COL, col);
args.put(KEY_ROT, rot);
int face = (facedn)? 1 :0;
args.put(KEY_FACEDN, face);
args.put(KEY_TSCORE, tScore);
Calendar cal = Calendar.getInstance();
args.put(KEY_DATE, cal.getTimeInMillis());
if (user_id == who) { // last player was me
if (score == -5) {
newscore = score + getScore(game_id, user_id); // adjust myscore
args.put(KEY_SCORE, newscore);
args.put(KEY_LASTT, -1);
}
else
args.put(KEY_SCORE, score);
}
else { // last player was opponent
if (score == -5) {
newscore = score + getScore(game_id, who); // adjust opponent's
args.put(KEY_OPPSC, newscore);
args.put(KEY_LASTT, -1);
}
else
args.put(KEY_OPPSC, score);
}
mDb.update(GAMES_TABLE, args, KEY_ID + "=" + game_id, null); // this table goes to
// and comes from the main server
InitialValues.put(KEY_ID, game_id);
InitialValues.put(KEY_ROW, row);
InitialValues.put(KEY_COL, col);
InitialValues.put(KEY_ROT, rot);
InitialValues.put(KEY_TILE, tile);
InitialValues.put(KEY_FACEDN, face);
mDb.insert(TILES_TABLE, null, InitialValues); // this table is local only
}
public Cursor fetchLastPlayed(int game_id){
Cursor c;
return mDb.query(GAMES_TABLE, new String[] {KEY_ID, KEY_SCORE,
KEY_OPP, KEY_OPPSC, KEY_OPPNM, KEY_DATE, KEY_LASTT, KEY_LASTN, KEY_TSCORE, KEY_WHO},
KEY_ID + "=" + game_id, null, null, null, KEY_DATE);
}
}
答案 0 :(得分:0)
在一个类似的问题中,建议在调用onDestroy的超级函数之前执行close操作。