I tried to find a solution to my problem already in google and here on stackoverflow, however nothing could answer my question.
I use a sqlite database to populate a listview. Also on top of the listview there is a tabhost where one can switch to a different activity. When switching to that activity and back then back again to the listview a leak is found. However whatever try there is apparently a leak. Can someone help me in finding that leak?
Here is the listview activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
contactlist = (ListView) findViewById(R.id.list);
entry.open();
c = entry.getData();
startManagingCursor(c);
String[] from = new String[] {"_id", "firstname", "lastname"};
int[] to = new int[] {R.id.iVPI, R.id.tvfirstName, R.id.tvlastName};
adapter = new MySimpleCursorAdapter(this, R.layout.contacts_list_item, c, from, to);
contactlist.setAdapter(adapter);
contactlist.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
// TODO Auto-generated method stub
Intent intent = new Intent(contacts.this, contactsdetails.class);
Cursor cursor = (Cursor) adapter.getItem(position);
intent.putExtra("ID", cursor.getInt(cursor.getColumnIndex("_id")));
c.close();
startActivity(intent);
}
});
}
protected void onStart() {
super.onStart();
entry.open();
}
protected void onPause() {
super.onPause();
entry.close();
}
protected void onStop() {
super.onStop();
entry.close();
}
protected void onDestroy() {
super.onDestroy();
entry.close();
}
The tabhostactivity is quite simple:
th = getTabHost();
th.addTab(th.newTabSpec("tag1").setIndicator("Contacts", getResources().getDrawable(R.drawable.book)).setContent(new Intent(this, contacts.class)));
th.addTab(th.newTabSpec("tag2").setIndicator("Profile", getResources().getDrawable(R.drawable.mailbox)).setContent(new Intent(this, incomingcallpopup.class)));
th.setCurrentTab(0);
Can anyone spot the leak?
Thanks in advance for you help.
Can you get source of contacts.class and incomingcallpopup.class?
The problem is that you try to connect to db twice when switch between tabs (you don't close db connection in the first tab).
I had this problem in my project when I had 2 SlqHelpers to 1 db and use them at the same time.
Related
I have a view with a button and a list view backed by a cursor adapter containing bindView() and newView() for customized views. Each row of a list contains a Text and a checkbox. The data for each view comes from the database. I'm passing my Database adapter in the cursor adapter constructor. This I use to update the database when a checkbox is check or unchecked (works well). Of course I run "re-query" on cursor and view.refreshDrawableState()). Is this a good idea? What would be a better solution?
Second problem more serious, when a Button is clicked it starts a new activity. After hitting the back button from the new activity I get back my list View. But when I try to click on the checkbox this time I get Database close exception. Why? How do I fix this error?
Following is the list view and code snippet.
Button --------> Starts a new activity
CheckBox | TextView
CheckBox | TextView
MyActivity.java
onCreate() {
...
Button add_item_btn = (Button) findViewById(R.id.add_item_btn_id);
add_item_btn.setOnclickListener(new OnClickListener() {
//Start a new activity
});
}
protected void onPause() {
adapter.close();
mCursor.close();
}
protected void onResume() {
mListView = getListView();
adapter = new DBAdapter(getApplication());
adapter.open();
mCursor = adapter.getAllItems();
mCustomAdapter = new MyCursorAdapter(MyActivity.this, mCursor, adapter);
mListView.setAdapter(mCustomAdapter);
}
MyCursorAdapter.java
public class MyCursorAdapter extends CursorAdapter {
Cursor mCursor;
DBAdapter adapter;
public MyCursorAdapter(Context context, Cursor c, DBAdapter _adapter) {
...
mCursor = c;
adapter = _adapter;
}
public void bindView(final View view, Context context, final Cursor cursor) {
final CheckBox itemStatusCB = (CheckBox)
view.findViewById(R.id.item_status_id);
idx = cursor.getColumnIndex(myItem.ITEM_STATUS);
final long itemStatus = cursor.getLong(idx);
if (itemStatus == 1) {
itemStatusCB.setChecked(true);
} else {
itemStatusCB.setChecked(false);
}
itemStatusCB.setOnClickListener(new OnClickListener() {
#Override public void onClick(View v) {
int newStatus = 0;
if (((CheckBox) v).isChecked()) {
newStatus = 1;
}
adapter.updateItemStatus(itemId, newStatus);
mCursor.requery();
view.refreshDrawableState();
});
}
}
}
I was able to solve this. The new activity which was called had a DB connection open on onStart() and DB close on onDestroy(). After returning from that activity I was getting Database Illegal state Exception error as described with stack trace. I think it was returning cached version of DB connection. Once I removed DB.close() from the guest activity, it stopped issuing database not open error. Normally you would think that every activity can open a DB connection in it's onResume() or onStart() and close it in it's onPause() or onStop() or onDestroy() and it won't affect the connection across activities. Does this Make sense?
I have one small problem, after i click on list item, checkbox on item dont change state. Update works perfectly, setChecked maybe, but change will appear after exiting and re-running activity. I read lot of about notifyDataSetChange(), it may work, but not. How can i fix it, like after click on item chechbox value will change.
public class SviatokPridajActivity extends Activity
{
private DatabaseOp mDbHelper;
ListView listview;
String username;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sviatok_pridaj);
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
listview = (ListView) findViewById(R.id.listSviatok);
showUserSettings();
mDbHelper = new DatabaseOp(this);
mDbHelper.open();
Cursor sviatokCursor = mDbHelper.fetchAllSviatokNastav(username, 3);
if (sviatokCursor.getCount()==0)
{
mDbHelper.naplnSviatky(username);
sviatokCursor = mDbHelper.fetchAllSviatokNastav(username, 3);
}
final SviatokCursorAdapter adapter = new SviatokCursorAdapter(this, sviatokCursor);
listview.setAdapter(adapter);
listview.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int stlpec, long arg3)
{
// TODO Auto-generated method stub
Cursor cur = (Cursor) adapter.getItem(stlpec);
String odosli = cur.getString(cur.getColumnIndex("_id"));
String zobraz = cur.getString(cur.getColumnIndex("dlzka"));
CheckBox check = (CheckBox)findViewById(R.id.checkBox);
if (Integer.parseInt(zobraz)==0)
{
mDbHelper.updateSviatok(odosli, username, 1);
} else {
mDbHelper.updateSviatok(odosli, username, 0);
}
check.setChecked(!check.isChecked());
adapter.notifyDataSetChanged();
}
});
}
#Override
public void onPause()
{
super.onPause();
mDbHelper.close();
}
private void showUserSettings()
{
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
username = sharedPrefs.getString("prefUsername", "NULL");
}
}
you have checked your CheckBox in itemclick but it's not reflected to your itemList so before calling adapter.notifyDataSetChanged(); you should have to refresh listItems with new changes.
I ve answered this question in this : https://stackoverflow.com/a/22954806/1332870
can you check it? if it does not solve your problem please let me know
Reload your Cursor and instead of adapter.notifyDataSetChanged(); use adapter.changeCursor(reloadedCursor);
Not sure if this is a strain on the main thread but you could just set the adapter again in the on click method.
i have a Main activity which has a list view.
the listview items are loaded with LoaderManager.
when i click an item in the listview i open another activity that shows more information
(with "startActivityForResult")
the problem is :
when i go back(using the return key on) from the activity that shows information to the main activity and then click again i get an exception - Attempt to re-open an already closed object.
but if i go back from that activity(that shows more information) with a Button that i made there(which is actually "finish()") to the main activity and then cllick again then i get no exception
anyone knows what is the problem?
Thanks !
private void display_listview()
{
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(
this,
R.layout.row_invite_layout,
null,
columns,
to,
0);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//Ensures a loader is initialized and active.
getSupportLoaderManager().initLoader(0, null, this);
//add implementation to listview item click
listView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
IInvite invite = LoadFromCursor(cursor);
cursor.close();
mUpdateFlag = true;
if(!mTrashFlag) // Trash Can is turned off
{
Intent intent = new Intent(getApplicationContext(), InviteViewActivity.class);
intent.putExtra("invite",(Invite)invite);
startActivityForResult(intent, INVITE_REQUEST_ID);
}
else // Trash Can is turned on
{
getContentResolver().delete(InviteContentProvider.CONTENT_URI, SQLdataHelper.KEY_ROWID+"="+invite.getID(), null);
ExtraDelete(invite);
getSupportLoaderManager().getLoader(0).forceLoad();
}
}
});
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
dataAdapter.swapCursor(null);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
String[] projection = {
SQLdataHelper.KEY_ROWID,
SQLdataHelper.INVITE_CLIENT_ID,
SQLdataHelper.INVITE_CREATION_DATE,
SQLdataHelper.INVITE_NAME,
SQLdataHelper.INVITE_NOTE,
SQLdataHelper.INVITE_REQUESTED_DATE,
SQLdataHelper.INVITE_TOTAL_PRICE,
SQLdataHelper.INVITE_STATUS
};
CursorLoader cursorLoader = new CursorLoader(this,
InviteContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
UPDATE: FIXED IT. i added this method to my main activity..
#Override
public void onResume()
{
super.onResume();
getSupportLoaderManager().getLoader(0).forceLoad();
}
You can do it by creating public static fields, but I wouldn't recomend it. You can store data in shared preferences and then retrieve it whenever you want.
I have an activity which is working without operating the shared preference. After adding it, got null pointer error after clicking the button.
I think the way I am using shared preference is not correct. Anyone can help point out what is the issue? Should I new() it before using?
public class BanknameActivity extends Activity {
String [] bank_name;
String selected_bank_config;
SharedPreferences prefs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bankname);
// Show the Up button in the action bar.
// setupActionBar();
bank_name = getResources().getStringArray(R.array.bankname);
ListView lv1 = (ListView) findViewById(R.id.bankname_listview);
lv1.setChoiceMode(1); // CHOICE_MODE_SINGLE
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, bank_name);
lv1.setAdapter(adapter);
lv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long arg) {
//selected_bank_config = "Citi";
prefs.edit().putString(selected_bank_config, "citi").commit();
return_to_config(view);
}
});
}
Below is part of my code:
public void return_to_config(View view){
Intent intent = new Intent(this, ConfigActivity.class);
startActivity(intent);
this.finish();
}
**you need to initialize your shared Preferences. Without initialization it will always be null.**
yo need to do following stuff:
// mPrefs=getSharedPreferences("Preferences Name",mode);
mPrefs=getSharedPreferences("FBPref",0);
Editor edit=mPrefs.edit();
edit.putString("UserName", "");
edit.putString("UserId","");
edit.putString("EmailId","");
edit.commit();
This code is working fine.
I hope you will appreciate it.
I am trying to create a refesh function for my main page. I have searched many site but i can't seem to find a (for me) acccesseble exaple. I am loading information from a sQLLite database. When i use my add activity and i return to the MainScreen activity the item i have added do not appear. How could i refresh this data the moment de activity is resumed.
Any help is welcome, thx in advance.
public ListView whiskeylist;
String[] DataArryWhiskey;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start db view of whiskey
DBConfig info = new DBConfig(this);
info.open();
DataArryWhiskey = info.getDataInArray();
info.close();
whiskeylist = (ListView) findViewById(R.id.listofWhiskey);
whiskeylist.setOnItemClickListener(this);
whiskeylist.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, DataArryWhiskey));
}// end onCreate
On The advice of Adil i change the code to
public ListView whiskeylist;
String[] DataArryWhiskey;
ListAdapter Adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start db view of whiskey
DBConfig info = new DBConfig(this);
info.open();
DataArryWhiskey = info.getDataInArray();
info.close();
whiskeylist = (ListView) findViewById(R.id.listofWhiskey);
Adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, DataArryWhiskey);
whiskeylist.setAdapter(Adapter);
// End db view of whiskey
}// end onCreate
#Override
public void onResume()
{
super.onResume();
DBConfig info = new DBConfig(this);
info.open();
DataArryWhiskey = info.getDataInArray();
info.close();
Adapter.notifyDataSetChanged(); // refresh adapter
}
however i get a error on notifyDataSetChanged "the method notifyDataSetChanged is undefined for the type ListAdapter" <- fixed it by changing the ListAdapter to ArrayAdapter but the app still crashes.
//made changes in your oncreate method, see below
DBConfig info;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start db view of whiskey
info = new DBConfig(this);
whiskeylist = (ListView) findViewById(R.id.listofWhiskey);
whiskeylist.setOnItemClickListener(this);
}// end onCreate
another method give below, call this method from on ActivityResult(), but before doing that ensure that
the field you added with another activity also saved into database.
call show Data() from onActivityResult method or from onResume()
private void showData()
{
info.open();
DataArryWhiskey = info.getDataInArray();
info.close();
whiskeylist.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, DataArryWhiskey));
}
Get a class level variable of ArrayAdapter adapter; and initialize it like this way:
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, DataArryWhiskey);
hiskeylist.setAdapter(adapter);
and inside onResume() do like this way.
public void onResume()
{
super.onResume();
info.open();
DataArryWhiskey = info.getDataInArray();
info.close();
adapter.notifyDataSetChanged(); // refresh adapter
}
My Recommendation:
Since you are getting your values from database, use SimpleCursorAdapter instead of ArrayAdapter. which will do lots of other work also for you.
Here is a tutorial how to use SimpleCursorAdapter
You can try using adapter.clear(); before adding items.