I am using three activities which are opened at the same time. All activities are retreive data from sqlite. I don't close or re open my connection when i am going from activity a->b or from b->c.
I just dispose my db when activity is destroying.
Activity A
SqliteConnection db;
OnCreate method
db = new SqliteConnection(mypath);
OnDestroy
db.Dispose();
db=null;
Onbuttonclick
startActivity(new Intent(this, ActivityB));
Same code is running when i am going from activity b->c.
Inside the same activity i use sqlite plenty of times.
Is this a good practice? Should i dispose my connection immediatelly after a use? Or should i close my connection on pause and reopen on resume? Or can i pass the same opened connection to the next activity? Which is the best approach?
Question modifieded
class databaseHelper
{
private static SqliteConnection db;
public static SqliteConnection openDatabase(Context context)
{
if(db==null)
db = new SqliteConnection(mypath);
return db;
}
}
And inside my activity on create
databaseHelper.openDatabase(this).myquery....
I don`t roll with Java nor xamarin. Here is a Kotlin code, it is pretty self-explanatory.
class DatabaseHelper { //Public class
companion object { ///This is equiavalent to java static.
private var instance: YourDatabase? = null
fun getDbInstance(context: Context): YourDatabase? //This functions returns the initialized DB instance.
{
if(instance == null)
instance = YourDatabase(context) // initializing the DB only one time
return instance
}
}
}
Just create a public class and name it for example "DatabaseHelper". Inside the class, create one static variable of your database type. Create a public function that returns the static variable. Inside the function, first, check if the static instance is null and if it is null, then initialize it with your database instance. This way, when you need to use your database instance, just, access the static function, provide it with the context and it will return you the initialized database instance.
In Kotlin
DatabaseHelper.getDbInstance(this).yourDbFunction()
UPDATE
Since this answer took off, I would like to suggest improvements to my previous solution. Instead of passing a context of activity to initialize the database, use application context. If you give an activity context to the static database instance, a memory leak will occur because the database instance holds a strong reference to the activity and the activity will NOT be eligible for garbage collection.
Proper usage:
val myDb = MyDb(applicationContext)
In general we should encapsulate access to a local store in another class such as a DAO/Repository/Service instead of having them directly in the Activity. this promotes loose coupling between views and data/network access. This also decouples the lifecycle of your DB connection, from the lifecycle of the currently running activity, giving you more control and opportunity for reuse.
Try using a bound Service to and have your DB connections there. Because it is a bound Service, it'll only be around if there is an Activity around that binds to it. Each Activity will bind to the same instance of the Service so it means you wont have duplicate connections. When no Activities are bind to it, it'll automatically be destroyed, destroying the connection along with it.
For a more modern, structured approach, using Jetpack components, you can look at https://github.com/googlesamples/android-sunflower
Related
Ok this is a bit more theoretical question.
I have PlayerRepository. This is a class that is used to make actions on my SQLite database. I've implemented there actions like select, insert, update etc.
public PlayerRepository(Context context) {
super(context, com.fixus.portals.model.Player.class);
open();
}
super in constructor is cause PlayerRepository extends Repository which is also my class. The most important part of Repository is this one
public class Repository<T> {
protected static SQLiteDatabase db = null;
protected static MainHelper helper = null;
protected Context context;
private Class<T> type;
public Repository(Context context, Class<T> classz) {
this.type = classz;
this.context = context;
if(helper == null) {
helper = new MainHelper(context.getApplicationContext());
}
}
public static void open() {
if(db == null) {
db = helper.getWritableDatabase();
}
}
}
As you can see when I create the repository I'm opening DB if it wasn't open before. To do that I need to pass the Context of the application/activity. That is not a problem.
BUT sometimes I want to use my repository out side of an activity. In some kind of tool class that need to get data. So I have two ways that I can think about
I get the data in activity and pass it to my tool class/method so I don't need to use repository in it. This is not very flexible
I need to pass context to my tool class/method. But that means that every kind of operation need to receive a context and I'm not sure this is a good way
Am I missing something ? is there any better way to handle it ?
You always need a Context to access the SQLite database so what u could do is change the constructor of that specific tool class and pass a new instance of PlayerRepository as a parameter. This prevents your tool class of needing a context itself.
Imo if u have multiple classes using the database best approach is to create a new class whose only job is doing database actions and put all the needed action inside that one.
Just create an object of this database class with the Context of the current activity the to Tools and PlayerRepository constructors. This way neither your PlayerRepository or Tools classes need Context and both can make actions on the database.
Even if you should really need Context in PlayerRepository it is always best to keep all database related functions centralized in a single class.
I understand that this is an old question but still i'll write for those like me who will pass by this in future.
In order to get rid of the context problem with repository pattern used for accessing database you can implement DI (Dependency Injection) pattern in your project. There are many reasons to do such and that question illustrates one of them.
If you implement DI you would have only one instance of database repository amongst the entire module (or app). This instance would be created in a class which has context, and injected to those classes where needed.
One of the simpliest approaches for using DI is to use Dagger 2 library. All of the related information you could find on their site.
I have a class that loads data from an API and stores/ handles the results as a POJO. It looks roughly like this (some stuff omitted that doesn't concern the question):
public class ResultLoader {
Search search;
Result result;
static ResultLoader instance;
private ResultLoader() {
}
public static synchronized ResultLoader getInstance() {
if (instance == null) {
instance = new ResultLoader();
}
return instance;
}
public void init(#NonNull Search search) {
this.search = search;
}
}
The Result object can get so large that it becomes too large to be passed around Activities via Intents, so, as you can see, I designed the ResultLoader as a Singleton so every class can access the Result object.
I simulate the Android device running low on memory by limiting background processes to one, then switch around some apps, go back to my app.
On my ResultLoader's instance, either the Result object became null or the instance was recreated; i checked this with
ResultLoader.getInstance().getResult() == null
How can this be and what can I do to prevent this?
Your objects GC'ed because android killed your app. So it destroyed all loaded classes with static data.
Next time when you get back to your app new app created and new ResultLoader class loaded with instance field equals null. When you try to get instance via getInstance method new instance created with empty result.
You should save your Result to persistent memory. E.g. when you get result save it to a file. And when you create ResultLoader check that file exists and load data from a file. Or load result again if your app got killed.
In android, your app can and will be recreated due to numerous reasons (low memory being one of them).
See this answer here to implement a saveinstancestate behavior on a custom class.
I am attempting to implement Sqlcipher for android. I am using this for multiple activities and as such am extending SQLiteOpenHelper {}.
However I am
I am aware that the OnCreate override method is not called until the database is accessed for the first time.
My oncreate method looks like
#Override
public void onCreate(SQLiteDatabase db) {
SQLiteDatabase.loadLibs(context);
File databaseFile = context.getDatabasePath("SyncableDiabetesDatabase.db");
databaseFile.mkdirs();
databaseFile.delete();
mDataBase = SQLiteDatabase.openOrCreateDatabase(databaseFile,SuperKey, null);
I am attempting to get the OnCreate Method to call this method by calling this.getWritableDatabase(SuperKey); However when I run this command i receive the error
net.sqlcipher.database.SQLiteException: attempt to write a readonly database: PRAGMA user_version = 1
I'm not exactly sure how this is possible since the command is for a writeable DB
I know sqlcipher is correctly installed, as I can put the DB creation code above outside of OnCreate and the data is stored correctly. However the file is overridden each time the activity is called than, hence why I am moving it to OnCreate which is only called once when I create the tables.
Any insight here would be more than welcome.
I can provide any additional code snippits if needed.
Thank you
You do not need to call SQLiteDatabase.openOrCreateDatabase within the onCreate method of the SQLiteOpenHelper, the database instance is already provided as a parameter to the function. An example of using the SQLiteOpenHelper can be found here.
How can I register data (like integer or poco objects) shared by all activity like the id of the user ? Have I to use a simple singleton or is there a special Android way ?
Note : I don't need to make that data persistant (no need of SharedPreferences or sqlite)
Thank you
You can create your own class that implements Application and specify this in your manifest file. In that case, every time you call getApplicationContext you will get a reference of your application that can hold any kind of information.
How to declare global variables in Android?
Sample code:
class MyApplication extends Application {
public void setMethod() {
//
}
}
((MyApplication)getApplicationContext()).setMethod()
The android way is to create a custom Application for your project. Then in onCreate of that application you initialize whatever you need, and for example from an Activity do something like:
((MyApplication) getApplication()).getMyData()
If using roboguice you can use a #Singleton injection which basically does the boilerplate of a singleton for you - that's much nicer.
I have some Android projects and most of them are connected with SQLite databases. I'm interested is it a good programming practice (or a bad habbit) to use some static class like "DatabaseHelper.class" in which I would have all static method related for database manipulation. For example
public static int getId(Context context, String name) {
dbInit(context);
Cursor result = db.rawQuery("SELECT some_id FROM table WHERE some_name = '" + name + "'", null);
result.moveToFirst();
int id = result.getInt(result.getColumnIndex("some_id"));
result.close();
return id;
}
where dbInit(context) (which is used in all my static methods for database manipluation) is
private static void dbInit(Context context) {
if (db == null) {
db = context.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
}
}
Then when I need something I can easily call those method(s) with for example
int id = DatabaseHelper.getId(this, "Abc");
EDIT: Do I have to use dbClose on every connection or leave it open per-activity and close per-activity? So do I have change that upper code to something like this?
...
dbClose();
return id;
}
private static void dbClose() {
if (db != null) {
db.close();
}
}
I would suggest you get into the habit of getting a database connection every time you need one, and releasing it back when you are done with it. The usual name for such a facility is a "database connection pool".
This moves the connection logic out of your actual code and into the pool, and allow you to do many things later when you need them. One simple thing, could be that the pool logs how long time a connection object was used, so you can get information about the usage of the database.
Your initial pool can be very simple if you only need a single connection.
I would definitely have your database related code in a separate class, but would really recommend against using a static class or Singleton. It might look good at first because of the convenience, but unfortunately it tightly couples your classes, hides their dependencies, and also makes unit testing harder.
The drawbacks section in wikipedia gives you a small overview of why you might want to explore other techniques. You can also head over here or over there where they give concrete examples of a class that uses a database access singleton, and how using dependency injection instead can solve some of the issues I mentioned.
As a first step, I would recommend using a normal class that you instantiate in your constructor, for ex:
public class MyActivity extends Activity {
private DBAccess dbAccess;
public MyActivity() {
dbAccess = new DBAccess(this);
}
}
As a second step, you might want to investigate frameworks like RoboGuice to break the hard dependency. You code would look something like:
public class MyActivity extends Activity {
#Inject private DBAccess dbAccess;
public MyActivity() {
}
}
Let us know if you want more details!
If you're going to use a singleton the very minimum requirement is that you make it stateless/threadsafe. If you use your getId method as it stands concurrent invocations could potentially cause all manner of strange bugs...
dbInit(context);
May be called for Thread A which then stops processing before hitting the query statement. Subsequently Thread B executes getId and also calls dbInit passing in a different context all together. Thread A would then resume and attempt to execute the query on B's context.
Maybe this isn't a problem in your application but I'd recommend sticking a synchronized modifier on that getId method!