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
}
}
Related
After using Android Room for a few weeks now and getting the hang of basic queries, I've run into an issue with attempting to update a list of custom objects. For some reason when Room tries to create the SQLLite string to insert my new data, it gets stuck with the placeholders:
From the debug window:
Caused by: android.database.sqlite.SQLiteException: near "?": syntax error (code 1): , while compiling: UPDATE player_characters SET ability_scores = ?,?,?,?,?,? WHERE playerCharacterID = ?
#################################################################
Error Code : 1 (SQLITE_ERROR)
Caused By : SQL(query) error or missing database.
(near "?": syntax error (code 1): , while compiling: UPDATE player_characters SET ability_scores = ?,?,?,?,?,? WHERE playerCharacterID = ?)
#################################################################
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1005)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:570)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:59)
at android.database.sqlite.SQLiteStatement.(SQLiteStatement.java:31)
at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1375)
at android.arch.persistence.db.framework.FrameworkSQLiteDatabase.compileStatement(FrameworkSQLiteDatabase.java:62)
at android.arch.persistence.room.RoomDatabase.compileStatement(RoomDatabase.java:204)
at com.pathfinderstattracker.pathfindercharactersheet.database.database_daos.PlayerCharacterDao_Impl.updatePlayerCharacterAbilityScores(PlayerCharacterDao_Impl.java:321)
The DAO that contains the query:
#Dao
#TypeConverters({UUIDConverter.class,
AbilityScoreConcreteConverter.class})
public interface PlayerCharacterDao
{
#Query("UPDATE player_characters "+
"SET ability_scores = :playerCharacterAbilityScores "+
"WHERE playerCharacterID = :characterIDToUpdate")
void updatePlayerCharacterAbilityScores(UUID characterIDToUpdate, List<AbilityScore> playerCharacterAbilityScores);
}
And the repository command that calls it:
private static class updatePlayerCharacterAbilityScoresAsyncTask extends AsyncTask<Object, Void, Void>
{
private PlayerCharacterDao asyncPlayerCharacterDao;
updatePlayerCharacterAbilityScoresAsyncTask(PlayerCharacterDao dao) {asyncPlayerCharacterDao = dao;}
#Override
protected Void doInBackground(final Object... params)
{
UUID playerCharacterID = (UUID)params[0];
List<AbilityScore> updatedAbilityScores = (ArrayList<AbilityScore>)params[1];
asyncPlayerCharacterDao.updatePlayerCharacterAbilityScores(playerCharacterID, updatedAbilityScores);
return null;
}
}
I can confirm that the data is getting to the room query properly, and I've tried passing both concrete and interface objects into the query, as well as had a converter for both individual AbilityScore objects and a list of AbilityScore objects. Any help would be greatly appreciated!
EDIT: A few people have requested the entity that's being updated:
#Entity(tableName = "player_characters")
#TypeConverters({AlignmentEnumConverter.class,
HitPointsConverter.class,
DamageReductionConverter.class,
StringListConverter.class,
UUIDConverter.class,
StringListConverter.class,
AbilityScoreListConverter.class,
CombatManeuverConverter.class})
public class PlayerCharacterEntity
{
#PrimaryKey
#NonNull
private UUID playerCharacterID;
#ColumnInfo(name="character_name")
private String playerCharacterName;
#ColumnInfo(name="character_level")
private int characterLevel;
#ColumnInfo(name="concentration_check")
private int concentrationCheck;
#ColumnInfo(name="character_alignment")
private AlignmentEnum characterAlignment;
#ColumnInfo(name="total_base_attack_bonus")
private int totalBaseAttackBonus;
#ColumnInfo(name="total_hit_points")
private IHitPoints totalHitPoints;
#ColumnInfo(name="total_ac")
private int totalAC;
#ColumnInfo(name="damage_reduction")
private IDamageReduction damageReduction;
#ColumnInfo(name="languages_known")
private List<String> languagesKnown;
#ColumnInfo(name="ability_scores")
private List<IAbilityScore> abilityScores;
#ColumnInfo(name="combat_Maneuver_stats")
private ICombatManeuver combatManeuverStats;
#ColumnInfo(name="spell_resistance")
private int spellResistance;
#ColumnInfo(name="initiative")
private int initiative;
#ColumnInfo(name="fortitude_save")
private int fortitudeSave;
#ColumnInfo(name="reflex_save")
private int reflexSave;
#ColumnInfo(name="will_save")
private int willSave;
~Getters/Setters and Constructors removed for brevity~
}
EDIT: And for good measure I thought I would include the #TypeConverter for AbilityScore (I've reverted this to an earlier form that uses interfaces rather than concrete, since that works elsewhere in the code and difference didn't seem to change anything):
public class AbilityScoreConverter
{
#TypeConverter
public IAbilityScore fromString(String value)
{
IAbilityScore formattedAbilityScore = new AbilityScore();
String[] tokens = value.split(" ");
formattedAbilityScore.setAmount(Integer.parseInt(tokens[0]));
switch(tokens[1])
{
case "STR":
formattedAbilityScore.setStat(AbilityScoreEnum.STR);
case "DEX":
formattedAbilityScore.setStat(AbilityScoreEnum.DEX);
case "CON":
formattedAbilityScore.setStat(AbilityScoreEnum.CON);
case "INT":
formattedAbilityScore.setStat(AbilityScoreEnum.INT);
case "WIS":
formattedAbilityScore.setStat(AbilityScoreEnum.WIS);
case "CHA":
formattedAbilityScore.setStat(AbilityScoreEnum.CHA);
default:
//This may cause issues down the line if a non existent enum gets in the db somehow, but we don't have any error handling yet
//Todo: Add error handling
formattedAbilityScore.setStat(AbilityScoreEnum.STR);
}
return formattedAbilityScore;
}
#TypeConverter
public String toString(IAbilityScore value)
{
return value.toString();
}
}
EDIT: I've cleaned up the logcat text to focus just on the Room/SQLLite issues.
After some searching I was unfortunately forced to give up on updating my db using a #Query command and instead had to fall back on using Rooms default #Update notation. While this does work and properly updates the data in the database, it doesn't allow for me to only update certain fields.
Using ArrayList instead of List in the UPDATE query works for me.
I've followed the solution.
The difference in the Impl build :
MutableList:
#Override
public void test(int tkID, List<Boolean> test) {
StringBuilder _stringBuilder = StringUtil.newStringBuilder();
_stringBuilder.append("UPDATE TasksTable SET test = ");
final int _inputSize = test.size();
StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
_stringBuilder.append(" WHERE taskID = ");
_stringBuilder.append("?");
final String _sql = _stringBuilder.toString();
SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
ArrayList:
#Override
public void test(int tkID, ArrayList<Boolean> test) {
final SupportSQLiteStatement _stmt = __preparedStmtOfTest.acquire();
__db.beginTransaction();
try {
int _argIndex = 1;
final String _tmp;
_tmp = Converters.listBooleanToString(test);
if (_tmp == null) {
_stmt.bindNull(_argIndex);
} else {
_stmt.bindString(_argIndex, _tmp);
}
_argIndex = 2;
_stmt.bindLong(_argIndex, tkID);
_stmt.executeUpdateDelete();
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
__preparedStmtOfTest.release(_stmt);
}
}
As you can see, the ArrayList uses the converter while the MutableList does not.
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 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?
I followed the tutorial given here to develop my code.
Since the whole code is given at the above link as well as here I wont type it all here to avoid cluttering.
On the app engine app, I have a class like this :
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
#Entity
public class Quote {
#Id
Long id;
String who;
String what;
public Quote() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getWho() {
return who;
}
public void setWho(String who) {
this.who = who;
}
public String getWhat() {
return what;
}
public void setWhat(String what) {
this.what = what;
}
}
And since I automatically generated my Endpoints class from the above class, it looks (partly) like this (As opposed to the one shown in the example)
#Api(
name = "quoteApi",
version = "v1",
resource = "quote",
namespace = #ApiNamespace(
ownerDomain = "QuoteApi.com",
ownerName = "QuoteApi.com",
packagePath = ""
)
)
public class QuoteEndpoint {
private static final Logger logger = Logger.getLogger(QuoteEndpoint.class.getName());
private static final int DEFAULT_LIST_LIMIT = 20;
static {
// Typically you would register this inside an OfyServive wrapper. See: https://code.google.com/p/objectify-appengine/wiki/BestPractices
ObjectifyService.register(Quote.class);
}
/**
* Returns the {#link Quote} with the corresponding ID.
*
* #param id the ID of the entity to be retrieved
* #return the entity with the corresponding ID
* #throws NotFoundException if there is no {#code Quote} with the provided ID.
*/
#ApiMethod(
name = "get",
path = "quote/{id}",
httpMethod = ApiMethod.HttpMethod.GET)
public Quote get(#Named("id") Long id) throws NotFoundException {
logger.info("Getting Quote with ID: " + id);
Quote quote = ofy().load().type(Quote.class).id(id).now();
if (quote == null) {
throw new NotFoundException("Could not find Quote with ID: " + id);
}
return quote;
}
}
Now in my Android App, I do this. This goes in an AsyncTask, doinbackground
try {
Long id = Long.getLong("9649299534313129");
Quote q1= endpointBldr.get(id).execute();
}catch (IOException e){
return e.getMessage();
}
I should mention, the "id" that I am asking to get, is accurate, and exists in the datastore.
Also, the 'list' and 'insert' and 'delete' ApiMethods work just fine. Just this 'get' is giving me trouble.
All of which are auto-generated methods.
And finally the Error I get , that stops my app is:
/com.blah.QuoteApi E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
Process: com.blah.QuoteApi, PID: 16820
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.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Required parameter id must be specified.
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull(Preconditions.java:208)
at com.google.api.client.util.Preconditions.checkNotNull(Preconditions.java:140)
at com.blah.QuoteApi$Get.<init>(Quote.java:151)
at com.blah.QuoteApi.QuoteApi.get(QuoteApi.java:129)
at com.blah.QuoteApi.EndpointsAsyncTask.doInBackground(EndpointsAsyncTask.java:72)
at com.blah.QuoteApi.EndpointsAsyncTask.doInBackground(EndpointsAsyncTask.java:22)
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)
Can someone please help me resolve this. Not sure where it is getting a null pointer , I give the assigned Long id, as argument to the get.
Also, should point out, the ApiMethod works as it should in the online Google Api Explorer (AppSpot)
I must admit, it was a silliest mistake.
Long.getLong is not the right one to use. Long.parseLong should have been the one.
getLong was obviously gonna give null in this case.
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.