I have a single Thread, which should execute some functions after one function is completed (Function 1 => Function 2 => Function 3).
My code
public class Foo () {
....
/* Some android stuff here */
myTestThread Foo = new myTestThread();
Foo.execute();
....
public void function1 () {
/* Some code stuff */
}
public void function2 () {
/* Some code stuff */
}
public class myTestThread extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void v) {
}
#Override
protected Void doInBackground(Void... params) {
function1();
function2();
}
}
}
As fair as I know, a single thread runs parallel to an existing programme code, so the myTestThread should run parallel. But I want to call the functions (function1, function2) AFTER the first one is finished. At the moment, both functions are called to the same time.
So what is my mistake?
See my comment: I had called close and disconnect but close is enough. Now, it seems to work. Shame on me :(
Related
I am an iOS developer who just recently tried Android development.
In iOS I use Completion Handlers in my codes.
I am wondering if there is an equivalent of it in Android development?
Thank you
If you need it for doing asynchronous operations then look into AsyncTask - this is a class where you implement doInBackground where your long operation is performed and onPostExecute method where code that is suppose to update UI is performed.
Now if you want to pass some special code to your AsyncTask to be performed after long operation you can:
(1) Pass an interface which would be implemented by your Activity/fragment, ex:
// Psedocode to reduce size!
interface MyInterface {
void doWork();
};
class MyAsyncTask extends AsyncTask<Void,Void,Void> {
MyInterface oper;
public MyAsyncTask(MyInterface op) { oper = op; }
// ..
public onPostExecute(Void res) {
oper.doWork(); // you could pass results here
}
}
class MyActivity extends Activity implements MyInterface {
public void doWork() {
// ...
}
public void startWork() {
// execute async on this
new MyAsyncTask(this).execute();
// or execute on anynomous interface implementation
new MyAsyncTask(new MyInterface() {
public void doWork() {
//MyActivity.this.updateUI() ...
}
});
}
};
(2) Use local broadcast receivers, EventBus, but those are more heavy weight solutions.
(3) If you already have some callback interface in you backgroung worker code then you can make it execute on UI thread using this code:
// This can be executed on back thread
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// do work on UI
}
});
For my app I want to disable/change a specific button that is pressed.
I have an onclick method called btnClicked which simplified looks like this:
Public class MainActivity extends Activity{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState) {
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi(){
// run querys
if(succesullyCalledApi){
vibrator.vibrate(500);
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
In the callApi method is a vibrate method which vibrates after the function gets a result.
Also if there is a result in the callApi myButton will be enabled and the text changed to search.
What happens is the following:
I click on the button, the phone vibrates first and afterwards it changes its text.
my question.
Why did callApi / vibrate run before myBytton.setText ?
what NigelK said is true.
When you arrive in the btnClicked method all the instructions are made on the UI thread. Therefore when you ask the System to vibrate, it will be blocked for XX time depending on the time you passed to the method vibrator.vibrate(XX);.
In order to avoid this "freeze" you need to make the vibration on another Thread.
Here is what it will look like :
Public class MainActivity extends Activity
{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState)
{
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi()
{
// run querys
if(succesullyCalledApi)
{
// here you create and run the Thread.
// put anything you want to do inside the run method
new Thread(
new Runnable()
{
public void run()
{
// here you start the vibration
vibrator.vibrate(500);
}
}
).start();
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
And that's it. It will launch another Thread that will handle the vibration and not freeze your UI thread.
EDIT
Here is the AsyncTask version :
The three elements asked when you extend AsyncTask are :
The type of the parameters you pass to the doInBackground() method
The Type of the elements that are passed in the onProgressUpdate() method.
The Type of the element returned by the doInBackground() method that is also the parameter of the onPostExecute() method.
This is what it looks like :
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
public MyTask(Button button)
{
mButton = button;
}
// Here everything will run on a background Thread
protected Boolean doInBackground(Void... voids)
{
boolean succesullyCalledApi = false;
// do your long querys here
// ...
return succesullyCalledApi;
}
// Here everything will run on the UI Thread
protected void onProgressUpdate(Integer... progress) {
// here you can make some update to the UI like updating a
// progress bar
}
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
And in your callApi() method you only have to to this :
public void callApi()
{
new MyTask(myButton).execute();
}
EDIT 2
In order to retrieve the query back to your main Thread (or UI Thread) all you have to do is ... nothing.
You are in the UI Thread when the onPostExecute() method is called.
But I assume that you want to retrieve the query back to your MainActivity. To do so :
Pass MainActivity in parameter of MyTask constructor,
Create a method in MainActivity named processQuery() (or whatever you want),
Finally call this method in the onPostExecute() method.
Here are some snippets :
Public class MainActivity extends Activity
{
Button myBytton;
...
public void callApi()
{
// add this to the constructor
new MyTask(this, myButton).execute();
}
// I put String here but adapt it to your query Type.
public void processQuery(String query)
{
// process your query here.
}
}
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
private MainActivity mMainActivity;
public MyTask(MainActivity mainActivity, Button button)
{
mButton = button;
mMainActivity = mainActivity;
}
...
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
// process your query
mMainActivity.processQuery("THE QUERY YOUR WANT TO PROCESS");
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
There probably is a better way to do this but this one is simple and work :)
Hope it helps.
Cheers
This is because your call to the API is being done on the UI thread. Even though you have made changes to the UI, the screen won't refresh until the processing invoked from the button clicked event completes. Call your API on a new thread or via an Async Task to get the behaviour you want.
Because you are doing all stuff at the UI Thread. You must use an AsyncTask for your long running operations.
Try below implementation:
public void callApi() {
MyTask myTask = new MyTask();
myTask.execute();
}
private class MyTask extends AsyncTask<Void, Void, Boolean> {
protected void doInBackground(Void... params) {
// This runs on a separate background thread
boolean succesullyCalledApi = false;
// run querys
// do your long running query here and return its result.
return succesullyCalledApi;
}
protected void onPostExecute(Boolean succesullyCalledApi) {
// this runs on UI Thread
if(succesullyCalledApi){
vibrator.vibrate(500);
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
} else {
// You should better think this part also. what will happen if result is false?
}
}
}
I have an async task that loads image urls from server.After loading urls than i load the images one by one through another asynctask.
On the click of a button i start the first asynctask
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
// line no 2
}
After i get the urls i use another async task to load images.
How can i find out when the image urls have been loaded and i can call the second async task at line no 2.
if i use a boolean variable which i toggle in the onpostexecute
#Override
protected void onPostExecute() {
urlgot=true;
}
then i shall have to use some repeating loop inside getit method at line no 2 to check the status of this variable urlgot. but it may take more time than allowed for ui thread.
Can there be a more cleaner method to do this check.
thanks
There are two solutions I can think of:
1) You create one AsyncTask that does everything (getting the urls, and downloading all images). Than you know exactly when to start downloading the images.
2) You start the next AsyncTask from the onPostExecute() of the first AsyncTask.
You won't be able to do your next piece of work in //line no 2 without defeating the purpose of AsyncTask. If you're doing network activity, you need to be doing it asynchronously, so that's not an option.
Instead, in onPostExecute() you can call another method in your activity that does what you would have done in //line no 2. This is safe to do, because onPostExecute() happens on the UI thread.
But depending on your design, it might make more sense to do all the //line no 2 stuff in your original AysncTask in onPostExecute, so you only have one task doing all of the work.
Use a Handler. In the method onPostExecute of your AsyncTask you can send a message informing the Handler to start another AsyncTask.
Something like this:
#Override
protected void onPostExecute(Void res) {
MyHandlerHandler handler = new MyHandlerHandler();
Message msg = new Message();
msg.what = MyHandler.TASK_FINISHED;
handler.sendMessage(msg);
}
And in your Handler class:
public class MyHandlerHandler extends Handler {
public static final int TASK_FINISHED = 2;
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TASK_FINISHED:
new MyAsyncTask().execute();
break;
}
}
}
instead of putting line 2 in getIt, put it in onPostExecute like below :
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
}
#Override
protected void onPostExecute() {
// line 2
}
I use a custom interface to do stuff after execution.
public Interface OnDataReceived
{
public void onReceive( Object result);
}
and on MyASyncTask
public class MyAsyncTask extends ASyncTask<Object,Object,Object>
{
OnDataReceived receiver;
public MyAsyncTask( OnDataReceived receiver )
{
this.receiver = receiver;
}
...
protected void onPostExecute( Object result)
{
receiver.onreceive( result );
}
}
and let my main class implement OnDataReceived
public class Main implements OnDataReceived
{
....
public void getit(View v)
{
new MyAsyncTask(this).execute("http://10.0.2.2/geturls.php");
}
#override
public void onReceive( Object result)
{
// do whatever
}
}
EDIT
Even for more control you can add onFailed and rename your interface to OnResponse
public Interface OnResponse
{
public void onReceive( Object result);
public void onFailed( Object errcode);
}
I'm having an issue with AsyncTask and onPostExecute. I am finding that onPostExecute is executing on a different thread than the main ui thread, which is causing a CalledFromWrongThreadException to happen when I modify any views.
I put in some logging to see what threads onPreExecute, doInBackground, and onPostExecute are running on. I would see a result like this...
onPreExecute ThreadId: 1
doInBackground ThreadId: 25
onPostExecute ThreadId: 18
I believe the main ui thread id is 1 and I would expect both onPre and onPost to both execute on thread 1. I am making sure to create and also call the execute method from the ui thread (for example in onCreate of an Activity).
Another thing to note that I have noticed is that later async tasks will run their onPostExecute method on the same thread as previous async task onPostExecute methods (in this case thread 18).
Right now in order to get around this I am wrapping the code in my onPostExecute methods in a call to runOnUiThread, but I think this is hacky and would like to get to the real issue.
I am out of ideas! Any one have any insight? I'm happy to answer any questions that could helper with further investigation!
EDIT:
There are two ways that async tasks are being run in the code. I am wondering if the latter in these examples is causing something weird to happen?
public class SomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
new SomeAsyncTask().execute();
}
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> {
#Override
public void onPreExecute() {
Thread.currentThread().getId() // 1
//Show a dialog
}
#Override
public Integer doInBackground(String... params) {
Thread.currentThread().getId() // 25
return 0;
}
#Override
public void onPostExecute(Integer result) {
Thread.currentThread().getId() // 18
//hide dialog
//update text view -> CalledFromWrongThreadException!!!
}
}
}
The above seems like a vanilla use of AsyncTask, but I still see this issue occurring even in simple cases like this. The next example uses an async task to run other async tasks. Maybe there is something I don't know about what happens when an async task gets constructed that is causing some weird behavior?
public class SomeActivity extends Activity implements TaskRunner.OnFinishListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
TaskRunner taskRunner = new TaskRunner();
taskRunner.setOnFinishListener(this);
taskRunner.addTask(new SingleTask());
taskRunner.addTask(new SingleTask());
taskRunner.execute();
}
#Override
public void onTaskFinish(List<Integer> results) {
//Thread id is 18 when it should be 1
//do something to a view - CalledFromWrongThreadException!!
}
}
//In a different file
public class SingleTask extends AsyncTask<String, Void, Integer> {
//This is a an async task so we can run it separately as an asynctask
//Or run it on whatever thread runnerExecute is called on
#Override
public Integer doInBackground(String... params) {
return runnerExecute(params);
}
//Can be called outside of doInBackground
public Integer runnerExecute(String... params) {
//some long running task
return 0;
}
}
//In a different file
public class TaskRunner {
private List<SingleTask> tasks;
private OnFinishListener onFinishListener;
public interface OnFinishListener {
public void onTaskFinish(List<Integer> results);
}
public TaskRunner() {
this.tasks = new ArrayList<SingleTask>();
}
public void setOnFinishListener(OnFinishListener listener) {
this.onFinishListener = listener;
}
public void addTask(SingleTask task) {
tasks.add(task);
}
public void executeTasks() {
new RunnerTask().execute((SingleTask[]) tasks.toArray());
}
//Calls the runnerExecute method on each SingleTask
private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> {
#Override
public void onPreExecute() {
//Runs on thread 1
}
#Override
public List<Integer> doInBackground(SingleTask... params) {
//Runs on arbitrary thread
List<Integer> results = new ArrayList<Integer>();
for(SingleTask task : params) {
int result =task.runnerExecute(task.getParams());
results.add(result);
}
return results;
}
#Override
public void onPostExecute(List<Integer> results) {
//Runs on thread 18
onFinishListener.onTaskFinish(results);
}
}
}
Maybe what is going on here is just super weird, and not at all how async tasks are meant to be used, either way it would be nice to get to the bottom of the issue.
Let me know if you need any more context.
I have been experiencing the same problem and it turned out the the issue was using Flurry 3.2.1. However, the issue is not limited to the Flurry library.
The issue behind the scenes is having the first ever (when the app is loaded for the first time) AsyncTask call from a looper thread which is not the Main UI thread. This call initializes a sHandler static variable in AsyncTask to the wrong thread id, and this id is then used in all subsequent AsyncTask$onPostExecute() calls.
To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.
try using:
getBaseContext().runOnUiThread(new Runnable()
{
#override
public void run()
{
}
});
and write your code inside the run function
The AsyncTask is designed to be used from the main thread. Your problem is the second case, and is that you call execute on the SingleTask from a background thread. You call it in the doInBackground method of RunnerTask. The onPostExecute is then run from the backgroundthread of RunnerTask
Two options for you.
1: Trash RunnerTask, and execute the SingleTasks from you main thread, they'll all run in parallell and you won't know which finishes first, but onPreExecute and onPostExecute is called on the main thread
2: Trash the SingleTask and define them as Runnables instead, then you can run them in sequence in the RunnerTask's doInBackground. They'll all run in the background thread of RunnerTask, in the order you call Run. When it is finished, the onPostExecute of RunnerTask is run on the main thread.
i just tried your code and onPreExecute and onPostExecute does run on the same thread, how do you output the thread id ? try:
Log.d("THREADTEST","PRE"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","BACKGROUND"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","POST"+Long.toString(Thread.currentThread().getId()));
P.S. it should be:
new SomeAsyncTask().execute();
and
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { ... }
you are actually executing the SingleTask from RunnerTask's doinbackground method which is incorrect as asynctask should be executed from a main thread only. You need to relook into the logic which runs the set of SingleTasks from RunnerTask.
I have a very simple AsyncTask implementation example and am having problem in testing it using Android JUnit framework.
It works just fine when I instantiate and execute it in normal application.
However when it's executed from any of Android Testing framework classes (i.e. AndroidTestCase, ActivityUnitTestCase, ActivityInstrumentationTestCase2 etc) it behaves strangely:
It executes doInBackground() method correctly
However it doesn't invokes any of its notification methods (onPostExecute(), onProgressUpdate(), etc) -- just silently ignores them without showing any errors.
This is very simple AsyncTask example:
package kroz.andcookbook.threads.asynctask;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.Toast;
public class AsyncTaskDemo extends AsyncTask<Integer, Integer, String> {
AsyncTaskDemoActivity _parentActivity;
int _counter;
int _maxCount;
public AsyncTaskDemo(AsyncTaskDemoActivity asyncTaskDemoActivity) {
_parentActivity = asyncTaskDemoActivity;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
_parentActivity._progressBar.setVisibility(ProgressBar.VISIBLE);
_parentActivity._progressBar.invalidate();
}
#Override
protected String doInBackground(Integer... params) {
_maxCount = params[0];
for (_counter = 0; _counter <= _maxCount; _counter++) {
try {
Thread.sleep(1000);
publishProgress(_counter);
} catch (InterruptedException e) {
// Ignore
}
}
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress = values[0];
String progressStr = "Counting " + progress + " out of " + _maxCount;
_parentActivity._textView.setText(progressStr);
_parentActivity._textView.invalidate();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
_parentActivity._progressBar.setVisibility(ProgressBar.INVISIBLE);
_parentActivity._progressBar.invalidate();
}
#Override
protected void onCancelled() {
super.onCancelled();
_parentActivity._textView.setText("Request to cancel AsyncTask");
}
}
This is a test case. Here AsyncTaskDemoActivity is a very simple Activity providing UI for testing AsyncTask in mode:
package kroz.andcookbook.test.threads.asynctask;
import java.util.concurrent.ExecutionException;
import kroz.andcookbook.R;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemo;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemoActivity;
import android.content.Intent;
import android.test.ActivityUnitTestCase;
import android.widget.Button;
public class AsyncTaskDemoTest2 extends ActivityUnitTestCase<AsyncTaskDemoActivity> {
AsyncTaskDemo _atask;
private Intent _startIntent;
public AsyncTaskDemoTest2() {
super(AsyncTaskDemoActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
_startIntent = new Intent(Intent.ACTION_MAIN);
}
protected void tearDown() throws Exception {
super.tearDown();
}
public final void testExecute() {
startActivity(_startIntent, null, null);
Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
btnStart.performClick();
assertNotNull(getActivity());
}
}
All this code is working just fine, except the fact that AsyncTask doesn't invoke it's notification methods when executed by within Android Testing Framework. Any ideas?
I met a similar problem while implementing some unit-test. I had to test some service which worked with Executors, and I needed to have my service callbacks sync-ed with the test methods from my ApplicationTestCase classes. Usually the test method itself finished before the callback would be accessed, so the data sent via the callbacks would not be tested. Tried applying the #UiThreadTest bust still didn't work.
I found the following method, which worked, and I still use it. I simply use CountDownLatch signal objects to implement the wait-notify (you can use synchronized(lock){... lock.notify();}, however this results in ugly code) mechanism.
public void testSomething(){
final CountDownLatch signal = new CountDownLatch(1);
Service.doSomething(new Callback() {
#Override
public void onResponse(){
// test response data
// assertEquals(..
// assertTrue(..
// etc
signal.countDown();// notify the count down latch
}
});
signal.await();// wait for callback
}
I found a lot of close answers but none of them put all the parts together correctly. So this is one correct implementation when using an android.os.AsyncTask in your JUnit tests cases.
/**
* This demonstrates how to test AsyncTasks in android JUnit. Below I used
* an in line implementation of a asyncTask, but in real life you would want
* to replace that with some task in your application.
* #throws Throwable
*/
public void testSomeAsynTask () throws Throwable {
// create a signal to let us know when our task is done.
final CountDownLatch signal = new CountDownLatch(1);
/* Just create an in line implementation of an asynctask. Note this
* would normally not be done, and is just here for completeness.
* You would just use the task you want to unit test in your project.
*/
final AsyncTask<String, Void, String> myTask = new AsyncTask<String, Void, String>() {
#Override
protected String doInBackground(String... arg0) {
//Do something meaningful.
return "something happened!";
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
/* This is the key, normally you would use some type of listener
* to notify your activity that the async call was finished.
*
* In your test method you would subscribe to that and signal
* from there instead.
*/
signal.countDown();
}
};
// Execute the async task on the UI thread! THIS IS KEY!
runTestOnUiThread(new Runnable() {
#Override
public void run() {
myTask.execute("Do something");
}
});
/* The testing thread will wait here until the UI thread releases it
* above with the countDown() or 30 seconds passes and it times out.
*/
signal.await(30, TimeUnit.SECONDS);
// The task is done, and now you can assert some things!
assertTrue("Happiness", true);
}
The way to deal with this is to run any code that invokes an AsyncTask in runTestOnUiThread():
public final void testExecute() {
startActivity(_startIntent, null, null);
runTestOnUiThread(new Runnable() {
public void run() {
Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
btnStart.performClick();
}
});
assertNotNull(getActivity());
// To wait for the AsyncTask to complete, you can safely call get() from the test thread
getActivity()._myAsyncTask.get();
assertTrue(asyncTaskRanCorrectly());
}
By default junit runs tests in a separate thread than the main application UI. AsyncTask's documentation says that the task instance and the call to execute() must be on the main UI thread; this is because AsyncTask depends on the main thread's Looper and MessageQueue for its internal handler to work properly.
NOTE:
I previously recommended using #UiThreadTest as a decorator on the test method to force the test to run on the main thread, but this isn't quite right for testing an AsyncTask because while your test method is running on the main thread no messages are processed on the main MessageQueue — including the messages the AsyncTask sends about its progress, causing your test to hang.
If you don't mind executing the AsyncTask in the caller thread (should be fine in case of Unit testing), you can use an Executor in the current thread as described in https://stackoverflow.com/a/6583868/1266123
public class CurrentThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
And then you run your AsyncTask in your unit test like this
myAsyncTask.executeOnExecutor(new CurrentThreadExecutor(), testParam);
This is only working for HoneyComb and higher.
I wrote enough unitests for Android and just want to share how to do that.
First off, here is helper class that responsible to wait and release waiter. Nothing special:
SyncronizeTalker
public class SyncronizeTalker {
public void doWait(long l){
synchronized(this){
try {
this.wait(l);
} catch(InterruptedException e) {
}
}
}
public void doNotify() {
synchronized(this) {
this.notify();
}
}
public void doWait() {
synchronized(this){
try {
this.wait();
} catch(InterruptedException e) {
}
}
}
}
Next, lets create interface with one method that should be called from AsyncTask when work is done. Sure we also want to test our results:
TestTaskItf
public interface TestTaskItf {
public void onDone(ArrayList<Integer> list); // dummy data
}
Next lets create some skeleton of our Task that we gonna test:
public class SomeTask extends AsyncTask<Void, Void, SomeItem> {
private ArrayList<Integer> data = new ArrayList<Integer>();
private WmTestTaskItf mInter = null;// for tests only
public WmBuildGroupsTask(Context context, WmTestTaskItf inter) {
super();
this.mContext = context;
this.mInter = inter;
}
#Override
protected SomeItem doInBackground(Void... params) { /* .... job ... */}
#Override
protected void onPostExecute(SomeItem item) {
// ....
if(this.mInter != null){ // aka test mode
this.mInter.onDone(data); // tell to unitest that we finished
}
}
}
At last - our unitest class:
TestBuildGroupTask
public class TestBuildGroupTask extends AndroidTestCase implements WmTestTaskItf{
private SyncronizeTalker async = null;
public void setUP() throws Exception{
super.setUp();
}
public void tearDown() throws Exception{
super.tearDown();
}
public void test____Run(){
mContext = getContext();
assertNotNull(mContext);
async = new SyncronizeTalker();
WmTestTaskItf me = this;
SomeTask task = new SomeTask(mContext, me);
task.execute();
async.doWait(); // <--- wait till "async.doNotify()" is called
}
#Override
public void onDone(ArrayList<Integer> list) {
assertNotNull(list);
// run other validations here
async.doNotify(); // release "async.doWait()" (on this step the unitest is finished)
}
}
That's all.
Hope it will help to someone.
This can be used if you want to test the result from the doInBackground method. Override the onPostExecute method and perform the tests there. To wait for the AsyncTask to complete use CountDownLatch. The latch.await() waits till the countdown runs from 1 (which is set during initialization) to 0 (which is done by the countdown() method).
#RunWith(AndroidJUnit4.class)
public class EndpointsAsyncTaskTest {
Context context;
#Test
public void testVerifyJoke() throws InterruptedException {
assertTrue(true);
final CountDownLatch latch = new CountDownLatch(1);
context = InstrumentationRegistry.getContext();
EndpointsAsyncTask testTask = new EndpointsAsyncTask() {
#Override
protected void onPostExecute(String result) {
assertNotNull(result);
if (result != null){
assertTrue(result.length() > 0);
latch.countDown();
}
}
};
testTask.execute(context);
latch.await();
}
How about using join?
fun myTest() = runBlocking {
CoroutineScope(Dispatchers.IO).launch {
// test something here
}.join()
}
Use this simple solution
runBlocking{
//Your code here
}
Most of those solutions require a lot of code to be written for every test or to change your class structure. Which I find very difficult to use if you have many situations under test or many AsyncTasks on your project.
There is a library which eases the process of testing AsyncTask. Example:
#Test
public void makeGETRequest(){
...
myAsyncTaskInstance.execute(...);
AsyncTaskTest.build(myAsyncTaskInstance).
run(new AsyncTest() {
#Override
public void test(Object result) {
Assert.assertEquals(200, (Integer)result);
}
});
}
}
Basically, it runs your AsyncTask and test the result it returns after the postComplete() has been called.