Android: Another thread is making the UI unresponsive? - java

I'm starting a new thread from my activity, this thread does a 10 second operation and then reports back to the UI with runOnUiThread()
During the 10 second operation, the UI becomes unresponsive and does not respond to any user interaction. In this case I am attempting to close the activity using a button in the toolbar. An ANR error is thrown but the button click is processed after the worker thread has finished.
Although, while the thread is working, the app is still able to display a spinning ProgressBar which wouldn't happen if the work was being done on the UI thread.
The Profiler shows that the UI thread is sleeping during this work, so to my understanding it should be responsive?. I've tried using AsyncTask instead but that also doesn't work. Anyway here is some code:
The new Thread is started when the window comes into focus:
Activity:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus && !recyclerSetup){
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.VISIBLE);
WorkThread thread = new WorkThread();
thread.start();
}
}
Thread:
private class WorkThread extends Thread {
#Override
public void run() {
getViewModelAndWords();
runOnUiThread(() -> setupRecycler());
}
}
private void getViewModelAndWords() {
viewModel = ViewModelProviders.of(this).get(WordViewModel.class);
adapter = new WordDetailedAdapter(this, viewModel, this, this, !favGroup.equals(ANY_WORD_PARAM));
allWords = viewModel.getAllWords();
}
I'm not sure if the viewModel has anything to do with the issue or not, but it's the viewModel.getAllWords() method which performs a heavy 10 second Room db operation.
Here's a snapshot of the Profiler showing the sleeping UI thread and worker Thread (AsyncTask #6):
EDIT:
Okay, so I think the issue lies within the room DB operation / viewModel. Replacing the contents of getAllWords() with Thread.sleep(10000); free'd up the UI thread for user interaction, therefore it's the following code which is (for some reason) preventing user input:
EDIT 2:
As suggested, I now use onPostExecute() along with an interface to retrieve the words:
public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>>{
WordViewModel.iGetWords listener;
WordDao wordDao;
public GetAllWordsWithCallBackTask(WordDao wordDao, WordViewModel.iGetWords listener) {
this.listener = listener;
this.wordDao = wordDao;
}
#Override
protected List<Word> doInBackground(Void... voids) {
return wordDao.getAllWords();
}
#Override
protected void onPostExecute(List<Word> words) {
listener.gotWords(words);
}
}
get() has been removed and I simply execute the task, passing in listener to handle the call back:
public void getAllWordsWithCallBack(WordViewModel.iGetWords listener) {
try {
new GetAllWordsWithCallBackTask(wordDao, listener).execute();
} catch (Exception e) {
Crashlytics.log("Getting all words exception: "+e.getMessage());
e.printStackTrace();
}
}
This works well and the words are returned to my activity successfully, but the UI is still unresponsive while the operation is being executed.

Edit 1
You call .get() on a AsyncTask. The calling thread waits for the AsyncTask to complete. You could implement interface callbacks to fix this problem.
Here is a solution for you're problem
Edit 2:
I took a closer look at your code, and again, there is no error in the code you posted here.
Using AsyncTask with callbacks is a possible solution. Your code runs in the background thread and the result is passed to the main thread without blocking it.
I think that your error lies in transferring the data from the callback to ViewModel or MainActivity.
The best solution to get around this is using LiveData.
I tried to rebuild your code as closely as possible. Maybe it will help you to find the mistake.
WordDb
#Database(entities = {Word.class}, version = 3)
#TypeConverters(DateConverter.class)
public abstract class WordDb extends RoomDatabase {
private static WordDb INSTANCE;
public abstract WordDao wordDao();
static synchronized WordDb getInstance(Context contextPassed){
if(INSTANCE == null){
INSTANCE = Room.databaseBuilder(contextPassed.getApplicationContext(),WordDb.class,"word_db")
.fallbackToDestructiveMigration()
.build();
}
return INSTANCE;
}
}
WordRepo
class WordRepo {
private WordDao wordDao;
WordRepo(Context applicationContext) {
WordDb wordDb = WordDb.getInstance(applicationContext);
wordDao = wordDb.wordDao();
}
void getAllWords(WordRepo.iGetWords listener) {
try {
Log.i("WordRepo", String.format("getAllWords() called from %s", Thread.currentThread().getName()));
new GetAllWordsWithCallBackTask(wordDao, listener).execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>> {
WordRepo.iGetWords listener;
WordDao wordDao;
GetAllWordsWithCallBackTask(WordDao wordDao, WordRepo.iGetWords listener) {
this.listener = listener;
this.wordDao = wordDao;
}
#Override
protected List<Word> doInBackground(Void... voids) {
Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.doInBackground() called from %s", Thread.currentThread().getName()));
return wordDao.getAll();
}
#Override
protected void onPostExecute(List<Word> words) {
Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.onPostExecute() called from %s", Thread.currentThread().getName()));
listener.gotWords(words);
}
}
public interface iGetWords {
void gotWords(List<Word> words);
}
}
MainViewModel
public class MainViewModel extends AndroidViewModel {
MutableLiveData<List<Word>> wordList = new MutableLiveData<>();
private static final String TAG = "MainViewModel";
public MainViewModel(#NonNull Application application) {
super(application);
}
void getAllWords() {
Log.i(TAG, String.format("getAllWords() called from %s", Thread.currentThread().getName()));
WordRepo repo = new WordRepo(getApplication());
repo.getAllWords(new WordRepo.iGetWords() {
#Override
public void gotWords(List<Word> words) {
wordList.setValue(words);
}
});
}
}
getViewModelAndWords() in MainActivity
private void getViewModelAndWords() {
Log.i(TAG, String.format("getViewModelAndWords() called from %s", Thread.currentThread().getName()));
viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.wordList.observe(this, new Observer<List<Word>>() {
#Override
public void onChanged(List<Word> words) {
//Do something with youre result
Log.i(TAG, String.format("viewModel.wordList livedata returned %d results", words != null ? words.size() : -1));
}
});
viewModel.getAllWords();
Log.i(TAG, "viewModel.getAllWords() done");
}
If you find out what is going wrong with youre code, please leave a comment
As #mayokun already mentioned i would recommend to use RxJava or migrating your project to Kotlin + Coroutines to keep your code nice an clean.
Here you can find more:
Medium - Coroutines on Android (part I): Getting the background
CodeLabs - Using Kotlin Coroutines in your Android App
Medium - RxAndroid Basics: Part 1
Medium - RxJava VS. Coroutines In Two Use Cases
I have successfully tested this code with about 300,000 records. Running this operation has blocked the Async Task on my emulator for about 30 sec. The main thread was accessible during this process.
I hope this works for you this time as well

return new GetAllWordAsyncTask(wordDao).execute().get();
By calling get(), you are forcing the current invoking thread to synchronously wait for the result to come back, which makes your background query block the main thread while it executes.
The solution is to use a callback and onPostExecute rather than blocking the main thread to obtain your query results.

Related

Android Firebase - Remove listeners from Task<T>

I defined this method, in MyActivity class that allow me to download in memory some data from a Firebase storage.
public void submitDownload() {
Task<byte[]> downloadTask=FirebaseStorage.getInstance.
getReference(DATA_PATH_TO_DOWNLOAD).getBytes(MAX_BYTES);
isTaskActive=true;
//remove eventually a previous callback from the handler
timeoutHandler.removeCallbacks(timeoutCallback);
downloadTask.addOnSuccessListener(MyActivity.this, onSuccessListener);
downloadTask.addOnFailureListener(MyActivity.this, onFailureListener);
timeoutHandler.postDelayed(timeoutCallback, 5000);
}
This is, instead, the onCreate() method:
protected void onCreate() {
super.onCreate();
onSuccessListener=new OnSuccessListener<byte[]>() {
public void onSuccess(byte[] bytes) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "SUCCESS");
}
}
};
onFailureListener=new OnFailureListener() {
public void onFailure(Exception e) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "FAILURE");
}
}
};
timeoutHandler=new Handler();
timeoutCallback=new Runnable() {
public voi run() {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "TIMEOUT");
submitDownload(); //retry download
}
}
};
submitDownload();
}
Obviously, onSuccessListener, onFailureListener, timeoutHandler, timeoutCallback and isTaskActive are instance variable.
As you can see in the run() method defined in timeoutCallback, in addition to a log message, is also called the sumbitDownload(). Pratically, if a timeout occurs and the task is still active, a new download is started.
Now, imagine this scenario.
When Activity is created, a download task is started. Suppose that downloadTask doesn't complete, and neither onSuccessListener nor onFailureListener are called, but timeout occurs. So, from the run() method of timeoutCallback a new download is started.
Now, what happens to the previous downloadTask? Is it canceled? Is it replaced by the current task? or does it continue to be active and potentially could trigger its attached listeners?
If the latter question is true, how to remove the listeners from a Task<T> object?
Does the getResult() method, however, complete (i.e finish) the task?

Waiting for other AsyncTasks' finish in AsyncTask before processing onPostExecute

I'm getting info from an URL and sending that info to multiple URLs. I'm running it on a "for" in AsyncTask, and at onPostExecute of first AsyncTask, I change a TextView and telling that "All is done" to user.
But the thing is even the children of AsyncTask is continuing to being executed, parent AsyncTask executes its onPostExecute. So I need to wait for them and I'm stuck about it.
So, I have two AsyncTask classes. That's the class I send to websites:
class SendTo extends AsyncTask <Object,Object,String>{
#Override
protected String doInBackground(Object... strings) {
....
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
//update ui for gathered information
}
}
That's the task which I get from websites:
class GetFrom extends AsyncTask <Object,Object,String>{
#Override
protected String doInBackground(Object... strings) {
....
String param = anotherMagicalFunctionThatGivesParamToSend(strings[1]);
for(i = 1; i < websites.length; i++){
publishProgress(websites[i],param);
}
return "";
}
protected void onProgressUpdate(final Object... values) {
super.onProgressUpdate(values);
new SendTo().executeOnExecutor(THREAD_POOL_EXECUTOR, values[1], values[0]);
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
//finish things
}
}
I would recommend use callback in this case.
Create an interface:
public interface MyCallback {
public void readycallback(int index_thread);
}
First class:
class SendTo extends AsyncTask <Object,Object,String>{
private MyCallback cb;
private int i;
public SendTo(int i, MyCallback cb) {
this.cb = cb;
this.i = i;
}
#Override
protected String doInBackground(Object... strings) {
....
}
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (cb != null)
cb.readycallback(i);
//update ui for gathered information
}
}
Second class:
class GetFrom extends AsyncTask <Object,Object,String>{
private boolean[] array_of_completed_tasks = new boolean[websites.length - 1];
#Override
protected String doInBackground(Object... strings) {
....
String param = anotherMagicalFunctionThatGivesParamToSend(strings[1]);
for(i = 1; i < websites.length; i++){
publishProgress(websites[i],param);
}
while(!checkIfAllThreadAreCompleted()) {
//waiting all threads
//you can wait
try {
Thread.sleep(10L);// ten milliseconds
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "";
}
protected void onProgressUpdate(final Object... values) {
super.onProgressUpdate(values);
new SendTo(i, new MyCallback() {
#Override
public void readycallback(int index_thread) {
//this is your ready callback
array_of_completed_tasks[index_thread] = true;
}
}).executeOnExecutor(THREAD_POOL_EXECUTOR, values[1], values[0]);
}
}
private boolean checkIfAllThreadAreCompleted() {
boolean ok = true;
for(i = 0; i < array_of_completed_tasks.length; i++){
ok = ok && array_of_completed_tasks[i];
}
return ok;
protected void onPostExecute(String s) {
super.onPostExecute(s);
//finish things
}
}
Or use Threads for much better coding style. But I really think that using AsyncTask is very ugly methods is real projects.
Run following for loop inside postExecute() of asyncTask as it's anyway not useful running thread inside the thread.
Maintain one global variable which will have count of completed asyncTasks and will be incremented by 1 when each asyncTask will completes it's execution. i.e it will come into postExecute.
In Every postExecute check if that count is equal to number of websites. If that number will be equal to number of websites that basically means all of your data is successfully synced else it's still in process.
It's probably not a good idea to have one AsyncTask simply wait for other AsyncTasks to complete. It makes the code more complex and prevents using that thread in the pool while it's waiting.
If you can, it would be simpler that you launch all AsyncTasks in parallel and have them all call the same method in onPostExecute(). That method would then check if all the results of all the AsyncTasks have been posted and show "All is done" in that case.
If you really want to have one AsyncTask wait in doInBackground() for one or more other AsyncTasks to complete (effectively pausing that background thread), you can use standard Java synchronization mechanisms. I recommend using CountDownLatch.
It's worth noting that synchronizing between background tasks is something that can be achieved quite easily with RxJava rather than using AsyncTask which has been designed for simple use cases.
You can pass you TextView in child asyncTask and update its value in onPost method of child asynchTask .

Creating a callback on a class method

I'm struggling with a network connection class I've created. The result of the Runnable I create returns a JSON object that contains all the information needed from the server. The thread runs, and receives the data perfectly, but of course, the program keeps running in the meantime, which results in a JSONException as being NULL.
I created a class called NetworkManager, which has the following method (jsonResponse is initialized at the beginning of the class)
JSONObject jsonResponse;
public void createNetworkThread(Context context, final String requestURI, final RequestBody formParameters) {
Runnable runnable = new Runnable() {
#Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
Response response = null;
try {
response = client.newCall(request).execute();
String stringResponse = response.body().string();
NetworkManager.this.jsonResponse = new JSONObject(stringResponse);
// This works perfectly, "message" is received and printed to the log //
Log.d("Net", NetworkManager.this.jsonResponse.getString("message"));
} catch (IOException e) {
Log.d("Net", "Failed");
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
The above is called from the Activity, as:
Net.createNetworkThread(SignupActivity.this, requestURI, formVars);
JSONObject jsonResponse = Net.jsonResponse;
The JSON object jsonResponse is returning as NULL because the Thread is still accessing the server for the response.
I need to figure out how to stop the jsonResponse Object from being populated by Net.jsonResponse until the thread completes in order to stop it from returning NULL.
Any help?
I would only agree to the comments on your question and let you know, what you can do here.
If you are creating a thread just to get of the main UI thread to do the Network call you probably want to use OkHttp feature which allows you to get the Network call off the thread and provides you with callbacks to get the result something like this. you can check some example here
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
}
#Override
public void onResponse(Response response) throws IOException {
// this is the callback which tells you the network call was successful, If like to make some changes to UI, you should call `runOnUiThread`.
"YourClassName".this.runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
}
});
or you can use AsyncTask which also gets your job done off the main UI thread and gives you the result in the callbacks.
private class MyTask extends AsyncTask<Void, Void, Void> {
//you can change the Type Void, Void, Void here to something which you want
//First Void belongs to doInBackground(Void... avoid)
//Second Void belongs to onProgressUpdate(Void... progress)
//Third Void belongs to onPostExecute(Void result)
// you may change these as you fit,
//when you want to start this class with your argument you can do something like this.
//new MyTask().execute("your argument to doInBackground");
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
// this is the method where you provide your implementation for doing a task off the main UI thread.
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
// in this callback you are back in the main UI thread to make changes to UI depending on your response
}
}
here is an example of AsyncTask
If you want to offload the main thread, you should consider that the parallel task could finish it's execution after, generally speaking, any amount of time. Sure, you can wait in the main thread for the child thread to finish (using 'join()'), though this is kind of questionable in terms of speed gain.
Anyway, answering your question:
I need to figure out how to stop the jsonResponse Object from being
populated by Net.jsonResponse until the thread completes in order to
stop it from returning NULL.
I suggest you change
public void createNetworkThread(...
to
public Thread createNetworkThread(...
{
...
Thread thread = new Thread(runnable);
thread.start();
return thread;
}
And consequently
Thread t = Net.createNetworkThread(SignupActivity.this, requestURI, formVars);
t.join(); // Wait until 't' finishes -- try-catch is omitted for the sake of demo.
JSONObject jsonResponse = Net.jsonResponse;
This, apparently, opens question of performance, since main thread will be effectively completely blocked by 't.join()' until the child thread will finish.
Answering the question in the topic: in Java 8 you can use lambda functional interface like this:
package multithreaded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Multithreaded {
public static void main(String[] args) throws Exception {
Logger logger = LoggerFactory.getLogger("Main");
Worker<String, String> worker = new Worker<String, String>(
(String s) ->
{ // This is actual call-back code.
// It will be called by method 'apply' of interface 'Function' in the 'Worker'.
// It will accept String parameter and pass it in this block as variable 's'
logger.info("Embrace the beauty and power of Java 8! "+s); // yes, we can use local variables of the parent thread.
return "Call-Back "+s;
}
);
logger.info("Application starts new Worker.");
worker.start();
logger.info("Worker is running in background.");
Thread.currentThread().sleep(500); // Simulate some activity here...
logger.info("Result is unpredictable (could be null): "+worker.getResult());
// Wait here until worker is fully finished
worker.join();
logger.info("Result is predictable: "+worker.getResult());
}
}
Worker.java:
package multithreaded;
import java.util.function.Function;
public class Worker<T extends String, R extends String> extends Thread {
private final Function<T, R> callBack;
private volatile R result;
public Worker(Function<T, R> callBack)
{ this.callBack = callBack; }
#Override
public void run()
{
try{
int i = (int)(Math.random()*1000);
// simulate some activity unpredictable in terms of duration
Thread.currentThread().sleep(i);
// After "activity" is finished -- call the call-back function and get result in local variable.
// (Synchronization ommited for the sake of simplicity)
result = this.callBack.apply((T)("Result "+i)); // now let's call the call-back function and save the result in local variable.
}
catch(InterruptedException e)
{e.printStackTrace();}
}
// Getter for the local variable, populated by call-back function.
// (Synchronization ommited for the sake of simplicity)
public R getResult()
{ return this.result; }
}
Running above code multiple times, you will notice that from the parent thread's perspective the result is still unpredictable until the child thread is completely finished.
P.s. I suggest you re-think entire logic of data processing in your app and consider re-factoring towards entirely independent multi-threaded processing (perhaps using producer-consumer logic).

Cancel AsyncTask after some time

This could be a duplicate question but I did not find what I was looking for.
I am calling an AsyncTask in the UI activity new LoadData().execute(); and in doInBackground I call a method which takes time. I want to interrupt this thread if the data is not return after some time.
Below is the code how I tried to do this.
class LoadData extends AsyncTask<String, String, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
startTime = System.currentTimeMillis();
}
protected String doInBackground(String... args)
{
DataCollector dc = new DataCollector();
data = dc.collectData(query);
//Here I check if the time is greater than 30 seconds then cancel
if(((System.currentTimeMillis()-startTime)/1000)>30)
{
cancel(true);
}
return null;
}
}
But this does not stop the task after 30 seconds, in fact it is taking more time.
I have tried get(long timeout, TimeUnit unit); as well but that does not work either.
Can anyone show me how can I do it or how do I use isCancelled() in doInBackground.
Thanks.
You need a thread that cancels your task after a certain amount of time. That Thread could look like this:
public class TaskCanceler implements Runnable{
private AsyncTask task;
public TaskCanceler(AsyncTask task) {
this.task = task;
}
#Override
public void run() {
if (task.getStatus() == AsyncTask.Status.RUNNING )
task.cancel(true);
}
}
And when you call your AsyncTask, you need to run the cancle task after a certain amount of time (=the timeout, in this case 20 sec)
private Handler handler = new Handler();
private TaskCanceler taskCanceler;
...
LoadData task = new LoadData();
taskCanceler = new TaskCanceler(task);
handler.postDelayed(taskCanceler, 20*1000);
task.execute(...)
It's a good idea if you clean this up on cancel or finish with
if(taskCanceler != null && handler != null) {
handler.removeCallbacks(taskCanceler);
}
You can of course wrap this in an custom implementation of AsyncTask. I've used this pattern many times and it works like a charm. One thing to note, in rare cases the handler would not start, I suspect if you create it in the wrong context it will not survive in certain instances, so I forced the handler to be an the UI Thread with handler= new Handler(Looper.getMainLooper());
You have to do the Time check on a different thread.
What you currently do is: executing the dc.collectData(query) (in background) and once it is ready you check if you should cancel. So if the query takes 1 minute, you will do the cancel check after 1 minute, which is already too late.
What you could do is schedule a TimerTask that should run 30 seconds after the LoadData().execute() and if the timer Task is run, you can cancel the AsyncTask (if it is still running)
I would translate this into an async/await problem making all the expensive methods as async methods.
First, Modify DataCollector's collectData(query) to collectDataAsync(query). (If you can't modify DataCollector, there are work arounds to wrap it in a lambda function or something similar).
Second, change doInBackground as an async task, something like this:
protected async Task<String> doInBackgroundAsync(String... args)
{
DataCollector dc = new DataCollector();
int timeout = 1000;
var task = dc.collectDataAsync(query);
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
data = task.Result;
} else {
// timeout logic
}
}
Basically, you have two tasks inside doInBackgroundAsync: collectDataAsync and a delay task.
Your code waits for the faster one. Then you know which one was and you can react accordingly.
If you also need to cancel collectDataAsync task, then you want to used a cancellationToken.
I use this to solve your problem https://stackoverflow.com/a/11191070/3307066.
Note that now doInBackgroundAsync is a async, so it changes a bit the way of using it.
Hope it helps.
Short answer is you CAN'T cancel an AsyncTask once its started. What you can do, is insert a loop inside doInBackGround() which will check for isCancelled() and if it is set to true sometime in the future - return a value from the function (which will in turn call onPostExecute() if you have defined it);
Note that just because you can't stop an AsyncTask doesn't mean that the OS won't cancel it if it's low on memory. You should have this in mind if you are doing essential tasks in the AsyncTask (ones that you want executed 100%). If so, it is better to use a Service - a component that is automatically killed and restarted by the OS as need.
try this :
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
#Override
protected void onPreExecute() {
progressDialog.show();
}
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
Courtesy and for more details see this answer.

General purpose network manager in Android

I'm working on an Android project (API level 10) which needs to send and receive http messages to/from a server.
I implemented a class named NetworkManager which provides different methods, one for each http request (e.g.: loginRequest(user pass), RegistrationRequest(user.....) ).
All these methods generates a JSON object that is passed to the method called sendMessage, which is the method that actually establish the connection, sends and receives the response (also a json object).
Of course network calls are time consuming, so i first decided to use an AsyncTask to display a progressDialog while the network operation is being performed.
The problem is that i need to get the response value retrived from the background thread before executing any other operation which involves the result itself done by the Main thread.
At the same time i would like to make a common and reusable implementation of the AsyncTask.
E.g.: I have a login activity which shows 2 EditText (username, password) and a button called Login. When I press the login button, a progressDialog must appear, and must be disposed once the doInBackground task is accomplished. Of course i could do this way:
onClick(View v) //called when the login button is pressed
{
onPreExecute()
{
//Show the progress dialog
}
doInBackground()
{
//Retreive the login response (an integer containing a message code) using sendLoginRequest(username, password);
//return the response
}
onPostExecute(int response)
{
//Dispose the progress dialog, then loginSucessfull ? start new activity : show error toast
}
}
But, doing this way i should implement an async task for every request i need to send which is what i would like to avoid because if i have N requests i should create N classes that extend AsyncTask.
Thank you!
What i would suggest you is to use INTERFACES for handling response of http request.
The background thread either it be a AysncTask or it be Thread needs to handle both
response
exception
Think it like this way
MainThread - Hey Background Thread do this operation and let me know when you are done.
MainThread - Ok till Background Thread executes its operation let me show progress dialog.
BackGroundThread - I am done with my work. hey MainThread here catch you response or exception
MainThread - Let me stop showing progress bar.
So we need to simulate this callback mechanism via code and also needs to take care that we implement a reusable architecture.
Something like this
Define a Interface
public interface HttpRequestResponse {
public void onSuccess(HttpResponse response);
public void onException(Exception exception);
}
class HttpRequestResponseHandler {
private ActionItem action;
private HttpRequestResponse hrr;
private Executor executor;
public enum ActionItem {
LOGIN_REQUEST ,
REGISTRATION_REQUEST
}
public HttpRequestResponseHandler(ActionItem action, HttpRequestResponse hrr) {
this.action = action;
this.hrr = hrr;
}
public void execute(){
executor = new Executor();
executor.execute();
}
private class Executor extends AsyncTask<Void,Void,Void> {
#Override
public Void doInBackground() {
switch(action) {
case LOGIN_REQUEST : doLogin();
break;
case REGISTRATION_REQUEST : doRegistration();
break;
}
}
}
private void doLogin() {
HttpResponse response = null;
Exception exception = null;
try {
response = makeHttpRequestHere();
} catch (Exception e) {
exception = e;
}
if(exception != null) {
hrr.onException(exception);
} else {
hrr.onSuccess(response);
}
}
}
Now in somewhere in your activity code file do like this.
HttpRequestResponse hrr = new HttpRequestResponse(){
#Override
public void onSuccess(HttpResponse response) {
hideProgressDialog();
handleResponse(response);
}
#Override
public void onException(Exception exception) {
hideProgressDialog();
showErrorDialog(exception.getMessage());
}
}
HttpRequestResponseHandler hrrh = new HttpRequestResponseHandler(ActionItem.LOGIN_REQUEST,hrr);
hrrh.execute();
showProgressDialog();
Hope all this lead to what you want.
Its been a long answer and took quite a effort of mine to figure. :)
why not just using AsyncTask.THREAD_POOL_EXECUTOR(Runnable run);
It wraps a thread pool based executor of #cores + 1 parallelity level.
Then you can simply invoke:
AsyncTask.THREAD_POOL_EXECUTOR(new Runnable(){
public void run(){
doLogin();
});

Categories

Resources