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().
Related
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?
I want to declare arrayList into this line:
public class tlcity extends Activity {
//ArrayList<String> idArray = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
and into the other method,for example this method:
protected void onPostExecute(String result) {
//fill the arraylist
...
and into the other method for example this method read arraylist data:
public void readlist(){
//read the arraylist data and show
}
How can i do this?
You can declare ArrayList like this
ArrayList<String> list;
list = new ArrayList<String>();
You can add, remove items in ArrayList Like this
list.add("A");
list.remove(0);
ArrayList<String> abc=new ArrayList<String>();
You can initialize or create an instance of your array list like this
idArray = new ArrayList();
You can perform any operations to it using idArray object.
For example you can add items like this
idArray.add("item1");//In you case its a list of strings.
In the same way you would do that in another Java app / class:
public class tlcity extends Activity {
List<String> idArray;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
idArray = new ArrayList<String>();
}
protected void onPostExecute(String result) {
idArray.add("One");
idArray.add("Two");
idArray.add("Three");
...
}
public void readlist(){
for (final String element : idArray) {
// Use the nth string
}
}
I want to declare arrayList into this line:
ArrayList<String> myList;
and into the other method,for example this method:
myList = new ArrayList<String>;
and into the other method for example this method read arraylist data:
for(int i=0; i<myList.size(); i++)
System.out.println(myList.get(i).toString());
If you want to use ArrayList locally then declare it locally. if you want to use it in all methods then declare it globally in class.
public class tlcity extends Activity {
ArrayList<String> idArray = new ArrayList<>(); // to Use this arraylist globally.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<String> localaraaylist = new ArrayList<>(); //to use arraylist in only in oncreate method.
....
According to your post, telling how to declae ArrayList will not enough as you have some methods like onPreExecute() which is a method ofAsyncTask Interface.
Look at this,
public class MainActivity extends ActionBarActivity {
ArrayList<String> arrayList; // declaring ArrayList here
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
arrayList = new ArrayList<String>(); // Initializing arrayList
arrayList.add("initial text"); // adding a data to arrayList
ListView listView = (ListView)findViewById(R.id.listView);
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,arrayList); // setting the arrayList in ArrayAdapter
listView.setAdapter(adapter);
new LongOperation().execute(); // async Task
}
private class LongOperation extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
// progress dialog starts here
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.setMessage("Loading...");
progressDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
// for understanding purpose, i made a thread to sleep for 5 sec and later it will add A,B & C to ArrayList.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// adding few more items to arrayList
arrayList.add("A");
arrayList.add("B");
arrayList.add("C");
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss(); // dismissing the progress Dialog
adapter.notifyDataSetChanged(); // refreshing listview
readA(); // read the arrayList
}
}
public void readA()
{
for (int i = 0; i<arrayList.size(); i++)
{
Log.d("key",arrayList.get(i));
}
}
}
Output :
If you run the above code, Initially your list view will only contain only one item & after 5 sec loading it will add another 3 items. The below information will print in logcat that reads the ArrayList.
04-13 14:07:32.395 1123-1123/? D/key﹕ initial text
04-13 14:07:32.395 1123-1123/? D/key﹕ A
04-13 14:07:32.395 1123-1123/? D/key﹕ B
04-13 14:07:32.395 1123-1123/? D/key﹕ C
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();
}
I keep getting repeated issues with: variable is accessed from within inner class
every time i want to use an object etc from an onclicklistener etc.
I know the problem is that its not set to final, but if i set to final im not able to do anything to it before pressing the onclicklistener button...
Example i have an EditText field, which is filled with getText from an object, then i want to press an apply button and the object with the new text from the textfield.
This doesnt work when the object need to be final for the "inner class" to use it.
How do i best handle this? i keep running into this bloody phenonemon...
public class EditPicture extends Activity{
private EditText text;
private Button applyBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID itemID = (UUID)getIntent().getSerializableExtra("itemUUID");
PictureItem pi = new PictureItem("","");
final ArrayList<PictureItem> tempArray = PictureTalkFragment.array;
for(int i = 0; i < tempArray.size(); i++){
if(itemID.equals(tempArray.get(i).getId())){
pi = tempArray.get(i);
tempArray.remove(i);
}
}
setContentView(R.layout.picturetalk_edit_pic);
text = (EditText)findViewById(R.id.editName);
text.setText(pi.getTitle());
applyBtn = (Button)findViewById(R.id.applyChangeBtn);
applyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tempArray.add(pi);
}
});
}
}
you should define tempArray variable outside of onCreate method.
public class EditPicture extends Activity{
private EditText text;
private Button applyBtn;
ArrayList<PictureItem> tempArray
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID itemID = (UUID)getIntent().getSerializableExtra("itemUUID");
PictureItem pi = new PictureItem("","");
tempArray = PictureTalkFragment.array;
for(int i = 0; i < tempArray.size(); i++){
if(itemID.equals(tempArray.get(i).getId())){
pi = tempArray.get(i);
tempArray.remove(i);
}
}
setContentView(R.layout.picturetalk_edit_pic);
text = (EditText)findViewById(R.id.editName);
text.setText(pi.getTitle());
applyBtn = (Button)findViewById(R.id.applyChangeBtn);
applyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tempArray.add(pi);
}
});
}
}
The problem is that you add th picture to a temporary array which is not accesible when you click on it. I think that you can do something like this to define the array outside the inCreate function.
public class EditPicture extends Activity{
private EditText text;
private Button applyBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID itemID = (UUID)getIntent().getSerializableExtra("itemUUID");
PictureItem pi = new PictureItem("","");
final ArrayList<PictureItem> tempArray = PictureTalkFragment.array;
for(int i = 0; i < tempArray.size(); i++){
if(itemID.equals(tempArray.get(i).getId())){
pi = tempArray.get(i);
tempArray.remove(i);
}
}
setContentView(R.layout.picturetalk_edit_pic);
text = (EditText)findViewById(R.id.editName);
text.setText(pi.getTitle());
applyBtn = (Button)findViewById(R.id.applyChangeBtn);
applyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(PictureTalkFragment.array != null){
PictureTalkFragment.array.add(pi);
}
}
});
}
}
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.