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
Related
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
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.
Been a lurker on this site to help find answers to some of my problems before, but I am currently stuck on this and could not find a recent solution. The closest answers I found to my problem were Yelp API Android Integration and Yelp Integration in Android
I tried following the steps in the 2nd link but they are a bit outdated. I have registered for an API, downloaded the jar files from the github and synced them, and made the YelpAPI.java and TwoStepOAuth.java files and removed the main method from YelpAPI. I am stuck on step 4 on the search part. I tried to call the queryAPI method from inside an onClick method I made for a button
public void getRandom(View view) {
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
YelpAPI.YelpAPICLI yelpApiCli = new YelpAPI.YelpAPICLI();
new JCommander(yelpApiCli);
YelpAPI yelpApi = new YelpAPI(CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET);
try {
YelpAPI.queryAPI(yelpApi, yelpApiCli);
} catch (JSONException e) {
e.printStackTrace();
}
}
Basically what I want this to do is, when the button is pressed, I want it to go to a second screen that will display what I query Yelp for. I haven't worked on that part yet, right now I just want to get a result back from Yelp. Keep in mind I am a complete noob at Android Studio and at most intermediate at Java.
Any help is greatly appreciated, it seems like its a really simple problem but its taking me forever to figure out on my own.
You can't do blocking task like downloading or image loading in android's Main thread (UI Thread). You can't block UI for more than 5 seconds. If you try it to block for more than 5 seconds than your app will stop working and display "Unfornutaley your app has stopped working" because of error too much work on main thread. So you need to make use of async task.
I have been working on a library for Android. The library has a method which fetches data from a web service and puts it in a database. The fetching part is, of course, not done on the main thread. Here's a sample method:
public void fetchData() {
remoteTable.get(new TableOperationCallback<TEntity>() {
public void onCompleted(TEntity entity, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
//CALBACK RECEIVED
//Put data in local database.
}
});
}
Now, somewhere else in my app, where the library is being consumed, I do something like this to refresh the data:
public void refreshData(){
mylibrary.fetchData();
List<MyItems> mList = localtable.getItems();
}
Here, the first statement will go and fetch the data on background thread. So, the second statement will be executed even before the data is actually fetched. How do I get around this? I want the second statement to be executed only after the callback of the first is complete.
Edit: If it matters, the method refreshData is not in any activity. I put that method in a separate class (and called it ViewModel - .NET habit!).
You can have a look at this link
Android Update Current Activity From Background Thread
You basically want a Callback interface. When the task in the library completes, then you do what you have to do
I am using eclipse's jobs API to run big task as a job, once task is completed I am setting boolean variable to true and if that variable is true I am executing WizardDialog in UI thread. My current code looks like this:
Job longRunningJob = new Job("Long running job...") {
#Override
protected IStatus run(IProgressMonitor monitor) {
boolean shouldShowDialog = doLongRunningJob();
if(shouldShowDialog) {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
//Will open wizard dialog here
WizardDialog wizardDialog = new WizardDialog(Display.getCurrent().getActiveShell(), new TestWizard());
wizardDialog.setBlockOnOpen(true);
wizardDialog.open();
}
});
}
}
}
longRunningJob.setUser(true);
longRunningJob.schedule();
My problem is run inside Display thread not executing in reliable way, means sometime it goes inside run method where as sometimes it doesn't, I tried putting breakpoint inside run method and testing it out but same happens.
My question is, is what I am doing is correct way? Is this expected behaviour? So how do I handle this scenario ie once shouldShowDialog is true how do I execute code inside Display thread?
Edit: One behaviour I observed while debugging is dialog gets displayed but suddenly it get closes, I think it's exiting the thread.
The problem with disappearing dialogs is most commonly caused by using currently active Shell as the parent for the dialog. E.g. if there is a ProgressDialog open when you create your dialog then that other dialog will be the parent of your dialog. And when the other dialog closes, so does yours.
Instead, use something like:
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();