I see a similar question here
AnimationDrawable in RecyclerView item not animating on notifyDataSetChanged.
But couldn't resolve this issue or fix. I think its because notifyDataSetChanged call multiple times to update UI. While I comment or notification change call for the position it works fine.
I have three items in the recycled view.
Progress bar
Text view
Button
I need to call an API on button click and increment the textview value. while API calls I need to show the progress bar and hide while API finish
Button click as follow,
holder.image_add_overlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.progressBar2.setVisibility(View.VISIBLE);
UpdateProductItem(holder, position);
}
});
private void UpdateProductItem(ShoppinglistAdapter.ViewHolder holder, int position) {
try {
Boolean value = CartFunctions.UpdateCartPostValue(cartAdd, mAPIService);
if (value) {
holder.text_item_cart_count.setText("" + holder.cart_count_value);
// notifyDataSetChanged();
holder.progressBar2.setVisibility(View.GONE);
Task task = new Task();
task.execute();
Toast.makeText(context, context.getResources().getString(R.string.cart_update_toast_message), Toast.LENGTH_SHORT).show();
} else {
holder.cart_count_value = holder.cart_count_value - 1;
}
} catch (IOException e) {
e.printStackTrace();
}
}
I also try the
#SuppressLint("StaticFieldLeak")
public class Task extends AsyncTask<String, Integer, Integer> {
#Override
protected Integer doInBackground(String... strings) {
Log.i("test", "doInBackground");
// notifyDataSetChanged();
return null;
}
#Override
protected void onPostExecute(Integer integer) {
Log.i("test", "onPostExecute");
notifyDataSetChanged();
super.onPostExecute(integer);
}
}
And call the this using
Task task = new Task();
//task.doInBackground();
task.onPostExecute(0);
May the 2 item in recyclerview are same, so the count is shown to both become same. so i need notifyDataSetChanged() instead of notifyItemChanged(position);
Did any guys know the solution?
doInBackground() method works in a separate thread, not even on UI thread.
Notify your adapter on UI thread, From AsyncTask : onPreExecute() & onPostExecute() method works on UI thread.
public class Task extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
}
}
Call your Task using,
Task task = new Task();
task.execute();
Refer AsynkTask for more details.
Related
I have sometimes this error
The content of the adapter has changed but ListView did not receive a
notification. Make sure the content of your adapter is not modified
from a background thread
But i don't understand because i run to UI thread. If you have an idea, thks.
class LoadingProducts extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
loading_indicator.show();
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.setData(listProducts);
mAdapter.notifyDataSetChanged();
loading_indicator.hide();
}
});
}
}
#Override
protected Void doInBackground(Void... params) {
get_data();
return null;
}
}
UPDATED :
This problem appear on refreshing list by search
previous code onQueryTextChange :
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (mQueryStringSearch.length() > 2)
new LoadingProducts().execute();
}
}, 300);
I am reading about how to interact between UI and background thread here.
This article has following note:
The AsyncTask does not handle configuration changes automatically,
i.e. if the activity is recreated. The programmer has to handle that
in his coding. A common solution to this is to declare the AsyncTask
in a retained headless fragment.
I dont understand what is retained headless fragment.
For example, in this way I can add fragment:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.frame, new MyFragment());
transaction.commit();
And in fragment I can execute AsyncTask like this:
MyTask myTask = new MyTask();
String[] args = {"Hello"};
myTask.execute(args);
Is this called "to declare the AsyncTask in a retained headless fragment"?
Headless fragment is nothing but a fragment which does not have a view. In onCreate() of the fragment lifeCycle, use setRetainInstance(true);. This will not destroy the fragment even if the activity recreates. So if an AsyncTask is running in fragment, on recreation of the activity, you wont lose the AsyncTask.
In onCreate of the activity, you have to add the fragment with a tag. Before adding, check if the fragment exist using getFragmentManager().findFragmentByTag(TAG), if the fragment is null then create a new instance of the fragment and add it.
In Fragment there will not be any view inflated, so no need to override onCreateView().
An example of headlessFragment :
public class HeadlessProgressFragment extends Fragment {
private ProgressListener mProgressListener;
private AsyncTask<Void, Integer, Void> mProgressTask;
public interface ProgressListener {
void updateProgress(int progress);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setProgressListener(Context context) {
mProgressListener = (ProgressListener) context;
}
public void startProgress(final int size) {
if (mProgressTask == null || mProgressTask.getStatus() != AsyncTask.Status.RUNNING || mProgressTask.getStatus() == AsyncTask.Status.FINISHED) {
mProgressTask = new AsyncTask<Void, Integer, Void>() {
#Override
protected Void doInBackground(Void... params) {
for (int index = 0; index < size; index++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
publishProgress(index + 1);
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mProgressListener != null) {
mProgressListener.updateProgress(values[0]);
}
}
};
mProgressTask.execute();
}
}
}
In Activity Something like this :
public class MainActivity extends FragmentActivity implements HeadlessProgressFragment.ProgressListener {
private static final String TAG = "progress_fragment";
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dummy_view);
mHeadlessProgressFragment = (HeadlessProgressFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if (mHeadlessProgressFragment == null) {
mHeadlessProgressFragment = new HeadlessProgressFragment();
getSupportFragmentManager().beginTransaction().add(mHeadlessProgressFragment,TAG).commit();
}
mHeadlessProgressFragment.setProgressListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
final Button startFillBtn = (Button) findViewById(R.id.btn_start_filling);
startFillBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHeadlessProgressFragment.startProgress(100);
}
});
}
#Override
public void updateProgress(int progress) {
mProgressBar.setProgress(progress);
}
}
As i simplified the complexity in my case by Just update your UI (if you have to) by checking the calling fragment or activity is present or not. Start the asynctask by assigning the weakreference of calling entity.
This question already has an answer here:
Using AsyncTask
(1 answer)
Closed 8 years ago.
I am trying to wait for a background process to finish and then do some stuff accordingly after it finishes.
Basically I have a class TwitterActivity and an inner class CheckInternetConnection which extends AsyncTask. I have also a button mSignin and I set the event handling for it. In addition I have also a boolean hasInternet.
My aim is when the mSignin button will be pressed I will call CheckInternetConnection. This is supposed to update my boolean value hasInternet. Then accordingly I expect to do some stuffs.
But I want exactly to do inside onClick() method.
Is there any way how to achieve it? Thanks.
public class TwitterActivity extends Activity
{
private boolean hasInternet = false;
private Button mSignin;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_twitter);
mSignin = (Button)findViewById(R.id.login_id);
mSignin.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
new CheckInternetConnection().execute();
if(hasInternet)
//do some stuff
else
//do some other stuff
}
});
}
class CheckInternetConnection extends AsyncTask<Void, Void, Boolean>{
#Override
protected void onPostExecute(Boolean result){
if(result)
hasInternet = true;
else
hasInternet = false;
}
#Override
protected Boolean doInBackground(Void... params) {
return true;
}
}
}
There are ways how you can wait for an AsyncTask to finish, but I don't think you wanna do that because it will block the UI Thread. Instead, move that check to onPostExecute. You can also already check for a connection when the button gets clicked.
public class TwitterActivity extends Activity
{
private boolean hasInternet = false;
private Button mSignin;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_twitter);
mSignin = (Button)findViewById(R.id.login_id);
mSignin.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
checkInternet();
}
});
}
private void checkInternet(){
if(hasInternet)
//do some stuff
else
new CheckInternetConnection().execute();
}
class CheckInternetConnection extends AsyncTask<Void, Void, Boolean>{
#Override
protected void onPostExecute(Boolean result){
if(result)
hasInternet = true;
else
hasInternet = false;
checkInternet();
}
#Override
protected Boolean doInBackground(Void... params) {
return true;
}
}
}
The purpose of an asynchronous task is to handle an expensive procedure that won't block the main thread. If this is a quick step, don't make it a separate task, simply do it right then and there.
One way to do it would be:
public class TwitterActivity extends Activity {
private boolean hasInternet = false;
private Button mSignin;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_twitter);
mSignin = (Button)findViewById(R.id.login_id);
mSignin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... params) {
// TODO: put your async job here
return true;
}
#Override
protected void onPostExecute(Boolean result){
hasInternet = result;
if( hasInternet ) {
// do some stuff
//
} else {
// do some other stuff
}
}
}.execute();
}
});
}
}
I have a class implementing runnable that made some task, then update some views:
public class DownloadPics implements Runnable {
#Override
public void run() {
//retrieve data
fillView(data);
}
public void refreshView(ArrayList<Object> new_pics) {
LinearLayout linearLayout = (LinearLayout) ((Storico)context).findViewById(R.id.linearInsideScroll);
View new_imageView = inflater.inflate(R.layout.history_element,
null, false);
//Build fill view
linearLayout.addView(new_imageView);
}
}
i Obtain an error when i call addView on refreshView method:
Only the original thread that created a view hierarchy can touch its views.
What's wrong?
Only the original thread that created a view hierarchy can touch its
views.
Updating ui from a background thread.
You cannot update ui from a background thread. You can update ui from the ui thread.
Use AsyncTask makes it easier. You can do background computation in doInBackground and update ui in onPostExecute
Use this method runOnUiThread(Runnable action)
EDIT
Use ASyncTask along with runOnUiThread
class download extends AsyncTask<Void, Void, Void>
{
String message = "";
ProgressDialog dialog = new ProgressDialog(v.getContext());
#Override
protected void onPreExecute()
{
// what to do before background task
dialog.setTitle("Downloading");
dialog.setIndeterminate(true);
dialog.setMessage(message);
dialog.setCancelable(false);
dialog.show();
}
void updateItem(int i)
{
class UpdateItem implements Runnable
{
int pos;
UpdateItem(int i) { pos = i; }
public void run()
{
dialog.setMessage(message);
}
}
runOnUiThread(new UpdateItem(i));
}
#Override
protected Void doInBackground(Void... params) {
downloadStuff();
i++
updateItem(i);
}
#Override
protected void onPostExecute(Boolean result) {
// what to do when background task is completed
dialog.dismiss();
};
};
new download().execute((Void[]) null);
You need a reference from a Handler that was created on the main thread (be sure that it is created on the main thead).
Handler handler = new Handler();
In the run() method of DownloadPics you would then use:
handler.post(new Runnable(){
public void run(){
//update UI on main thread
}
}
I have the following code in which information about images is loaded from a db and shown in a custom gallery view. The problem is I cannot see a way to put the setting on the OnItemClickListener for the gallery in the background thread of the AsyncTask as it is part of the main UI thread (I think?). When I load the Activity the dialog pops up but freezes and I think it's because I am setting the OnItemClickListener in the UI thread which is taking a lot of work. How can I solve this problem?
private class loadTask extends AsyncTask<String, Integer, String>{
ProgressDialog dialog = new ProgressDialog(ActivityGallery.this);
#Override
protected void onPreExecute(){
b = getIntent().getExtras();
dialog.setMessage("Loading...");
dialog.show();
}
#Override
protected String doInBackground(String... params) {
Log.v("SC", "ASYNC GETS HERE!!");
DBHandler handler = new DBHandler(ActivityGallery.this);
SQLiteDatabase db = handler.getReadableDatabase();
Cursor c = db.rawQuery("SELECT * FROM images", null);
if (c.moveToFirst()) {
do {
paths.add(c.getString(1));
Log.v("SC", "Cursor: " + c.getString(1));
} while (c.moveToNext());
}
c.close();
db.close();
handler.close();
return null;
}
#Override
protected void onProgressUpdate(Integer... s){
}
#Override
protected void onPostExecute(String result){
Log.v("SC", "Final Paths: " + paths.toString());
setContentView(R.layout.gallery);
gallery = (Gallery) findViewById(R.id.gallery1);
gallery.setAdapter(new ImageAdapter(ActivityGallery.this));
gallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
selected_image_position = position;
GoToShareActivity();
}
});
dialog.dismiss();
}
}
I think GoToShareActivity() causes freezing. Check that function in detail.
What does GoToShareActivity() do?
If it's another resource intensive operation, then it will cause the dialog to freeze up. This is because onPostExecute() runs on the UI thread.
I've a similar program that uses asynctask like you do. I've tried putting my onItemClickListener() in onPostExecute but it doesn't freeze the dialog as i'm not doing any heavy process in it.
I understand the part whereby you want to show the gallery to the user after the dialog box dismisses.
ANSWER: You can do the gallery.setOnItemClickListener() in there but, the actual execution of the onClick() should not be in there.
#Override
public void onCreate(Bundle savedInstanceState)
{
setContentView(R.layout.gallery); //place before your loadTask()
.
. //your other implementations
.
}
private OnItemClickListener galleryOnItemClickListener = new OnItemClickListener()
{
#Override
public void onClick(View v)
{
selected_image_position = position;
GoToShareActivity();
}
};
private class loadTask extends AsyncTask<String, Integer, String>
{
.
.
.
protected void onPostExecute(String result)
{
Log.v("SC", "Final Paths: " +paths.toString());
gallery = (Gallery)findViewById(R.id.gallery1);
gallery.setAdapter(new ImageAdapter(ActivityGallery.this));
gallery.setOnItemClickListener(galleryOnItemClickListener);
dialog.dismiss();
}
}