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.
Related
This probably demonstrates the most appalling lack of understanding of the activity life cycle, but please be sympathetic. I am ultimately going to want to invoke Activity B from Activity A a number of times, each time passing a different parameter to Activity B which is then responded to by the user and stores/sets various public variables. As a precursor to this, I just want to get my head round how Activity A sees the change to a public variable that Activity B has changed.
I have three very simple classes: Common.java that holds the public variables, the main activity MainActivity.java and the child activity Child.java. There is only one public variable right now; it's the string mess1 which is initialized to "***". All the code does at the moment is when mainbutton is clicked in MainActivity, it invokes Child. In Child, we immediately set mess1 to "Child here" then set the text in a Child-based TextView to mess1. On clicking the childbtn button in Child, we finish() the child activity (and of course the system returns us to MainActivity.
When this app is run, wee see the three stars displayed in MainActivity. When mainbutton is pressed we go to Child and see "Child here" displayed. When the childbtn is pressed, we return to MainActivity BUT, the three stars are still there although we know for sure that mess1 now holds "Child here".
My questions are:
1. Why, when we know mess1 has been changed, does MainActivity still display "***" on return from the Child activity?
2. What do I need to change in the code to get "Child here" to display?
Relevant code extracts follow. Thanks in advance for your help.
Common.java
public class Common
{
public static String mess1 = "***";
}
MainActivity.java
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mainbutton = (Button)findViewById(R.id.mainbutton);
TextView maintop = (TextView)findViewById(R.id.maintop);
mainbutton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
startActivity(new Intent(MainActivity.this, Child.class));
}
});
maintop.setText(Common.mess1);
}
Child.java
public class Child extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);
TextView childtext = (TextView) findViewById(R.id.childtext);
final Button childbtn = (Button) findViewById(R.id.childbtn);
Common.mess1 = "Child here";
childtext.setText(Common.mess1);
childbtn.setOnClickListener
(new View.OnClickListener()
{public void onClick(View v)
{finish();
}
}
);
}
Likely you are moving back on the back stack history and you are resuming the previous activity that was placed in a paused state and therefore the onCreate isn't being called but the onResume (of the initial activity)..
Using global state this way isn't advised but this should work if you place the appropriate code in the onResume method.
You should set the text in onResume() of MainActivity. When you get back from Child.java onResume() (not onCreate()) is invoked and, since maintop's text is set in onCerate() only, nothing changes it on return.
protected void onResume() {
super.onResume();
maintop.setText(Common.mess1);
}
Reference: Activity Lifecycle and Implementing the lifecycle callbacks
Actually i am new to test case here i go on with the integration tests for the two activities.
Below code:
public class MainActivityFunctionalTest extends
ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity activity;
public MainActivityFunctionalTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
activity = getActivity();
}
public void testStartSecondActivity() throws Exception {
// add monitor to check for the second activity
ActivityMonitor monitor =
getInstrumentation().
addMonitor(SecondActivity.class.getName(), null, false);
// find button and click it
Button view = (Button) activity.findViewById(R.id.button1);
// TouchUtils handles the sync with the main thread internally
TouchUtils.clickView(this, view);
// to click on a click, e.g., in a listview
// listView.getChildAt(0);
// wait 2 seconds for the start of the activity
SecondActivity startedActivity = (SecondActivity) monitor
.waitForActivityWithTimeout(2000);
assertNotNull(startedActivity);
// search for the textView
TextView textView = (TextView) startedActivity.findViewById(R.id.resultText);
// check that the TextView is on the screen
ViewAsserts.assertOnScreen(startedActivity.getWindow().getDecorView(),
textView);
// validate the text on the TextView
assertEquals("Text incorrect", "Started", textView.getText().toString());
// press back and click again
this.sendKeys(KeyEvent.KEYCODE_BACK);
TouchUtils.clickView(this, view);
}
}
Below line which calls SecondActivity gives me null pointer error how can get the value for second activity tried a lot to fix but remains the same.
SecondActivity startedActivity = (SecondActivity) monitor
.waitForActivityWithTimeout(2000);
If anyone have idea about this please help me.
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.
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?
Tts.java
public void onInit(int arg0) {
Bundle dataBundle = Tts.this.getIntent().getExtras();
speech = dataBundle.getString("IMSENS");
tts.setLanguage(Locale.ENGLISH);
tts.speak(speech, TextToSpeech.QUEUE_FLUSH, null);
}
Im_SensShow.java
public class Im_SensShow extends ListActivity {
public void buttonGetClicked(View v){
Cursor cc = cursor;
Intent showSound = new Intent(Im_SensShow.this,Tts.class);
Bundle dataBundle = new Bundle();
dataBundle.putString("IMSENS",cc.getString(cc.getColumnIndexOrThrow(DBAdapter.KEY_IM_SENS)));
showSound.putExtras(dataBundle);
startActivityForResult(showSound,1);
}
I set tts to read a sentece from Im_SensShow.java and when I press the button in Im_SensShow.java page this progarm change to tts page and make sound
but actually I want it to send only sentence and doesn't change page (stay in Im_SensShow.java page)
I try to not declare .Tts in Manufest but it force close
What should I do?
Put the tts.speak() call in a [onItemClick()][1] listener.
You need to make sure that the TTS subsystem is already initialized (e.g. within onCreate() of your Im_SendShow activity).