I have two classes, both are not activity, one is to perform database operation and one is to forward the values as mediator class. DataBase class calling openOrCreateDataBase method from a 3rd Connectivity class.
contextWrapper.openOrCreateDatabase(sqlDBName, MODE_PRIVATE, null);
but here since this class is not activity, i am unable to pass parameter of ContextWrapper. Is there any other way to open database. I have tried,
sqLiteDatabase = SQLiteDatabase.openDatabase(path, factory, flags);
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(file, factory);
But these not working for me.
thanks
You can't create a database without Context. This is one of the thing context exists for: it allows you to access shared preferences, database and so on.
Yes there is a way. In the constructor of the db wrapper class you can add the ContextWrapper as a parameter and call it, like this:
public ctor(ContextWrapper wrapper) {
SQLiteDatabase db = wrapper.openOrCreateDatabase("myDB", wrapper.MODE_PRIVATE, null);
}
Related
I am new to database in Android but now I know the way to apply CRUD (Create, Read, Update, Delete) operations on an activity but in case of fragments, getWritableDatabase() is not working. I would like to know the best way to apply CRUD operations in fragments.
In activities, I made a class to handle database operations and then made its' object in the activity and was able to work on it thereafter.
Try
DbHelper helper = DbHelper.getInstance(getContext());
SQLiteDatabase db = helper.getWritableDatabase();`
instead
Here I tried to illustrate calling db from fragment class :
SQLiteDatabase db = context.getWritableDatabase(); // 'this' for activity
Initialize db object inside onAttach() or onActivityCreated() any other method :
[onAttach() is preferable]
public Cursor getAllData() {
Cursor result = db.rawQuery("SELECT * FROM "+ TABLE_NAME, null);
return result;
}
Tips: You should use these type of db calls into the background threads, not to invoke or block the UI thread which will result a better performance.
My app uses an SQLite DB, wrapped with a SQLiteOpenHelper and a ContentProvider. I added a sign-in feature to the app, and now I want every user to only be able to see his own data. The way I thought to achieve this is for the app to create a separate DB for every user that signs in to the app, and use the user's ID in the filename of the database.
I have this ContentProvider:
public class MyProvider extends ContentProvider {
//...
#Override
public boolean onCreate() {
dbHelper = new MyDBHelper(getContext());
return true;
}
I have this SQLiteOpenHelper:
public class MyDBHelper extends SQLiteOpenHelper {
Which has this constructor:
public MyDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
Up until now the app couldn't have multiple users, so it just had one database. so DB_NAME was always the same String. I now tried to set it like that:
private static String UID = FirebaseAuth.getInstance().getCurrentUser().getUid();
public static final String DB_NAME = String.format("data%s.db", UID);
(As you can see, I'm using Firebase Authentication)
but this resulted in a crash, because apparently the content provider is created on app start, before the user has authenticated. (so user is null. Yeah, I should check that user is not null before I try to call getUid(). but this won't make this thing work)
So this doesn't seem like the right approach. How can I use a different DB according to the signed user? Can I make the content provider to first be created after a user has authenticated?
I could also just keep everything in one database and add a UID column. But will this be protect the different users' data good enough from each other? Also, this would mean a lot more code changes.
How can I use a different DB according to the signed user?
The simple solution is to get rid of the ContentProvider. The only reason to use a ContentProvider is if you are going to be serving this data to other apps.
Also, I would be wary of just taking getUid() and putting it in a filename. You are not in control over what getUid() returns, and it might someday contain characters that are invalid in filenames.
Can I make the content provider to first be created after a user has authenticated?
No, sorry.
Seems that the right solution here is to not use ContentProviders. So I accepted the other answer.
But to answer my actual question, for people that are determined to make different DBs work with one ContentProvider, here is how it can be done:
I changed the custom SQLiteOpenDBHelper's constructor to also take a uid:
public MyDBHelper(Context context, String uid) {
super(context, String.format(DB_NAME, uid), null, DB_VERSION);
UID = uid;
}
and I changed the onCreate of my ContentProvider not to create the DBHelper. I created this function that initializes the DBHelper instead:
public void initDB(Uri uri) {
String uid = uri.getPathSegments().get(0);
if (dbHelper == null){
dbHelper = new MyDBHelper(getContext(), uid);
} else if (!uid.equals(dbHelper.UID)){
dbHelper.close();
dbHelper = new MyDBHelper(getContext(), uid);
}
}
and I call this method at the start of the query, insert, update and delete methods.
So the DBHelper which holds the open connection to the DB, is initialized whenever the content provider is preforming some action on the DB but there is either not yet an existing connection with the DB, or the connection is with a DB of a different user.
This is not the right way to solve this problem and this probably has consequences in some cases. But I didn't want to leave the question I asked unanswered.
I'm trying to use the SQLite rawQuery method, based on the code here:
Android record exists() in database?
...but I get "The method rawQuery(String, String[]) is undefined for the type OdaaDBOpenHelper
Authorize_Activity_DynamicControls.java" with this code:
OdaaDBOH = new OdaaDBOpenHelper(this);
. . .
private boolean RecordExists(String _id) {
// This:
Cursor cursor = OdaaDBOH.rawQuery("select 1 from NAPOLEON_DYNAMITE_TABLE where _id=%s",
// or this:
//Cursor cursor = OdaaDOdaaDBOpenHelperBOH.rawQuery("select 1 from
NAPOLEON_DYNAMITE_TABLE where _id=%s",
new String[] { _id });
boolean exists = (cursor.getCount() > 0);
cursor.close();
return exists;
}
. . .
// from referenced unit:
public class OdaaDBOpenHelper extends SQLiteOpenHelper {
I tried it both using the instance name of my SQLiteOpenHelper-derived class, and using the class
name itself (commented out in the code above).
What must I do to implement rawQuery() or cause it to be recognized/acknowledged?
rawQuery() is a method on SQLiteDatabase, not SQLiteOpenHelper. Call getReadableDatabase() or getWritableDatabase() on your SQLiteOpenHelper to get a SQLiteDatabase.
From that question correct answer:
Consider that mDb is your SqlLiteDatabase class...
OdaaDBOH should be an instance of a SQLiteDatabase if you want to use rawQuery on it, most likely you are providing the implementation of a SQLiteOpenHelper there, which of course doesn't have a method rawQuery.
I use a ContentProvider in my app and everything works great except for one little issue. I have a backup and restore function that backs up the database to a file on the SD card and then those backup files can be restored to overwrite the current database. This whole process is working, but the ContentProvider still holds the reference/cache to the original database once one of the old backup files is restored. I can't seem to find a way to refresh or reload the database reference in the ContentProvider. I know the restore works because I can see the records in the db with SQLite Editor and when I close and re-open the app, it displays the correct records.
Does anybody know a way to do this? Is there a way to close and re-open the ContentProvider that I'm not seeing?
If you are targeting >= API 5 you can get a reference to your ContentProvider via a ContentProviderClient, and run a method specific to your implementation:
ContentResolver resolver = context.getContentResolver();
ContentProviderClient client = resolver.acquireContentProviderClient("myAuthority");
MyContentProvider provider = (MyContentProvider) client.getLocalContentProvider();
provider.resetDatabase();
client.release();
Add the reset method to your ContentProvider implementation:
public void resetDatabase() {
mDatabaseHelper.close();
mDatabaseHelper = new MyDatabaseOpenHelper(context);
}
Are you maintaining a reference to the actual SQLiteDatabase in your content provider (something like calling SQLiteOpenHelper.getWritableDatabase() in onCreate() and then keeping that reference)? Or do you get the DB object from someplace like a helper in each provider method?
Typically, if you only keep a local reference to the helper and get the writable/readable database instance inside of each method as needed then this problem should go away. If not, perhaps we can take a look at the provider code?
Hope that Helps!
Here is my solution.
public class DataProvider extends ContentProvider {
private DataDbHelper dbHelper;
#Override
public boolean onCreate() {
// nothing here
return true;
}
private DataDbHelper getDbHelper() {
if (dbHelper== null) {
// initialize
dbHelper = new DataDbHelper(getContext());
} else if (dbHelper.getReadableDatabase().getVersion() != DataDbHelper.VERSION) {
// reset
dbHelper.close();
dbHelper = new DataDbHelper(getContext());
}
return this.mOpenHelper;
}
}
query(), insert(), update(), delete() use getDbHelper() to obtain an SQLiteDatabase
The full code of my Android app is available here if you need more info.
You can also simply use the delete method without a selection:
context.getContentResolver().delete(YourProvider.CONTENT_URI, null, null);
I have a DB that I use in all my activities. There is only one record in the DB.
In the first activity it is opened or created and then put in my globally used object like this
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// first get the current data from the DB
myDBAdapter = new MyDBAdapter(this);
GlobalVars.myDBAdapter = myDBAdapter; // we store the DBAdapter in our global var
myDBAdapter.open();
Cursor cursor = myDBAdapter.fetchMainEntry();
startManagingCursor(cursor);
// if there is no DB yet, lets just create one with default data
if (cursor.getCount() == 0) {
createData();
cursor = myDBAdapter.fetchMainEntry();
startManagingCursor(cursor);
}
Now in another activity I access the already open DB like this...
GlobalVars.myDBAdapter.updateMainEntry(1,.....);
I do not close the DB when leavin one activity to go to the next. The DB is just accessed (since it has been opened at the very first activity).
Only when leaving the app I clode the DB like this...
#Override
protected void onDestroy() {
super.onPause();
myDBAdapter.close();
}
The background why I am also asking this is I get this error...
Finalizing cursor android.database.sqlite.SQLiteCursor#48106730 on
mainEntry that has not been deactivated or closed
and it seems that my app crashes on certain devices - but I can't find the reason for it during debugging.
Is that correct and best practice, or do I have to close the DB when I leave the activity and open it when entering the next activity when switching between activities?
Many thanks!
The best thing (I have it tested in a few apps of mine) is to:
declare database adapter as an activity's instance variable:
private DBAdapter mDb;
create it and open in activity's onCreate():
mDb = new DBAdapter(this);
mDb.open();
close it in activity's onDestroy():
mDb.close();
mDb = null;
Works like charm.
A side note: the Application class onTerminate "will never be called on a production Android device" according to the docs.
You can use sqlite as DB for your application. Then you have to create a common class for your whole application Like " DBAdapter ". Then write codes to manipulate the DB. After that you just have to create DBAdapter's object in your activity. Thus you can access your DB from every activity of your app. http://developer.android.com/guide/topics/data/data-storage.html#db This link can be useful.