I currently have one class with 4 methods. I need to change that to AsyncTask. Every method receives different parameters (File, int, String ...) to work with and connects to different URL with post or get. My question is can I still somehow have all those operations in one AsyncTask class or I will need to create new AsyncTask class for every method?
private class Task extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
This depends if you need all 4 AsyncTasks to run simultaneously or if they can run sequentially.
I would imagine they can run sequentially since that's how they are running currently in the Main thread, so just pass all the needed parameters and execute their operations one by one. In fact, if the functions are already written, just move those functions into your AsyncTask class:
MainActivity.java:
public static final int FILE_TYPE = 0;
public static final int INT_TYPE = 1;
public static final int STRING_TYPE = 2;
taskargs = new Object[] { "mystring", new File("somefile.txt"), new myObject("somearg") };
new Task(STRING_TYPE, taskargs).execute();
AsyncTask
private class Task extends AsyncTask<URL, Integer, Long> {
private Int type;
private Object[] objects;
public Task(Int type, Object[] objects) {
this.type = type;
this.objects = objects;
}
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
}
//obviously you can switch on whatever string/int you'd like
switch (type) {
case 0: taskFile();
break;
case 1: taskInteger();
break;
case 2: taskString();
break;
default: break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
protected void taskFile(){ //do something with objects array }
protected void taskInteger(){ //do something with objects array }
protected void taskString(){ //do something with objects array }
}
Related
I have built a user interface in Android Studio to test the PSO algorithm in Java. I took this project on from someone else who did it last year, the person before me used AsyncTask with Boolean[] parameters to execute his application. Below is his version of this class, this is because he used a checkbox in his MainActivity that the user can check, so it can either be one or the other.
public class runTests extends AsyncTask<Boolean, Void, Void> {
#Override
protected Void doInBackground(Boolean... params) {
boolean one = params[0];
boolean custom = params[1];
if (one)
results = runTest("TestOne");
else if (custom) {
double[] re = runTest("customTest");
if (re != null) results = re;
}
return null;
}
#Override
protected void onPostExecute(Void v) {
// execution of result of Long time consuming operation
pd.dismiss();
if (results[0] != -1 || results != null) {
loadIntent(results);
}
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(MainActivity.this, "Busy", "Algorithm is currently executing");
}
}
Whereas my code doesn't have a checkbox it only needs to implement one test rather than having the option of two. I just want to run "CustomUseCase" and nothing else. I don't want to use a boolean parameter however I still want to have AsyncTask. Please help me!
public class runTests extends AsyncTask<Boolean, Void, Void> {
#Override
protected Void doInBackground(Boolean... params) { //sort this out
boolean one = params[0];
boolean custom = params[1];
if (one)
results = runTest("CustomUseCase"); //i only want to run this one!!!
else if (custom) {
double[] re = runTest("testOne"); //I don't need this, I dont want to run this test
if (re != null) results = re;
}
return null;
}
#Override
protected void onPostExecute(Void v) {
// execution of result of Long time consuming operation
if(pd!=null){
pd.dismiss();
pd = null;
}
if (results[0] != -1 || results != null) {
loadIntent(results);
}
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(ParticleActivity.this, "Busy", "Algorithm is currently executing");
}
}
First, you need to change
if (results[0] != -1 || results != null) {
loadIntent(results);
}
to
if (results != null && results.length > 0 && results[0] != -1) {
loadIntent(results);
}
That is an example of a "short circuiting" if statement. The logical AND (&&) operator will cause the entire statement to fail if results is null, otherwise it will evaluate the next logic statement results[0] != -1 with no chance of a NullPointerException.
Try this as well
public class runTests extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) { //sort this out
results = runTest("CustomUseCase"); //i only want to run this one!!!
}
...
}
UPDATE
do I return null when executing the doInBackground?
To clarify how the Template Type List (ie, <Void,Void,Void>) works, here is an example. Here, we have <URL, Integer, Long>. Notice how doInBackground takes an array of URL (urls) and returns a Long, onProgressUpdate takes an Integer array, and onPostExecute takes a Long.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
long totalSize = urls.length;
...
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
For <Void, Void, Void> we would have (empty function argument list "()" corresponds to Void):
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground() {
...
return;
}
protected void onProgressUpdate() {
setProgressPercent("hi");
}
protected void onPostExecute() {
showDialog("bye");
}
}
Current scenario: Example app which stores images from several URLs in the SD cache and displays them in a ListView.
Task: instead of take hard-coded URLs inside a private method in the MainActivity retrieve them from JSON data placed in a URL resource.
I'm retrieving the JSON and parsing the data well, but I'm having difficulties on how to send this parsed data to the MyImageLoaderAdapter because the returned list seems to come later..
File: MainActivity.java
public class MainActivity extends Activity {
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
try{
SimpleAsyncTask mTask = new SimpleAsyncTask();
mTask.execute(resource);
ArrayList list = mTask.list;
String[] strArray = new String[ list.size() ];
int length = strArray.length; // lenght = 0
mStrings = new String[ list.size() ];
int length = strArray.length;
for( int j = 0; j < length; j++ ) {
mStrings[j] = list.get(j).toString();
}
}catch (Exception e){}
// Create custom adapter for listview
adapter=new MyImageLoadAdapter(this, mStrings);
...
}
private String[] mStrings={
"http://resourse1.com",
"http://resourseN.com",
};
}
File: SimpleAsyncTask.java
public class SimpleAsyncTask extends AsyncTask<String, String, String>{
ArrayList list = new ArrayList();
protected String doInBackground(String... uri) {
//working code
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
...
return list //expected value;
}
}
You could do it like this:
public class MainActivity extends Activity implements OnTaskCompleteListener{
private ArrayList list;
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
try{
SimpleAsyncTask mTask = new SimpleAsyncTask();
mTask.execute(resource);
}catch (Exception e){}
...
}
private String[] mStrings={
"http://resourse1.com",
"http://resourseN.com",
};
#Override
private void onTaskComplete(ArrayList taskList){
list = taskList;
//String[] strArray = new String[ list.size() ];
//int length = strArray.length; // lenght = 0
//mStrings = new String[ list.size() ];
//int length = strArray.length;
//for( int j = 0; j < length; j++ ) {
// mStrings[j] = list.get(j).toString();
//}
//Instead of the above code you can also use this
String[] array = list.toArray(new String[list.size()]);
// Create custom adapter for listview
adapter=new MyImageLoadAdapter(this, array);
}
}
Now change your Asynctask as follows:
public class SimpleAsyncTask extends AsyncTask<String, String, String>{
private OnTaskCompleteListener listener;
public SimpleAsyncTask(OnTaskCompleteListener listener) {
this.listener = listener;
}
protected String doInBackground(String... uri) {
//working code
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
...//Convert the response to list and call your listener
listener.onTaskComplete(list);
// return list //expected value;// no need of it now.
}
}
Create an interface in your package.
public interface OnTaskCompleteListener {
void onTaskComplete(ArrayList list);
}
Here you are implementing an interface in your activity, passing it to the async task while creating it, once the onpostexecute is called in the async task you are calling the method implemented in the activity using the passed interface object.
Hope this helps.
I want to create 10 Employee objects in an AsyncTask and return the result back to the MainActivity class to print it on a ListView with the 3 attributes of an Employee object.
This is what i have so far, but it just crashes after running
Menu class
public class Menu
{
public Employee person;
public void onButtonClick(View v) {
new setEMPInfo() {
protected void onPostExecute(Employee person)
{
doSomething(person);
}
}.execute();
}
public void doSomething(Employee person) {
//use person object to print on TextView
}
}
setEMPInfo class
public class setEMPInfo extends AsyncTask<Void, Void, Employee>
{
Public Employee person;
protected Bus doInBackground(String... params) {
String id = "100A";
String Fname = "John";
String Lname = "Smith";
for (int i = 0; i < 10; i++) {
person = new Employee(id, Fname, Lname);
}
return person;
}
}
Try this :
public class TestActivity extends Activity {
ListView list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
list = (ListView) findViewById(R.id.list_item);
setEMPInfo task = new setEMPInfo().execute();
}
private class setEMPInfo extends AsyncTask<Void, Void, ArrayList<Employee>> {
#Override
protected ArrayList<Employee> doInBackground(Void... params) {
String id = "100A";
String Fname = "John";
String Lname = "Smith";
ArrayList<Employee> employees = new ArrayList<>();
for (int i = 0; i < 10; i++) {
person = new Employee(id, Fname, Lname);
employees.add(person);
}
return employees;
}
#Override
protected void onPostExecute( ArrayList<Employee>result)
//print it on a ListView
list.setAdapter(new YourAdapret(getApplicationContext(), result));
}
}
}
when your doInBackground done , its return some value to onPostExecute . then you can do anything (save in database , save in SDcard , etc) in this method .
You have here an example of a complete asyncTask (by google docs).
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
You can create a static object in your caller class and change it in onPostExecute for example.
The problem is that your class setEMPInfo extends AsyncTask<Void, Void, Employee> and not AsyncTask<String, Void, Bus>
The fix is to change (inside your setEMPInfo class)
protected Bus doInBackground(String... params) { ... }
to
protected Employee doInBackground(String... params) { ... }
and
public class setEMPInfo extends AsyncTask<Void, Void, Employee>
to
public class setEMPInfo extends AsyncTask<String, Void, Employee>
Also, don't forget the #Override annotation.
Finally, I recommend you use an IDE to automatically fix your code spelling and other common mistakes. This problem wouldn't happen in Android Studio or Eclipse. :)
So basically, your code should look like that :
SetEMPInfo.java
public class SetEMPInfo extends AsyncTask<String, Void, Employee>
{
public Employee person;
#Override
protected Employee doInBackground(String... params)
{
String id = "100A";
String Fname = "John";
String Lname = "Smith";
person = new Employee(id, Fname, Lname);
return person;
}
}
Menu.java
public class Menu
{
public Employee person;
public void onButtonClick(View v)
{
new SetEMPInfo()
{
#Override
protected void onPostExecute(Employee employee)
{
doSomething(employee);
}
}.execute();
}
public void doSomething(Employee person)
{
//use person object to print on TextView
}
}
Try this
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new setEMPInfo() {
protected void onPostExecute(Employee person) {
doSomething(person);
}
}.execute();
}
}
);
here just for testing add all employee to list-
public void doSomething(Employee person) {
eList.add(person);
Log.e("Emp->", eList.toString());
}
Your AsyncTask should look like this-
class setEMPInfo extends AsyncTask<Employee, Void, Employee> {
Employee person;
#Override
protected Employee doInBackground(Employee... params) {
String id = "100A";
String Fname = "John";
String Lname = "Smith";
for (int i = 0; i < 10; i++) {
person = new Employee(id, Fname, Lname);
}
return person;
}
}
How can I call a function defined in class#A from class#B? Class#B extends AsynchTask and fetches remote data from a web service. The class#A function I am attempting to call from class#B is used to send the retrieved remote data for Class#A to display.
I am trying to pass the current instance of class#A to class#B using this but that just passes the context so the functions are not recognized.
I also tried using static but as the function runs a new thread, defining the function as static generates a compiler error.
The code I am trying to call is as follows:
public void test(List<DATA> Data){
this.Data = Data;
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for(DATA data : MainActivity.this.Data){
Toast.makeText(MainActivity.this, data.title, Toast.LENGTH_SHORT).show();
}
}
});
}
This is how you would do it:
In MainActivity:
ClassBAsyncTask mat = new ClassBAsyncTask(this, .., ..);
ClassBAsyncTask's contructor:
public ClassBAsyncTask(MainActivity context, .., ..) {
mContext = context;
....
....
}
To call MainActivity's method test(List<DATA>) from within ClassBAsyncTask:
((MainActivity)mContext).test(yourListVariable);
Look into weak references to make sure that the AsyncTask does not use mContext when the activity no longer exists.
From your question, I understood that u want to show the result of asynctask in another class. You can use onProressUpdate api of asynctask. You can do the work in doInBackground and then call publishProgress(value) whihc in turn calls onProgressUpdate and run on UI thred.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
Other way:
You can define a handler in class#A and call it from class#B
Sorry if i mis understood your question.
as I understand you start from A an AsyncTask B, cause web fetching only works in async way in Android. You want the result of the fetch to be accessible for an object in A.
A would code something like this.
public class A extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
TextView fetch = (TextView) findViewById(R.id.fetch);
new B(getApplicationContext(),fetch).execute();
// This will update a TextView when fetching is done.
}
}
public class B extends AsyncTask<Void, Integer, Void> {
Context c;
TextView f;
public B(Context c, TextView f) {
super();
this.f = f;
this.c = c;
}
#Override
protected Void doInBackground(Void... params) {
// TODO: the fetching: fetch()
String t = fetch();
f.setText();
return null;
}
}
You can do other tricks with pre and post execute methods. I hope I could help!
You can make an interface ie.:
public interface RecieveDataDelegate {
public void receiveData(Object dataFromWebService, boolean success);
}
Have Class#A implement the interface. And when calling the asyncTask from Class#A pass itself to a contructor for the AsyncTask that has a parameter of type ReceiveDataDelegate.
new AsyncTask1 (this).execute();
And setup AsyncTask with a constructor and send data back to Class#A onPostExecute() so you can update ui.
class AsyncTask1 extends AsyncTask<Void, Void, Boolean> {
Object dataReceived ;
protected RecieveDataDelegate delegate;
public AsyncTask1 (RecieveDataDelegate delegate) {
this.delegate = delegate;
}
protected Boolean doInBackground(Void... params) {
//do your service request and set dataRecieved
}
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
delegate.RecieveDataDelegate (dataReceived, result.booleanValue());
}
That way any class that implements the interface can use the same asyncTask.
I'm trying to create an async task to handle a whole bunch of database entries and then let the user know that the entry has been made with a textView that appends to itself. I understand that I cannot touch the views inside the doInBackground, but I cannot get any other methods to work. Can anyone explain to me on how to get my code to work inside of an AsyncTask?
Code:
private class DBADDITION extends AsyncTask<Object, Void, Object> {
#Override
protected String doInBackground(Object... params) {
DBAdapter my_database = new DBAdapter(getApplicationContext());
logout.append("\n" + "Start" + " ");
my_database.open();
String temp = input.getText().toString();
int i = Integer.parseInt(temp);
for (int j = 1; j <= i; j++) {
db.createEntry("example", 10 + j);
logout.setText("\n" + j + logout.getText());
}
db.close();
return "it worked";
}
protected void onProgressUpdate(Integer... progress) {
}
}
You need to override the onPostExecute() method. This is automatically called after the doInBackground() method. Also this is on the UI thread and hence you can modify your textView here.
In case , you need to perform some UI updation before the doInBackground() then you override the onPreExecute() method.
Also, remove instances of any UI element updation from your doInBackground() like setText()
You use Activity.runOnUIThread() to setText, like these:
private class DBADDITION extends AsyncTask<Object, Void, Object> {
#Override
protected String doInBackground(Object... params) {
DBAdapter my_database = new DBAdapter(getApplicationContext());
logout.append("\n" + "Start" + " ");
my_database.open();
final String temp = input.getText().toString();
int i = Integer.parseInt(temp);
for (int j = 1; j <= i; j++) {
db.createEntry("example", 10 + j);
youractivity.this.runOnUiThread(new Runnable() {
public void run() {
logout.setText("\n" + j + logout.getText());
}
);
}
db.close();
return "it worked";
}
protected void onProgressUpdate(Integer... progress) {
}
}
logout.setText()
You can not perform operation on UI from a different Thread. All the UI Operations have to be performend on the UI thread. Since logout is a TextView object, you can not touch it directly from the doInBackground method, since it runs on a different Thread. YOu should use a Handler instance or, you have a reference to your Activity, you should call runOnUiThread. runOnUiThread allows you to post a Runnable on the looper queue of the UI Thread, without the need to instantiate an Handler.
final int finalJ = j;
runOnUiThread(new Runnable() {
public void run() {
logout.setText("\n" + finalJ + logout.getText());
}
});
runOnUiThread(new Runnable() {
public void run() {
logout.append("\n" + "Start" + " ");
}
});