I have an android app that is retrieving data from a mysql db via php.
It works fine, but i have a (simple) variable problem.
I want to create a variable inside MainActivity class.
Then inside MainActiviy class i have onCreate method - and inside that I have some json stuff that retrieves my data from mysql.
I now want to assign some mysql value to the variable i created in MainActivity class (it is assigned inside onResponse method from the json stuff), and then I simply want to use that variable and write it out on a textview, and I will do that in the bottom of the onCreate method.
But for some reason, it "forgets" the value I assigned to the variable, when I have to use it outside the onResponse method.
(if i set the textview text inside the onResponse method, it works fine).
public class MainActivity extends ActionBarActivity {
// I create the variable here
String someString;
TextView text;
RequestQueue reqq;
String showUrl = "http://www.someurl.com/get_data.php";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.textid);
reqq = Volley.newRequestQueue(getApplicationContext());
JsonObjectRequest jsonob = new JsonObjectRequest(Request.Method.POST,
showUrl, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray dataAr = response.getJSONArray("json_data");
for (int i = 0; i < dataAr.length(); i++) {
JSONObject dat = dataAr.getJSONObject(i);
// Here I want to assign some data from my mysql db to the variable
someString = dat.getString("aar");
// If I set the the textview to display value of someString HERE, it works!
// text.setText(someString);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
});
reqq.add(jsonob);
// HERE i want to display the value of the variable in the textview!
// - but it doesnt "remember" the value
text.setText(someString);
}
}
If I use static keyword on the someString variable, it remembers the value, but only the SECOND time i open the app!
I'm very new at this, and have tried google, and tried some stuff with a singleton class, but I just don't seem to understand this!
I would love it, if someone could link me some information to help me get this, AND give an example of how my code should be, so it will work!
THANKS! :D
This behavior is due to the fact that
text.setText(someString);
is executed immediately in the onCreate method, & by immediately I mean that it does not wait for any response from the Volley request (the Volley request that you set up before). In other words, you need to wait till you get a response before you set the text on to your TextView.
That's why it successfully sets your TextView's text from within the onResponse method.
Related
I am writing a code where I request JSON Object from url using Volley library. In my MainActivity I need to use this JSON Object so I need to wait for the response to be ready before going on with the code. For that, I implemented an interface with a method ProcessFinished that I define in my MainActivity and an AppController. Even with that, I still have an error when trying to call the requested Object in my Main Activity. Here's an extract of the used code :
public class GetData{
public String loadAllData(final DataAsyncResponse callBack){
AppController.getInstance().addToRequestQueue(jsonObjectRequest);
return mydata;}
public interface DataAsyncResponse {
void processFinished(String mydata);
}
In my ActivityMain :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new GetData().loadAllData(new DataAsyncResponse() {
#Override
public void processFinished(String mydata) {
}
});
You need to set the value for data within the processFinished() callback. The way you're setting it now won't work because the value isn't ready yet at that point. Although it's hard to tell because you haven't included all the code, something like this should work:
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new GetData().loadAllData(new DataAsyncResponse() {
#Override
public void processFinished(String mydata) {
data = myData;
}
});
Outside my onCreate method my stringarray isnt able to be called , why is that ?
I tried to initialize the stringarray again in the method outside onCreate with getResources(); but I cant call that either,
im guessing there is some fundemental knowledge im lacking as im fairly new in the programming world, can u help me or explain why the string array can't be called outside onCreate and/or how to work around it ?
example inside onCreate :
final String[] list = res.getStringArray(R.array.fact);
outside onCreate :
public void facts(){
getResources();
final String[] list = res.getStringArray(R.array.fact);
}// marked res. in red saying "qualifier must be an expression" and without that I cant call "list"..
I believe your issue is variable scope. You've declared your string array in the onCreate() method, giving it "method scope" and then you're tying to access it in another method. I would suggest declaring your string array as a class level variable so that you can access it from any method within that class.
To avoid the NullPointerException issue pointed out by Ben P. in a comment, you could initialize your array variable in the onResume() method so that you have it ready to go before you need to use it anywhere else.
Make sure to take a look at the reference links below the code example. I think they'll help explain "why" better than I can in a quick answer here.
public class MainActivity extends AppCompatActivity {
private static String[] list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
// onResume() runs after onCreate() and onStart() in the Android Activity lifecycle.
// If your array has not been initialized yet, do it now.
if (list == null || list.length == 0) {
list = getResources().getStringArray(R.array.fact);
}
}
public void facts() {
// Do something with your array...
for (String arrayItem : list) {
String fact = arrayItem.toUpperCase();
}
}
}
References:
The Activity Lifecycle: https://developer.android.com/guide/components/activities/activity-lifecycle
Variable scope: https://www.java-made-easy.com/variable-scope.html
Replace
final String[] list = res.getStringArray(R.array.fact);
by
final String[] list = getResources().getStringArray(R.array.fact);
I am using a class with static values called DB (for Data Base) in my application. When I first run the app, a static byte array from this class is filled and used. Then, when I partially close my app (not closing it definitily just put in background) if a reopen it after 20 seconds more or less, the value of the variable is still here but if I let my app in the background for more than 1 minute the value turns to null.
How can I avoid this to happen?
store your variable value to shared preferences and load the value from shared preferences in the onResume() Method of activity and store the value in the onPause() Method.
Handling lifestyle events properly is an important aspect of Android development.
I suggest that you read the following to make sure that you understand what happens to your app when you turn off your screen, change to another application or any other action that might change the state of your app:
http://developer.android.com/training/basics/activity-lifecycle/index.html
My suggestion is to store your data by overriding onSaveInstanceState() like so:
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Then on your onCreate(), you can reload it like so:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null)
{
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
else
{
// Probably initialize members with default values for a new instance
}
...
}
I hope that this helps!
Good luck in your future developing!
I found a solution with the help of the commenters.
For those who had the same problem:
Copy this in all your Activities to ensure that the data is constantly updated in the preferences:
#Override
public void onPause(){
super.onPause();
String bytearray = Base64.encodeToString(DB.bytearray, Base64.DEFAULT);
prefs.edit().putString("BYTEARRAY", bytearray).apply();
}
#Override
public void onResume(){
super.onResume();
String bytearray = prefs.getString("BYTEARRAY", Base64.encodeToString(DB.bytearray, Base64.DEFAULT));
DB.bytearray = Base64.decode(bytearray, Base64.DEFAULT);
}
Then, add this code in all your Activities to ensure that the values are not saved when you close your app.
#Override
public void onDestroy(){
super.onDestroy();
String bytearray = "";
prefs.edit().putString("BYTEARRAY", bytearray).apply();
}
So I've debugged my program and have found that the part of my program is updating, whilst another isn't.
I have a method:
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
The debugger reports that an object is contained in the LinkedList (peopleAttending).
In another method:
public void populateListView() {
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
This method can be called after the previous one and states that there isn't an object within the LinkedList.
I've checked the object references just to make sure that they are pointing at the same reference and they are.
Any help would be greatly appreciated.
EDIT: Entire Class:
public class GuestsAttending extends Activity {
private LinkedList<Application> peopleAttending = new LinkedList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_guests_attending);
populateListView();
}
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
public void populateListView() {
// GuestsAdapter adapter = new GuestsAdapter(this, peopleAttending);
// ListView listView = (ListView) findViewById(R.id.listView);
// listView.setAdapter(adapter);
peopleAttending.size();
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
Second Edit:
Java Booking Screen Method:
public void saveBookingInfo(View view) {
GuestsAttending sendApplication = new GuestsAttending();
EditText applicantNameText = (EditText) findViewById(R.id.applicantNameTextField);
EditText itemToBurnText = (EditText) findViewById(R.id.itemToBurnTextField);
String appName = applicantNameText.getText().toString();
String appItemToBurn = itemToBurnText.getText().toString();
if (appItemToBurn.isEmpty() || appName.isEmpty()) {
Toast.makeText(BookingScreen.this, "Please fill in all fields.", Toast.LENGTH_SHORT).show();
}
else {
sendApplication.storeApplication(appName, appItemToBurn);
}
}
GuestsAttending Java Class: -- See Above.
Useful hint: It's really popular to set type of List as a List<> interface from java.util package instead of LinkedList<> itself.
Anyway, i am pretty sure that storeApplication method is not automatically triggered before onCreate method ran by Activity framework. Maybe your debugger is stopoing on it in different order (because of using threads or smth), but you should to log some invoke. Try to find it out.
I've found out what the problem is:
When I submit the booking information, it runs all the necessary methods. However, when the "storeApplication()" method has finished executing, the ArrayList 'empties' all the objects out.
I only noticed this when I used breakpoint and tried running the method twice, on the second time I entered booking details, the ArrayList stated it was empty.
I'm going to see if I can try and store the ArrayList in a more secure place.
I am using volley with android. As I am studying this example on github https://github.com/evancharlton/folly . That project made a perfect solution of querying json.
Now comes the problem. If I replace the json url there with another, then the fetching seems not to work!
I know different jsonws need different parsing jobs so I extremely simplified the code. Like this.
public class MainActivity extends SherlockFragmentActivity {
/**
* Called when the activity is first created.
*/
private ViewPager pager;
private PagerSlidingTabStrip tabs;
private MyPagerAdapter adapter;
private String url = "http://konachan.com/post.json?limit=1";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView txt = (TextView) findViewById(R.id.text);
txt.setText("initial");
RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
public void onResponse(JSONObject response) {
txt.setText("somethinghappend");
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
Log.e("wtf", error.toString()) ;
}
});
queue.add(jsObjRequest);
}
}
As you can tell, I removed everything about the content of json but only doing this to figure out why the code goes wrong with only one change: the url
To make it clear:(i dont have enough reputation for multi links so use the comment insdead)
when I set url to
(comment 1)
my TextView successfully change his text(this is the json used in the example project from github, though this is meaningless information since I had built very unrelated code from the example)
but when I switch the address to
(comment 2)
nothing happen to my TextView!(this is the json I really need to work with)
And yes, both jsons are correctly fetched and parsed and displayed in firefox(without any kind of proxy), so I think there are nothing wrong with the jsons and the network.
In fact I think the ONLY difference is that they are different jsons.... with 2 different urls....
Now I seek help from you guys, how can that be? Nothing changed but only a smaller json.. makes everything broken? (is it about timeout matters? but both are loaded very fast in firefox... and obviously the working one contains much more bytes than the not working one...)
the error logged 410 error. The page has Gone?! But it is alive in firefox... and the other json works in both environment(firefox and app)