I have a function that requests data from an api and fills an array list.
Then i use the data from the arraylist in a textView. The problem that occurs is that the function takes time to load the data and the code in which i set the text view gets executed before the arraylist is populated resulting in a crash...I have used Countdown latch to tackle this problem but it isnt working
i have used it wrong most probably.
apirequest function
private void RequestDataFromApi() {
DotaAPIEndpoints textApiService= API_Client.getClient().create(DotaAPIEndpoints.class);
Call<List<Heroes>> call2 =textApiService.getHeroes();
call2.enqueue(new Callback<List<Heroes>>() {
#Override
public void onResponse(Call<List<Heroes>> call, Response<List<Heroes>> response) {
hero_list.clear();
hero_list.addAll(response.body());
}
#Override
public void onFailure(Call<List<Heroes>> call, Throwable t) {
Toast.makeText(MainActivity.this, "hero_list call failed!", Toast.LENGTH_SHORT).show();
}
});
requestLatch.countDown();
}
setText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
requestLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
textt.setText(hero_list.get(0).getHeroImg());
}
});
setText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
requestLatch.await();
You cannot call await on the UI thread. Calling await at this point in the above code is telling the UI thread to wait - if the UI thread is waiting, it cannot draw the screen updates, so the system will crash with an Activity Not Responding error.
Perhaps this helps, this is a way to safely allow the button to be clicked and not crash if the data has not loaded yet. (No need for a CountdownLatch at all)
setText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(hero_list.isEmpty()) {
Toast.makeText(MainActivity.this, "List not ready", Toast.LENGTH_SHORT).show();
return;
}
textt.setText(hero_list.get(0).getHeroImg());
}
});
Related
I am working on an app which would store data in Firebase. The issue is that when I click submit, the data stays on the form. I need when I click submit that it returns to the home screen.
I tried using the start activity method like I did when I wanted to move from MainActivity to another class. It would work but then my data will not be saved.
I am grateful for any assistance. Thanks in advance!
buttonSubmitReport.setOnClickListener(v -> {
reports.setDate(textDate.getText().toString().trim());
reports.setTime(textTime.getText().toString().trim());
reports.setReport(editRep.getText().toString().trim());
reff.push().setValue(reports);
reff.child(String.valueOf(maxID+1)).setValue("Reports");
Toast.makeText(submitReport.this,"Thanks for the information!", Toast.LENGTH_LONG).show();
}
});
What I am getting from your problem is that you have one activity from there you are starting another activity for editing the details and on click of some button, you want to save those details and come back to previous activity, right?
To do this just add some listener to that and when it completed simply finish the activity
buttonSubmitReport.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
reports.setDate(textDate.getText().toString().trim());
reports.setTime(textTime.getText().toString().trim());
reports.setReport(editRep.getText().toString().trim());
reff.push().setValue(reports);
reff.child(String.valueOf(maxID + 1)).setValue("Reports")
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
// Write was successful!, here either finish or move to another activity
Toast.makeText(submitReport.this, "Thanks for the information!", Toast.LENGTH_LONG).show();
finish();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Write failed
Toast.makeText(submitReport.this, "Failed to store the data", Toast.LENGTH_LONG).show();
}
});
});
You can use firebase value update listener, Once value update performs the operation.
Or you can use the firebase transaction handler.
override fun onComplete(dataBaseError: DatabaseError?, status: Boolean, snapShot: DataSnapshot?) {
//Operation complete call back
}
override fun doTransaction(data: MutableData): Transaction.Result {
//Perform your operation here
return Transaction.success(data)
}
})
You can try it once if possible
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
buttonSubmitReport.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
InsertData();
ReportsID = Fetch Reports Id and use condition is empty or not
if (TextUtils.isEmpty(ReportsID)) {
InsertData();
} else {
//---Start Activity
}
}
});
}
public bool InsertData()
{
reports.setDate(textDate.getText().toString().trim());
reports.setTime(textTime.getText().toString().trim());
reports.setReport(editRep.getText().toString().trim());
reff.push().setValue(reports);
reff.child(String.valueOf(maxID+1)).setValue("Reports");
Toast.makeText(submitReport.this,"Thanks for the information!", Toast.LENGTH_LONG).show();
}
}
I am doing an HTTP request from my InitActivity:
private void initUserById(int challengerId) {
Call call = StaticGlobalContainer.api.getUser(challengerId);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Response<User> response, Retrofit retrofit) {
StaticGlobalContainer.currentUser = response.body();
if (response.code() == 200) {
// 200
// User object returned from server
Log.i("Getting user by id", ACTIVITY_TAG+"_Success");
} else {
// 404 or the response cannot be converted to User.
Log.e("Getting user by id", ACTIVITY_TAG+"_Error:" + response.errorBody());
}
}
#Override
public void onFailure(Throwable t) {
Log.i("HttpRequest - Get user", ACTIVITY_TAG+"_Failure");
}
});
}
This activity ends as soon as user presses a button to go to the next activity:
setUserIdBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
StaticGlobalContainer.CURRENT_USER_ID = userIdEditText.getText().toString();
initUserById(Integer.parseInt(StaticGlobalContainer.CURRENT_USER_ID));
startActivity(new Intent(InitActivity.this, MainMenuActivity.class));
finish();
}
});
What happens, if there is a slight delay on the server and it won't respond before user presses the button?
Since activity has already ended, I would expect an exception, but isn't it handled somehow? Like that the activity wouldn't be disposed until callback has been made?
EDIT:
After some thinking, I assume that such callbacks as described above are one of the reason they invented ProgressBars, is that right?
Hello stackoverflow citizen!
I want start and stop 2 progressBars on 1 activity.
I think oll progressBars must start in new thread and join with others.
For 1 progressBar i am write this code.
startBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
handler.post(runnable);
}
});
stopBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
handler.removeCallbacks(runnable);
}
});
}
int prolength=0;
Runnable runnable=new Runnable() {
#Override
public void run() {
prolength = inProgressBar.getProgress() + 1;
inProgressBar.setProgress(prolength);
timeTextView.setText(String.valueOf(prolength));
if (prolength < 100) {
handler.postDelayed(runnable, 1000);
}
else {
inProgressBar.setProgress(0);
timeTextView.setText(String.valueOf(0));
handler.post(runnable);
}
}
};
Thank you advance for help!
I suggest you to use sendMessage Api instead of post Api.
You can pass some information using Message object in your case (contentProgressBar1 or 2 or3 view) and refactor you implementation to this design..
Make sure to cleanup handler in activity life cycle else you will introduce memory leak and unwanted bugs.
I have placed the parse method inside onCreate method. But my problem is how to show the Android Loading... Dialog??
Parse.initialize(this, "a", "b");
ParseQuery query = new ParseQuery("Category");
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> catObjects, ParseException arg1) {
Log.d("Catlength", String.valueOf(catObjects.size()));
for(int i =0; i<catObjects.size(); i++){
Log.d("lengthName"+String.valueOf(i), String.valueOf(catObjects.get(i).getInt("Id")));
Category category = new Category();
category.Name= catObjects.get(i).getString("CatName");
category.id= catObjects.get(i).getInt("Id");
categories.add(category);
}
if(categories.size()>0){
setListAdapter(new CategoryArrayAdapter(CategoryListActivity.this, R.layout.row_category, categories));
}
else{
Toast.makeText(CategoryListActivity.this, "Our servers are busy. Hit refresh..", 3000).show();
}
}
});
Everything works fine in the above code but I couldn't figure out how to show the Dialog.
I'm unable to use AsycTask also as parse sdk invokes its own thread in the background and before the findInBackground execution finishes, the doInBackground completes the Asyc thread. That's why I invoked it in the main thread.
As the result I always get no results in my ArrayList.
Can someone please enlighten me.
I was in the same situation regarding the progress dialog, tried a few tricks and finally just declared a ProgressDialog class member:
protected ProgressDialog proDialog;
then created two methods:
protected void startLoading() {
proDialog = new ProgressDialog(this);
proDialog.setMessage("loading...");
proDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
proDialog.setCancelable(false);
proDialog.show();
}
protected void stopLoading() {
proDialog.dismiss();
proDialog = null;
}
and called startLoading() before the background operation and stopLoading()
inside the background operation after I got the the results.
startLoading();
ParseUser.logInInBackground(userName.getText().toString(), hashedPass, new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
Log.d(Constants.TAG, "User Loged in.");
ParseManager.sCurrentUser = user;
stopLoading();
finish();
} else {
stopLoading();
invalidCreds();
}
}
});
if you want to use AsyncTask don't call findInBackground() you can use find().
you can check it out in the api https://parse.com/docs/android/api/com/parse/ParseQuery.html#find()
hope this helps.
It's easy to get the progress of both uploads and downloads using ParseFile by passing a ProgressCallback to saveInBackground and getDataInBackground. For example:
byte[] data = "Working at Parse is great!".getBytes();
ParseFile file = new ParseFile("resume.txt", data);
file.saveInBackground(new SaveCallback() {
public void done(ParseException e) {
// Handle success or failure here ...
}
}, new ProgressCallback() {
public void done(Integer percentDone) {
// Update your progress spinner here. percentDone will be between 0 and 100.
}
});
I try to use this code to prevent multi-click in ImageView but it doesn't help.
Boolean isClicked = false;
#Override
public void onClick(View v)
{
if (v == imgClick && !isClicked)
{
//lock the image
isClicked = true;
Log.d(TAG, "button click");
try
{
//I try to do some thing and then release the image view
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
isClicked = false;
}
}
In the log cat, I can see 5 lines "button click" when I click on ImageView for 5 times as quickly as possible. I can see the log cat print the first line, wait for a while (2 seconds) and then print the next line. I think when I click the ImageView, the fired event is moved to queue in order, isn't it?. So how can I stop that?
I also try to use setEnable() or setClickable() instead of isClicked variable but it doesn't work too.
Just try this working code
Boolean canClick = true; //make global variable
Handler myHandler = new Handler();
#Override
public void onClick(View v)
{
if (canClick)
{
canClick= false; //lock the image
myHandler.postDelayed(mMyRunnable, 2000);
//perform your action here
}
}
/* give some delay..*/
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
canClick = true;
myHandler.removeMessages(0);
}
};
Instead of sleeping in 2 seconds, I use some task like doSomeThing() method (has accessed UI thread), and I don't know when it completed. So how can I try your way?
//I referred this android link. You can handle thread more efficiently but i hope below code will work for you..
//you try this and
Boolean canClick = true; //make global variable
public void onClick(View v) {
if(canClick){
new DownloadImageTask().execute();
}
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
Log.d("MSG","Clicked");
canClick =false;
//perform your long operation here
return null;
}
protected void onPostExecute(Bitmap result) {
canClick =true;
}
}
You could keep track of the last consumed click upon your View, and based on it either perform the necessary actions, or simply return:
private long calcTime;
private boolean isClickedLately(final long millisToWait)
{
if (System.currentTimeMillis() - calcTime < millisToWait)
return true;
return false;
}
#Override
public void onClick(View v)
{
if (isClickedLately(2000))
return;
calcTime = System.currentTimeMillis();
Log.d(TAG, "consuming button click");
// perform the necessary actions
}
With the millisToWait parameter you can adjust the threshold of "waiting", but if you know that you want to wait exactly 2 seconds between two consecutive clicks, you can eliminate it.
This way you don't have to deal with Threads, which is good, since it's not a great idea to make the gui thread wait.