简体   繁体   中英

how can an AsyncTask wait for another without deadLock

I'm trying to implement 2 Async Tasks where one should wait for the other to finish.

I make a test case for it, that looks like this:

public class MainActivity extends Activity{

    public static boolean SYNCING = false;
    public static Object LOCK = new Object();

    public static void setSync(){
        Log.i(TAG, "setting Sync=true");
        synchronized (LOCK) {
            SYNCING = true;
        }
    }

    public static void waitForSync(){
        Log.i(TAG, "waiting for Sync");
        synchronized (LOCK) {
            try{
                while (SYNCING){
                    LOCK.wait();
                }
            } catch (InterruptedException ignore){}
        }
    }

    public static void setSyncFinish(){
        Log.i(TAG, "setSyncFinish to notify all");
        synchronized (LOCK) {
            SYNCING = false;
            LOCK.notifyAll();
        }
    }

    public class WaitingOnLoaderTask extends AsyncTask<Void, Void, Void>{

        @Override
        protected Void doInBackground(Void... params) {
            Log.i(TAG, "doInBackground of WaitingForLoaderTask");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {

            }

            waitForSync();
            Log.i(TAG, "finishedWaiting!");
            return null;
        }

    }

    public class LoaderTask extends AsyncTask<Void, Void, Void>{

        long mRuntime;
        int mTimes;

        public LoaderTask(long runtime, int times){
            mRuntime = runtime;
            mTimes = times;
        }

        @Override
        protected Void doInBackground(Void... params) {
            while(mTimes >= 0){
                Log.i(TAG, "sleeping for " + mTimes + " times for " + mRuntime + " milliseconds");
                mTimes--;
                try {
                    Thread.sleep(mRuntime);
                } catch (InterruptedException e) {
                    Log.e("MainActivity", "Interrupted", e);
                }
            }

            setSyncFinish();

            return null;
        }

    }
}

But I can't get the WaitingOnLoaderTask to wait correctly. if I run this in onCreate

new WaitingOnLoaderTask().execute((Void[])null);
new LoaderTask(1000, 5).execute((Void[])null);

I get a logOutput like this (a DeadLock happens):

03-08 18:17:23.309: I/myUniqueLogTag(4432): setting Sync=true
03-08 18:17:23.317: I/myUniqueLogTag(4432): doInBackground of WaitingForLoaderTask
03-08 18:17:23.817: I/myUniqueLogTag(4432): waiting for Sync

and on

new LoaderTask(1000, 5).execute((Void[])null);
new WaitingOnLoaderTask().execute((Void[])null);

i get a Log like this:

03-08 18:18:53.082: I/myUniqueLogTag(4621): sleeping for 5 times for 1000 milliseconds
03-08 18:18:54.082: I/myUniqueLogTag(4621): sleeping for 4 times for 1000 milliseconds
03-08 18:18:55.090: I/myUniqueLogTag(4621): sleeping for 3 times for 1000 milliseconds
03-08 18:18:56.082: I/myUniqueLogTag(4621): sleeping for 2 times for 1000 milliseconds
03-08 18:18:57.090: I/myUniqueLogTag(4621): sleeping for 1 times for 1000 milliseconds
03-08 18:18:58.106: I/myUniqueLogTag(4621): sleeping for 0 times for 1000 milliseconds
03-08 18:18:59.113: I/myUniqueLogTag(4621): doInBackground of WaitingForLoaderTask
03-08 18:18:59.113: I/myUniqueLogTag(4621): setSyncFinish to notify all
03-08 18:18:59.621: I/myUniqueLogTag(4621): waiting for Sync
03-08 18:18:59.621: I/myUniqueLogTag(4621): finishedWaiting!

so I guess one is preventing the other to run. Did they compute their doInBackground actually in the same Thread, and LOCK.wait() blocks this Thread from executing further?

Is there a way to fix it (and stay compatible to Android 2.2)?

Note: the second Time is basically what I want, but in other Task implementations I cannot be shure that WaitingOnLoaderTask is only executed when the other one has finished.

Your best bet would be to use Geobits suggestion.

You can use the onPostExecute() function to call the next Async task so it will perform it:

class WaitingOnLoaderTask extends AsyncTask<Void,Void,Void>
{
    protected void onPreExecute(Void)
    {
        super.onPreExecute();
    }

    protected void doInBackground(Void... params)
    {
        //Commands for first AsyncTask
    }

    protected void onPostExecute(Void)
    {
        //Call AsyncTask 2
    }
}

That way it will wait until Task 1 completes.

Your code works as expected on Android 3.2 and lower. On higher Android versions AsyncTasks are not executed parallel try this to make it work on Android 4+:

LoaderTask loaderTask = new LoaderTask(1000, 5);
WaitingOnLoaderTask waitingTask = new WaitingOnLoaderTask();
if (Build.VERSION.SDK_INT >= 14) {
    loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    waitingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
    loaderTask.execute();
    waitingTask.execute();
}

Note: You should mark your AsyncTasks as static to avoid leaking Activity on configuration change (eg rotation).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM