NullPointerException on getWritableDatabase after adding Fragments to my project - android

I had a working project, now I wanted to upgrade the layout and I'm trying to add Fragments (pre honeycomb), but now seems like I'm having some troubles connecting to my DB, and I'm getting NullPointerException:
ERROR/AndroidRuntime(27294):
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:118)
ERROR/AndroidRuntime(27294):
at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:187)
I believe it has something to do with the Context i'm sending to the database constructor.
here is my class:
public class ShowFragment extends ListFragment {
ArrayList<String> results = new ArrayList<String>();
private SQLiteDatabase db;
WorkTrackdb workdb = new WorkTrackdb(getActivity());
private ViewGroup mRootView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor c = getYears();
//some code here
}
Cursor getYears() {
db = workdb.getReadableDatabase();
String years[] = {"year"};
Cursor cursor = db.query(WorkTrackdb.TABLE_NAME, years, null,
null, null, null,"year DESC");
return cursor;
}

Try using SQLiteDatabase.openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags) instead.
I'm not sure maybe you need to instantiate WorkTrackdb workdb = new WorkTrackdb(getActivity()); inside the onCreate()

Related

Android: getContext() error

just a simple error I have but I am really having a hard time trying to solve this problem. why is this getContext() are not applied?
public void ClearRecentPlayer() {
mDbHelper = new DataConn(getContext()); //<---getContext() in redline(not applied)
SQLiteDatabase db = mDbHelper.getWritableDatabase();
ContentValues v = new ContentValues();
v.put(FeedReaderContract.FeedEntry.COLUMN_NAME_STATS, 0);
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_STATS + " = ?";
String[] selectionArgs = { "0" };
int c = db.update(
FeedReaderContract.FeedEntry.TABLE_NAME_PLAYER,
v,
selection,
selectionArgs);
}
and with this...
public class DataConn extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "db_egame.db";
DataConn mDbHelper;
public DataConn(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_EASY_ENTRIES);
db.execSQL(SQL_CREATE_HARD_ENTRIES);
db.execSQL(SQL_CREATE_DIFF_ENTRIES);
db.execSQL(SQL_CREATE_PLAYER_ENTRIES);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(SQL_DELETE_EASY_ENTRIES);
db.execSQL(SQL_DELETE_HARD_ENTRIES);
db.execSQL(SQL_DELETE_DIFF_ENTRIES);
db.execSQL(SQL_DELETE_PLAYER_ENTRIES);
onCreate(db);
}
#Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
onCreate(db);
}
As explained here (https://stackoverflow.com/a/10641257/2319627)
•View.getContext(): Returns the context the view is currently
running in. Usually the currently active Activity.
•Activity.getApplicationContext(): Returns the context for the
entire application (the process all the Activities are running inside
of). Use this instead of the current Activity context if you need a
context tied to the lifecycle of the entire application, not just the
current Activity.
•ContextWrapper.getBaseContext(): If you need access to a Context
from within another context, you use a ContextWrapper. The Context
referred to from inside that ContextWrapper is accessed via
getBaseContext.
So, it will be better to use getApplicationContext() when you are trying to use a DataBaseHelper.
And, you can call getApplicationContext from activity or service only, or from an instance of context. Like activity.getApplicationContext()
You need an application context for a Database helper class. So, pass a context to the database on initialization
ClearRecentPlayer method is in an activity? else, you have to pass the application context to the class from which you call ClearRecentPlayer method.
you can either create a member variable .Context in that class, or you can call the ClearRecentPlayer method as ClearRecentPlayer (Context context)
getContext() is only an available method of a View.
If your method is in that database class, you don't actually need the Context. Or any instance of DataConn within its own class.
public class DataConn extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "db_egame.db";
private Context mContext;
public DataConn(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.mContext = context;
}
public void clearRecentPlayer() {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues v = new ContentValues();
v.put(FeedReaderContract.FeedEntry.COLUMN_NAME_STATS, 0);
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_STATS + " = ?";
String[] selectionArgs = { "0" };
int c = db.update(
FeedReaderContract.FeedEntry.TABLE_NAME_PLAYER,
v,
selection,
selectionArgs);
}
Try getApplicationContext() instead of getContext() for activity/AppCompactActivity,

How to test a ListActivity by mocking its contentProvider and thus isolate test from database?

I have a ListView activity which loads its data asynchronously from a SQLite database using a ContentProvider.
I want to test this activity but I don't want to use the database. Because I want it to be repeatable.
I'm trying to mock my content provider this way:
public class MockRecipeContentProvider extends MockContentProvider{
private List<HashMap<String, String>> results;
#SuppressWarnings("nls")
public MockRecipeContentProvider(){
super();
this.results = new ArrayList<HashMap<String,String>>();
//..populate this.result with som data
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
MatrixCursor mCursor = new MatrixCursor(new String[] {RecipeTable.COLUMN_ID,
RecipeTable.COLUMN_NAME,
RecipeTable.COLUMN_INGREDIENTS,
RecipeTable.COLUMN_DIRECTIONS});
for(int i =0; i<this.results.size(); i++){
mCursor.addRow(this.getDataAtPosition(i));
}
return mCursor;
}
}
And this is my test case:
public class MainActivityTest extends ActivityUnitTestCase<MainActivity>{
private static final int INITIAL_NUM_ITEMS = 2;
private MainActivity mActivity;
public MainActivityTest() {
super(MainActivity.class);
}
#Override
public void setUp() throws Exception{
super.setUp();
final MockContentResolver mockResolver = new MockContentResolver();
MockRecipeContentProvider mockContentProvider = new MockRecipeContentProvider();
mockResolver.addProvider(RecipeContentProvided.AUTHORITY,
mockContentProvider);
ContextWrapper mActivityContext = new ContextWrapper(
getInstrumentation().getTargetContext()) {
#Override
public ContentResolver getContentResolver() {
return mockResolver;
}
};
this.setActivityContext(mActivityContext);
startActivity(new Intent(mActivityContext, MainActivity.class), null, null);
this.mActivity = getActivity();
}
public void testDisplayCorrectNumOfItems(){
ListView lv = this.mActivity.getListView();
assertTrue(lv.getCount()==INITIAL_NUM_ITEMS);
}
}
The problem is that the MockRecipeContentProvider.query() is not called, neither the RecipeContentProvider.query(), so the listView is not populate.
What am I doing wrong?
On the other hand, I would like to write an integration test case, extending the ActivityInstrumentationTestCase2, I would like to test the life cycle etc, but I've read that mocking is not possible if you are extending ActivityInstrumentationTestCase2. So, how can I code a test like this without relaying on the database?
I don't want to use the database because I think that although I will use RenamingDelegatingContext it would cause that the tests wouldn't be repeatable at all, unless that I could drop the test database and recreate it in the setUp() method. Could this be done?

How to read an SQLite DB in android with a cursorloader?

I'm setting up my app so that people can create groups of their friends. When a group is created, it writes 2 tables to the SQL database. The first table has a group name and a group id. The second table has 2 columns, a group id and a user id. This is working fine.
However, now I want to be able to read from the database. I'm using a listview fragment with a cursorloader but I'm having trouble getting the information to display. I want to list all the group names from the first table in my list view.
My problem is that, when I first used the cursorloader to list my contacts, I was using a Uri from the content provider in the onCreateLoader method. Specifically I had CONTENT_URI from the ContactsContracts.Contacts class.
Example of cursorloader with contentprovider:
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
Uri contentUri = ContactsContract.Contacts.CONTENT_URI;
return new CursorLoader(getActivity(),contentUri,PROJECTION,SELECTION,ARGS,ORDER);
}
However, without using a content provider, I don't know what to put in the onCreateLoader method because return new CursorLoader(...) requires a Uri in the second argument.
Any suggestion on how I might be able to display my database data in a listview?
fragment class code:
public class GroupListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
CursorAdapter mAdapter;
private OnItemSelectedListener listener;
private static final String[] PROJECTION ={GroupContract.GroupDetails.COLUMN_NAME_GROUP_NAME};
private static final String SELECTION = null;
final String[] FROM = {GroupContract.GroupDetails.COLUMN_NAME_GROUP_NAME};
final int[] TO = {android.R.id.text1};
private static final String[] ARGS = null;
private static final String ORDER = null;
private Cursor c;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1,null,FROM,TO,0 );
ReadDBAsync readDB = new ReadDBAsync();
readDB.execute();
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0,null,this);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
Uri contenturi = Uri.parse("content://preamble.oneapp");
Uri tableuri = Uri.withAppendedPath(contenturi,GroupContract.GroupDetails.TABLE_NAME);
return new CursorLoader(getActivity(),tableuri,PROJECTION,SELECTION,ARGS,ORDER);
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
mAdapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
mAdapter.swapCursor(null);
}
private class ReadDBAsync extends AsyncTask<Void,Void,String> {
#Override
protected String doInBackground(Void... voids) {
ContractDBHelpers mDBHelper = new ContractDBHelpers(getActivity());
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String returnvalue = "database read";
c = db.query(GroupContract.GroupDetails.TABLE_NAME,PROJECTION,null,null,null,null,null);
return returnvalue;
}
#Override
protected void onPostExecute(String result){
Toast.makeText(getActivity(), result, Toast.LENGTH_LONG).show();
}
}
}
Android Guide suggests to create a ContentProvider when you want to share your data with other applications. If you don't need this, you can just override method loadInBackgroud() of the CursorLoader class. For example write like this in your onCreateLoader:
return new CursorLoader( YourContext, null, YourProjection, YourSelection, YourSelectionArgs, YourOrder )
{
#Override
public Cursor loadInBackground()
{
// You better know how to get your database.
SQLiteDatabase DB = getReadableDatabase();
// You can use any query that returns a cursor.
return DB.query( YourTableName, getProjection(), getSelection(), getSelectionArgs(), null, null, getSortOrder(), null );
}
};
These are the steps to create a cursorloader in a list fragment
1) Create a class extending SQLiteOpenHelper and override onCreate and onUpgrade to create your tables.
2) Create a class extending ContentProvider and create the URIs to access your database. Refer http://developer.android.com/guide/topics/providers/content-providers.html. Add your URIs to the URIMatcher which you use in onCreate, onUpdate, query, etc (overridden methods) to match the URI. Refer http://developer.android.com/reference/android/content/UriMatcher.html
3) In the insert method call getContext().getContentResolver().notifyChange(uri, null). In the query method call setNotificationUri(ContentResolver cr, Uri uri) before returning the content provider for the insertion change to reflect automatically to your loader. (https://stackoverflow.com/a/7915117/936414).
4) Give that URI in onCreateLoader.
Note:
Without a content provider, automatic refreshing of changes to the list is not feasible as of the current android version. If you don't want to have your contentprovider visible, set exported attribute in manifest to false. Or you can implement your custom CursorLoader as in https://stackoverflow.com/a/7422343/936414 to retrieve data from the database. But in this case automatic refreshing of data is not possible

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;
}
}

Testing database on Android: ProviderTestCase2 or RenamingDelegatingContext?

I've implemented access to a database using SQLiteOpenHelper from the android.database package within some classes (with pattern DAO).
I wrote some junit tests for these classes using an AndroidTestCase but this causes the tests to use the same database as the application.
I read that the ProviderTestCase2 or RenamingDelegatingContext can be used to test the database separately. Unluckily I couldn't find any nice tutorial/example that shows how to test a database with ProviderTestCase2/RenamingDelegatingContext.
Can anyone point me somewhere OR give me some tip OR share some code for database testing?!
Cheeerrrrsss!!
Giorgio
Both the ProviderTestCase and RenamingDelegatingContext will destroy the database if one already exists before opening it within it's context, so in that sense they both have the same low-level approach towards opening a SQLite database.
You leverage this by opening the database in your fixture in setUp(), which will then ensure that your working with a fresh database before each test case.
I would suggest that you go for writing content providers rather than creating database adapters. You can use a common interface for accessing data, be it stored in the DB or somewhere over the network, the design of content providers can be accommodated to access such data at the cost of a bit of IPC overhead involved that most of us shouldn't have to care about.
If you did this for accessing a SQLite database, the framework would completely manage the database connection for you in a separate process. As added beef, the ProviderTestCase2<ContentProvider> completely bootstraps a test context for your content provider without you having to a write a single line of code.
But, that's not said it isn't such a huge effort to do the bootstrapping yourself. So supposing you had a database adapter as such; we'll just focus on open() for getting write access to our database, nothing fancy:
public class MyAdapter {
private static final String DATABASE_NAME = "my.db";
private static final String DATABASE_TABLE = "table";
private static final int DATABASE_VERSION = 1;
/**
* Database queries
*/
private static final String DATABASE_CREATE_STATEMENT = "some awesome create statement";
private final Context mCtx;
private SQLiteDatabase mDb;
private DatabaseHelper mDbHelper;
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE_STATEMENT);
}
#Override
public void onUpgrade(SQLiteDatabase db, int a, int b) {
// here to enable this code to compile
}
}
/**
* Constructor - takes the provided context to allow for the database to be
* opened/created.
*
* #param context the Context within which to work.
*/
public MyAdapter(Context context) {
mCtx = context;
}
/**
* Open the last.fm database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure.
*
* #return this (self reference, allowing this to be chained in an
* initialization call)
* #throws SQLException if the database could be neither opened or created
*/
public MyAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
}
Then you could write your test as such:
public final class MyAdapterTests extends AndroidTestCase {
private static final String TEST_FILE_PREFIX = "test_";
private MyAdapter mMyAdapter;
#Override
protected void setUp() throws Exception {
super.setUp();
RenamingDelegatingContext context
= new RenamingDelegatingContext(getContext(), TEST_FILE_PREFIX);
mMyAdapter = new MyAdapter(context);
mMyAdapter.open();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
mMyAdapter.close();
mMyAdapter = null;
}
public void testPreConditions() {
assertNotNull(mMyAdapter);
}
}
So what's happening here is that the context implementation of RenamingDelegatingContext, once MyAdapter(context).open() is called, will always recreate the database. Each test you write now will be going against the state of the database after MyAdapter.DATABASE_CREATE_STATEMENT is called.
I actually use database with SQLiteOpenHelper and I have a trick for testing.
The idea is to use standard on-file stored DB during the normal use of the app and an in-memory DB during tests. In this way you can use a clear DB for each test without insert/delete/update data in your standard DB. It works fine for me.
Keep in mind you can use in-memory database, just passing null as name of database file. This is clearly documented in the API documentation.
Advantages of using in-memory DB during tests is explained here:
https://attakornw.wordpress.com/2012/02/25/using-in-memory-sqlite-database-in-android-tests/
In my project I have the DBHelper class wich extends SQLiteHelper. As you can see, there are the standard methods. I simply added a constructor with two parameters. The difference is that when I call the super constructor, I pass null as DB name.
public class DBHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "mydatabase.db";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public DBHelper(Context context, boolean testMode) {
super(context, null, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
//create statements
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//on upgrade policy
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//on downgrade policy
}
}
Every "model" in the project extends DBModel that is an abstract class.
public abstract class DBModel {
protected DBHelper dbhelper;
public DBModel(Context context) {
dbhelper = new DBHelper(context);
}
//other declarations and utility function omitted
}
As discussed here: How can I find out if code is running inside a JUnit test or not?
there is a way to establish if you are running JUnit tests, simply searching in stack trace elements.
As a conseguence, I modified DBModel constructor
public abstract class DBModel {
protected DBHelper dbhelper;
public DBModel(Context context) {
if(isJUnitTest()) {
dbhelper = new DBHelper(context, true);
} else {
dbhelper = new DBHelper(context);
}
}
private boolean isJUnitTest() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = Arrays.asList(stackTrace);
for (StackTraceElement element : list) {
if (element.getClassName().startsWith("junit.")) {
return true;
}
}
return false;
}
//other declarations and utility function omitted
}
Note that
startsWith("junit.")
may be
startsWith("org.junit.")
in your case.
I have an application that uses a ContentProvider backed by an sqlite database to provide data to the application.
Let PodcastDataProvider be the actual dataprovider used by the application.
Then you can set up a test provider with something like the following:
public abstract class AbstractPodcastDataProvider extends ProviderTestCase2<PodcastDataProvider>{
public AbstractPodcastDataProvider(){
this(PodcastDataProvider.class, Feed.BASE_AUTH);
}
public AbstractPodcastDataProvider(Class<PodcastDataProvider> providerClass,
String providerAuthority) {
super(providerClass, providerAuthority);
}
public void setUp() throws Exception{
super.setUp();
//clear out all the old data.
PodcastDataProvider dataProvider =
(PodcastDataProvider)getMockContentResolver()
.acquireContentProviderClient(Feed.BASE_AUTH)
.getLocalContentProvider();
dataProvider.deleteAll();
}
}
to setup a test data provider that will be backed by a different database than the actual application.
To test the DAO, create another class which extends AbstractPodcastDataProvider and use the
getMockContentResolver();
method to get an instance of a content resolver that will use the test database instead of the application database.
private static String db_path = "/data/data/android.testdb/mydb";
private SQLiteDatabase sqliteDatabase = null;
private Cursor cursor = null;
private String[] fields;
/*
* (non-Javadoc)
*
* #see dinota.data.sqlite.IDataContext#getSQLiteDatabase()
*/
public SQLiteDatabase getSQLiteDatabase() {
try {
sqliteDatabase = SQLiteDatabase.openDatabase(db_path, null,
SQLiteDatabase.OPEN_READWRITE);
sqliteDatabase.setVersion(1);
sqliteDatabase.setLocale(Locale.getDefault());
sqliteDatabase.setLockingEnabled(true);
return sqliteDatabase;
} catch (Exception e) {
return null;
}
}
if you give the exact location of the sqlite db(in my case it's db_path), using the above method you can find-out whether it returns an sqlitedatabase or not.
A possible solution can be to open database using this method
myDataBase = SQLiteDatabase.openDatabase(DATABASE_NAME, null, SQLiteDatabase.OPEN_READWRITE);
And change database name in your tests. Here you can find some info about this method.

Categories

Resources