I have the following class which is responsible to fetch non synced receipts from the receipts table and upload them to the server, the following function as of now just iterates through the cursor result set:
public class MidnightUpload {
public static void checkLocalAndUpload(final Context ctx) {
Cursor cursor = DatabaseHandler
.getInstance(ctx)
.getReadableDatabase()
.query(Receipt.TABLE_NAME, Receipt.FIELDS,
Receipt.WEB_RECEIPT_ID + " IS ?", new String[]{"dummy"},
null, null,
null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
Log.d("_id", cursor.getString(cursor.getColumnIndexOrThrow("_id")));
Log.d("receipt_id", cursor.getString(cursor.getColumnIndexOrThrow("receipt_id")));
Log.d("web_receipt_id", cursor.getString(cursor.getColumnIndexOrThrow("web_receipt_id")));
Log.d("receipt_name", cursor.getString(cursor.getColumnIndexOrThrow("receipt_name")));
// Log.d("image", cursor.getString(cursor.getColumnIndexOrThrow("image")));
Log.d("date_added", cursor.getString(cursor.getColumnIndexOrThrow("date_added")));
Log.d("status", cursor.getString(cursor.getColumnIndexOrThrow("status")));
Log.d("currency", cursor.getString(cursor.getColumnIndexOrThrow("currency")));
Log.d("category", cursor.getString(cursor.getColumnIndexOrThrow("category")));
Log.d("sub_category", cursor.getString(cursor.getColumnIndexOrThrow("sub_category")));
Log.d("payment", cursor.getString(cursor.getColumnIndexOrThrow("payment")));
Log.d("invoice", cursor.getString(cursor.getColumnIndexOrThrow("invoice")));
Log.d("custom_field", cursor.getString(cursor.getColumnIndexOrThrow("custom_field")));
Log.d("organization", cursor.getString(cursor.getColumnIndexOrThrow("organization")));
Log.d("person", cursor.getString(cursor.getColumnIndexOrThrow("person")));
} while (cursor.moveToNext());
}
}
}
I am aware that I can start multiple Async Tasks using:
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
The above method I am planning to call from an IntentService. So here are my confusions:
1) Will the do while loop wait till control has returned from asyncTask for the next iteration?
2)Will using and spawning multiple threads within an intentService disrupt my program?
3) Am I better off using Runnable r = new Runnable() than AsyncTask - as I dont intend any UI operation?
Related
I created a database with a table named flagTable, this table only has two fields, which are id(auto increment) and an integer field. Next, in my program, I have a button that will trigger a thread to start. When the thread is starting, it constantly retrieve data from database, and check for the for the value, if the value is equal to one then it will trigger another new Thread, something like this:
private class statusOfStrummingInAnotherDevice extends Thread {
int value;
public void run() {
try{
while(true){
try{
if(flagCursor == null){
flagCursor = cdb1.getFlagAll();
}
}catch(Exception e){break;}
try{
Log.i("MAIN3ACTIVITY","getting status");
int size = cdb1.getSize(flagCursor);
Log.i("MAIN3ACTIVITY","SIZE is" + String.valueOf(xyz));
for(int i = 0 ; i < size ; i++){
flagCursor.moveToPosition(i);
Log.i("MAIN3ACTIVITY","getting status jkasdfasdf");
value = cdb1.getFlag();
if(value == 1){
Log.i("FLAGCURSOR=====>>>>","Succesful");
releasingNotes = new ReleasingNotes(IntendedChord);
releasingNotes.start();
//break;
}
cdb1.updateFlag(0);
Log.i("FLAGCURSOR=====>>>>",String.valueOf(value));
}
flagCursor = null;
}catch(Exception e){break;}
Log.i("MAIN3ACTIVITY","thread is sleeping");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}catch(Exception e){
}
}
}
In the meantime, the data that were retrieved from the database is using this function:
public Cursor getFlagAll(){
return getReadableDatabase().rawQuery(
"SELECT _ID, flag from flagTable", null);
}
And, the data that were updated to the database through this method:
public int updateFlag(int i) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("flag",i);
return db.update("flagTable" , contentValues , "_ID" + "= ?",new String[]{String.valueOf(1)});
}
Now, above codes will give no error, however, the data that were retrieved from the database is always 1, it keeps trigger a new function. In my above codes, I stated if the value is equal to 1, then the current thread will trigger a new thread to start, When its finished, the program will update the current data to 0. So that, the next round of the infinite loop can stop triggering new thread until a the conditon is met. What is problem overhere? did my codes really updated the new value? or I need to referesh the database every time I updated a new value.
Use Listeners to your database.
use SQLiteTransactionListener and do your things in onCommit()
Some guide in details here :
https://developer.android.com/reference/android/database/sqlite/SQLiteTransactionListener.html and
http://www.programcreek.com/java-api-examples/index.php?api=android.database.sqlite.SQLiteTransactionListener
I'm using a cursor in a while loop to execute multiple queries on my database, so each time Content Resolver returns new cursor object instance. I am uncertain about proper way I should reuse the cursor during each iteration of the loop:
Close it once, after all operations being performed
Cursor c;
try {
while(condition) {
c = Context.getContentResolver().query(...);
// fetching values
}
} finally {
if (c != null) {
c.close()
}
}
Close it at the end of each iteration
Cursor c;
try {
while(condition) {
c = Context.getContentResolver().query(...);
// fetching values
if (c != null) {
c.close()
}
}
} finally {
if (c != null) {
c.close()
}
}
Create new cursor variable inside while loop
while(condition) {
Cursor c = Context.getContentResolver().query(...);
try {
// fetching values
} finally {
if (c != null) {
c.close()
}
}
}
?
Case 1 is bad since you're assigning a new cursor in your loop and you only get to close the last assigned cursor.
Case 2 and 3 are very similar but I prefer case 3 because with case 2 you can break out of your loop unexpectedly but with case 3 you keep the cursor in the loop scope and the loop can keep running.
In your examples the third variant is most useful because you open-close the same Cursor object in each loop. You lost nothing: niether memory leaks no app+db crashes.
My app makes use of SQLiteDatabase to save two arraylists to separate tables.
I have noticed that since implementing the database, whenever it updates the database (involving dropping the tables, recreating them, then populating them with the arraylists) the app briefly freezes and I get the following message in logcat:
"I/Choreographer: Skipped 236 frames! The application may be doing too much work on its main thread."
To confirm it was the updating, I removed the code used to update the database. Upon doing that, I no longer got the warning message, and my app didn't freeze.
This is the code inside my custom DB helper, which extends SQLiteOpenHelper, that is used to update the table:
public void insertData(ArrayList<SavedWifiHotspot> hotspots, ArrayList<MarkerOptions> markers) {
Log.d("insert LocationsDB", "Data inserted");
SQLiteDatabase db = this.getWritableDatabase();
ContentValues hotspotValues = new ContentValues();
ContentValues markerValues = new ContentValues();
for(SavedWifiHotspot hotspot : hotspots) {
hotspotValues.put("Ssid", hotspot.getSsid());
hotspotValues.put("Password", hotspot.getPassword());
hotspotValues.put("LocationName", hotspot.getHotspotLoc());
hotspotValues.put("Lat", hotspot.getLatitude());
hotspotValues.put("Lng", hotspot.getLongitude());
db.insert(HOTSPOT_TABLE_NAME, null, hotspotValues);
}
for(MarkerOptions marker : markers) {
markerValues.put("LocationName", marker.getTitle());
markerValues.put("Lat", marker.getPosition().latitude);
markerValues.put("Lng", marker.getPosition().longitude);
db.insert(LOCATION_TABLE_NAME, null, markerValues);
}
}
And this is the code used to clear the tables before they are updated:
public void clearData() {
Log.d("clear LocationsDB", "Tables cleared");
SQLiteDatabase db=this.getWritableDatabase();
String dropHSTable = "DROP TABLE IF EXISTS "
+ HOTSPOT_TABLE_NAME + ";";
String dropLocTable = "DROP TABLE IF EXISTS "
+ LOCATION_TABLE_NAME + ";";
db.execSQL(dropHSTable);
db.execSQL(dropLocTable);
createTables(db);
}
How should I go about updating my database in the background? I've read about threads, should I use a thread to do this?
Edit: This is the error, in reference to my comment.
FATAL EXCEPTION: AsyncTask #5
Process: com1032.cw2.fm00232.fm00232_assignment2, PID: 8830
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExe
at java.lang.Thread.run(Thread.java:818)cutor$Worker.run(ThreadPoolExecutor.java:587)
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
at com1032.cw2.fm00232.fm00232_assignment2.LocationsDB$3.doInBackground(LocationsDB.java:124)
at at com1032.cw2.fm00232.fm00232_assignment2.LocationsDB$3.doInBackground(LocationsDB.java:119)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
For reference, line 124 is:
for(MarkerOptions marker: markers[0]) {
And line 119 is:
new AsyncTask<ArrayList<MarkerOptions>, Void, Void>() {
Edit2: Fixed the above problem. My app was invoking the insert data method using empty lists. So I've added .empty check before the insert data method.
An async task sounds like a good idea.
public void insertData(ArrayList<SavedWifiHotspot> hotspots, ArrayList<MarkerOptions> markers) {
Log.d("insert LocationsDB", "Data inserted");
new AsyncTask<ArrayList<SavedWifiHotspot>, Void, Void>() {
#Override
protected Void doInBackground(ArrayList<SavedWifiHotspot>... hotspots) {
ContentValues hotspotValues = new ContentValues();
for(SavedWifiHotspot hotspot : hotspots[0]) {
hotspotValues.put("Ssid", hotspot.getSsid());
hotspotValues.put("Password", hotspot.getPassword());
hotspotValues.put("LocationName", hotspot.getHotspotLoc());
hotspotValues.put("Lat", hotspot.getLatitude());
hotspotValues.put("Lng", hotspot.getLongitude());
db.insert(HOTSPOT_TABLE_NAME, null, hotspotValues);
}
}
}.execute(hotspots);
new AsyncTask<ArrayList<MarkerOptions>, Void, Void>() {
#Override
protected Void doInBackground(ArrayList<MarkerOptions>... options) {
ContentValues hotspotValues = new ContentValues();
for(MarkerOptions marker: options[0]) {
markerValues.put("LocationName", marker.getTitle());
markerValues.put("Lat", marker.getPosition().latitude);
markerValues.put("Lng", marker.getPosition().longitude);
db.insert(LOCATION_TABLE_NAME, null, markerValues);
}
}
}.execute(options);
}
public void clearData() {
Log.d("clear LocationsDB", "Tables cleared");
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
SQLiteDatabase db=this.getWritableDatabase();
String dropHSTable = "DROP TABLE IF EXISTS "
+ HOTSPOT_TABLE_NAME + ";";
String dropLocTable = "DROP TABLE IF EXISTS "
+ LOCATION_TABLE_NAME + ";";
db.execSQL(dropHSTable);
db.execSQL(dropLocTable);
createTables(db);
}
}.execute();
}
you are doing this task on MainThread which freezes the UI. this is the reason for getting I/Choreographer: Skipped 236 frames! The application may be doing too much work on its main thread. warning.
doing something on a background thread might be the best solution.
First solution: using Executors to make a new thread:
create a class like this:
source
public class AppExecutors {
private final Executor mDiskIO;
private final Executor mNetworkIO;
private final Executor mMainThread;
private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
this.mDiskIO = diskIO;
this.mNetworkIO = networkIO;
this.mMainThread = mainThread;
}
public AppExecutors() {
this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
new MainThreadExecutor());
}
public Executor diskIO() {
return mDiskIO;
}
public Executor networkIO() {
return mNetworkIO;
}
public Executor mainThread() {
return mMainThread;
}
private static class MainThreadExecutor implements Executor {
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
#Override
public void execute(#NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
}
this class has three usable methods that might come in handy:
diskIO is the best usecase for working with database, as it has only ONE thread, so calling multiple calls to the database won't cause problem and all the jobs assigned to the database will be done sequentially.
you can use it like this:
AppExecutors.getInstance().mDiskIO().execute {
someIntensiveAndtimeConsumingTask()
}
keep in mind that you need to switch back to mainThread if you want to deal with UI components:
AppExecutors.getInstance().mDiskIO().execute {
String textViewText = getSomeStringThatTakesTime()
AppExecutors.getInstance().mMainThread().execute {
awesomeTextView.setText(textViewText)
}
}
also notice that you can use AppExecutors.getInstance().mNetworkIO() to handle network requests. the difference is that mDiskIO uses a newSingleThreadExecutor which instantiates a single thread. but the mNetworkIO uses a pool of threads that enables you to handle multiple requests at the same time.
second solutions: Kotlin Coroutines getting started guide
lifeCycleScope.launch(Dispatchers.IO) {
val dataString = someIntensiveTaskOnDatabase()
//like what we did on the first solution, we need to switch to mainThread to do modifications on UI elements
withContext(Dispatchers.Main) {
textView.text = dataString
}
}
Many people said that SQLiteDatabase is thread-safe,but ,when i ran some simple tests with setup like this:
private class MyRunnable implements Runnable{
#Override
public void run() {
for(int i = 0 ;i < 100 ; i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Cursor cursor = database.query(true,"testdb",new String[]{"num"},"id=?",
new String[]{"1"},null,null,null,null);
cursor.moveToFirst();
int original = cursor.getInt(0);
ContentValues values = new ContentValues();
values.put("num",++original);
database.update("testdb",values,"id=?",new String[]{"1"});
}
}
}
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
new Thread(runnable1).start();
new Thread(runnable2).start();
In MyRunnable ,there is a loop that runs 100 times. Each time, the num field will add by 1 and the num's initial value is 0. we can see that the above code's expected result is 200,but i only get 197.
so does that mean SQLiteDatabase is not thread-safe?
No, the way you are handling the data is just not thread-safe. You could use an atomic SQL-query like:
UPDATE table SET num = num + 1
Instead of retrieving the data in your application code, modifying it and storing it back.
SQLite being threadsafe means that you can safely use all SQLite functions in multiple threads at once, it does not mean the database gets locked automatically depending on what you are doing. If your thread gets interrupted anywhere between the query() call and the update() call, returning to the thread at that point will mean the data you retrieved earlier is no longer up to date.
I have a database, and I am upgrading the version now. The problem scenario is such that on the DB upgrade I have to read from a file and insert records into a primary database table. Before inserting a record I have to check if this record might already exist and based upon this make a decision of inserting or not inserting. There are tens of thousands of records, which I have to check and insert.
My question here is that would it be right to use a separate thread handler or an Async task on DB upgrade? Or does the system handle this?
Now I have created an AsyncTask
public class AsyncUpgrade extends AsyncTask<Void, Void,String>{
#Override
public void onPreExecute(){
super.onPreExecute();
}
#Override
public void onPostExecute(String result){
//TODO
}
#Override
protected String doInBackground(Void... params) {
System.out.println("UPGRADE 3");
Cursor nameCur = readName();
String name_friend = null;
if(nameCur != null && nameCur.moveToFirst()){
do{
name_friend = nameCur.getString(nameCur.getColumnIndexOrThrow("SelfName"));
}while (nameCur.moveToNext());
}
assets = new AssetsDbHelper(con);
Cursor mCur = assets.getMax_id();
if(mCur != null && mCur.moveToFirst()){
do{
String name = mCur.getString(mCur.getColumnIndexOrThrow("Name"));
String DOB = mCur.getString(mCur.getColumnIndexOrThrow("DOB"));
String imageData = mCur.getString(mCur.getColumnIndexOrThrow("imageUri"));
String type = mCur.getString(mCur.getColumnIndexOrThrow("Type"));
String selection = mCur.getString(mCur.getColumnIndexOrThrow("Radio"));
Cursor checkCur= checkIfRecordExists(name.trim(), DOB.trim());
if(checkCur != null && checkCur.moveToFirst()){
}else{
insertAdhoc(name, DOB,imageData, type, selection, name_friend);
}
}while(mCur.moveToNext());
}
return "Success";
}
}
and I am calling the same from the upgrade method:
if(oldVersion <3){
new AsyncUpgrade().execute();
}
And now to my luck, I have an error popping up:
No enclosing instance of type DBAdapter is accessible. Must qualify the allocation with an enclosing instance of type DBAdapter (e.g. x.new A() where x is an instance of DBAdapter).
Always use an asyctask.Whenever you are doing something which is not related to updating the UI thread.From Android best practices for saving data
Note: Because they can be long-running, be sure that you call getWritableDatabase() or getReadableDatabase() in a background thread, such as with AsyncTask or IntentService.