Issue
I am trying to create my own implementation of android Asynctask. For that I created an abstract class which extends Thread class. I declared methods for onPreExecute, onPostExecute, onProgressUpdate, and doInBackground.
I am running the doInBackground method by creating a handler from Looper of main thread. But I am unable to modify UI elements inside my onProgressUpdate() method but I am able to modify UI elements in onPostExecute() method.
Here is what I tried.
package com.example.myapplication;
import android.os.Handler;
import android.os.Looper;
public abstract class MyAsyncTask extends Thread {
String[] urls;
Handler handler;
abstract protected void onPreExecute();
abstract protected void onPostExecute(String result);
abstract protected void onProgressUpdate(String result);
protected void publishProgress(String progress) {
onProgressUpdate(progress);
}
abstract protected void doInBackground(String... urls);
protected void execute(String... urls) {
onPreExecute();
this.urls = urls;
start();
}
public void run() {
try {
handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
doInBackground(urls);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
This is my MainActivity. It has progress bar and progressText to show progress.
package com.example.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
ProgressBar bar;
TextView progressText;
LinearLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar = findViewById(R.id.progress);
progressText = findViewById(R.id.progressText);
container = findViewById(R.id.container);
}
public void startProcess(View view) {
String[] assignments = {"assgn1", "assgn2", "assgn3", "assgn4", "assgn5"};
new ProgressAsync().execute(assignments);
}
public void showProgress() {
container.setVisibility(View.VISIBLE);
}
public void hideProgress() {
container.setVisibility(View.GONE);
}
public class ProgressAsync extends MyAsyncTask {
@Override
protected void onPreExecute() {
showProgress();
}
@Override
protected void onPostExecute(String result) {
progressText.setText("All processed");
hideProgress();
}
@Override
protected void onProgressUpdate(String result) {
progressText.setText(result + " processed");
}
@Override
protected void doInBackground(String... urls) {
for (int i = 0; i < urls.length; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(this.urls[i]);
}
onPostExecute("SUCCESS");
}
}
}
It supposed to set progressText like assgn1, assgn2 etc after every 2 seconds. But it doesn't set any text for progressText during onProgressUpdate(), but sets "All Processed" in onPostExecute() method.
Can someone help me if I am missing something here.
Solution
There are some rules that you need to know before implementing your own AsyncTask
AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
- AsyncTask will spawn at least one background thread to do its work.
doInBackground()
will run on this background thread, because if it runs on the UI thread, your app will freeze or even get crash.onPreExecute()
,onPostExecute()
,onProgressUpdate()
will run on UI thread.
In your code, because the code in doInBackground()
run on UI thread, that why your app get freeze, until it reaches the last line in the doInBackground()
method.
Here is my solution:
MyAsyncTask.java
public abstract class MyAsyncTask extends Thread {
private String[] mUrls;
private Handler mMainHandler;
public MyAsyncTask() {
// Using this handler to update UI
mMainHandler = new Handler(Looper.getMainLooper());
}
abstract protected void onPreExecute();
abstract protected void onPostExecute(String result);
abstract protected void onProgressUpdate(String result);
protected void publishProgress(final String progress) {
// This will run in UI thread.
mMainHandler.post(new Runnable() {
@Override
public void run() {
onProgressUpdate(progress);
}
});
}
abstract protected String doInBackground(String... urls);
protected void execute(String... urls) {
mUrls = urls;
start();
}
public void run() {
// This will run in UI thread.
mMainHandler.post(new Runnable() {
@Override
public void run() {
onPreExecute();
}
});
String result = null;
try {
// This will run in the background thread.
result = doInBackground(mUrls);
} finally {
// This will run in UI thread.
final String finalResult = result;
mMainHandler.post(new Runnable() {
@Override
public void run() {
onPostExecute(finalResult);
}
});
}
}
}
ProgressAsync.java
public class ProgressAsync extends MyAsyncTask {
@Override
protected void onPreExecute() {
showProgress();
}
@Override
protected void onPostExecute(String result) {
progressText.setText(result);
hideProgress();
}
@Override
protected void onProgressUpdate(String result) {
progressText.setText(result + " processed");
}
@Override
protected String doInBackground(String... urls) {
for (String url : urls) {
publishProgress(url);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "SUCCESS";
}
}
Note: I just give the simple and quick solution, In practice, you need to write more code to handle some scenarios, such as handling exceptions, cancelling the background tasks, etc.
Answered By - Son Truong
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.