I am building an application which is dynamically populating/depopulating arraylist depending on Futuretask thread which communicates with server.
Problem is that when i add new object into my arrayadapter and in any way i call notifyDataSetChanged or setNotifyOnChange
(for example after adding item, i also tried make some postadd function which calls notifyDataSetChanged, or even tried adding setNotifyOnChange in adapter constructor, or before adding (adapter.setNotifyOnChange(true)))
in this Futuretask it simply freezes the thread and causes application malfunction. Without notifyonchange when i slide with my finger to call ArrayAdapter's getView() it works, but that is an undesired behavior.
Note: I am accessing the adapter by static variable reference.
Note#2: I dont think creating new adapter over and over again is a smart choice.
/edit:
My remove method looks like this:
public void remove (int itemid) {
for (ContactItem i : items)
if (i.ID == itemid)
items.remove(i);
}
(and it hangs in the end at the last pharantesis)
Pass a weak reference to the activity in your other thread, so you don't leak the context.
WeakReference<Activity> mRef_Activity = new WeakReference<Activity>(activity);
From that thread, run the notify update on the ui thread of the main activity.
final Activity activity = mRef_Activity.get();
if(activity != null)
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
ArrayAdapter<String> adapter = activity.getAdapter();
adapter.notifyDataSetChanged();
}
});
Note if you are running async task, instead of a callable or runnable, you can just put the notify update in the postExecute of the async, as it is a callback from the main UI thread.
Using static variable reference means that you should be carefully in the thread. As far as i know, the adapter updates the data only in the UI thread.
One more thing you should know is you should implement the getCount() function to return correct number.
I had a problem when my list view does not update my data. even i trigger the onDatasetChange(). I forgot the getCount() function
Related
I am trying to read a list of integers inside of the doInBackground of AsyncTask. When I pass the list into the constructor of AsyncTask, it is full. But, by the time I get to the doInBackground function, it is empty. Any ideas?
public class floatingActionButtonClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
if(mAdapter.getDeleteModeStatus()){
// Delete items from database
ArrayList<Integer> IDsToDelete = mAdapter.getJournalIDsToDelete();
new DeleteDatabase().execute(IDsToDelete);
// Turn FAB back to regular button
mFAB.setImageResource(R.drawable.baseline_add_white_48); // Turn FAB to delete button
// Disable delete mode
mAdapter.exitDeleteMode();
// Load database
new LoadDatabase().execute();
}
else{
Intent intent = new Intent(getBaseContext(), AcitivtyJournal.class);
int journalType = Constants.JOURNALTYPE_FULL;
intent.putExtra(Constants.JOURNAL_TYPE, journalType);
startActivity(intent);
}
}
}
private class DeleteDatabase extends AsyncTask <ArrayList<Integer>, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBarHolder.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(ArrayList<Integer>... arrayLists) {
ArrayList<Integer> IDsToDelete = arrayLists[0];
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "JournalEntries")
.build();
for(Integer idToDelete : IDsToDelete){
db.mJournalEntriesDao().deleteCompleteJournalEntry(idToDelete);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
mProgressBarHolder.setVisibility(View.GONE);
}
}
}
This is not how you use and AsyncTask. You need to declare the parameters and then recieve them in the callback.
Please also note that you are trying to access the same data(IDsToDelete) from two threads(Main and Background), in your way, without proper syncronization.
private class DeleteDatabase extends AsyncTask<ArrayList<Integer>, Void, Void> {
#Override
protected Void doInBackground(ArrayList<Integer>... arrayLists) {
ArrayList<Integer> params = arrayLists[0];
// Do what you need
}
}
ArrayList<Integer> IDsToDelete = mAdapter.getJournalIDsToDelete();
new DeleteDatabase().execute(IDsToDelete);
When you have multithreading you need to look for two things:
atomic execution of operations
memory visibility.
There is a shared memory and every CPU caches the data. When you create something from one thread you can't just expect that the second thread will just read it. In your case you are creating the AsyncTask and inject the params from one thread, but then you read them in doInBackground from another. In general when you go through a synchronized block or hit a volatile variable(I say in general, because I also don't fully understand how JVM works), the thread flushes it's cache to the main memory and then reads also from it. This is how the data is shared. That is why it is better to use the framework way, because the frame will take care of proper publication of your data between threads. You are ok with immutable data, but a List is not such thing. And even if you declare the reference as immutable, you might see the right object from both threads, but the data they are holding might be old.
Just to be clear. I am not saying that the previous way was not working. I am saying that it is on good will. You can't just share data betweeb threads and hope it works.
Figured it out. Posting for people in the future who may have similar questions.
Embarrasingly enough, the ArrayList<Integer> was coming empty because I was deleting it in the function mAdapter.exitDeleteMode(); after I call AsyncTask().execute().
I was not aware that when I send the list to the AsyncTask it was the exact address of the list and not just a new list (that is, until I posted the comment above, and then it clicked). I think I got that train of thinking from C++ or another language. I don't remember which.
Solution: The solution I came up with is to just move mAdapter.exitDeleteMode() into of onPostExecute()instead of having it in the onClick() method.
Another Potential Solution: I believe another solution that would work (but I did not test) would be to just insert a new ArrayList<Integer> () into the AsyncTask
I can call Snackbar.make() from a background thread without any problems. This is surprising to me since I thought UI operations are only allowed from the UI thread. But that is definitely not the case here.
What exactly makes Snackbar.make() different? Why doesn't this cause exceptions like any other UI component when you modify it from a background thread?
First of all: make() doesn't perform any UI related operations, it just creates a new Snackbar instance. It is the call to show() which actually adds the Snackbar to the view hierarchy and performs other dangerous UI related tasks. However you can do that safely from any thread because it is implemented to schedule any show or hide operation on the UI thread regardless of which thread called show().
For a more detailed answer let's take a closer look at the behaviour in the source code of the Snackbar:
Let's start where it all begins, with your call to show():
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
As you can see the call to show() gets an instance of the SnackbarManager and then passes the duration and a callback to it. The SnackbarManager is a singleton. Its the class which takes care of displaying, scheduling and managing a Snackbar. Now lets continue with the implementation of show() on the SnackbarManager:
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// Means that the callback is already in the queue. We'll just update the duration
mCurrentSnackbar.duration = duration;
// If this is the Snackbar currently being shown, call re-schedule it's
// timeout
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
scheduleTimeoutLocked(mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
// We'll just update the duration
mNextSnackbar.duration = duration;
} else {
// Else, we need to create a new record and queue it
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// If we currently have a Snackbar, try and cancel it and wait in line
return;
} else {
// Clear out the current snackbar
mCurrentSnackbar = null;
// Otherwise, just show it now
showNextSnackbarLocked();
}
}
}
Now this method call is a little more complicated. I am not going to explain in detail what's going on here, but in general the synchronized block around this ensures thread safety of calls to show().
Inside the synchronized block the manager takes care of dismissing currently shown Snackbars updating durations or rescheduling if you show() the same one twice and of course creating new Snackbars. For each Snackbar a SnackbarRecord is created which contains the two parameters originally passed to the SnackbarManager, the duration and the callback:
mNextSnackbar = new SnackbarRecord(duration, callback);
In the above method call this happens in the middle, in the else statement of the first if.
However the only really important part - at least for this answer - is right down at the bottom, the call to showNextSnackbarLocked(). This where the magic happens and the next Snackbar is queued - at least sort of.
This is the source code of showNextSnackbarLocked():
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
// The callback doesn't exist any more, clear out the Snackbar
mCurrentSnackbar = null;
}
}
}
As you can see first we check if a Snackbar is queued by checking if mNextSnackbar is not null. If it isn't we set the SnackbarRecord as the current Snackbar and retrieve the callback from the record. Now something kind of round about happens, after a trivial null check to see if the callback is valid we call show() on the callback, which is implemented in the Snackbar class - not in the SnackbarManager - to actually show the Snackbar on the screen.
At first this might seem weird, however it makes a lot of sense. The SnackbarManager is just responsible for tracking the state of Snackbars and coordinating them, it doesn't care how a Snackbar looks, how it is displayed or what it even is, it just calls the show() method on the right callback at the right moment to tell the Snackbar to show itself.
Let's rewind for a moment, up until now we never left the background thread. The synchronized block in the show() method of the SnackbarManager ensured that no other Thread can interfere with everything we did, but what schedules the show and dismiss events on the main Thread is still missing. That however is going to change right now when we look at the implementation of the callback in the Snackbar class:
private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
#Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
}
#Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
}
};
So in the callback a message is send to a static handler, either MSG_SHOW to show the Snackbar or MSG_DISMISS to hide it again. The Snackbar itself is attached to the message as payload. Now we are almost done as soon as we look at the declaration of that static handler:
private static final Handler sHandler;
private static final int MSG_SHOW = 0;
private static final int MSG_DISMISS = 1;
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SHOW:
((Snackbar) message.obj).showView();
return true;
case MSG_DISMISS:
((Snackbar) message.obj).hideView(message.arg1);
return true;
}
return false;
}
});
}
So this handler runs on the UI thread since it is created using the UI looper (as indicated by Looper.getMainLooper()). The payload of the message - the Snackbar - is casted and then depending on the type of the message either showView() or hideView() is called on the Snackbar. Both of these methods are now executed on the UI thread!
The implementation of both of these is kind of complicated, so I won't go into detail of what exactly happens in each of them. However it should be obvious that these methods take care of adding the View to the view hierarchy, animating it when it appears and disappears, dealing with CoordinatorLayout.Behaviours and other stuff regarding the UI.
If you have any other questions feel free to ask.
Scrolling through my answer I realize that this turned out way longer than it was supposed to be, however when I see source code like this I can't help myself! I hope you appreciate a long in depth answer, or maybe I might have just wasted a few minutes of my time!
Snackbar.make is completely safe from being called form non-ui thread. It uses an handler inside its manager which operates on the main looper thread and thus hides the caller form the underlying complexities of it.
Only the original thread that created a view hierarchy can touch its views.
If you use the onPostExecute you'll be able to access the views
protected void onPostExecute(Object object) { .. }
Hey m a newbie to android programming and I was working on this project.
This question is pretty long so here's the deal.
I have this GCMIntentService class extending GCMBaseIntentService and whenever a message arrives from the server, the GCMBroadcastReceiver automatically recognizes it and calls the overriden onMessage() method in the GCMIntentService class. Now in the onMessage body, I am doing some operations on the SQLiteDatabase and I am notifying my adapter for list view by calling the adapter.notifyDataSetChanged() in the ui thread inside the onMessage body.
Now, if more than 2 or 3 gcm messages come simultaneously to the device the app crashes since more than one thread is calling the same onMessage() method and is messing up with my database and adapter as well. I figured I needed to use synchronized keyword on the method that should be used by only one thread at a time.
But since my onMessage method is an overriden method, I decided to make another method and put synchronized modifier on it but once again I need to call the runOnUiThread() method from inside it since i need to notify changes to my list view's adapter.
I just want to ask if doing this is the right way or is it possible to use a much simpler solution to my problem?
Here is the sample code to what m doing:
#Override
protected void onMessage(Context arg0, Intent intent) {
// called when a new cloud message has been received
Log.w("Service ", "Started");
dbh = new DatabaseHandler(this);
sld = dbh.getWritableDatabase();
who = this;
// processing json object
putDataFromJSON();
//other stuff
}
synchronized private void putDataFromJSON(){
//do some work on JSON Object
//complete work on JSON by putting in database
dbh.saveInDB();
//notify the adapter
((MainActivity) MainActivity.con).runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
//do other stuffs as well
}
}
}
I'm writing a dummy code here that I think can demonstrate you an abstract architecture..
public class GCMIntentService extends GCMBaseIntentService{
private static ArrayList<Message> messageQueue = new ArrayList<Message>();
private static boolean isProcessingMessage = false;
onMessage(Context context, Intent intent)
{
if(isProcessingMessage)
{
Message currentMsg = new Message();//Create a instance of message and put it in arrayList
}
else{
isProcessingMessage = true;
for(int i = 0; i < messageQueue.size(); i++)
{// Process all your messages in the queue here
messageQueue.remove(i);
}
isProcessingMessage = false;
}
}
private class Message{
//In this class you can configure your message that you are going to queue.
}
}
Firstly, the onMessage() method gets executed every single time a new GCM message arrives(even when you are not into your app, because we register this receiver inside the manifest file.) So, getting the context of your activity my cause your app to crash (NullPointerException).
Now, as far as your question is concerned, you can maintain a queue that keeps track of incoming GCM messages. And, upon processing a message you can check for the entries in the queue and process them. For this purpose, you can use a boolean that flags if any message is currently being processed (flag == true). And when (flag == false), you can take the next entry from the queue and process that..
I hope it was useful.
I have a main activity where I create an adapter for a arraylist data.
I read the news from a website using a separate thread with Jsoup.
In onCreate() i have
this.newsItemAdapter = new NewsItemAdapter(this,
R.layout.newsitem_row,NewsItemAdapter.getAllNews());
parseThread.start();
this.newsItemAdapter.notifyDataSetChanged();
When i read from the adapter, i get empty list. This is because the thread is not completed yet. Any idea as to how i should proceed?
I cannot do notifyDataSetChanged inside my thread because it is not the owner of the adapter.
You need to notify after the seperate thread finishes, and invoke notifyDataSet on UI-thread, one possible method is to use handler,
you can define a handler in activity, invoke notifyDataSet change in handleMessage, such as
handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
newsItemAdapter.notifyDataSetChanged();
}
}
and in the thread run method, you need to send a message to the handler,
public void run() {
// add this to the end
Message msg = new Message()
handler.sendMessage(msg);
}
or else you can use AsyncTask instead of seperate thread, use jsoup in task's doInBackground method, and notifyDataSet in onPostExecute.
The handler document is http://developer.android.com/reference/android/os/Handler.html, and AsyncTask's is http://developer.android.com/reference/android/os/AsyncTask.html.
Pass the adapter to your thread in its constructor. Then your thread can call notifyDataSetChanged.
I'm working on an Android app and I've got an Activity on which I am displaying two TableLayout's on top of each other. The first TableLayout holds a scoreboard for a race (type 1) and the second the scoreboard for another race (type 2).
I inflate both of these TableLayout's from the same XML layout file. I populate them with data from a doInBackground() (AsyncTask). However, the population part is pretty heavy, so I have put this into a custom synchronized method (inside the AsyncTask class) and pass the inflated TableLayout objects as an argument to this method.
I call said method from the doInBackground() method by using an ExecutorService object. This ExecutorService object is created with Executors.newSingleThreadExecutor(). I put the first method call in one Runnable and the second method call in another Runnable and fire off the ExecutorService.execute() on these runnables.
In the method I am accessing UI elements by calling findViewById() on the passed in TableLayout object parameter and calling methods like TextView.setText() on these views. This works fine most of the time, but I am starting to see a some exceptions like
ERROR/AndroidRuntime(409): FATAL EXCEPTION: pool-9-thread-1
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
and
ERROR/AndroidRuntime(378): FATAL EXCEPTION: pool-2-thread-1
java.lang.NullPointerException
which lets me to belive that I must be doing this incredibly backwards. I'll include a simplified code snippet below to help describe what I am doing currently:
class AddRacesTask extends AsyncTask<Void, View, Void> {
synchronized void populateTable(ArrayList<Race> data, TableLayout table) {
TextView headerText = table.findViewById(R.id...);
headerText.setText("Header");
for(Race race : data) {
TableRow row = getLayoutInflater().inflate(R.layout....);
TextView pos = row.findViewById(R.id....);
pos.setText(race.getPos());
TextView person = row.findViewById(R.id....);
person.setText(race.getPerson());
// etc.
// etc..
// etc...
table.addView(row);
}
publishProgress(table);
}
#Override
private Void doInBackground(Void... voids) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
TableLayout raceType1Layout = getLayoutInflater().inflate(R.layout...);
populateTable(data, raceType1Layout);
}
});
executor.execute(new Runnable() {
#Override
public void run() {
TableLayout raceType2Layout = getLayoutInflater().inflate(R.layout...);
populateTable(data, raceType1Layout);
}
});
}
#Override
protected void onProgressUpdate(View... values) {
if(values[0] instanceof TableLayout) {
container.addView(values[0]);
}
}
}
I welcome any feedback at all. Also if you need more to go on, I'd be happy to post my actual source code but I figured a simplified and clean version would be easier.
You need implement a custom Adapter that populate your custom view with Race object. You receive your data in background with custom thread or AsyncTask and pass to the UI thread via Handler or onPostExecute method respectively. Then, you can call Adapter.clear method and after
for(Race r: ArrayList<Race>)
Adapter.add(r)
It would probably be recommended to update your UI via a Handler after you have completed your work in the AsyncTask. This is the standard for how messages between an AsyncTask/Thread should communicate with the UI Thread.
The reason for this is because you are not allowed to access UI
elements in a thread that is NOT the main UI Thread.
Another method you could utilize is the runOnUiThread Method that is available from the Activity class. However; I'd recommend using a handler.