How can initialized array became null? - android

Here is the code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
//... Filling this array.
}
Later, from another activity StartPage.rowItems.size() throw NullPointerException
It can be 0 (failed to retrieve data or I did .clear()), but how, the hell, it became null? I definitely never set it to null.
One more point - this array variable is public static and I use it from another activity. Can it be possible android unloads parent activity (what contains all global variables for the whole app)?
P.S. I cannot check it more thoroughly, because this error is not appears in my emulator/devices, but I got reported it on Google Play. So I can't check what was before and when the array became null...
Thank you
More exact code:
public class StartPage extends Activity implements View.OnTouchListener {
public static List<RowItem> rowItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
pDialog = new ProgressDialog(this);
pDialog.setMessage("Loading data...");
pDialog.setCancelable(false);
pDialog.show();
gc=new GetData();gc.execute();
}
public class GetData extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
rowItems.clear();
inProgress=true;
}
#Override
protected Void doInBackground(Void... arg0) {
ServiceHandler sh = new ServiceHandler();
String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
items = jsonObj.getJSONArray(TAG_COINS);
for (int i = 0; i < itemss.length(); i++) {
JSONObject c = items.getJSONObject(i);
String id = c.getString(TAG_ID).toUpperCase();
String price = c.getString(TAG_PRICE);
String name = c.getString(TAG_NAME);
RowItem item = new RowItem(id, name, price);
rowItems.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("ServiceHandler", "Couldn't get any data from the url");
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
inProgress=false;
pDialog.dismiss();
}
}
Then call another activity:
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
int x = (int) event.getX();
int y = (int) event.getY();
int w=view.getWidth()-20;
int h=view.getHeight()-20;
if (x<w*0.05 || x>w*0.95 || y<h*0.13 ) return false; // Misclicked
if (x<w*0.5 && y<h*0.38) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}
return true;
}
On another activity (MainActivity), try to refresh the listview with the data from main activity:
public class MainActivity extends ListActivity implements View.OnClickListener {
void refresh_list() {
if (StartPage.rowItems.size()>0) { <-- Here is NPE
ListAdapter adapter = new CustomListAdapter(MainActivity.this,R.layout.list_item,StartPage.rowItems);
setListAdapter(adapter);
((BaseAdapter) getListAdapter()).notifyDataSetChanged();
}
}
Google play report:
Caused by: java.lang.NullPointerException
at halfprice.coinmanager.MainActivity.refresh_list(MainActivity.java:116)
at halfprice.coinmanager.MainActivity.onCreate(MainActivity.java:105)
at android.app.Activity.performCreate(Activity.java)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java)
Hope this helps...

you are loading your data in static ArrayList and Acessing it to different activity. its not good practice to do.
Let me first tell your answer as you have created this object in Oncreate(). Its better you make create it Globally than this problem will not occure.
Example :
public class StartPage extends Activity implements View.OnTouchListener {
public static List<RowItem> rowItems = new ArrayList<RowItem>();
OnCreate(){
//and perform the action you want to do.
}
//Hope this will help you definately.
Now Another Method which is the good Practice in Programming language
Passing data object from one Activity to another is simple, If you want to pass Array object than the object should be serialized. Eg;
ArrayList rowItems = new ArrayList();
for Passing array object you have to use intent PutExtra, Eg:
Intent intent = new Intent(SplashScreen.this, MainActivity.class);
intent.putExtra("key",array); startActivity(intent);
//intent.putExtra("key",array); will show error if your Model class is not implements Serializable eg: public class Model implements Serializable{
String id;
String price;
String name;
//generate your getter setter and set data in to this.
}
//For getting data in to another class just use
ArrayList<Model> data = (ArrayList<Model>)getIntent().getSerializable("key");
Now you can play arround with this data object. You should always try to play around with private or protected object.
Hope this will help you.

If i'm not mistaken:
When your activity is launched, the onCreate() method is called.
But when you come back to the same activity from another activity, then the onCreate method is skipped and onResume() method is called..so my suggestion is to initialize in the onResume() method
#Override
protected void onResume(Bundle savedInstanceState) {
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
//... Filling this array.
}

This answer might not solve your current problem ( not enough code to give a suggestion) but will help you head in the right direction.
Do provide a central data store for your objects, you should consider using singleton design pattern. Also, since the data will be accessed from multiple threads, you should make the arraylist (in your case) thread safe.
Note: if you are using synchronized list, you should lock the object to prevent access when it is iterated.

Related

Innerclass AsyncTask Static or by WeakReference?

I have a question regarding this simple frequently occurring situation in android .
I have an activity that will invoke the async task and async task will draw values from SQLite database and update on the UI. I used Async task to make the UI reponsive and fast.
This is the code I have been working on.
SqlHandler sqlHandler;
#BindView(R.id.list) ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1);
ButterKnife.bind(this);
sqlHandler = new SqlHandler(this);
new DisplayAll(this).execute();
listView.setOnItemClickListener((AdapterView<?> parent, View view,
int position, long id) -> {
Intent i = new Intent(getApplicationContext(), Activity2.class);
String text = textView.getText().toString();
startActivity(i);
});
}
private class DisplayAll extends AsyncTask<Void, Void, Void> {
int null_val;
final ArrayList<listRow=> myList = new ArrayList<>();
private WeakReference<Activity> mActivity;
public DisplayAll(Activity activity) {
mActivity = new WeakReference<>(activity);
}
#Override
protected Void doInBackground(Void... params) {
myList.clear();
String query = " ...";
Cursor c1 =sqlHandler.selectQuery(query);
if (c1 != null && c1.getCount() != 0) {
if (c1.moveToFirst()) {
do {
.....
} while (c1.moveToNext());
}
}
try {
null_val = Objects.requireNonNull(c1).getCount();
c1.close();
}
catch (NullPointerException e)
{
Log.e("NPE", "" + e);
}
return null;
}
#Override
protected void onPostExecute(Void param) {
// get a reference to the activity if it is still there
Activity activity = mActivity.get();
if (activity == null || activity.isFinishing()) return;
ProgressBar prgBar=findViewById(R.id.prgbar);
listAdapter Adapter;
prgBar.setVisibility(View.GONE);
Adapter = new listAdapter(getApplicationContext(), myList);
listView.setAdapter(Adapter);
}
}
I had checked this question also.I have added the weak reference to my class now. But still Android Studio warns me about the memory leak.
I tried to change it to static, but changing the sqlhandler as static also causes memory leak. To change the async task to a top-level class is not good for me. I have many async tasks in different activities.
So anyone have any idea how to tackle this?

AsyncTask request data later than I create button depending on these data

This is my first time to write Android code and I am stark with a problem.
In the MainActivity, I use AsyncTask to request "Category" list to create buttons. The MainActivity buttons can be clicked and it redirects to GetProductsActivity with an extras String "Category(e.g.drink)". In the GetProductsActivity, I request server again using "Category" to get "Products" list to create product button.
Here is the problem: the code create button first, then AsyncTask request server to get "Products" list, I want to get the "Products" list before creating the button. What should I do?
"orga.getAttributes" is the function to request server.
Here is MainActivity
public class MainActivity extends AppCompatActivity {
private ArrayList<String> data = new ArrayList<String>();
List<String> attributes = new ArrayList<String>();
List<String> categoryList = new ArrayList<String>();
final Organisation orga = Organisation.getInstance();
private class CallSocketTask extends AsyncTask<Integer, Integer, String> {
protected String doInBackground(Integer... nochnix) {
orga.SetInit();
categoryList = orga.getAttributes(orga.GET_CATEGORIES,null,true);
return null;
}
protected void onPostExecute(String string) {
//attributes = orga.getAttributes(orga.GET_PRODUCTS_BY_CATEGORY,null,true);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new CallSocketTask().execute();//orga.stop();
//requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.activity_main);
LinearLayout layer = (LinearLayout) findViewById(R.id.layer);
//getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.activity_main);
for(int i=0; i < categoryList.size(); i++)
{
Button button = new Button(this);
button.setId(i);
final String category = categoryList.get(i);
button.setText(category);
//click action
View.OnClickListener productHandler = new View.OnClickListener(){
public void onClick(View v) {
// doStuff
Intent intentMain = new Intent(MainActivity.this ,
GetProductsActivity.class);
intentMain.putExtra("categroy",category);
MainActivity.this.startActivity(intentMain);
Log.i("Content "," Main layout Click to Get Products by Category");
}
};
button.setOnClickListener(productHandler);
layer.addView(button);
}
}
}
Here is the GetProductsActivity
public class GetProductsActivity extends AppCompatActivity{
private ArrayList<String> data = new ArrayList<String>();
List<String> attributes = new ArrayList<String>();
final Organisation orga = Organisation.getInstance();
String category;
private class CallSocketTask extends AsyncTask<Integer, Integer, String> {
protected String doInBackground(Integer... nochnix) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
category = extras.getString("categroy");
Log.i("Category Selected",category);
}
//orga.SetInit();
attributes = orga.getAttributes(orga.GET_PRODUCTS_BY_CATEGORY,category);
Log.i("Product number ",attributes.size()+"");
//attributes = orga.getAttributes("getProducts","getCategories","Orangensaft");
return null;
}
protected void onPostExecute(String string) {
//Log.i("Result ","");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//this.notifyAll();
CallSocketTask myTask = new CallSocketTask();
myTask.execute();//orga.stop();
setContentView(R.layout.get_products);
LinearLayout layer = (LinearLayout) findViewById(R.id.productsLayer);
//getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.activity_main);
//Bundle extras = getIntent().getExtras();
//data= extras.getStringArrayList("products");
Log.i("Product number OnCreate",attributes.size()+"");
for(int i=0; i < attributes.size(); i++)
{
Log.i("Product",attributes.get(i));
Button button = new Button(this);
button.setId(i);
button.setText(attributes.get(i));
layer.addView(button);
}
}
}
Move your code for setting up the buttons into onPostExecute.
Solve the problem quite easy: use Thread.sleep() in onCreate() function, so loop button can wait for the AsyncTask running.
Non-static inner AsyncTask may lead to memory leaks, check some gotchas.
Thread.sleep() is a bad way. What if the request "Category" runs longer because of network issues?
Buttons can be created in a method YourActivity.createButtons() which should be called in onPostExecute().

putExtra doesn't seem to be working at all. Using ListView to get a string and using listView to display a string

I am programming a messaging app and I want to add users in a group. However, when a list of users pops up and I select one from the list, it doesn't pass the string (the username) to the other activity. All I get is an empty list.
Here is my code:
First Activity = Sending data (usernames from list) through putExtra()
public class ListUsersActivity extends Activity {
private String currentUserId;
private ArrayAdapter<String> namesArrayAdapter;
private ArrayList<String> names;
private ListView usersListView;
private Button logoutButton;
private ProgressDialog progressDialog;
private BroadcastReceiver receiver = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_users);
Parse.initialize(this, "embpZ0spRUv5XwDgI23innll1sgHg0KZNiKzg6kl", "LPsU4UffPeqFXkQB1GfLCIJ4kvg20llPgbOnLise");
currentUserId = ParseUser.getCurrentUser().getObjectId();
names = new ArrayList<>();
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereNotEqualTo("objectId", currentUserId);
query.findInBackground(new FindCallback<ParseUser>() {
public void done(List<ParseUser> userList, com.parse.ParseException e) {
if (e == null) {
for (int i=0; i<userList.size(); i++) {
names.add(userList.get(i).getUsername().toString());
}
usersListView = (ListView)findViewById(R.id.usersListView);
namesArrayAdapter =
new ArrayAdapter<String>(getApplicationContext(),
R.layout.user_list_item, names);
usersListView.setAdapter(namesArrayAdapter);
usersListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int i, long l) {
Intent goBackToAddPoolIntent = new Intent(ListUsersActivity.this, addNewPoolActivity.class);
addNewPoolActivity checker = new addNewPoolActivity();
checker.checkIfUserIsSelected(usersListView.getItemAtPosition(i).toString());
goBackToAddPoolIntent.putExtra("username", usersListView.getItemAtPosition(i).toString());
startActivity(goBackToAddPoolIntent);
}
});
} else {
Toast.makeText(getApplicationContext(),
"Error loading user list",
Toast.LENGTH_LONG).show();
}
}
});
}
Second Activity = Receiving data from putExtra()
public class addNewPoolActivity extends Activity {
private static ArrayList<String> addedUsers;
private ArrayAdapter <String> addedUserAdapter;
private boolean userIsSelected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_new_pool);
Button addMembers = (Button) findViewById(R.id.bAddMembers);
addedUsers = new ArrayList<>();
//addedUsers.add("Group Members");
addMembers.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent showUsersToSelect = new Intent(addNewPoolActivity.this, ListUsersActivity.class);
startActivity(showUsersToSelect);
}
});
ListView addedUsersList = (ListView) findViewById(R.id.addedUsersListView);
addedUserAdapter = new ArrayAdapter<>(this, R.layout.user_list_item, addedUsers);
addedUsersList.setAdapter(addedUserAdapter);
if(userIsSelected){
Bundle extras = getIntent().getExtras();
addedUsers.add(extras.getString("username"));
}
}
public void checkIfUserIsSelected(String user){
if (user!=null){
userIsSelected = true;
}else{
userIsSelected = false;
}
}
Since the default value for a boolean is false, the code is never called because
if(userIsSelected){
will always evaluate to false since you have declared the varaible as
private boolean userIsSelected;
and the first snippet here is in onCreate() so it will only run the first time the Activity is created.
Maybe you are wanting to call checkIfUserIsSelected(someUser) before that code but without more context of what you hope to accomplish, it's hard to say.
Possibly, you want to use startActivityForResult() in some way?
In addition to #codeMagic 's answer (Since your boolean value is false, it won't call the statement that you are adding the new data). It's also because of you parse the Data "username" after you setAdapter of your ListView. So basically you are setting the data, and then trying to add the new data you parsed to the list. Either you need to do it before setting your data set to your adapter, or call addedUsersAdapter.notifyDataSetChanged() to refresh your listView's data set.
addedUserAdapter = new ArrayAdapter<>(this, R.layout.user_list_item, addedUsers);
addedUsersList.setAdapter(addedUserAdapter);
Bundle extras = getIntent().getExtras();
// Check if the username has been sent to this Activity.
if(extras != null && extras.containsKey("username")){
addedUsers.add(extras.getString("username"));
// Refresh Your Data Set
addedUserAdapter.notifyDataSetChanged();
}

Return array list from asynctask, android json parser

I have a requirement where i need to parse the content of a URL in JSON format. I am able to do that successfully. But i need to save the contents of the URL in a array list and pass them back to the calling functions. Below is the code snippet of what i am trying to achieve.
#Override
protected ArrayList<String> onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
dialog.dismiss();
return ar; // ar is the arraylist i have created and updated it with the content of the url.
}
But running this gives an error. Can anyone please suggest how i can make this possible. However, when i make the return type of onPostExecute as void and toast the contents, its displaying properly. When i call this after the execute, its returning null even though i have updated the contents in doinbackground(). Hence i am unable to get the return values on arraylist format.
// Calling function
Myadapter.execute();
ArrayList<string> str = new ArrayList<string>();
str = print();
// Here str is getting null
// Called function
public ArrayList<String> print() {
ArrayList<String> names = new ArrayList<String>();
for(int i=0;i<al.size();i++)
{
names.add(al.get(i).getConstituencyName());
}
return names;
}
Use a handler
In your activity
mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
ArrayList s=(ArrayList)msg.obj;
tv.setText("Result = "+s.get(0));
}
};
In your onPostexecute
Message msg=new Message();
msg.obj=ar;
mHandler.sendMessage(msg);
The proper way would be to let your activity implement an interface, and when you instantiate the AsyncTask pass the current activity as a parameter to the constructor. Then in onPostExecute() invoke the callback method defined in the Activity and pass the json result as an argument.
Something like this:
interface OnTaskFinished {
void onTaskFinished(String result);
}
public class MainActivity extends Activity implements OnTaskFinished {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ....
new MyAsyncTask(this).execute();
}
#Override
public void onTaskFinished(String result) {
// Process the json result here how you need.
}
}
And this is how the scheleton of your AsyncTask should look like:
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
private final OnTaskFinished listener;
public MyAsyncTask(OnTaskFinished listener) {
this.listener = listener;
}
// ...
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
listener.onTaskFinished(result);
}
}

how do i send data back from onPostExecute in an AsyncTask?

my issue is the same as this
Instance variable of Activity not being set in onPostExecute of AsyncTask or how to return data from AsyncTask to main UI thread
but i want to send the data back to the same calling activity. Doesnt startActivity for intents always restart the activity
On option is to use listeners, where you create an interface that your activity implents, something like:
public interface AsyncListener {
public void doStuff( MyObject obj );
}
That way, if you're subclassing AsyncTask, it is easy to add this listener, then in onPostExecute(), you could do something like:
protected void onPostExecute( MyObject obj ) {
asyncListener.doStuff(obj);
}
This depends on your class structure, but if your AsyncTask is a class within your Activity then you can reference methods of that activity.
What you would do is in your onPostExecute method call a function of your Activity that passes some data that was retrieved in the AsyncTask to the activity where you can then use it..
The code would look like this
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
showDialog(DIALOG_LOADING);
new LongRunningTask1().execute(1,2,3);
}
protected Dialog onCreateDialog(int dialogId) {
switch(dialogId) {
case DIALOG_LOADING:
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setTitle("Loading Data");
pDialog.setMessage("Loading Data, please wait...");
return pDialog;
default:
return super.onCreateDialog(dialogId);
}
}
private void onBackgroundTaskDataObtained(List<String> results) {
dismissDialog(DIALOG_LOADING);
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) {
}
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
So the typical flow is like this, set the view of the current page, and then show a progress dialog. Right after that start the async task (or whenever, it doesn't matter really).
After your async task is complete, call a function of the activity and pass it the data.
Don't use shared data within the async task or you risk issues with threading.. Instead once you are done with it pass it to the activity. If you want to update the view progressively while doing work you can use on onProgressUpdate

Categories

Resources