My app consists of a number of activities, upto now I have had no problems with accessing the database when moving between the activities. However on the last listActivity (LocationActivity), I have an embedded button on each listView item.
When one of these buttons are clicked, it sends you to SpecificationEdit.java where the user inputs the specfication into some EditText fields for that listView item (a damaged component), but when you click Save it crashes with the following error message (note the data is saved to database ok):
java.lang.RuntimeException: Unable to resume activity blah blah
Exception: trying to requery an already closed cursor blah blah
Here is the listActivity class:
public class LocationActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);
setLongClick();
rmDbHelper = new RMDbAdapter(this);
rmDbHelper.open();
getIntents();
setUpViews();
setAdapter();
setTextChangedListeners();
}
protected void onResume(){
super.onResume();
final Cursor locationCursor = (Cursor) rmDbHelper.fetchLocationsForRun(runId);
startManagingCursor(locationCursor);
locationCursorSize = locationCursor.getCount();
setAdapter();
setTextChangedListeners();
}
And here is the bit in this activity where is sends you to SpecificationEdit.java
private void startComponentEdit() {
Intent i = new Intent(LocationActivity.this, SpecificationEdit.class);
i.putExtra("Intent_InspectionID", inspectionId);
i.putExtra("Intent_AreaID", areaId);
i.putExtra("Intent_RunID", runId);
i.putExtra("Intent_LocationID", locationId);
i.putExtra("Intent_Ref", locationRef);
i.putExtra("Intent_DamagedComponentID", damagedComponentId);
startActivityForResult(i, ACTIVITY_CREATE);
}
And here is the OnCreate in SpecificationEdit.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rmDbHelper = new RMDbAdapter(this);
rmDbHelper.open();
Intent i = getIntent();
inspectionId = i.getLongExtra("Intent_InspectionID", -1);
areaId = i.getLongExtra("Intent_AreaID", -1);
runId = i.getLongExtra("Intent_RunID", -1);
locationId = i.getLongExtra("Intent_LocationID", -1);
damagedComponentId = i.getLongExtra("Intent_DamagedComponentID", -1);
setContentView(R.layout.edit_specification);
setUpViews();
populateFields();
fillSpinner();
setListeners();
}
With the bit of code which fires when you click the save button:
protected void saveDamagedComponentSpec() {
String manufacturer = ((Cursor)manufacturerSpinner.getSelectedItem()).getString(1).toString();
String text1 = specEditText1.getText().toString();
String text2 = specEditText2.getText().toString();
String text3 = specEditText3.getText().toString();
String text4 = specEditText4.getText().toString();
String notes_spec = specEditTextNotes.getText().toString();
rmDbHelper.saveDamagedComponentSpec(damagedComponentId, manufacturer, text1, text2, text3, text4, notes_spec);
if ("Yes".equals(specSaved)){
Toast.makeText(getApplicationContext(), "Component specification updated",
Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(), "Component specification added",
Toast.LENGTH_SHORT).show();
}
finish();
}
Finally, here is the code in my database helper class:
//Constructor - takes the context to allow the database to be opened/created
public RMDbAdapter(Context ctx) {
this.mCtx = ctx;
}
/**
* Open the rm 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
* Initialisation call)
* #throws SQLException if the database could be neither opened or created
*/
public RMDbAdapter open() throws SQLException {
rmDbHelper = new DatabaseHelper(mCtx);
rmDb = rmDbHelper.getWritableDatabase();
return this;
}
public void close() {
rmDbHelper.close();
}
The weird thing is, you can click on one of the listView item (the actual item not the embedded item) or the button 'add new component' and this will send you to another activity ComponentEdit.java with very similar interface (to add a component to the list) as SpecificationEdit but which when it finishes doesn't crash the app.
I can't see any major difference between the two activities, yet one is crashing with this error when you return to LocationActivity and one is not.
I have just tried removing onResume and this made no difference.. Hit a brick wall with this and it's driving me nuts.
I should add that it is working ok on my emulator, but crashes when I test it on my phone (HTC One S). Very strange..
Don't forget to call rmDbHelper.close(); before start another activity
Right, found the issue (spot the obvious mistake):
Cursor componentsCursor = (Cursor) rmDbHelper.fetchDamagedComponentSpecForInspection(inspectionId, componentType);
startManagingCursor(componentsCursor);
Intent i = new Intent(this, SpecificationEdit.class);
i.putExtra("Intent_InspectionID", inspectionId);
i.putExtra("Intent_AreaID", areaId);
i.putExtra("Intent_RunID", runId);
i.putExtra("Intent_LocationID", locationId);
i.putExtra("Intent_Ref", locationRef);
i.putExtra("Intent_DamagedComponentID", damagedComponentId);
startActivityForResult(i, ACTIVITY_CREATE);
componentsCursor.close();
So it wasn't this obvious (I had some blocked out code left in before the componentsCursor.close()), but when I finished SpecifcationEdit.class, I guess it returns to this activity and tries to close the componentsCursor but obviously fails.
Stupid thing is, I hadn't actually got this cursor doing anything yet! Doh!
Just for some additional advice/whittering; my app is fundimentally different to the Google notePad example, as I don't actually use the startActivityForResult as they do (in fact now I understand it better I will replace these all with just startActivity) as I input the data into the database while still on the edit activities, there and then (rather than passing this information through an intent then addinig when you return to the previous activity.
I find this more logical in the realms of my code, but any feedback on this approach?
Related
I am developing a Quizz App in which an activity shows question and options from SQLite and on selecting option, another activity is showing result for 2000 ms(it has a timer)and then it calls First Activity via an Intent.
So, Most of the interaction is between 2 activities. But each time my MainActivity is called, it re-initializes all the variables again and again.
I am opening my database connection in onCreate() and also keeping a counter (that can count how many questions have been asked yet) whose value is not retained after the intent from Second Activity. I am worried on how to solve this.
I am a bit confused about the life cycle that is followed. Whether the call to First Activity from Second one starting with onCreate() or it's also initializing the instance variables again.
This is onCreate() method I wrote:
public class MainActivity extends Activity {
protected static final int SCORE_INCREMENT = 5;
TextView question;
Button score, opt1, opt2, opt3;
MyDatabaseManager dbManager;
QuizManager quizManager;
private int quiz_counter =1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbManager = new MyDatabaseManager(getApplicationContext());
dbManager.open();
quizManager = new QuizManager(MainActivity.this, dbManager);
Toast.makeText(MainActivity.this, "Asking The First Question", 0).show();
askQuestion();
}
}
Is there any difference between the above written code and the one I am writing now... if the activity is called again via an Intent
public class MainActivity extends Activity {
protected static final int SCORE_INCREMENT = 5;
TextView question;
Button score, opt1, opt2, opt3;
MyDatabaseManager dbManager = new MyDatabaseManager(getApplicationContext());
QuizManager quizManager = new QuizManager(this, dbManager);
private int quiz_counter =1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(MainActivity.this, "Asking The First Question", 0).show();
askQuestion();
}
}
This might be a silly question. But it's a bit confusing for me. Suggestions are welcome.
If you have variables that you want to maintain between changing activities, then you should either
Store them in SharedPreferences
or
Pass them between the Activites in the Intents (see Starting another activity)
I am try to write a junit test case on selecting a list item and intent to next activity, but i dont know how to simulate this user action by junit coding. Can anyone help?
Also i wanna ask, is there any material teaching the function or syntax on simlate different user action in junit?
The following is a example from my school tutorial notes, and i want to do something like this one, but on a listview item.
public void testKilosToPounds() {
/* INTERACTIONS */
TouchUtils.tapView(this, textKilos); // tap the EditText textKilos
sendKeys("1"); // sent the number 1
TouchUtils.clickView(this, buttonPounds); // click the button buttonPounds
/*CHECK THE RESULT*/
double pounds;
try {
pounds = Double.parseDouble(textPounds.getText().toString());
} catch (NumberFormatException e) {
pounds = -1;
}
//JUnit Assert equals
// message expected actual delta for comparing doubles
assertEquals("1 kilo is 2.20462262 pounds", 2.20462262, pounds, DELTA);
}
I am working on JUnit from last few months to test android applications. And so i am now able to test almost things like webservices and views. Anyway i am sharing my code to test listview with item click and in next activity(InfoActivity) to check data that i sent using intent. InfoActivity is activity where i am sending data of clicked item from ListActivity.
public class ListActivityTest extends ActivityInstrumentationTestCase2<ListActivity> {
private Activity activity;
private ListView lv;
private InfoActivity contextInfoActivity;
private TextView tvInfo;
public ListActivityTest(){
super(ListActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
activity = (ListActivity)getActivity();
lv = (ListView)activity.findViewById(R.id.lv);
}
public void testCase1(){
assertNotNull(activity);
assertNotNull(lv);
}
public void testCase2(){
Instrumentation instrumentation = getInstrumentation();
Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(InfoActivity.class.getName(), null, false);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
lv.performItemClick(lv,4,0);
//lv is listview,4 is item position,0 is default id
}
});
Activity currentActivity = getInstrumentation().waitForMonitor(monitor);
contextInfoActivity = (InfoActivity) currentActivity;
assertNotNull(contextInfoActivity);
tvInfo = (TextView)contextInfoActivity.findViewById(R.id.tvInfo);
assertNotNull(tvInfo);
assertEquals("Karan",tvInfo.getText().toString());
//karan is name at position 4 in listview and i am checking it with name set in textview of next activity i.e infoActivity.
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
activity = null;
lv = null;
tvInfo = null;
contextInfoActivity = null;
}
Hope this ll b helpfull for you.I you want to ask something feel free to ask.Thanks
You can click on a specific row in a ListView by first getting the View that holds that child, and then passing that view in to TouchUtils.clickView.
If you have a ListView view and ActivityInstrumentationTestCase2 this, and you want to click position p in the view:
TouchUtils.clickView(this, view.getChildAt(p));
You probably also want to check if the view is actually on screen too.
This is my first question here in SO. So, here's my problem: I'm developing an app for android. This is how my app flows:
I call a MainActivity;
The user press a button that calls a LoginActivity;
The user fill in the username and password;
LoginActivity calls a AsynchronousTask that send the data for a JAX-WS (I use ksoap2 library to call my SOAP web service);
If the information are right, a MenuActivity is called;
There is a button for "License Plate Search". When it is pressed, I call a Activity for input the license plate and an "OK" button for search;
When the "OK" button is pressed, I send the data for a new AsynchronousTask that search for the plate in my web service;
If the license plate is a valid one (activity result = OK) I populate my database with: License Plate Number, Year, Model and Developer;
After this I call a new Activity called ResultActivity where a show the vehicle information in a new Dialog (HERE IS THE PROBLEM);
But, if the result is CANCELED (when my web service doesn't found the license plate) I just show a Alert for the user.
The problem is happening when I try to show the information for the user. Here is my ResultActivity:
public class ResultadoBuscaPlacaActivity extends Activity {
private VeiculoDAO vDao;
TextView campoPlaca;
TextView campoModelo;
TextView campoMarca;
TextView campoAno;
TextView campoRenavam;
TextView campoProprietario;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_resultado_busca_placa);
campoPlaca = (TextView) findViewById(R.id.valor_placa);
campoModelo = (TextView) findViewById(R.id.valor_modelo);
campoMarca = (TextView) findViewById(R.id.valor_marca);
campoAno = (TextView) findViewById(R.id.valor_ano);
campoRenavam = (TextView) findViewById(R.id.valor_renavam);
campoProprietario = (TextView) findViewById(R.id.valor_proprietario);
vDao = new VeiculoDAO(this);
vDao.open();
}
public void confirm(View v){
finish();
}
#Override
protected void onStart() {
super.onStart();
Veiculo v = new Veiculo();
v = vDao.getFirstElement();
if (v.getPlaca()!=null){
campoPlaca.setText(v.getPlaca());
campoModelo.setText(v.getModelo());
campoMarca.setText(v.getMarca());
campoAno.setText(v.getAno());
campoRenavam.setText(v.getRenavan().toString());
campoProprietario.setText(v.getNomeProprietario());
}
}
#Override
protected void onDestroy() {
vDao.close();
super.onDestroy();
}
#Override
protected void onPause() {
vDao.close();
super.onPause();
}
}
The VeiculoDAO is a simple DAO that calls a Custom SQLiteOpenHelper class. The method getFirstElement() is here:
public Veiculo getFirstElement(){
Cursor cursor = database.query(CustomSQLiteOpenHelper.TABLE_VEICULO,
columns, null, null, null, null, null);
cursor.moveToFirst();
Veiculo v = new Veiculo();
while (!cursor.isAfterLast()) {
v.setAno(cursor.getInt(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_ANO)));
v.setMarca(cursor.getString(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_MARCA)));
v.setModelo(cursor.getString(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_MOD)));
v.setNomeProprietario(cursor.getString(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_NMPROP)));
v.setPlaca(cursor.getString(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_PLACA)));
v.setRenavan(cursor.getLong(cursor.getColumnIndex(CustomSQLiteOpenHelper.COLUMN_MARCA)));
}
cursor.close();
return v;
}
What is actually happening is: when the result activity calls the method onStart() and goes to the line:
v = vDao.getFirstElement();
The UI goes back to MenuActivity (because I called the SearchActivity by startActivityForResult() ) but it stops there, when it supposed to call ResultActivity.
In logcat, this message runs in loop:
D/dalvikvm(28637): GC_CONCURRENT freed 631K, 15% free 18650K/21824K, paused 1ms+4ms, total 25ms<br>
If I comment the specific line above, the app runs "normally" and the result Activity is called, but the information are empty (of course).
Does anyone know what should I do?
This loop never terminates since the cursor is never advanced inside the loop and the return value of isAfterLast() does not change.
while (!cursor.isAfterLast()) {
}
Since you are only interested in the first result, change it to
if (cursor.moveToFirst()) {
}
You can remove the earlier moveToFirst() call as redundant. It's still important to check the return value as there may be no result rows and then moveToFirst() returns false.
I am setting my textview using columns from a sqlite database file.Problem is that when i change the view back and forth, there is a very obvious lag while it retrieves from the database.Here is the code:
public class Flipcard extends Activity implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layMain = (LinearLayout) findViewById(R.id.FlashCardFront);
layMain.setOnClickListener(this);
Cursor cur;
DbH db = null;
TextView tv=(TextView) findViewById(R.id.TV_Word);
TextView txtView=(TextView) findViewById(R.id.TV_CardNo);
try {
db=new DbH(this);
} catch (IOException e2) {
e2.printStackTrace();
}
try {
db.createdatabase();
} catch (IOException e) {
e.printStackTrace();
}
db.opendatabase();
cur=db.data();
cur.moveToFirst();
tv.setText(cur.getString(0));
cur.close();
}
public void onClick(View v) {
Intent i=new Intent(this,Flipcard_back.class);
startActivity(i);
}
}
the other java file that is being called on click is:
public class Flipcard_back extends Activity implements OnClickListener{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_back);
LinearLayout layMain = (LinearLayout) findViewById(R.id.FlashCardRear);
layMain.setOnClickListener(this);
}
public void onClick(View v) {
Intent i=new Intent(this,Flipcard.class);
startActivity(i);
}
}
which is actually calling previous one.The problem is i'm one a very initial phase on my app development.Right now i am only trying to change two textviews in first java file using my database.When i switch back and forth there is a lag of around a second.How do i correct it? am i doing something wrong.
Also i am not able to retrieve all the rows using getstring(1) etc.Why so?How do i get the columns one by one?
The lag is caused by accessing your database in your main thread, you want to have it run in its own thread. Here is a the Developer's Guide on Processes and Thread which will give you the grand overview. Look into something like AsyncTask next, for one approach to accessing databases.
As for not getting results from your database, I cannot tell without seeing the class associated with db. Include that code and any logcat errors in your question if you want more help.
EDIT
With the error code you provided I found this link to a similar question and detailed answer.
My experience from sqlite + android is that its initially very slow when quering the database, but after that it can retrieve large amounts of data very fast. If you want to avoid lag, use a file instead.
My tiny experience comes from developing the dictionary app Wordlist Pro where I had to abandon the database for textfiles since it was way too slow (and big in size).
In my application, after enough clicking around, I get this error:
06-08 19:47:59.967: ERROR/AndroidRuntime(2429): java.lang.RuntimeException: Unable to pause activity {com.MYAPP.app/com.MYAPP.app.MainActivity}: android.database.StaleDataException: Access closed cursor
What I have is a Tab Activity (my MainActivity), which has a ListActivity as the contents for each tab. Inside the onCreate for each ListActivity I get a cursor that represents the data to be displayed in that list.
The onListItemClick for each list also creates another activity, so clicking on an item in the list will show more information about that item in a new screen. It's inconsistent, but after enough clicking into these new activities, or going back to the ListView from a new activity, the program crashes.
In searching around for a solution to my problem, I did stumble upon registerDataSetObserver, but it doesn't seem to be the whole answer. I am also having trouble finding documentation on it, so I'm not sure I fully understand it. I have a custom ListAdapter that both my ListViews use and have called registerDataSetObservers on the cursors there.
I have attached the relevant code from one of my ListActivities and from my custom ListAdapter class.
The ListActivity. I have two of these, almost identical, except they both have different cursors created from different database queries:
import com.MYAPP.app.listmanager.DeviceListAdapter;
public class AllSensorsActivity extends ListActivity{
private DeviceListAdapter AllList;
private DbManager db;
protected Cursor AllCur;
protected Cursor AllSensors;
private static final String TAG = "AllSensorsActivity";
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.e(TAG, "Calling All onCreate");
db = new DbManager(this);
db.open();
AllCur = db.fetchAllDevices();
startManagingCursor(AllCur);
AllSensors = db.fetchAllSensors();
startManagingCursor(AllSensors);
AllList = new DeviceListAdapter(this, AllCur, AllSensors);
setListAdapter(AllList);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id){
String device_name = (String) ((DeviceListAdapter)getListAdapter()).getItem(position);
String sensor_string = ((DeviceListAdapter)getListAdapter()).getSensors(id);
Intent i = new Intent(this, SensorActivity.class);
Bundle bundle = new Bundle();
bundle.putString("NAME", device_name);
i.putExtras(bundle);
bundle.putString("SENSORS", sensor_string);
i.putExtras(bundle);
this.startActivity(i);
}
The custom ListAdapter:
public class DeviceListAdapter extends BaseAdapter {
private static final String TAG = "DeviceListAdapter";
private Context mContext;
private Cursor mSensors;
private Cursor mDevices;
protected MyDataSetObserver sensors_observer;
protected MyDataSetObserver devices_observer;
public DeviceListAdapter(Context context, Cursor devices, Cursor sensors){
mContext = context;
mDevices = devices;
mSensors = sensors;
sensors_observer = new MyDataSetObserver();
mSensors.registerDataSetObserver(sensors_observer);
devices_observer = new MyDataSetObserver();
mDevices.registerDataSetObserver(devices_observer);
}
// ... more functions and stuff that are not relevant go down here...
}
private class MyDataSetObserver extends DataSetObserver {
public void onChanged(){
Log.e(TAG, "CHANGED CURSOR!");
}
public void onInvalidated(){
Log.e(TAG, "INVALIDATED CURSOR!");
}
}
Should I just have MyDataSetObserver catch the exception and move on? I'd like a more robust solution than that if possible. Or is there some other way I could rearrange my program so that the staleDataException doesn't occur (as often)? I believe that it is happening because I am launching the new activity in my onListItemClick.
I believe when it's invalidated you want to call requery() on the cursor. You could possibly do that in onInvalidated().