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
Related
In my android app I have an option to backup the database to Google Drive. For that I am using DriveServiceHelper class, but I just noticed, in Android 11 the Task.call is deprecated.
public Task<FileList> queryFiles() {
return Tasks.call(mExecutor, () ->
mDriveService.files().list().setSpaces("drive").execute());
}
From my BackupActivity then I call queryFiles from backup method:
public void backup(View v) {
driveServiceHelper.queryFiles()
.addOnSuccessListener(fileList -> {
// another code
})
.addOnFailureListener(e -> showMsgSnack(getString(R.string.uploaderror)));
I did not find any solution how to deal with this to avoid complete rework of that class.
What I tried:
I tried to replace with runnable, also callable, but it doesn't work as Task is expected to be returned, not Filelist.
also I tried to use TaskCompletionSource:
public Task<FileList> queryFiles(int delay) throws IOException, ExecutionException, InterruptedException {
new Thread(
new Runnable() {
#Override
public void run() {
TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();
FileList result = null;
try {
result = mDriveService.files().list().setSpaces("drive").execute();
} catch (IOException e) {
e.printStackTrace();
}
FileList finalResult = result;
new Handler().postDelayed(() -> taskCompletionSource.setResult(finalResult), delay);
return taskCompletionSource.getTask();
}
}).start();
}
but the return works not from a method of void type.
Ok, after hours of testing I tried this solution and this seems working for now: (using executorService, and in Handler a Looper is needed.)
public Task<FileList> queryFiles() {
final TaskCompletionSource<FileList> tcs = new TaskCompletionSource<FileList>();
ExecutorService service = Executors.newFixedThreadPool(1);
service.execute(
new Runnable() {
#Override
public void run() {
FileList result = null;
try {
result = mDriveService.files().list().setSpaces("drive").execute();
} catch (IOException e) {
e.printStackTrace();
}
FileList finalResult = result;
new Handler(Looper.getMainLooper()).postDelayed(() -> tcs.setResult(finalResult), 1000);
}
});
return tcs.getTask();
}
I meant something like this:
public Task<FileList> queryFiles(int delay) throws IOException {
Task<FileList> retVal;
final FutureValue<Task<FileList>> future = new FutureValue<>();
// now run this bit in a runnable
/*
TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();
FileList result = mDriveService.files().list().setSpaces("drive").execute();
new Handler().postDelayed(() -> taskCompletionSource.setResult(result), delay);
return taskCompletionSource.getTask();
*/
new Thread(
new Runnable() {
#Override
public void run() {
TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();
FileList result = mDriveService.files().list().setSpaces("drive").execute();
new Handler().postDelayed(() -> taskCompletionSource.setResult(result), delay);
// and we replace the return statement with something else
// return taskCompletionSource.getTask();
future.set(taskCompletionSource.getTask());
}
}).start();
// And block (wait) for future to finish so we can return it, deadlocking the main thread...
// return future.get();
//FIXME do either this
// retVal = future.get();
// For bonus points, we'll do a timed wait instead -- OR THIS
try {
retVal = future.get(30, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true);
Log.d(LOG_TAG, "Exception "+e+" happened!", e);
} catch (InterruptedException | ExecutionException e) {
Log.d(LOG_TAG, "Exception "+e+" happened!", e);
}
return retVal;
}
and that should set you on some path to solving the problem.
However, if the only reason for using the Task<> is just so you can add success/fail listeners to these methods - i strongly suggest you come up with something better that actually runs on background threads instead of the thread you're calling them on.
The FutureValue class:
/**
* Implementation of {#link Future}, allowing waiting for value to be set (from another thread).
* Use {#link #set(Object)} to set value, {#link #get()} or {#link #get(long, TimeUnit)} to retrieve
* value.
* TODO: tests
*
* #param <T> type of awaited value
*/
public class FutureValue<T> implements Future<T> {
private static final String LOGTAG = "FutureValue";
private static final long NANOS_IN_MILLI = TimeUnit.MILLISECONDS.toNanos(1);
private volatile T value;
private volatile boolean isDone = false;
private volatile boolean isCanceled = false;
/**
* Sets value awaited by this future.
*
* #param value value
*/
public synchronized void set(T value) {
this.value = value;
isDone = true;
notifyAll();
}
/** {#inheritDoc} */
#Override
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
isCanceled = true;
notifyAll();
return !isDone;
}
/** {#inheritDoc} */
#Override
public boolean isCancelled() {
return isCanceled;
}
/** {#inheritDoc} */
#Override
public boolean isDone() {
return isDone;
}
/** {#inheritDoc} */
#Override
public synchronized T get() {
while (!isDone) {
if (isCanceled) {
return value;
}
try {
wait();
} catch (InterruptedException ignored) {
Log.w(LOGTAG, "We're just gonna ignore this exception: " + ignored, ignored);
}
}
return value;
}
/** {#inheritDoc} */
#Override
public synchronized T get(long timeout, #NonNull TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
final long targetTime = System.nanoTime() + unit.toNanos(timeout);
while (!isDone && !isCanceled) {
try {
final long waitTimeNanos = targetTime - System.nanoTime();
if (waitTimeNanos <= 0) {
throw new TimeoutException();
}
wait(waitTimeNanos / NANOS_IN_MILLI, (int) (waitTimeNanos % NANOS_IN_MILLI));
} catch (InterruptedException ignored) {
Log.w(LOGTAG, "We're just gonna ignore this exception: " + ignored, ignored);
}
}
return value;
}
}
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'm trying to have user registration for my android application. I'm able to successfully make user register and store their details in Cloudant. They can also login using the phone they had used to register.
However, when I try using another phone to login the account, it doesn't work. Is possible to replicate all data from Cloudant so that users can also login to other phones too? Here is my code:
public class CloudantConnect {
private static final String TAG = CloudantConnect.class.getSimpleName();
private static final String DATASTORE_DIRECTORY = "data";
private Datastore datastore;
private IndexManager indexManager;
private Replicator push_replicator;
private Replicator pull_replicator;
private Context context;
private final Handler handler;
private RegisterActivity register_listener;
public CloudantConnect(Context context, String datastore_name) {
this.context = context;
// Set up information within its own folder in the application
File path = this.context.getApplicationContext().getDir(DATASTORE_DIRECTORY, Context.MODE_PRIVATE);
DatastoreManager manager = new DatastoreManager(path.getAbsolutePath());
try {
this.datastore = manager.openDatastore(datastore_name);
} catch (DatastoreNotCreatedException e) {
Log.e(TAG, "Unable to open Datastore", e);
}
// Reach here if datastore successfully created
Log.d(TAG, "Successfully set up database at" + path.getAbsolutePath());
// Set up replicator objects
try {
this.reloadReplicationSettings();
} catch (URISyntaxException e) {
Log.e(TAG, "Unable to construct remote URI from configuration", e);
}
this.handler = new Handler(Looper.getMainLooper());
Log.d(TAG, "CloudantConnect set up " + path.getAbsolutePath());
}
/**
* Creates new document for user details database storage
* #param user to store user details into database
* #return document of user details stored
*/
public User createNewUserDocument(User user) {
MutableDocumentRevision revision = new MutableDocumentRevision();
revision.body = DocumentBodyFactory.create(user.asMap());
try {
BasicDocumentRevision created = this.datastore.createDocumentFromRevision(revision);
return User.fromRevision(created);
} catch (DocumentException e) {
return null;
}
}
/**
* Sets replication listener
*/
public void setReplicationListener(RegisterActivity listener) {
this.register_listener = listener;
}
/**
* Start push replication
*/
public void startPushReplication() {
if(this.push_replicator != null) {
this.push_replicator.start();
} else {
throw new RuntimeException("Push replication not set up correctly");
}
}
/**
* Start pull replication
*/
public void startPullReplication() {
if(this.pull_replicator != null) {
this.pull_replicator.start();
} else {
throw new RuntimeException("Pull replication not set up correctly");
}
}
/**
* Stop running replication
*/
public void stopAllReplication() {
if(this.push_replicator != null) {
this.push_replicator.stop();
}
if(this.pull_replicator != null) {
this.pull_replicator.stop();
}
}
/**
* Stop running replication and reloads replication settings
* from the app's preferences.
*/
public void reloadReplicationSettings() throws URISyntaxException {
this.stopAllReplication();
// Set up new replicator objects
URI uri = this.createServerURI();
// Push replication
PushReplication push = new PushReplication();
push.source = datastore;
push.target = uri;
push_replicator = ReplicatorFactory.oneway(push);
push_replicator.getEventBus().register(this);
// Pull replication
PullReplication pull = new PullReplication();
pull.source = uri;
pull.target = datastore;
pull_replicator = ReplicatorFactory.oneway(pull);
pull_replicator.getEventBus().register(this);
Log.d(TAG, "Set up replicators for URI:" + uri.toString());
}
/**
* Calls when replication is completed
*/
public void complete(ReplicationCompleted rc) {
handler.post(new Runnable() {
#Override
public void run() {
if(register_listener != null) {
register_listener.replicationComplete();
}
}
});
}
/**
* Calls when replication has error
*/
public void error(ReplicationErrored re) {
Log.e(TAG, "Replication error:", re.errorInfo.getException());
handler.post(new Runnable() {
#Override
public void run() {
if(register_listener != null) {
register_listener.replicationError();
}
}
});
}
}
It looks like you've got all the code there to do replication. Do you actually call startPullReplication() from somewhere?
If you want your complete and error callbacks to run when replication completes/fails, you will need to add the #Subscribe annotation on them both so they're triggered when the events are put on the EventBus.
I have a project: myApp
these files...
- myFragment.java.
- myDialogFragment.java.
- myAsyncTask.java
I have a project: myLibrary
This project "is Library" of "myApp"
I have...
- myMethodsToUpload.java
One of these methods, have a While bucle for write the file on php server.
Everything works like magic! :)
and the reason for the file structure is to make the library reusable.
but...
How can I send the increments of a value inside of this While bucle, to myAsyncTask.java?
Considering that...
what I want to do... is to make "myMethodsToUpload.java", reusable.
Some code...
myFragment.java
myDialogFragment df = new myDialogFragment();
df.setMyThings(new myAsynctask(), myParameters);
df.setTargetFragment(this, 0);
df.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
.
myDialogFragment.java
public class myDialogFragment extends DialogFragment {
myAsyncTask async;
public void setMyThings(myAsynctask inAsynctask, String[] inArray){
async = inAsynctask;
async.sendFragment(this);
parameters = inArray;
}
//...
//Only called from "myAsyncTask.java"
public void updateFromAsyncTask(Integer porcent){
progressbar.setProgress(porcent);
}
//...
}
.
myAsyncTask.java
public class myAsynctask extends AsyncTask<String, Integer, String> {
void sendFragment(myDialogFrament inFragment){
myDialogFrament = inFragment;
}
//...
#Override
protected String doInBackground(String... inArray) {
String urlPHP = inArray[0];
String pathImg = inArray[1];
String paramValue = inArray[2];
String msj = "";
try {
methodsToUpload up = new methodsToUpload(urlPHP);
up.connectNow();
up.insertFile(pathImg);
up.insertParams("pName", paramValue);
up.insertFinish();
msj = up.coonectClose();
} catch (Exception e) {
e.printStackTrace();
}
return msj;
}
//Called from "myMethods.java"
public void updateFromAsyncTask(int porcent){
publishProgress(porcent);
}
#Override
protected void onProgressUpdate(Integer... inPorcent) {
if(myDialogFragment == null){
return;
}
myDialogFragment.updateFromAsyncTask(inPorcent[0]);
}
}
.
myMethodsToUpload.java
public class myMethodsToUpload {
//...
public myMethodsToUpload(String url_in){
this.url = url_in;
}
public void insertFile(String path) throws Exception {
//...
//...
while (bytesRead > 0) {
salidaStream.write(buffer, 0, bufferSize);
sendedPorcent += bytesRead;
completedPorcent = (int) (sendedPorcent * 100 / fileSize);
//This line doesn't work...
//because myAsyncTask.java, is in another project.
myAsyncTask.updateFromAsyncTask(completedPorcent);
bytesAvailable = archivoStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = archivoStream.read(buffer, 0, bufferSize);
}
//...
//...
}
}
.
I've already tried...
"MyLibrary" -> propeties -> java build path -> projects -> add -> Project(myApp)
but...
throws me errors:
W/System.err(32469): at java.util.concurrent.FutureTask.run(FutureTask.java:237)...
ThreadPoolExecutor.runworker...
etc.
And, in the status bar of eclipse appears every moment "Building Workspace (X%)"
I'm a newbie, but I think the error happens because "MyLibrary" is Library of "MyApp", and I'm trying use "java build path".
So... how can I resolve this?, I'm lost!!!
sorry by my english... thanks in advance! :)
Here is a simple exemple :
Your AsyncTask class :
private CallBack mCallback;
public static interface CallBack {
public void updateValue(int value);
}
public void setCallBack(CallBack callBack){
this.mCallBack = callBack;
}
#Override
protected void onProgressUpdate(Integer... inPorcent) {
mCallback.updateValue(inPorcent[0].intValue());
}
Your fragment class :
public class Fragment extends Fragment implements Callback {
private AsyncTask yourAsyncTask;
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
yourAsyncTask = new AsyncTask();
yourAsyncTask.setCallBack(this);
yourAsyncTask.excecute();
}
#Override
public void updateValue(int value){
Log.e(TAG,"Value : " + value);
}
}
EDIT 1 :
public class AdsHttpRequest {
private static final String TAG = AdsHttpRequest.class.getSimpleName(); // log
private GetHttpTask mGetAsyncTask;
private static AdsHttpRequest mInstance;
private OnGetRequestListener mCallBack;
private static final String SUCCESS = "success";
private static final String SUCCES = "succes";
private static final String FAILED = "fail";
/**
* #return a singleton instance of {#link AdsHttpRequest}
*/
public static AdsHttpRequest getInstance() {
if (mInstance == null) {
synchronized (AdsHttpRequest.class) {
if (mInstance == null) {
mInstance = new AdsHttpRequest();
}
}
}
return mInstance;
}
/**
* Initialize the {#link AsyncTask}, set the callback, execute the task
*
* #param url
* url for the request
* #param callback
* {#link OnGetRequestListener} for feed back
*/
public void post(String url, OnGetRequestListener callback) {
mCallBack = callback;
if (mGetAsyncTask == null) {
mGetAsyncTask = new GetHttpTask();
} else {
cancelGetTask();
mGetAsyncTask = new GetHttpTask();
}
mGetAsyncTask.execute(url);
}
/**
* cancel the {#link AsyncTask} if it's still alive <br>
* <b>see </b> {#link Status}
*/
public void cancelGetTask() {
if (mGetAsyncTask != null && mGetAsyncTask.getStatus().equals(Status.RUNNING)) {
mGetAsyncTask.cancel(true);
}
mGetAsyncTask = null;
}
private AdsHttpRequest() {
super();
}
/**
* Actually construct and launch the HTTP request
*
* #param url
* url of the request
* #return response of the server
*/
private String getResponseFromUrl(String url) {
String xml = null;
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
xml = EntityUtils.toString(httpEntity);
} catch (Exception e) {
Log.e(TAG, "", e);
}
return xml;
}
/**
* Manage the http request in background
*
* #param String
* url for the request
*/
private class GetHttpTask extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
if (params[0] != null) {
return getResponseFromUrl(params[0]); // return the response of the server
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
if (result.contains(SUCCES) || result.contains(SUCCESS)) {
mCallBack.onGetRequestResult(SUCCESS);
} else {
mCallBack.onGetRequestResult(FAILED);
}
}
}
}
}
The way that I'm doing this consume more memory, time, threads? (I'm guessing)
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.