Android database created list, how use in ListFragment - android

well I am using my existing database, I made a list<> "mSorular" with this code:
public class TestAdapter {
private final Context mContext;
private SQLiteDatabase mDb;
private DataBaseHelper mDbHelper;
public TestAdapter(Context context) {
this.mContext = context;
mDbHelper = new DataBaseHelper(mContext);
}
public List<soru> getTestData() {
List<soru> mSorular = new LinkedList<soru>();
try {
Cursor mCur = mDb.query("Soru", null, null,
null,
null,
null,
null,
null);
if (mCur != null) {
if (mCur.moveToFirst()) {
do {
soru Soru = new soru();
Soru.setmSoru(mCur.getString(2));
Soru.setmCevap1(mCur.getString(3));
Soru.setmCevap2(mCur.getString(4));
Soru.setmCevap3(mCur.getString(5));
mSorular.add(Soru);
} while (mCur.moveToNext());
}
}
return mSorular;
} catch (SQLException mSQLException) {
Log.e(TAG, "getTestData >>" + mSQLException.toString());
throw mSQLException;
}
}
after that I want to use my mSorular list in a listfragment. When I try to
public class framelist extends ListFragment {
private static String tag = "sqllist";
TestAdapter adapter = new TestAdapter(this);
it gives this error:
The constructor TestAdapter(framelist) is undefined
without this my list will return empty. so how can I Use my list in this listfragment

ListFragment is not a Context. First change it to use the attached Activity as Context, and then move it into the onCreate() method of the Fragment because before this call there might not be an Activity attached. (actually it's after the onAttach(Activity) call)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate();
TestAdapter adapter = new TestAdapter(getActivity());
...
{

Related

Android SQLiteAssetHelper Database doesn't open

I am trying to connect SQLite database in Android Studio through SQLiteAssetHelper, I have tried everything, but it is showing ... Unable to Open Database.
I don't know what is the problem here. I am new to android development.
I have seen other question's answers, but it couldn't help me.
This is my DB Class:
public class MyDB extends SQLiteAssetHelper {
private static final String DB_NAME="Demo.db";
private static final int DB_ver=1;
//private static MyDB myDB=null;
public MyDB(Context context) {
super(context, DB_NAME, null, DB_ver);
}
}
this is DB Access Class:
public class OpenDBHelper {
private SQLiteOpenHelper openHelper;
private SQLiteDatabase db;
private static OpenDBHelper openDBHelper ;
private OpenDBHelper(Context context)
{
this.openHelper = new MyDB(context);
}
public static OpenDBHelper getInstance(Context context)
{
if(openDBHelper==null)
{
openDBHelper=new OpenDBHelper(context.getApplicationContext()) ;
}
return openDBHelper;
}
public SQLiteDatabase openwrite()
{
this.db= openHelper.getReadableDatabase();
return db;
}
}
and this is the main activity where I am trying to load the data in listview:
public class MainActivity extends AppCompatActivity {
ListView lv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv=(ListView) findViewById(R.id.lv_main);
loadStudents();
}
public void loadStudents()
{
ArrayList<HashMap<String, String>> userList = GetAllStudents();
ListAdapter adapter = new SimpleAdapter(this, userList, R.layout.lv_row,new String[]{"name","cast","phone"}, new int[]{R.id.tv_name, R.id.tv_cast, R.id.tv_phone});
lv.setAdapter(adapter);
}
//GEt All Students to listview
public ArrayList<HashMap<String, String>> GetAllStudents(){
ArrayList<HashMap<String, String>> userList = new ArrayList<>();
OpenDBHelper open= OpenDBHelper.getInstance(this);
SQLiteDatabase db=open.openwrite();
String query = "SELECT * FROM tbl_bio";
Cursor cursor = db.rawQuery(query,null);
if(cursor.getCount()>0) {
while (cursor.moveToNext()) {
HashMap<String, String> user = new HashMap<>();
user.put("name", cursor.getString(1));
user.put("cast", cursor.getString(2));
user.put("phone", cursor.getString(3));
//user.put("pass", cursor.getString(4));
userList.add(user);
}
}
return userList;
}
}

Memory leaks in context

I have a class App which extends Application and has a static method which returns context(which is static).
public class App extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = this.getApplicationContext();
DBHelper dbHelper = new DBHelper();
DatabaseManager.initializeInstance(dbHelper);
}
public static Context getContext() {
return context;
}
}
Now in the DBHelper class which extends SQLiteAssetHelper in the constructor, i have this:
public DBHelper() {
super(App.getContext(), DATABASE_NAME, null, DATABASE_VERSION);
}
This is the DataBaseManager class:
public class DatabaseManager {
private Integer mOpenCounter = 0;
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase() {
mOpenCounter+=1;
if(mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
}
Everything works fine, but there is a design problem because context fields should not be static. How do I use a context in DBHelper while keeping the code working and the field non-static?
You can pass ApplicationContext inside the DBHelper constructor like below:
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
and then
DBHelper dbHelper = new DBHelper(getApplicationContext());

Converting Adapter onBindViewHolder itemView context to Class context with SQLite database

I have a working recycle view:
public class MtbListAdapter extends RecyclerView.Adapter<MtbListAdapter.RecyclerViewHolder> implements Filterable {
private ArrayList<ApiObject> arrayList;
private ArrayList<ApiObject> arrayListFiltered;
private DatabaseHelper databaseHelper;
public MtbListAdapter(ArrayList<ApiObject> arrayList) {
this.arrayList =arrayList;
this.arrayListFiltered =arrayList;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_mtb, viewGroup, false);
return new RecyclerViewHolder(view);
}
#Override
public void onBindViewHolder(RecyclerViewHolder recyclerViewHolder, int position){
//load texts to card
recyclerViewHolder.tv_name.setText(arrayListFiltered.get(position).getName());
recyclerViewHolder.tv_length.setText(arrayListFiltered.get(position).getLength());
//is card already saved?
String s_identifier = arrayListFiltered.get(position).getId();
databaseHelper = new DatabaseHelper(recyclerViewHolder.tv_name.getContext());
boolean isSaved = databaseHelper.isSaved(s_identifier);
if (isSaved) recyclerViewHolder.iv_isSaved.setVisibility(View.VISIBLE);
else recyclerViewHolder.iv_isSaved.setVisibility(View.INVISIBLE);
}
In the onBindViewHolder I check if the item is already saved and if so place a "saved" image on the card. I do this by cheking an ID value against already saved items in my SQLite db. One thing I'm concerned about is that databaseHelper = new DatabaseHelper(recyclerViewHolder.tv_name.getContext()); gets called a lot. Question 1. Is this a concern?
To alleviate my concerns, I tried passing context through the constructor and making databaseHelper a class Object, but whatever context I passed caused my SQLite database method to crash in the databaseHelper Class. I tried passing Activity.this, application context, base context, etc. I've only been successful passing the viewHolder item context as shown. Like I said the code as written works, but I fear it is costly.
Here is the code I tried:
public class MtbListAdapter extends RecyclerView.Adapter<MtbListAdapter.RecyclerViewHolder> implements Filterable {
private Context context;
private ArrayList<ApiObject> arrayList;
private ArrayList<ApiObject> arrayListFiltered;
private DatabaseHelper databaseHelper = new DatabaseHelper(context);
public MtbListAdapter(ArrayList<ApiObject> arrayList, Context context) {
this.arrayList =arrayList;
this.arrayListFiltered =arrayList;
this.context =context;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_mtb, viewGroup, false);
return new RecyclerViewHolder(view);
}
#Override
public void onBindViewHolder(RecyclerViewHolder recyclerViewHolder, int position){
//load texts to card
recyclerViewHolder.tv_name.setText(arrayListFiltered.get(position).getName());
recyclerViewHolder.tv_length.setText(arrayListFiltered.get(position).getLength());
//is card already saved?
String s_identifier = arrayListFiltered.get(position).getId();
boolean isSaved = databaseHelper.isSaved(s_identifier);
if (isSaved) recyclerViewHolder.iv_isSaved.setVisibility(View.VISIBLE);
else recyclerViewHolder.iv_isSaved.setVisibility(View.INVISIBLE);
}
Here is my SQLite method:
//returns true if is already saved
public boolean isSaved(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {KEY_IDENTIFIER};
String where = KEY_IDENTIFIER + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
try (Cursor c = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1")){
return c.moveToFirst();
}
}
Here is my error (that I only get with the code posted that passes context through the contructor). Otherwise it all works.
java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference
You could include the context via the constrcutor and then create an instance of the helper in the constructor.
e.g. Instead of :-
public MtbListAdapter(ArrayList<ApiObject> arrayList) {
this.arrayList =arrayList;
this.arrayListFiltered =arrayList;
}
use :-
public MtbListAdapter(Context context, ArrayList<ApiObject> arrayList) {
this.arrayList =arrayList;
this.arrayListFiltered =arrayList;
databaseHelper = new DatabaseHelper(context);
}
and remove the line :-
databaseHelper = new DatabaseHelper(recyclerViewHolder.tv_name.getContext());
One thing I'm concerned about is that databaseHelper = new
DatabaseHelper(recyclerViewHolder.tv_name.getContext()); gets called a
lot. Question 1. Is this a concern?
It's not the best way simply because you are re-constructing the instance. With regard to opening the database, that is actually done not at construction but when either getReableDatabase or getWriteableDatabase is called, which will be the same as that's done when the isSaved method is invoked. The get????ableDatabase are designed to cope with multiple invocations. So, I believe that there is little to be concerned about.
However, you don't close the Cursor in the isSaved method. This should be of concern as if you have too many Cursors open then an exception will occur.
You may want to change :-
//returns true if is already saved
public boolean isSaved(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {KEY_IDENTIFIER};
String where = KEY_IDENTIFIER + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
try (Cursor c = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1")){
return c.moveToFirst();
}
}
to something like :-
//returns true if is already saved
public boolean isSaved(String s) {
boolean rv = false;
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {KEY_IDENTIFIER};
String where = KEY_IDENTIFIER + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
(Cursor c = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1"));
if(c.moveToFirst) {
rv = true;
}
c.close()
return rv;
}

Having an error connecting to my SQLite database

The user is looking at a list LibraryFragment and clicks one of the options (Item1 or Item2), from there I wanted to show another list (GFragment) that is created dynamically from the items received from the database. In the logCat I get this error:
08-30 13:56:54.087: E/SqliteDatabaseCpp(22622): sqlite3_open_v2("/data/data/j.j.l.library.v11/databases/library_dev.db", &handle, 1, NULL) failed
Failed to open the database. Closing it.
Does anyone know what is wrong with the code or why it is doing this?
The code I am using for the database is:
public class DatabaseHelper {
private static String DB_PATH = "/data/data/j.j.l.library.v11/databases/";
private static String DB_NAME = "library_dev.db";
private SQLiteDatabase myDataBase;
public DatabaseHelper(){
}
//Open the database.
public void openDatabase() throws SQLException{
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
//Return the columns we want.
public List<String> getQueryColumn(String tableName, String[] columns){
Cursor cursor;
List<String> info = new ArrayList<String>();
cursor = myDataBase.query(tableName, columns, null, null, null, null, null);
cursor.moveToFirst();
while(!cursor.isAfterLast()){
info.add(cursor.getString(0));
cursor.moveToNext();
}
cursor.close();
return info;
}
//Close the Database.
public void closeDatabase() throws SQLException{
myDataBase.close();
}
}
Another List I am trying to create dynamically from the database:
public class GFragment extends ListFragment {
private DatabaseHelper gList;
public static final String GROLE = "role";
public static final String[] ROLENAME = {"name"};
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
gList = new DatabaseHelper();
gList.openDatabase();
List<String> values = gList.getQueryColumn(GROLE, ROLENAME);
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, values));
gList.closeDatabase();
}
}
This is the list the user is looking at right before there is a call to retrieve the dynamic list from the database:
public class LibraryFragment extends ListFragment{
String[] libraryList = {"Item1", "Item2"};
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, libraryList));
}
#Override
public void onListItemClick(ListView l, View v, int position, long id){
//Get the position the user clicked.
Fragment newFragment = null;
String listPosition = libraryList[position];
getListView().setItemChecked(position, true);
if(listPosition.equals("Item1")){
newFragment = new GFragment();
}else if (listPosition.equals("Item2")){
newFragment = new ITFragment();
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.myFragments, newFragment);
transaction.addToBackStack(null);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
}
It's because you don't create this database library_dev.db, so it's empty, resulting in a NULL reference; a closing operation is taken afterward.
You need to handle the creation/upgrade/remove of the database in a class which should extends from SQLiteOpenHelper first. Then use this class to get your database:
public class MyDatabaseHelper extends SQLiteOpenHelper { // blah...database create/upgrade handling }
MyDatabaseHelper myHelper = new MyDatabaseHelper(yourContext);
SQLiteDatabase myDatabase = myHelper.getReadableDatabase(); // now you can use `myDatabase` freely
You can refer to a proper guideline for this at: http://www.vogella.com/articles/AndroidSQLite/article.html

java.lang.IllegalStateException: attempt to re-open an already-closed object

I'm trying to figure out why occasionally I'm getting the IllegalStateException. I can't find any good examples that show how to load a list using a thread to query a SQLite database. I've included my code below. Most of the time it works correctly, but occasionally, I'm getting the IllegalStateException.
I have also gotten a similar exception in another activity of mine that is an instance of ExpandableListActivity. That exception states "trying to requery an already closed cursor".
Can somebody tell me the correct way to do this so that it doesn't cause any errors? I would prefer to use the cursors instead of copying all of the data into memory. If I can't figure this out then I will have to load it all into memory.
I think the issue has something to do with startManagingCursor(Cursor) and the fact that the database connection is closed in onDestroy().
plz help
-- tale
public class MyListActivity extends ListActivity {
private MyCursorAdapter adapter;
private SQLiteDatabase db = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
new RetrieveCursorTask(this).execute((Void[]) null);
} catch (Exception e) {
}
}
#Override
protected void onDestroy() {
super.onDestroy();
// Null out the cursor.
if (adapter != null) {
adapter.changeCursor(null);
adapter = null;
}
if (db != null && db.isOpen()) {
db.close();
}
}
private class RetrieveCursorTask extends AsyncTask<Void, Void, Cursor> {
private Context ctx;
public RetrieveCursorTask(Context ctx) {
this.ctx = ctx;
}
#Override
protected Cursor doInBackground(Void... params) {
Cursor cursor = null;
DbHelper helper = new DbHelper(ctx);
try {
db = helper.getReadableDatabase();
cursor = db.query("users",
new String[] {
DbHelper.ID_COLUMN,
DbHelper.UID_COLUMN
},
null, null, null, null, null);
startManagingCursor(cursor);
} catch (Exception e) {
}
return cursor;
}
#Override
protected void onPostExecute(Cursor cursor) {
super.onPostExecute(cursor);
if (cursor != null) {
try {
adapter = new MyCursorAdapter(ctx, cursor);
} catch (Exception e) {
}
setListAdapter(adapter);
}
}
}
private class MyCursorAdapter extends CursorAdapter {
private Context ctx;
public MyCursorAdapter(Context context, Cursor c) {
super(context, c);
this.ctx = context;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// ...
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// ...
}
}
}
Look into AsyncQueryHandler if you want to query DB the way you want.
Your task RetrieveCursorTask is running on separate thread so when your activity gets destroyed your AsyncTask might still be running in background but as you have closed your cursor in main activity onDestroy it might be requeried again after your AsyncTask returns.
Sounds like you need to syncronize the block where you set your adapter in onPostExecute. The problem is since AsyncTask is running on a separate thread, the order in which the cursor is set and subsequently requested isn't guaranteed. Try this..
#Override
protected void onPostExecute(Cursor cursor) {
super.onPostExecute(cursor);
synchronized(anyObject) {
if (cursor != null) {
try {
adapter = new MyCursorAdapter(ctx, cursor);
} catch (Exception e) {
}
setListAdapter(adapter);
}
}
}

Categories

Resources