I'm creating an queued upload manager. With this answer to my previous question's guidance, I'll be using a Service, to upload these images. It was recommended that I use a database to keep track of the successfully uploaded, and the pending files.
My initial research leads me to believe that I'll want to create a Bound Service, so I can update my UI once the photos have uploaded, as well as a Started Service, so it can run independent of my Activities that create it. It seems that I'll also need to start it in its own process via the process=":something" directive in the app manifest.
My question is, what would the best way of sharing an SQLite (unless there is a better way) database amongst the N activity clients and the uploader service?
I envision it working like this, in pseudo code:
// in an app
writeRecordToDb( . . . );
// start service
if( service doesn't exist )
{
// start service, and bind
}
// in the service:
if( shared db has another row )
{
doDownload( . . . );
if( download worked )
{
notifyActivity();
if( db has another row )
doDownload( . . . );
}
else
{
retryDownload( . . . );
}
}
Is this the correct way to go about this? I'm again attempting to circumvent the problem of having multiple Activity instances request photo uploads when there is little to no cellular signal. I've just finished reading though the Service and Bound Service docs, and I'm feeling good, but not great.
My initial research leads me to believe that I'll want to create a Bound Service
I wouldn't.
so I can update my UI once the photos have uploaded
You do not need to use the binding pattern to update the UI. You can:
send a local broadcast using LocalBroadcastManager that the activity picks up, or
invoke a PendingIntent supplied in an Intent extra on startActivity() by the activity, or
give Square's Otto event bus a try (looks interesting, but I haven't used it yet)
etc.
as well as a Started Service, so it can run independent of my Activities that create it
Which is why you should not bother with binding, as you do not need that, but you do need to start the service.
My question is, what would the best way of sharing an SQLite (unless there is a better way) database amongst the N activity clients and the uploader service?
Option #1: Keep your SQLiteOpenHelper in a static data member
Option #2: Use a ContentProvider wrapper around your database
Is this the correct way to go about this?
Using a database as a communications channel between components is akin to two next-door neighbors communicating with each other using a banner towed by a biplane. Yes, it works. However, it is slow and expensive.
(also, there's never a biplane when you need one, but I digress...)
If you wish to use a database as a backing store for pending downloads, in case there is some interruption (e.g., user powers down the device) and you wish to pick up those downloads later on, that's fine. However, the service will know what to download by the command you send to it via startService().
CommonsWare covers basically everything you need... but here is some code illustrating the two options just in case there is any confusion.
Keep your SQLiteOpenHelper in a static data member.
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private static final String DATABASE_NAME = "databaseName";
private static final String DATABASE_TABLE = "tableName";
private static final int DATABASE_VERSION = 1;
private Context mCxt;
public static DatabaseHelper getInstance(Context ctx) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you dont accidentally leak an Activitys
* context (see this article for more information:
* http://developer.android.com/resources/articles/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(ctx.getApplicationContext());
}
return mInstance;
}
/**
* constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context ctx) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.mCtx = ctx;
}
}
Then in your Service/Activity, keep a reference to your DatabaseHelper and call getInstance() on it.
Wrap the database in a ContentProvider. If you already have one implemented, then you can simply use
mContext.getContentResolver().query(...);
This works because Activity and Service both extend Context (which holds a reference to the ContentResolver).
Related
The work flow of my program is:
Launch app
Splash screen, check the server api, from the api get a list of file name
Download some of the file in file list , remove the downloaded file name from the list
App opened
when the download is finished , jump to main page that will start download another file in the list
The problem is , the list I was keep in the download manager , when I select don't leave activities in android setting , it will be killed. If I need a class that is some Data Class , that means I put a share data (A several hash map , array list) in it, and it keep updating (delete after async download finish) , and it never get killed. How can it be done? Thanks
The more general problem you are encountering is how to save state across several Activities and all parts of your application. A static variable (for instance, a singleton) is a common Java way of achieving this. I have found however, that a more elegant way in Android is to associate your state with the Application context. As you know, each Activity is also a Context, which is information about its execution environment in the broadest sense. Your application also has a context, and Android guarantees that it will exist as a single instance across your application. The way to do this is to create your own subclass of android.app.Application, and then specify that class in the application tag in your manifest. Now Android will automatically create an instance of that class and make it available for your entire application. You can access it from any context using the Context.getApplicationContext() method (Activity also provides a method getApplication() which has the exact same effect):
class MyApp extends Application {
private String myState;
public String getState() {
return myState;
}
public void setState(String s) {
myState = s;
}
}
class Blah extends Activity {
#Override public void onCreate(Bundle b) {
...
MyApp appState = ((MyApp) getApplicationContext());
String state = appState.getState();
...
}
}
This has essentially the same effect as using a static variable or singleton, but integrates quite well into the existing Android framework. Note that this will not work across processes (should your app be one of the rare ones that has multiple processes).
I have a list of custom objects ... these objects simple monitor a few of my websites for any new data that users may have added. I keep these objects in a static List<> that is a member of a final class .... I create a service to go through the list at a given interval and update as needed.
final public class MonitorHandler {
public static List<MonitorSettings> monitorsSettings = new ArrayList<MonitorSettings>();
ect....
}
everything works perfectly fine for some time ... no exceptions come up at all. But if I let the app run for a period of time .. like say over night the list is empty ... like the objects have been garbage collected or removed by android. Why is this?? don't static objects persist even after the app is closed or sent to backround?
Why is this?
Your process was terminated.
don't static objects persist even after the app is closed or sent to backround?
Only while the process is running. Your process can be terminated once it is no longer in the foreground. Whether that takes seconds or hours will depend upon what is going on with the device.
If you have data that you want to hang around, please store it in a persistent fashion, using a database, file, or SharedPreferences.
Why is this?? don't static objects persist even after the app is closed or sent to backround?
No, if your app was actually closed by system, this might happen. You should log in your Service.onDestroy() to make sure this is really the cause, but it is not guaranted that onDestroy will actually be called.
So you should persist your data in some storage, preferable SharedPreferences or DB - sqlite.
You haven't shown enough code to be certain, but it's very likely that you are not holding a reference to your data anywhere in your application except the static elements of the MonitorHandler class. The Dalvik VM will unload unreferenced classes from time to time, so the next time you access the class it is reloaded, the static initialisation is re-run, and you end up with a new, empty list.
The bottom line is that the only class that you can rely on not being unloaded during the execution of your Android application is the Application class itself.
If you want to retain data in your application the only approach that works is to maintain a reference to it from your Application class. In the case of your list the simplest way to do that is put it into your Application class directly. With more complex data you would probably want to create an instance of a separate class and hold a reference to it statically in your Application class. Here is an example showing two different ways to create the data, one statically, one lazily.
import android.app.Application;
import android.content.Context;
import com.example.DiskLruImageCache;
import java.util.ArrayList;
import java.util.List;
public class TestApp extends Application {
static final public int CACHE_SIZE = 1024 * 1024 * 15; // 15MB
private static List<String> stringList = new ArrayList<String>();
private static DiskLruImageCache imageCache = null;
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = this;
}
public static List<String> getStringList() {
return stringList;
}
static public DiskLruImageCache getImageCache() {
if(imageCache == null) {
synchronized(context) {
if(imageCache == null)
imageCache = new DiskLruImageCache(context, "TestImages", CACHE_SIZE);
}
}
return imageCache;
}
}
Now in your Activities or Service you can simply call TestApp.getStringList() to get the list. Any changes to this will be preserved as long as the application is running.
I am looking for how to share functions and data across multiple activities within a single application. I researched the daylights out of it and find some ideology war between overriding the extend for the application and doing a singleton, neither of which I can find examples sufficient to make me understand. Basically I want to share data and share functions. All activities need the same functions and data so this is not one activity sharing data with another activity. It is all activities needing to have access to the same functions and data.
What I want to know is what is the way to go and how do I do it. I need to see what I need to do in my 34 activities, what the class that is going to be common looks like, and what the Manifest entry needs to be. I also need to be sure the common data area will not be closed by the OS.
This is my first Android - Java program and now find my 15,000 line, 34 activity application needs some structure. I know, should have done things differently but the app works really well with two exceptions. One is that it is structurally a mess. Two is that the fact it is a mess is making it hard to fix one behavior I would like to fix.
This is a GPS based application for racing sailboats. It is timing critical and every activity basically runs a once a second loop inside the location manager onLocationChanged function. That part is fine and I do not want to put the GPS code in one place. The problem is that most activities need to filter the data so a lot of code is copied and pasted to the activities. The filter needs history so it needs to remember a state. There are other functions that are used by several activities so these have been copied as well. Think of a function that averages the last three GPS speed readings. It needs to save some history, do its thing, and give a result. All activities need to do the exact same thing. All this works but the problem is that the averaging starts over every time I switch activities because every activity has its own filter. That gives a glitch in the data that I need to get rid of. I need common place to save the data and hopefully a common place to run the filtering and other functions that are common. If every activity can call the filter function that is using common state data, there will be no glitch across activity changes.
I would appreciate some guidance.
Why you don't just make a Class with only static functions, passing needed Parameters? An example if you want to show an ErrorDialog
public class SharedHelper{
public static Dialog showErrorDialog(Context ctx, String message, String title, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener){
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setMessage(message).setTitle(tilte);
if (okListener != null){
builder.setPositiveButton(R.string.button_positive, okListener);
}
if (cancelListener != null){
builder.setNegativeButton(R.string.button_negative, cancelListener);
}
return builder.show();
}
}
Singletons are (from my point of view) one of the uglyest design pattern and will bite you sooner or later. Putting anything in Application requires you to cast it everytime to the Special Application class you designed. A class with only statics however is very flexible in its usage and doesn't need an instance to work.
For the storage-issue:
lookup "SharedPreferences" & "SQLite" and decide afterwards which storage-type suits your needs more.
For the methods-issue:
This question is a bit more complex and there are different ways to do it. For example you could write a parent-class that implements all your globally needed questions and you let all your activity-classes inherit from it.
public class MyParentActivity extends Activity {
public void myMethod() {
}
}
and:
public class Activity1of34 extends MyParentActivity {
myMethod();
}
I think what this comes down to is not an Android problem but an Object-Oriented Programming problem. If I understand the situation correctly, I'm betting the best solution would be to take your shared filter and create a new Filter class that is instantiated within each Activity (this is likely more manageable than a singleton, but not having seen your use case, it's hard to say for sure). If you need to centrally track the averaging, you can simply create a static variable within the Filter class that maintains the same value during the life of the application. If you really want to maintain that average (even past the application's current lifecycle), you can persist it in a database or other local data options. However, I don't see any reason to put everything in a singleton just to maintain that average. Singletons (and all static data structures) can be potentially troublesome if used incorrectly.
I, for one, do not mind the singleton pattern. Of course as everything else it should not be abused.
This is the construction I use for my shared objects. My app is divided into modules this way but can just as well be used in your case.
public class SharedDataObject {
private Context context;
private static SharedDataObject instance;
public static SharedDataObject getInstance() {
if (instance == null) throw new RuntimeException("Reference to SharedDataObject was null");
return instance;
}
public static SharedDataObject createInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new SharedDataObject(context.getApplicationContext());
}
// notice the constructor is private
private SharedDataObject(Context context) {
this.context = context;
}
...
public void myMethod() {
// do stuff
}
}
Notice that it uses the application context, that means among other things, means that the context owned by SharedDataObject cannot be used for GUI operations. But, the context will live for the entire lifetime of the application, which is nice.
Furthermore I hate having to pass a context everytime I wish to call methods on my SharedDataObject, thus I have a splashscreen calling SharedDataObject.createInstance() on all my modules.
Once an instance is create, I can call:
SharedDataObject.getInstance().myMethod();
Anywhere in my code, regardless of a context being present or not (from the place calling this code that is).
Recently I started to get following error in my application. This is NOT in any specific place and I can reproduce only when loop through all data read/write functionality. It comes up pretty much anywhere.
09-14 08:52:15.089: WARN/SQLiteConnectionPool(19268): The connection pool for database '/data/data/com.nnn/databases/data.db' has been unable to grant a connection to thread 1 (main) with flags 0x5 for 30.000002 seconds.
Connections: 0 active, 1 idle, 0 available.
Is there any way to avoid this? I understand that somehow I exhause all connections to database?
I'm using approach #1: http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html
And my database code looks like this:
public class DatabaseHelper extends SQLiteOpenHelper
{
private final static String LOG_TAG = "com.nnnn.data.DatabaseHelper";
private static final String DATABASE_NAME = "data.db";
private static final int DATABASE_VERSION = 260;
private static SQLiteDatabase databaseInstance;
public DatabaseHelper()
{
super(MyApplication.Me, DATABASE_NAME, null, DATABASE_VERSION);
}
public static synchronized SQLiteDatabase getDatabase()
{
if (databaseInstance == null) databaseInstance = (new DatabaseHelper()).getWritableDatabase();
return databaseInstance;
}
I have never seen this error. But, I would like to point out that you are not using approach #1 from the page you linked to, and I do believe that your implementation is causing you trouble.
In the design pattern you linked, their approach is to ensure that only one DatabaseHelper instance exists throughout your applications life-cycle. Your approach is different, you are trying to ensure that only one SQLiteDatabase instance exist though-out the applications life-cycle.
So in summery, you are trying to reuse the same database connection for everything(which is not a good idea, and I would strongly suggest you change this approach), but in reality, I believe this approach is causing some issues between SQLiteOpenHelper and your static instance variable.
You are creating new instances of DatabaseHelper in your getDatabase method, sure, your databaseInstance is static, but there is a potential problem with SQLiteOpenHelper instances here. For example, lets say the databaseInstance instance is closed and set to null by a piece of code. Aka, somebody is cleaning up after them-self, then the next time getDatabase() is called, you will create a new database helper, and more importantly, a new SQLiteOpenHelper.
Try removing the synchronized from the getDatabase method. This should not be necessary.
I believe that the only correct way you can handle SQLite DB in Android is to use ContentProvider. For your code you can try to store DatabaseHelper in that static field instead SQLiteDatabase instance, as described in the first approach.
Some backstory on my applications code organization:
Right now I'm kind of using a ad-hoc ORM for DB access, where each table is split up into a model (just a plain Java class not inheriting from any Android components) which covers normal CRUD activities.
I'm not using a content provider as all this data is private to the application.
I'm keeping a singleton of my databaseopenhelper, and the application context in my application's Application class
class MyApplication extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
dbHelper = new DatabaseHelper(this); // Subclass of SQLiteOpenHelper
}
public static Context getContext(){
return mContext;
}
All activities that touch the db open the database onResume, and close it onPause.
This seemed to be working fine until I started unit testing, when I ran into the first problem with using singletons - breaking encapsulation (specifically not being able use RenamingDelegatingContext to test with a specific test database).
So,
1) Something tells me if I can't unit test properly, my architecture is bunk - but I can't think of a better way to do this (short of passing context and dbhelpers explicitly - which is a pain)
2) If it is not an entirely crazy idea, what would be the best way to go about unit testing this setup?