Using Async Task inside getview() method - java

I have these lines of code , which i want to use inside getview() method of CustomAdapter .
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
final MobileServiceList<User> result =
mUser.where().field("name").eq(currentItem.getTo()).select("designation").execute().get();
int counter=
mUser.where().select("designation").execute().get().getTotalCount();
for (User item : result) {
// Log.i(TAG, "Read object with ID " + item.id);
desig[0] = item.getDesignation();
Log.v("FINALLY DESIGNATION IS", desig[0]);
}
} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
// gb.setDesignation(desig[0]);
designation.setText(desig[0]);
}
}.execute();
This is my AsyncTask code which , I am using to populate custom listview .This piece of code keeps on running , I don't know how many times. But at the end it is giving me right results after so many updates on TextView (designation). This is not only degrading the performance of my application but also showing multiple updates on TextView before reaching to result.
I am getting data in desig[0] variable , have a look on my code .
Data is coming from azure But don't worry if you are not an azure guy . Just help me on Java part.
QUESTION
My question is how can use Async task inside getview() method . Do i have to make functions , which i can call latter . Data is coming from azure But don't worry if you are not an azure guy . Just help me on Java part. plzz help

getView gets called every time a line of list view must be drawn. So don't forget that there will be multiple doInBackground running at the same time. What is designation ? A view inside an element of ListView ?
EDIT
As designation is a text in each cell, you have to make sure that the cell you will upgrade is the good one.
What I usually do is that I use a ViewHolder that I attach to the recycled View. Then I register the ViewHolder as a listener (listening to AsyncTask).
ViewHolder starts the request and gives an ID to the AsyncTask. Then, while the AsyncTask is running, I periodically ask the ViewHolder if the ID that was given to the AsyncTask is the good one. If it's not, I cancel the AsyncTask (no need to be synchronous).
Then when the AsyncTask is finished, I check a last time that the ID matches the one given by the ViewHolder and I notify the listener (ViewHolder) that the result is available.
This way, you can be sure that the AsyncTask is not doing work for nothing.
But of course it will depend of what kind of task you are actually doing in AsyncTask...

Call the AsyncTask subclass in side Activity or onActivityCreated of Fragment and pass the result to CustomAdapter to update the view. If you can play your Activity or Fragment class to see where appropriate to put it.

you need make a data model for you view. like make a class which contains all display information(here a string for desig[0]). so you can update the view in AsyncTask.onPostExecute().
or if you need the view only show when the desig got, hide it or add a loading waiting dialog when you getting the desig from azure.

Related

The content of the adapter has changed but ListView did not receive a notification -Android

I have looked through a lot of content online but none of the suggestions work. I have a listview that sometimes work and sometimes crashes my app with the following:
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 only from the UI thread. Make sure your
adapter calls notifyDataSetChanged() when its content changes.
I am definitely calling the notifyDataSetChanged() (In the onPostExecute() of an async task). I have tried as suggested by examples online to run it on the main thread like below:
getActivity().runOnUiThread(new Runnable() {
public void run() {
CAdapterFilter.notifyDataSetChanged();
}
});
but that did not work either. Still my app will crash at random times. Can anyone shed some light here? Why is it only crashing randomly and not every time. What am I missing?
Try this :-
doInBackground(....) {
mPseudoList.addAll(fetched_list);
return xyz;
}
onPostExecute(...) {
mAdapterList.addAll(mPseudoList);
mAdapter.notifyDataSetChanged();
}
Change the adapter list reference in onPostExecute(...) and then notify the adapter !!
Note :-
In doInBackground dont update the list whose reference the adapter holds , instead use a pseudo-list and update the adapter reference list in onPostExecute

RealmChangeListener does not execute when added shortly after asynchronous write

Long story short: I do not know why my RealmChangeListener does not trigger as intended under certain circumstances and am looking for advice on why a RealmChangeListener could not be working.
Update: I have verified that the RealmResults stay valid = true; loaded=false.
If I execute RealmResults.load() after adding the change listener, it will output a vague Throwing Exception 7 in the log and a BadVersionException when I step through the Realm source. I think this exception makes some sense, the asynchronous write updated the Realm and therefore the query seems to no longer work. However, both the executeTransactionAsync that writes in the MainActivity as well as the asynchronous queries are started from the main thread.
-
I have a MainActivity, in which upon pressing a button an asynchronous write will be performed.
I have another button that opens a second activity that displays data.
The second activity uses a ViewPager with a fragment for each tab. Each tab has a different query.
So what happens now is the following: I open the second activity, which instantiates four fragments, not attaching them to the activity.
The activity then executes queries, passing each RealmResults to the fragment, in which a RealmChangeListener will be installed to display the data once it is loaded. Could it be that the RealmChangeListener does not work when the fragment is not attached to an Activity?
Anyway, this is the method in the fragment that receives the RealmResults (created by findAllAsyncSorted()) and is supposed to update the data on the Adapter:
public void updateData(OrderedRealmCollection<Bean> realmCollection) {
Timber.v("Delegated data to fragment of adapter %s.", adapter);
this.data = (RealmResults<Bean>) realmCollection;
if (data.isLoaded()) {
Timber.d("Data is already loaded on adapter %s.", adapter);
adapter.updateDataManual(data);
}
if (!data.isValid()) {
Timber.e("Data is not valid.");
}
listener = new RealmChangeListener<RealmResults<Bean>>() {
#Override public void onChange(RealmResults<Bean> newResults) {
Timber.v("Change listener for manual data triggered: %d results in fragment for category %s and adapter %s.",
newResults.size(), category.toString(), adapter);
adapter.updateDataManual(newResults);
}
#Override protected void finalize() throws Throwable {
Timber.d("Finalizing change listener for adapter %s.", adapter);
super.finalize();
}
};
data.addChangeListener(listener);
MyTimer.setRepeatingCallback(() -> {
Timber.v("RealmResults in adapter %s are %s and %s, and the ChangeListener is %s.",
adapter,
data.isValid() ? "valid" : "invalid",
data.isLoaded() ? "loaded" : "not loaded",
listener);
return true;
}, 5000);
}
As you can see, I made efforts to ensure that the query is valid and has not loaded until the change listener is added and that neither the RealmResults nor the RealmChangeListener are garbage collected.
Still, out of four RealmChangeListeners, only two or less (sometimes zero) trigger.
Note that this only happens if the second activity is opened shortly after starting the asynchronous write on the MainActivity. If I wait for 2 seconds, everything works as intended. I did verify that the RealmChangeListener is not garbage collected, since the finalize() is called after exiting the app. I have no idea what could prevent the listener from working. Is there anything specific I should pay attention to?
You need to have a (strong) field reference to the RealmResults<T> so that you keep it from getting GC'd.
If a RealmResults gets GC'd, then Realm can no longer auto-update it.

How to make a fragment refresh and load afresh on back pressed android

I am in a peculiar situtation in my app.
When i app first loads there is a custom listview which is populated with data from the server.I am also using a class which contains different fields for the string data from the server.
When i click an item on the custom listview,the object of the corresponding class is passed onto the next fragment.
That is the current fragment is replaced with a new fragment and the object is passed with bundle.
Now a new listview loads with different tasks.On clicking a task a new fragment with a camera is loaded.
After taking the image and uploading to server, the status in the JSON changes to "COMPLETED".But now when i press back the old listview is shown.
Is there a way to populate the listview on back pressed with new data?
The issue is that I am passing an object right from the first fragment.
Now i need a new object on back pressed,how to pass the new object on back pressed?
When Fragment 2 gets the data, it should pass it along at some point before Fragment 1 is woken.
There are almost a half dozen ways to pass data, and the best way depends on a number of factors like who should own the lifecycle of the data, data pull vs push, dependency between fragments, do multiple components need updating, etc.
I'm just going to advise to simply cache the data on the activity until you learn more about the different methods.
//Fragment 2 puts data to activity
((MyActivity) getActivity).mListViewData = listViewData;
Then the next part of the question is how does fragment 1 get the data. Fragment 1 is hibernating on the backstack. When it wakes up it will call the onViewCreated() method (because it's previous view was destroyed before being placed on the backstack).
In that method, we check if there's new data waiting for Fragment 1.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
MyDataType listViewData = ((MyActivity) getActivity).mListViewData;
if(listViewData != null){
//setData is your own function for replacing the adapters
//data backing
listView.getAdapter().setData(listViewData);
}else{
listView.getAdapter().setData(...defaultData);
}
listView.getAdapter.notifyDataChanged();
}
Override the onBackPressed in the Activity that manages the Fragments. In it you can check if the fragment is visible or not (the one from which an action should be performed if the back is pressed) and then execute your action.
#Override
public void onBackPressed(){
Fragment myFragment = getFragmentManager().findFragmentByTag("MY_FRAGMENT");
if (myFragment.isVisible()) {
String json = myFragment.getJsonData(); //update it locally
if(isUpdated){
Fragment listFragment = getFragmentManager().findFragmentByTag("MY_LIST_FRAGMENT");
listFragment.updateListView(json); //Add this method on your fragment
}
}
super.onBackPressed();
}
Obs.: To use the .findFragmentByTag() you should add tags once you're making the transaction like so:
fragTrans.replace(android.R.id.content, myFragment, "MY_FRAGMENT");
If, for any reason the listFragment has been cleaned from memory, you would have to reload the data anyway so just download the new data again.
To update the ListView please see: How to refresh Android listview? . Note thought that you will need to will need to send a new data set to the list view (which you can do inside the updateListView() method)

Android views can not be touched by other threads?

I am a newbie in Android and creating a mini FTP download manager for myself.. I am using multithreading, each thread to handle one download or upload. In the MainActivity.java, I am using two spinners in the view. One to list the files on the server (which can be downloaded), another to list files on my phone folder (which can be uploaded). I want to update the first (download) spinner when a new file is uploaded, and the second (upload) spinner when a new file is downloaded. However I am not able to make out how to update the spinners only when the corresponding threads finish their job. I created methods to update the spinners in the MainActivity.java and tried to call them in the end of the run() of the threads, so that they'l be updated once the threads finish downloading/uploading. However, I am getting an error in LogCat saying
android.view.ViewRoot$CalledFromWrongThreadException : Only the original thread that created a view hierarchy can touch its views.
The method for updating upload spinner is:
void upScrollUpdate() {
spinup=(Spinner)findViewById(R.id.uploadspin);
spinup.setEnabled(false);
String[] upload={"No Files"};
File sdDir=Environment.getExternalStorageDirectory();
File dir=new File (sdDir.getAbsolutePath() + "/aFTP");
File[] fArray=dir.listFiles();
if(fArray.length>0) {
upload=new String[fArray.length];
}
for(int i=0;i<fArray.length;i++) {
upload[i]=fArray[i].getName();
}
ArrayAdapter<String> saaUpload=new ArrayAdapter<String>(this,android.R.layout.simple_spinner_dropdown_item,upload);
spinup.setAdapter(saaUpload);
if(spinup.getSelectedItem().toString().equalsIgnoreCase("No Files")) {
uploadButton.setEnabled(false);
}
spinup.setEnabled(true);
}
Why cant this method be called from another thread, and what is the other way out of this, I simply want to update the spinners but I have spent a whole day on this only thing...
For any piece of code that will update the UI, put that in this block:
Refer to this link for more info on runOnUiThread
runOnUiThread(new Runnable() {
public void run() {
// RUN THE CODE WHICH IS GIVING THAT EXCEPTION HERE
}
});
The same can also be done like this:
Runnable run = new Runnable() {
#Override
public void run() {
// RUN THE CODE WHICH IS GIVING THAT EXCEPTION HERE
}
}; YourActivity.this.runOnUiThread(run);
Alternatively, you can make use of an AsyncTask. You can do your processing in the doInBackground() method and then update the Spinners in the onPostExecute() method of the AsyncTask
EDIT: Check these tutorials to help you get started with using AsyncTask:
http://www.vogella.com/articles/AndroidPerformance/article.html#asynctask
http://androidresearch.wordpress.com/2012/03/17/understanding-asynctask-once-and-forever/
http://android10.org/index.php/articlesother/239-android-application-and-asynctask-basics
http://mobileorchard.com/android-app-developmentthreading-part-2-async-tasks/
http://thenewboston.org/watch.php?cat=6&number=101
The 5th link to thenewboston.org has about 200 odd video tutorials on YouTube here: http://www.youtube.com/course?list=EC2F07DBCDCC01493A&feature=plcp
EDIT 2: Check the edit in this link here: https://stackoverflow.com/a/13265776/450534
It is at the bottom of the answer.
Anything to do with views should be done on UI thread.
You can use activityInstance.runOnUIThread() to handle this scenario of updating views from different thread.
Refer: Android: RunOnUiThread vs AsyncTask

check Checkbox state of main view in other class

In the main view class / activity I have several checkboxes. Now in another class and with another view active, I would like to check the state of the checkboxes in the 'main' window of my Android app. How can I do that?
Here is what I tried:
protected String doInBackground(String... args) {
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
setContentView(R.layout.main);
View v = findViewById(R.id.cbHasWifi);
CheckBox check = v.findViewById(R.id.cbHasWifi);
v.setContentView(v);
The whole code block gets me nowhere as it seems. I'm also worrying that another View context is active and may create followup problems if it is not 'restored' after my setContentView(), is that justified?
You cannot do anything to the UI in any thread other than the main UI thread, so trying to set a content view from doInBackground is not going to work.
Additionally, you haven't made clear if you are running another activity or not. If you are running a new activity, there is no guarantee your data is still there from the first one.

Categories