I'm developing an Android 2.2 application.
I want to catch and re throw the same exception. I want to do this because I have to close a cursor before exit the method (a finally statement, isn't it?)
Can I do that? How?
Thanks
If this is just to close the cursor correctly, you can do a try...finally without a catch. That would be something like that :
Cursor cursor = null;
try {
// initialize and do things with the cursor
} finally {
if (cursor != null) {
cursor.close();
}
}
Alternatively, if you're in an activity, you can use startManagingQuery; which will take care of your cursor lifecycle depending on the activity lifecycle.
Without discussing if this is a good practice you can do this:
throw new YourException();
Related
I need save some objects in DB. I'm using this code in my Dao class.
public void saveActions(List<Action> actionList) throws SQLException {
for (Action action : actionList) {
createOrUpdate(action);
}
}
And sometimes I have CursorWindowAllocationException in createOrUpdate() function.
Does anyone have solution of this problem?
If you look up the source of CursorWindowAllocationException it reads:
This exception is thrown when a CursorWindow couldn't be allocated,
most probably due to memory not being available.
If you follow the stack, you'll see that the call com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong is creating a cursor for every createOrUpdate call.
So what's likely happening here is that there are too many Cursors being created before the memory is freed.
You should execute these calls in a transaction, or better yet, use batch tasks. E.g.
actionDao.callBatchTasks(new Callable<Void>() {
public Void call() throws SQLException {
for (Action action : actionList) {
actionDao.createOrUpdate(action);
}
return null;
}
});
You must call cursor.close(); in finally { } block when you do not need cursor anymore. Cursor allocation will fail without calling close() on cursor after some time when system resources for cursor allocation will be not available.
I regularly get reports from users with this error.
Unable to getWritableDatabase.
try {
//dbOpenHelper is a standard SQLiteOpenHelper
dbOpenHelper.getWritableDatabase();
} catch (Exception e) {
//unable to connect to database.
return;
}
This is only happening sometimes. What could it be? Any fixes or workarounds?
There isn't much to go off of, need to see your LogCat to help any further. Are you closing your dbOpenHelper?
Here is a quick example:
public DatabaseControl open() throws SQLiteException {
dbHelper = new DatabaseHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
public void close() {
dbHelper.close();
}
Edit 1 - Again, this is my best guess since a LogCat is unobtainable. (In the end it might be in your best interest to run this application on your Dev device and try to replicate and then get your full LogCat ouput)
It sounds to me like the Activity that has the DB is being accessed and processed fine with a proper close(), but in some instances maybe instead of running through that Activity the user presses the back button where onBackPressed() maybe should have a DB close() call?
In other words, to troubleshoot, I would #Override Acitivity lifecycle methods: onPause(), onResume(), onStart(), onStop(), and onDestroy() as well as onBackPressed() simply adding the DB close() and test your application then. If you do this then, I would think that, any returning lifecycle, such as onStart() or onResume() should contain the DB getWritableDatabase()
According to the documentation (see below), there really is no way to get Unable to getWritableDatabase unless the database was not closed properly. Also make sure that if you change anything in your SQlite code / structure that you need to update the DB Version #.
public SQLiteDatabase getWritableDatabase ()
Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called.
Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.) Errors such as bad permissions or a full disk may cause this method to fail, but future attempts may succeed if the problem is fixed.
Edit 2
I should have shown how I called on the open() and close() methods the first time. Sorry about that. You don't need the DatabaseHelper open unless you are retrieving or posting to it, so immediately after processing close it until the next time you need it. Same goes for the Cursor, etc
Below is how I query the database, I open everything, retrieve the information, process the information and then immediately close everything. See below:
DatabaseControl control = new DatabaseControl(this);
try {
database = (new DatabaseHelper(this)).getWritableDatabase();
DatabaseControl control = new DatabaseControl(DbTestDisplay.this);
control.open();
control.fetchAllItems();
Cursor c = database.query(GlobalDBVars.TABLE_NAME, null, null, null, null, null, null, null);
c.moveToFirst();
int initialCount = c.getCount();
Log.i("cursor", "Initial Count = " + initialCount);
List<Integer> x = new ArrayList<Integer>();
if (initialCount > 0) {
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
/* Process everything here */
} /* End for loop */
} /* End if statement */
control.close();
c.close();
database.close();
} catch (SQLException sqe) {
Log.e("fetchAllItems", "FAILED: " + sqe.getMessage() + " allData = ");
}
Try getReadableDatabase() instead of getWritableDatabase()
try {
//dbOpenHelper is a standard SQLiteOpenHelper
dbOpenHelper.getReadableDatabase();
} catch (Exception e) {
//unable to connect to database.
return;
}
I have an application with database and content provider. In order to display some data from the database in ListView's, I useLoaderManager.
It's simple and works perfectly well in most of case.
But, when to many change notifications from the content provider arrives, the UI is totally overloaded. It happens when there are many inserts, about 15 per seconds, on the uri observed by the cursor. I think even if the cursor load is in background, the large amount of bindView() calls is enough to overload the UI.
So I would like, in some case as above, limit the number of loads (and so onLoadFinished() calls) to something like one per seconds.
Is there a way to achieve that with LoaderManager ? I tried to play with startLoading() and stopLoading but without success, and anyway it's not recommended.
Or maybe I have to manage a Loader by myself, but I will be surprised if this situation is not common.
Thanks for your advice.
EDIT
I actually missed a method... setUpdateThrottle(long delayMS) do exactly what I want.
You can override applyBatch(...) in your content provider and do something like this:
#Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
results[i] = operations.get(i).apply(this, results, i);
}
db.setTransactionSuccessful();
return results;
} finally {
db.endTransaction();
}
}
You can use this to batch your inserts into one transaction so notification only happens once.
I have programmed an App which I wanted to test on higher API-Levels for checking the compatibility. For API 10 (2.3.3) there were no problems, but as soon as I ran my app on API 15 (4.0.3) I got an NullPointerException in one of my SurfaceViews when I was quitting the Activity.
I have to say that i solved the problem, but i can't figure out why the Exception occured actually. So maybe you could tell me.
Here is the code that worked for me on API 10:
It's the common structure of the run()-method.
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
doDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
On API 15 when quitting the activity:
The Exception accoured when the doDraw()-method tried to write on "c". I checked c and found out it was null, so no surprise I got an Exception. I also checked mThreadActive and found out that although i set it to false, the while-loop still triggers.
Here is the Code sample:
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
if(!mThreadActive) // so it really is!
Log.d("Thread", "mThreadActive is false!");
if(c == null) // so it is too!
Log.d("Thread", "c is null!");
doDraw(c); // error
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
I can imagine why mThreadActive becomes false AFTER being checked by the while-statement, but I can't figure out why "c" is null after mSurfaceHolder.lockCanvas(null). It seems that the code is not running sequential.
Well, the solution would be checking c != null before drawing on it:
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
if(c != null) // prevent drawing on c if c doesnt exist.
doDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
So why do i get an Exception on API 15 whereas it works fine on API 10?
Another funny thing is, that i have other SurfaceViews with the same structure, but in comparison to this one, they work all fine!
Why is the code not running sequential? Is it because I am testing on an emulator (which is pretty laggy)?
Thank You.
You mention that your while() loop seems to proceed despite mThreadActive being false. Is mThreadActive marked volatile? It may need to be.
Also, lockCanvas(null) smells. Since we can't see the rest of your code, it's not exactly clear what you're trying to do. What does the API say about passing a null in to lockCanvas? (And are we talking about a raw instance of SurfaceHolder or a subclass?)
Note that the API spec for SurfaceHolder.lockCanvas() indicates it can return null:
A null is returned if the surface has not been created or otherwise cannot be edited. You will usually need to implement Callback.surfaceCreated to find out when the Surface is available for use.
From reading the API, it looks like you should be implementing the SurfaceHolder.Callback interface and responding to the surfaceCreated() event, which is really the event that tells you you're ready to go in writing to the canvas.
I also get a very similar issue when calling:
public void surfaceDestroyed(SurfaceHolder holder) {
isAttached = false;
this.drawthread = null;
}
When exiting my application to stop the drawing thread - the while boolean I use isAttached is false, but the drawing code within that while loop still executes - giving a nullexception on exit. Not a show stopper but something I really want to fix. Was considering a if (canvas != null) sort of solution too but I thought there must be a better way.
Now here's the weird thing with this error - it only happens some of the time - and happens less on faster devices. The problem the way I see it is that holder.lockCanvas() is returning null - meaning the holder is being destroyed before the boolean is set to false and the while has a chance to stop executing the Thread. A thread race problem?
Found a possible solution that used Thread.join() before making the while boolean null, but with holder.lockCanvas() returning null, the holder is being destroyed before the drawing is finished, so it's kind of pointless and still caused a nullexception.
Only other solution I can think of is to override the back button method and force the while bool to null before destroying the surfaceview(if that's poss), but I think if (canvas != null) is probably cleaner. Any other ideas out there keen to hear!
edit: haven't tested elsewhere, but I get the error using API 17
In my application, I am using an AsyncTask to write some data to a database in a transaction. This database is also accessed from the UI thread. While looking through the available database methods, I came across yieldIfContendedSafely(). It seems like this method should be used for any cases where a transaction is being made from a separate thread. But there is hardly any documentation on this method other than the following:
Temporarily end the transaction to let other threads run. The transaction is assumed to be successful so far. Do not call setTransactionSuccessful before calling this. When this returns a new transaction will have been created but not marked as successful. This assumes that there are no nested transactions (beginTransaction has only been called once) and will throw an exception if that is not the case.
Here is how I would assume that you would use this method from a thread:
try {
db.beginTransaction();
//insert some stuff into the database here
...
// is this how you use this method?
boolean yielded = db.yieldIfContendedSafely();
if (yielded) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
db.setTransactionSuccessful();
} catch (SQLException e) {
return false;
} finally {
db.endTransaction();
db.close();
}
Is this the correct way to use this method? Is it alright to use db.yieldIfContendedSafely() more than once in the same transaction, in between multiple writes to different tables in the database? Any suggestions?
Pulling some example code from the Android libraries it seems much simpler to use than that...
This is taken from com.android.providers.calendar.SQLiteContentProvider.java
#Override
public int bulkInsert(Uri uri, ContentValues[] values) {
int numValues = values.length;
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransactionWithListener(this);
try {
for (int i = 0; i < numValues; i++) {
Uri result = insertInTransaction(uri, values[i]);
if (result != null) {
mNotifyChange = true;
}
mDb.yieldIfContendedSafely();
}
mDb.setTransactionSuccessful();
} finally {
mDb.endTransaction();
}
onEndTransaction();
return numValues;
}
Also looking into the source code for the function itself, it seems that, if yielded, the call will defer execution of your thread for a short period in any case.