Good day everyone.
So, I have an AsyncTask that calculates a Gaussian Blur from an image. The original image data is retrieved from SQLite using Blob field, also the filtered image is stored in SQLite. Here's the code (setQuizData is the function that does this):
public class SaveFilterTask extends AsyncTask<Void, Void, Void>{
private Context mContext;
private Context mApplicationContext;
private Quiz mQuiz;
public SaveFilterTask(Context context, Quiz quiz, Context applicationContext) {
super();
this.mContext = context;
this.mApplicationContext = applicationContext;
this.mQuiz = quiz;
}
#Override
protected Void doInBackground(Void... arg0) {
try {
setQuizData(mQuiz);
} catch (UnsupportedEncodingException e) {
cancel(true);
}
return null;
}
#Override
protected void onCancelled() {
Toast toast = Toast.makeText(mContext, R.string.error_loading_quiz, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
#Override
protected void onPreExecute() {
if(!((Activity)mContext).isFinishing()){
((MainActivity)mContext).showDialog();
}
}
protected void onPostExecute(Void args) {
if(!((Activity)mContext).isFinishing()){
((MainActivity)mContext).hideDialog();
}
Intent solveQuizIntent = new Intent(mContext, SolveQuizActivity.class);
solveQuizIntent.putExtra(
QuizConstants.KEY_PARCELABLE_FINISHED_QUIZ, mQuiz);
solveQuizIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(solveQuizIntent);
}
private void setQuizData(Quiz quiz) throws UnsupportedEncodingException {
if(quiz.getType().equals(QuizConstants.TYPE_PHOTO)){
QuizDataSource quizdatasource = new QuizDataSource(mApplicationContext);
quizdatasource.open();
String data = quizdatasource.getData(quiz.getId());
quizdatasource.close();
QuizDataSource quizdatasource2 = new QuizDataSource(mApplicationContext);
quizdatasource2.open();
String filterData = quizdatasource2.getDataFilter(quiz.getId());
quizdatasource2.close();
String filter = quiz.getFilter();
if(filter != null){
if(!filter.equals(QuizConstants.FILTER_DEFAULT)){
Bitmap original = decodeImage(data);
if(filterData == null){
data = FilterManager.applyFilter(original, quiz.getFilter());
Log.d("FilterManager","Data: "+ data);
Log.d("FilterManager","Id: "+ quiz.getId());
QuizDataSource quizdatasourceW = new QuizDataSource(mApplicationContext);
quizdatasourceW.open();
quizdatasourceW.setDataFilter(quiz.getId(), data);
quizdatasourceW.close();
}
}
}
}
}
private static Bitmap decodeImage(String data) {
byte[] b = Base64.decode(data, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(b, 0, b.length);
}
}
This task is called from my Activity, like this:
SaveFilterTask sftask = new SaveFilterTask(this, quiz, getApplicationContext());
sftask.execute();
I don't know why sometimes (and randomly) I get this error:
0java.lang.RuntimeException: An error occured while executing doInBackground()
1at android.os.AsyncTask$3.done(AsyncTask.java:300)
2at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
3at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
4at java.util.concurrent.FutureTask.run(FutureTask.java:242)
5at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
6at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
7at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
8at java.lang.Thread.run(Thread.java:841)
9Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/Data.db
10at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
11at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1156)
12at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1032)
13at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1200)
14at QuizDataSource.getDataFilter(QuizDataSource.java:119)
15at SaveFilterTask.setQuizData(SaveFilterTask.java:82)
16at SaveFilterTask.doInBackground(SaveFilterTask.java:39)
17at SaveFilterTask.doInBackground(SaveFilterTask.java:1)
I tried in different ways, using a single connection, using one connection for the read operations and one for the write operation. But the bug persists...
Any ideas?
thanks.
First of all you shouldnt reinitialize QuazDataSource everytime.
Initialize it in the Construcotr and check it by using
if (quizdatasource == null) {
quizdatasource = CreateaQuizDataSourceHelper().open;
}
Then you should always make sure to close it, even when it fails.
Cursor cursor = null;
try {
if (quizdatasource == null) {
quizdatasource = CreateaQuizDataSourceHelper().open;
}
cursor= quizdatasource.rawQuery(...);
} catch (Exception e) { e.printStackTrace(); }
finally {
if (quizdatasource != null) { quizdatasource.close(); }
}
at least make sure that your cursors are closed aswell by putting the validating in the finally block.
if (cursor != null) { cursor.close; }
then you shouldnt get multiple instances.
You should also create a SINGLETON Instance of your database, to make sure it's accessed only onec.
The Helper itself may have such a open method to make sure it's using the same connection.
public SQLiteDatabase open() {
SQLiteDatabase sqLiteDatabase = null;
try {
try {
sqLiteDatabase = this.getDatabaseConnection();
} catch (SQLiteException e) {
e.printStackTrace();
DataBaseConnectionPool.DbHelper DbHelper = this.dbHelper;
if (DbHelper != null) {
return this.dbHelper.getReadableDatabase();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return sqLiteDatabase;
}
Related
I am busy with trying to get an array which i get from MSSQL to display in a table view form in my application. I have tried to google it but i cant seem to find an example of this. I have tried it but i am running into one small error.
I get the following error Cannot resolve constructor:Simpletabledata adapter[package.mainactivity, package.itemarray]
Here is my mainactivy.java class:
public class MainActivity extends AppCompatActivity {
static String[] spaceProbeHeaders={"Name"};
private ArrayList<ClassListItems> itemArrayList; //List items Array
private MyAppAdapter myAppAdapter; //Array Adapter
final TableView<String[]> tableView = (TableView<String[]>) findViewById(R.id.tableView);
private boolean success = false; // boolean
Connection conn; // Connection Class Initialization
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tableView.setHeaderBackgroundColor(Color.parseColor("#777777"));
tableView.setHeaderAdapter(new SimpleTableHeaderAdapter(this,spaceProbeHeaders));
tableView.setColumnCount(4);
itemArrayList = new ArrayList<ClassListItems>(); // Arraylist Initialization
// Calling Async Task
SyncData orderData = new SyncData();
orderData.execute("");
}
// Async Task has three overrided methods,
private class SyncData extends AsyncTask<String, String, String>
{
String msg = "Internet/DB_Credentials/Windows_FireWall_TurnOn Error, See Android Monitor in the bottom For details!";
ProgressDialog progress;
#Override
protected void onPreExecute() //Starts the progress dailog
{
progress = ProgressDialog.show(MainActivity.this, "Synchronising",
"Tableview Loading! Please Wait...", true);
}
#Override
protected String doInBackground(String... strings) // Connect to the database, write query and add items to array list
{
try
{
ConnectionClass conStr=new ConnectionClass();
conn =conStr.connectionclass();
//Connection Object
if (conn == null)
{
success = false;
}
else {
// Change below query according to your own database.
String query = "SELECT customer_first_name FROM cc_customer";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
if (rs != null) // if resultset not null, I add items to itemArraylist using class created
{
while (rs.next())
{
try {
itemArrayList.add(new ClassListItems(rs.getString("customer_first_name")));
} catch (Exception ex) {
ex.printStackTrace();
}
}
msg = "Found";
success = true;
} else {
msg = "No Data found!";
success = false;
}
}
} catch (Exception e)
{
e.printStackTrace();
Writer writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
msg = writer.toString();
success = false;
}
return msg;
}
#Override
protected void onPostExecute(String msg) // disimissing progress dialoge, showing error and setting up my listview
{
progress.dismiss();
Toast.makeText(MainActivity.this, msg + "", Toast.LENGTH_LONG).show();
if (success == false)
{
}
else {
try {
//myAppAdapter = new MyAppAdapter(itemArrayList, MainActivity.this);
tableView.setDataAdapter(new SimpleTableDataAdapter(MainActivity.this,itemArrayList ));
} catch (Exception ex)
{
}
}
}
}
and here is my classlist.java file:
public class ClassListItems
{
public String name; //Name
public ClassListItems(String name)
{
this.name = name;
}
public String getName() {
return name;
}
Update
N.B: OP is using SortableTableView Library.
You need to import the following to solve Cannot resolve constructor:SimpleTableDataAdapter-
import de.codecrafters.tableview.toolkit.SimpleTableDataAdapter;
Original
Do you have SimpleTableDataAdapter class in your project? It seems it can't find the class so it is not in the same package. If it is in different package, you need to import it.
And on a different note, your .java file names should match the class name
And on another different note, have you tested that itemArrayList is actually populating? For Android-MSSQL, here is a tutorial pointer -
https://parallelcodes.com/connect-android-to-ms-sql-database-2/
There are many tutorials if you google it.
I am trying to execute connected test for P4, however I am reciing an "Null pointer exception error" for P4
Error message:
:00:02 PM null
java.lang.NullPointerException
at com.android.ddmlib.Client.read(Client.java:692)
at com.android.ddmlib.MonitorThread.processClientActivity(MonitorThread.java:304)
at com.android.ddmlib.MonitorThread.run(MonitorThread.java:256)
It is a standard test, verifying non-empty string in the Async task
Test function:
public void runCloudModuleTest() {
String joke = null;
JokesAsyncTask jokesAsyncTask = new JokesAsyncTask(getContext(), null);
jokesAsyncTask.execute();
try {
joke = jokesAsyncTask.get();
Log.d("CloudModuleTest", "Retrieved a non-empty string successfully: " + joke);
} catch (Exception e) {
e.printStackTrace();
}
assertNotNull(joke);
}
Can someone help me understand what the issue is?
AsyncTask: The Async task pulls data from google cloud engine
public class JokesAsyncTask extends AsyncTask, Void, String> {
private static JokeApi myApiService = null;
private Context mContext;
private String mResult;
private ProgressBar mProgressBar;
public JokesAsyncTask(Context context, ProgressBar progressBar) {
this.mContext = context;
this.mProgressBar = progressBar;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
if (mProgressBar != null) {
mProgressBar.setVisibility(View.VISIBLE);
}
}
#Override
protected String doInBackground(Pair<Context, String>... pairs) {
if (myApiService == null) {
JokeApi.Builder builder = new JokeApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
.setRootUrl("https://testandroiddevelopment.appspot.com/_ah/api/");
myApiService = builder.build();
}
try {
return myApiService.sendJoke(new JokeBean()).execute().getJoke();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if (mProgressBar != null) {
mProgressBar.setVisibility(View.GONE);
}
mResult = result;
startJokeDisplayActivity();
}
private void startJokeDisplayActivity() {
Intent intent = new Intent(mContext, JokeViewActivity.class);
intent.putExtra(JokeViewActivity.JOKE_KEY, mResult);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
I have referenced the variable and it is not an issue due due to the below post, however I did investigate and finally cleaned up and rebuild the project that helped resolved the issue
I have referenced the variable and it is not an issue due due to the post #AxelH, however I did investigate and finally cleaned up and rebuild the project that helped resolved the issue
I get the NullPointerException and I can't figure out what's wrong. I've tried only to bring the necessary code.
I have 3 classes: MainActivity, GoogleCommunicator and CustomAdapter.
The error is caused by following in CustomAdapter:
mActivity.updateBought(position, "1");
The errors I get are line 283 and 277 which are:
283: URL listFeedUrl = mWorksheet.getListFeedUrl();
277: private class UpdateBought extends AsyncTask<Void, Void, String>
The logcat:
3011-3026/com.example.andb.apop_l6_google_communicator_app E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
Process: com.example.andb.apop_l6_google_communicator_app, PID: 3011
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: Attempt to invoke virtual method 'java.net.URL com.google.gdata.data.spreadsheet.WorksheetEntry.getListFeedUrl()' on a null object reference
at com.example.andb.apop_l6_google_communicator_app.GoogleCommunicator$UpdateBought.doInBackground(GoogleCommunicator.java:283)
at com.example.andb.apop_l6_google_communicator_app.GoogleCommunicator$UpdateBought.doInBackground(GoogleCommunicator.java:277)
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)
MainActivity
public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener{
public GoogleCommunicator mGCom = new GoogleCommunicator(this,"torprode#gmail.com");
TextView tvStatus;
EditText etAdd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = (TextView) findViewById(R.id.tvStatus);
doSomeGoogleStuff();
buttonListener();
update();
}
private void doSomeGoogleStuff(){
mGCom.setupFeed("mandatoryProject","BuyMe");
}
private void drawListview() {
ListAdapter listAdapter = new CustomAdapter(this, mGCom.listItem, mGCom.listBought);
ListView listView = (ListView) findViewById(R.id.lv_items);
listView.setAdapter(listAdapter);
}
public void updateBought(int name, String bought) {
mGCom.updateBought(name, bought);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
}
GoogleCommunicator
public class GoogleCommunicator {
//Spreadsheet communication
private static final String mScope = "oauth2:https://www.googleapis.com/auth/userinfo.profile https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
private MainActivity mActivity;
private SpreadsheetService mSpreadSheetService;
private SpreadsheetFeed mFeed;
private String mSpreadsheetName;
private String mWorksheetName;
private SpreadsheetEntry mSpreadsheet;
private WorksheetEntry mWorksheet;
private String itemName;
private int itemNameIndex;
private String itemBought;
//Constructor
public GoogleCommunicator(MainActivity activity, String email) {
mEmail = email;
mActivity = activity; //possibility for callback to method in activity class
}
//Method to be called from your application.
//Creates an instance of SetupFeedTask (an AsyncTask) and executes it
public void setupFeed(String spreadsheet_name, String worksheet_name){
mSpreadsheetName = spreadsheet_name;
mWorksheetName = worksheet_name;
new SetupFeedTask().execute();
}
public void updateBought(int name, String bought) {
itemNameIndex = name;
itemBought = bought;
new UpdateBought().execute();
}
//AsyncTask that handles network comminucation e.t.c.
private class SetupFeedTask extends AsyncTask<Void, Void, String> {
//Executes in its own "worker thread" and doesnt block the main UI thread
#Override protected String doInBackground(Void... params) {
// Do work
mToken = fetchToken();
mSpreadSheetService = new SpreadsheetService("MySpreadsheetService");
mSpreadSheetService.setAuthSubToken(mToken);
URL feed_url;
try {
feed_url = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
mFeed = mSpreadSheetService.getFeed(feed_url, SpreadsheetFeed.class);
}catch(MalformedURLException e){
//TODO: handle exception
Log.v(TAG, "MalformedURLException");
return null;
}catch(ServiceException e){
//TODO: handle exception
Log.v(TAG, "ServiceException");
return null;
}catch(IOException e){
//TODO: handle exception
Log.v(TAG, "IOException");
return null;
}
try{
List<SpreadsheetEntry> spreadsheets = mFeed.getEntries();
// Iterate through all of the spreadsheets returned
for (SpreadsheetEntry spreadsheet : spreadsheets) {
if (spreadsheet.getTitle().getPlainText().equals(mSpreadsheetName)) {
List<WorksheetEntry> worksheets = spreadsheet.getWorksheets();
//Iterate through worksheets
for (WorksheetEntry worksheet : worksheets) {
if (worksheet.getTitle().getPlainText().equals(mWorksheetName)) {
mSpreadsheet = spreadsheet;
mWorksheet = worksheet;
Log.v(TAG,"Spreadsheet and Worksheet is now setup.");
}
}
}
}
}catch(ServiceException e){
//TODO: handle exception
Log.v(TAG, "Service Exception");
return null;
}catch(IOException e){
//TODO: handle exception
Log.v(TAG, "IO Exception");
return null;
}
//Just for the example.. mToken not important to return
return mToken;
}
//Call back that is called when doInBackground has finished.
//Executes in main UI thread
#Override protected void onPostExecute(String result) {
//TODO: Notify rest of application, e.g.:
// * Send broadcast
// * Send message to a handler
// * Call method on Activity
}
//Helper method
private String fetchToken(){
try {
return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);
} catch (UserRecoverableAuthException userRecoverableException) {
// GooglePlayServices.apk is either old, disabled, or not present, which is
// recoverable, so we need to show the user some UI through the activity.
//TODO:
if(mActivity instanceof MainActivity){
((MainActivity)mActivity).handleException(userRecoverableException);
if(D) Log.e(TAG,"UserRecoverableAuthException");
}
} catch (GoogleAuthException fatalException) {
//TODO:
//onError("Unrecoverable error " + fatalException.getMessage(), fatalException);
if(D) Log.e(TAG,"GoogleAuthException");
} catch (IOException ioException){
if(D) Log.e(TAG,"IOException");
}
return null;
}
}
private class UpdateBought extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
URL listFeedUrl = mWorksheet.getListFeedUrl();
ListFeed listFeed = mSpreadSheetService.getFeed(listFeedUrl, ListFeed.class);
ListEntry row = listFeed.getEntries().get(itemNameIndex);
row.getCustomElements().setValueLocal("bought", itemBought);
row.update();
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
}
return null;
}
}
}
CustomAdapter
class CustomAdapter extends ArrayAdapter<String> {
ArrayList boughtList;
MainActivity mActivity = new MainActivity();
CustomAdapter(Context context, ArrayList<String> item, ArrayList<String> bought) {
super(context, R.layout.custom_listview, item);
boughtList = bought;
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
LayoutInflater listViewInflater = (LayoutInflater.from(getContext()));
final View customView = listViewInflater.inflate(R.layout.custom_listview, parent, false);
final String foodItem = getItem(position);
TextView foodText = (TextView) customView.findViewById(R.id.tv_Item);
final CheckBox checkBox = (CheckBox) customView.findViewById(R.id.cb_checked);
foodText.setText(foodItem);
String foodBought = String.valueOf(boughtList.get(position));
int foodBoughtInt = Integer.parseInt(foodBought);
if (foodBoughtInt == 1) {
checkBox.setChecked(true);
} else {
checkBox.setChecked(false);
}
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (checkBox.isChecked()) {
System.out.println("Jep");
mActivity.updateBought(position, "1");
} else {
System.out.println("Nope");
mActivity.updateBought(position, "0");
}
}
});
return customView;
}
}
You are encountering a race condition. You have to asynchronous tasks that execute, and the second asynchronous task depends on the first task to be done for it to work correctly. Because both tasks are done asynchronously, they are done in the background, on separate threads. Your setupFeed method is not done working, and then you start your updateBought method on a new thread. What happens is that updateBought begins while mWorksheet is still null. You will have to reorganize your code logic to avoid this race condition. What I have done in the past when I have two async tasks is to put the second asynchronous task in onPostExecute() of the first async task, because onPostExecute only occurs once doInBackground is finished.
Here is an execellent article on AsyncTasks and Threads from the developer guides.
I'm developing an android 3.1 application.
This question is not specific for Android, it is about how to design a class that access a database. I asked here because my code is for Android.
I have a class, DBManager, to work with Sqlite database. This is a part of its implementation:
public class DBManager
{
// Variable to hold the database instance
private SQLiteDatabase db;
// Database open/upgrade helper
private DatabaseHelper dbHelper;
public DBManager(Context _context)
{
Log.v("DBManager", "constructor");
dbHelper = new DatabaseHelper(_context, SqlConstants.DATABASE_NAME, null, SqlConstants.DATABASE_VERSION);
}
public DBManager open() throws SQLException
{
Log.v("DBManager", "open");
db = dbHelper.getWritableDatabase();
return this;
}
public void close()
{
Log.v("DBManager", "close");
db.close();
}
...
/**
* Query all forms available locally.
* #return A list with all forms (form.name and form.FormId) available on local db
* or null if there was a problem.
*/
public ArrayList<Form> getAllForms()
{
Log.v("DBManager", "getAllForms");
ArrayList<Form> list = null;
Cursor c = null;
try
{
c = this.getAllFormsCursor();
if (c != null)
{
int formNameIndex = c.getColumnIndex(SqlConstants.COLUMN_FORM_NAME);
int formIdIndex = c.getColumnIndex(SqlConstants.COLUMN_FORM_ID);
c.moveToFirst();
if (c.getCount() > 0)
{
list = new ArrayList<Form>(c.getCount());
do
{
Form f = new Form();
f.Name = c.getString(formNameIndex);
f.FormId = c.getString(formIdIndex);
list.add(f);
}
while (c.moveToNext());
}
}
}
catch (Exception e)
{
e.printStackTrace();
list = null;
}
finally
{
if (c != null)
c.close();
}
return list;
}
private Cursor getAllFormsCursor()
{
Log.v("DBManager", "getAllFormsCursor");
return db.query(SqlConstants.TABLE_FORM,
new String[] {
SqlConstants.COLUMN_FORM_ID,
SqlConstants.COLUMN_FORM_NAME}, null, null, null, null, null);
}
}
And this is an AsyncTask that uses DBManager:
private class DbFormListAsyncTask extends AsyncTask<Void, Void, ArrayList<Form>>
{
private Context mContext;
private ProgressDialog loadingDialog;
private DBManager dbMan;
DbFormListAsyncTask(Context context)
{
this.mContext = context;
loadingDialog = new ProgressDialog(mContext);
loadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loadingDialog.setMessage("Retriving forms. Please wait...");
loadingDialog.setCancelable(false);
loadingDialog.show();
}
#Override
protected ArrayList<Form> doInBackground(Void... arg0)
{
dbMan = new DBManager(mContext);
dbMan.open();
return dbMan.getAllForms();
}
protected void onPostExecute(ArrayList<Form> forms)
{
if (forms != null)
{
ListActivity act = (ListActivity) mContext;
act.setListAdapter(new AvaFormAdapter(act, R.layout.ava_list_item, forms));
}
else
{
TextView errorMsg = (TextView)
((FormsListActivity) mContext).findViewById(R.id.formErrorMsg);
errorMsg.setText("Problem getting forms. Please try again later.");
}
loadingDialog.dismiss();
if (dbMan != null)
dbMan.close();
}
}
As you can see I have to:
Create DBManager instance.
Open database with dbMan.open()
Call dbMan.getAllForms()
Close database with dbMan.close() on onPostExecute.
I thought that I could add db.open() and db.close() on dbMan.getAllForms() to avoid calling it every time I use dbMan.getAllForms().
What do you think about this? What is the best approach?
I would put it inside getAllForms() or do something like that
protected ArrayList<Form> doInBackground(Void... arg0)
{
dbMan = new DBManager(mContext);
dbMan.open();
ArrayList<Form> resutl = dbMan.getAllForms();
dbMan.close();
return result;
}
Since you don't need the db connection after you have the result you can close it right away.
Edit: if you run that AsyncTask several times then opening/closing will introduce unnecessary overhead. In that case you may want to instanciate the dbManager from your Activity (maybe open() in the constructor of DbManager) and close it once you leave your activity. Then pass Dbmanager to the AsyncTask.
Make your database helper class a singleton, and don't explicitly close the SQLiteDatabase. It will be closed and flushed when your app's process exits.
I'm having an issue with db4o on Android 3.0+ because it turns out that on the creation of the db4o database, it uses some of the network apis by default. (I stumbled upon this post: http://mavistechchannel.wordpress.com/2011/11/18/db4o-at-honeycomb-and-ice-cream-sandwich/ about it)
However, I've attempted to make the db creation requests async, but I think I'm running into an issue of calling the db before it's fully created as it locks the DB. (And I now get a locking error) Is there any way I can do this synchronous? or, at a minimum wait until it's been finished? Here's my db4o helper:
public class Db4oHelperAsync implements Constants{
private static final String USE_INTERNAL_MEMORY_FOR_DATABASE = "USE_INTERNAL_MEMORY_FOR_DATABASE";
private static ObjectContainer oc = null;
private Context context;
/**
* #param ctx
*/
public Db4oHelperAsync(Context ctx) {
context = ctx;
}
/**
* Create, open and close the database
*/
public ObjectContainer db() {
if (oc == null || oc.ext().isClosed()) {
if (Utilities.getPreferences(context).getBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true)) {
new GetDbFromInternalMemory().execute();
} else {
new GetDbFromSDCard().execute();
}
return oc;
} else {
return oc;
}
}
/**
* Configure the behavior of the database
*/
private EmbeddedConfiguration dbConfig() throws IOException {
EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).objectField("name").indexed(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnUpdate(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnActivate(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnDelete(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).objectField("name").indexed(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnUpdate(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnActivate(true);
return configuration;
}
/**
* Returns the path for the database location
*/
private String db4oDBFullPathInternal(Context ctx) {
return ctx.getDir("data", 0) + "/" + "testapp.db4o";
}
private String db4oDBFullPathSdCard(Context ctx) {
File path = new File(Environment.getExternalStorageDirectory(), ".testapp");
if (!path.exists()) {
path.mkdir();
}
return path + "/" + "testapp.db4o";
}
/**
* Closes the database
*/
public void close() {
if (oc != null)
oc.close();
}
private class GetDbFromInternalMemory extends AsyncTask<Void, Void, ObjectContainer>{
#Override
protected ObjectContainer doInBackground(Void... params) {
try {
ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathInternal(context));
CLog.v("USING INTERNAL MEMORY FOR DATABASE");
return obj;
} catch (Exception ie) {
ie.printStackTrace();
CLog.e(Db4oHelper.class.getName(), ie.toString());
return null;
}
}
#Override
protected void onPostExecute(ObjectContainer result)
{
oc = result;
}
}
private class GetDbFromSDCard extends AsyncTask<Void, Void, ObjectContainer>{
#Override
protected ObjectContainer doInBackground(Void... params) {
try {
ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathSdCard(context));
CLog.v("USING SDCARD FOR DATABASE");
SharedPreferences.Editor edit = Utilities.getPreferencesEditor(context);
edit.putBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true);
edit.commit();
return obj;
} catch (Exception ie) {
ie.printStackTrace();
CLog.e(Db4oHelper.class.getName(), ie.toString());
return null;
}
}
#Override
protected void onPostExecute(ObjectContainer result)
{
oc = result;
}
}
}
Update: This db4o bug has been fixed. If you get the newest 8.1 bits the error should not occur and the workaround is obsolute:
You get a file-locked exception when trying to get the database? Right.
Well the issue is that you are not waiting for the async task to finish and just start a new one in case the Db4oHelperAsync.oc is null. You basically have to wait until the initialization has finished and only then use the Db4oHelperAsync.oc variable. So your in Java synchronization land.
For example you can do this: Synchronize the Db4oHelperAsync.oc access. When requesting the database wait until the variable is set. Now unfortunately I don't know the exact behavior of the async task. My guess is that it will run the .onPostExecute() method back on the main activity. That also means that you cannot just wait for it, because it would mean that you block the Activity-Thread and .onPostExecute() will never be executed.
Here's my draft of what I would try to do. I never executed nor compiled it. And it probably has synchronization issues. For example when the initialization fail it will just hang your applicaition on the .db() call, because it waits forever. So be very careful and try to improve it:
public class Db4oHelperAsync implements Constants{
private static final String USE_INTERNAL_MEMORY_FOR_DATABASE = "USE_INTERNAL_MEMORY_FOR_DATABASE";
private static ObjectContainer oc = null;
private static final Object lock = new Object();
private Context context;
/**
* #param ctx
*/
public Db4oHelperAsync(Context ctx) {
context = ctx;
}
/**
* Create, open and close the database
*/
public ObjectContainer db() {
synchronized(lock){
if (oc == null || oc.ext().isClosed()) {
if (Utilities.getPreferences(context).getBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true)) {
new GetDbFromInternalMemory().start();
} else {
new GetDbFromSDCard().start();
}
while(oc==null){
this.wait()
}
return oc;
} else {
return oc;
}
}
}
/**
* Configure the behavior of the database
*/
private EmbeddedConfiguration dbConfig() throws IOException {
EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).objectField("name").indexed(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnUpdate(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnActivate(true);
configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnDelete(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).objectField("name").indexed(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnUpdate(true);
configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnActivate(true);
return configuration;
}
/**
* Returns the path for the database location
*/
private String db4oDBFullPathInternal(Context ctx) {
return ctx.getDir("data", 0) + "/" + "testapp.db4o";
}
private String db4oDBFullPathSdCard(Context ctx) {
File path = new File(Environment.getExternalStorageDirectory(), ".testapp");
if (!path.exists()) {
path.mkdir();
}
return path + "/" + "testapp.db4o";
}
/**
* Closes the database
*/
public void close() {
synchronized(lock){
if (oc != null)
oc.close();
}
}
private class GetDbFromInternalMemory extends Thread{
#Override
protected void run() {
try {
ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathInternal(context));
CLog.v("USING INTERNAL MEMORY FOR DATABASE");
synchronized(Db4oHelperAsync.lock){
Db4oHelperAsync.oc = obj;
Db4oHelperAsync.lock.notifyAll()
}
} catch (Exception ie) {
ie.printStackTrace();
CLog.e(Db4oHelper.class.getName(), ie.toString());
}
}
}
private class GetDbFromSDCard extends Thread{
#Override
protected void run() {
try {
ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathSdCard(context));
CLog.v("USING SDCARD FOR DATABASE");
SharedPreferences.Editor edit = Utilities.getPreferencesEditor(context);
edit.putBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true);
edit.commit();
synchronized(Db4oHelperAsync.lock){
Db4oHelperAsync.oc = obj;
Db4oHelperAsync.lock.notifyAll()
}
} catch (Exception ie) {
ie.printStackTrace();
CLog.e(Db4oHelper.class.getName(), ie.toString());
}
}
}
}
P.S. Added this problem as a bug to db4o: http://tracker.db4o.com/browse/COR-2269
Thanks for posting this issue, this is a serious fun-spoiler on Android.
When a new db4o database file is created, db4o generates it's unique internal signature by calling java.net.InetAddress.getLocalHost().getHostName(). Exceptions are not caught in this call. We will find a workaround for Android and post back here and to our forums when this is fixed.
Update Feb 9 2012:
The issue has been fixed and new builds are online.
http://community.versant.com/Blogs/db4o/tabid/197/entryid/1057/Default.aspx