How to use handle multiple fragment to use method - android

I have method select() was defined in class DBHelper that extend SQLiteOpenHelper , in MainActivity I initiated the dtabases variable dbHelper :
DBHelper dbHelper = new DBHelper(this, "DB");
I have 4 fragments each one need to call the method select() , How to do that ??
second in each fragment do I need to do the definition of the dbHelper in each fragment or is there a way to get it from the MainActivity in other words defined it once.
DBHelper dbHelper = new DBHelper(getActivity(), "DBTEST");
SQLiteDatabase db =dbHelper.getReadableDatabase();
This is the select() method :
public Cursor selectdb(String tsql) {
DBHelper dbHelper = new DBHelper(getActivity(), "DBTest");
SQLiteDatabase db =dbHelper.getReadableDatabase();
Cursor c = db.rawQuery(tsql, null);
db.close();
return c;
}

You can follow this recipe:
define an interface for retrieving the DBHelper:
public interface DBHelperSource {
DBHelper getDBHelper();
}
Declare your activity to implement the interface and implement the method to return your activity's DBHelper object.
public class MyActivity extends Activity implements DBHelperSource {
...
private DBHelper mDBHelper;
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDBHelper = new DBHelper(this, "DBTest");
...
}
#Override public DBHelper getDBHelper() {
return mDBHelper;
}
...
}
In each fragment's onAttach(Context) method, cast the context to DBHelperSource, retrieve the DBHelper object, and stash it in an instance variable for that fragment.
public class MyFragment extends Fragment {
private DBHelper mDBHelper;
...
#Override public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DBHelperSource) {
mDBHelper = ((DBHelperSource) context).getDBHelper();
}
}
#Override public void onDetach() {
super.onDetach();
mDBHelper = null;
}
}
For more information on this pattern, see the Android tutorial Communicating with Other Fragments.
There's one little gotcha: if there's a chance that the fragment is attached before the activity instantiates the DBHelper object, then you'd probably want to alter the above as follows. Instead of initializing a DBHelper field in onAttach(), just store the DBHelperSource object itself as a field. Then only call getDBHelper() when you need the DBHelper object itself (i.e., just when you're ready to call select()).
Another approach is to just define the interface to contain the select method itself, and don't bother communicating the DBHelper object to the fragments. Of course, this only works if the logic is identical among all four fragments.

Related

Android MVP-Architecture How to make Database Calls in the Model with SQLiteHelper

I'm currently working on an Android App and i choosed the MVP-Arhitecture.
My Problem is right now, that i need to read and write something from the Database in the Model, but therefor you need a reference to the Context and that is in the View. I want to know, how to get the Context from the View to the Model without breaking the MVP-Architecture (if it is possible).
Thx!!!
Something has to create the model and the presenter i.e.:
new MyModel();
new Presenter();
Usually this is the Activity
#Override
public void onCreate(Bundle savedState) {
Model model = new MyModel();
Presenter presenter = new Presenter(model, this); // this being the View
}
If you are using a database inside of your model you want to use a dependency to do this, maybe called DatabaseReader
#Override
public void onCreate(Bundle savedState) {
DatabaseReader db = new DatabaseReader(this); // this being context
Model model = new MyModel(db);
Presenter presenter = new Presenter(model, this); // this being the View
}
Now you have a class called DatabaseReader that has a Context passed to it through the constructor so you can do "database things", and this class itself is used by the model.
public class DatabaseReader {
private final Context context;
public DatabaseReader(Context context) {
this.context = context;
}
}
and
public class MyModel implements Model {
private final DatabaseReader db;
public MyModel(DatabaseReader db) {
this.db = db;
}
}

Android database helper singleton - instantiate in activity or all fragments?

I have a DBHelper class set up as a singleton:
public class DBHelper extends SQLiteOpenHelper {
private static DBHelper sInstance;
public static synchronized DBHelper getInstance(Context context) {
if (sInstance == null) {
sInstance = new DBHelper(context.getApplicationContext());
}
return sInstance;
}
private DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
databasePath = context.getDatabasePath(DATABASE_NAME).getPath();
}
}
I have a MainActivity and a number of fragments. Many of these fragments need access to my DBHelper methods
Should I be using dbHelper = DBHelper.getInstance(getApplicationContext()) in every fragment that needs database access? Instantiation will only happen once due to the singleton pattern, so I don't need to worry about the class being instantiated in every single fragment with that code
Or is it better to instantiate the DBHelper in MainActivity only, and then in any fragment that needs database access get a reference to the mainactivity and call the object methods from there? Something like this in each fragment:
mainActivity = (MainActivity) getActivity();
mainActivity.dbHelper.insertData();
Since you are sure Singleton will be instantiated in MainActivity the first approach shouldn't have any problems, you could even call getInstance(null) in your fragments
I think the most prodcutive decision will be create custom fragment class, extend it your fragment or v4.fragment, initialise in it dbHelper and use your custom fragment in your activity. it is my humble opinion :)
Codes here is one simple way to resolve concurrent problem in singleton pattern.
public DBHelper extends SQLiteOpenHelper {
// declare private constructor
// some public method
public static class Wrapper {
private static DBHelper dbHelper;
public static void init(Context ctx, Object otherArgs) {
// init DBHelper
dbHelper = new DBHelper(ctx, otherArgs);
}
public static DBHelper get(){
return dbHelper;
}
}
}
In custom Application
public MyApp extends Application{
void onCreate(){
DBHelper.Wrpper.init(this, otherArgs);
}
}
Code like this where DBHelper is needed:
DBHelper.Wrapper.get().insertData();

How to initialize context where we cannot get context from getApplicationContext()?

I want to access a class extending SQLiteOpenHelper to get the context of database from a java class. I need to pass application context to get that but don`t have access to getApplicationContext().
How can I get Application Context in java class that is not activity?
I suggest you create a constructor that has a parameter of the Context type.
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
// Variables
private Context ctx;
public MySQLiteOpenHelper(Context ctx) {
this.ctx = ctx;
}
//More code
}
Now, in your activities, you can do this:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this);
And in your fragments, you can do this:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper(getActivity().getApplicationContext()); //getActivity() would work too, Activity (indirectly) extends Context.
You can create custom Application class and write method getContext().
Someting like this:
public class MyApplication extends Application {
private static MyApplication mCurrentInstance;
#Override
public void onCreate() {
super.onCreate();
mCurrentInstance = this;
}
public static MyApplication instance() {
return mCurrentInstance;
}
public static Context context() {
return mCurrentInstance.getApplicationContext();
}
}
And add this class into manifest:
<application
android:name=".MyApplication">

ListView SimpleCursorAdapter Updated Asynchronously

I have a class that has a SimpleCursorAdapter as a field. That adapter is used feed a listview that has a a viewBinder.
I have an asynchronous task that runs that adds an entry to the database and then updates the cursor.
In testing, if I click too quickly on the button that runs the async process, I get an error:
java.lang.RuntimeException: An error occured while executing doInBackground()
...
Caused by: java.lang.IllegalStateException: database [path_to_my_db] already closed
The code WORKS perfectly - unless... the user clicks the save button rapidly in succession... I'm new to all of this, so any input would be greatly appreciated!
Here is a stripped down version of what I'm trying to do:
public class MyActivity extends Activity {
private DatabaseConnector connector; // this is my class for managing SQLite
private SimpleCursorAdapter adapter;
....
#Override
public void onCreate(Bundle savedInstanceState){
...
myListView = (ListView)findViewById(R.id.my_list_view);
String[] = new String{"This", "part", "is", "working"};
int[] to = new int[] {1,2,3,4}; // again, this is working...
adapter = new SimpleCursorAdapter(this, R.layout.my_list_item_row, null, from, to);
adapter.setViewBinder(new ViewBinder(){
... // this is all working
... // the viewBinder is for custom date formatting... again, all works
});
myListView.setAdapter(adapter);
}
private class MyAsyncTask extends AsyncTask<Context, Void, ExerciseInstanceViewModel>{
MyViewModel vm; // this viewModel has a cursor member...
public MyAsyncTask([variables-all-working]){
}
#Override
protected MyViewModel doInBackground(Context... params) {
connector = new DatabaseConnector(MyActivity.this);
connector.open(); // TODO: Getting 'did not close database error here...'
vm = connector.runMethodThatIncludesCursorInReturnType([input-paramters-working]);
return vm;
}
// use the cursor returned from the doInBackground method
#Override
protected void onPostExecute(MyViewModel result){
super.onPostExecute(result);
// set instance fields in outer class...;
// set textView, progressBar, etc..
if (result.MyCursor != null)
{
adapter.changeCursor(result.MyCursor);
}
connector.close(); // aren't i closing the db here???
[Code to reload page with next detail items]
}
}
}
if (result.MyCursor != null)
{
adapter.changeCursor(result.MyCursor);
}
}
}
connector.close();
}
try this.
Make a wrapper around the connector, synchronized and as a singleton. I believe you are accessing the database at the same time. This is a stripped version of one of my own:
(I am overriding the getWriteable database to enable foreign keys but you dont have too do that)
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "myDatabase";
private static final int DATABASE_VERSION = 1;
private Context context = null;
private static DatabaseHelper instance = null;
public static synchronized DatabaseHelper getInstance(Context context){
if (instance == null){
instance = new DatabaseHelper(context, null);
}
return instance;
}
#Override
public synchronized SQLiteDatabase getWritableDatabase() {
SQLiteDatabase db = super.getWritableDatabase();
db.execSQL("PRAGMA foreign_keys=ON;");
return db;
}
private DatabaseHelper(Context context, CursorFactory factory) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
this.context = context;
}
}

Android database connection best practice

What is considered to be best practice for handling database connections? (I omitted the constructor and onUpgrade method within the DatabaseHelper class) These are just 2 ways I found on the internet, perhaps you have a better way of handling? I would love to hear.
Option 1
public class DatabaseManager {
private SQLiteDatabase mDb;
public DatabaseManager(Context context) {
DatabaseHelper helper = new DatabaseHelper(context);
helper.getWritableDatabase();
}
// ... methods that use mDb
private class DatabaseHelper extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase db) {
mDb = db;
//create database
}
#Override
public void onOpen(SQLiteDatabase db) {
mDb = db;
}
}
}
Option 2
public class DatabaseManager {
private DatabaseHelper mDbHelper;
public DatabaseManager(Context context) {
mDbHelper = new DatabaseHelper(context);
}
// ... methods that fetch the db
private void sampleMethod() {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
//do stuff with database
mDbHelper.close();
}
private static class DatabaseHelper extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase db) {
//create database
}
}
}
Also, is it needed to call close() everytime you used the database within option 2? As for using option 1, I guess you need to call close() when the app's onDestroy is called?
I used to worry about all this but recently I started using ORMLite. It is a very light ORM with Android libraries and saves you worrying about this kind of stuff.
I would say that this could very soon become best practice as it removes a lot of repeated code when dealing with databases. It is also being updated regularly and the maintainer responds to queries very quickly.

Categories

Resources