在AsyncTask中使用wait

49

在使用AsyncTask时,如果使用wait,我会收到ERROR/AndroidRuntime(24230): Caused by: java.lang.IllegalMonitorStateException: object not locked by thread before wait()的错误提示。

是否可以仅使用Asynctask进行等待?怎样做?

谢谢

class WaitSplash extends AsyncTask<Void, Void, Void> {
    protected Void doInBackground(Void... params) {
        try {
            wait(MIN_SPLASH_DURATION);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }       

    protected void onPostExecute() {
        waitSplashFinished = true;
        finished();
    }
}  

我意识到这里的启动画面只是一个例子,但如果有人想要为启动画面做这件事,请考虑用户体验以及是否真的有必要为了一些静态图像而延迟用户使用应用程序的能力。请参考此处 - Alex Peters
6个回答

98

请注意,取消AsyncTask时可能会产生副作用 链接 - Samuel
7
如果AsyncTask被取消,Thread.sleep()应该抛出InterruptedException。 - Jules
3
在Honeycomb(Android 3.0)之后,使用AsyncTask运行任务的其他程序也会被阻塞吗?因为它们都使用同一个线程。 - mskw
@mskw 是的,但听起来他想让他的UI线程追上来。 - Joshua Pinter
此外,您可以使用executeOnExecutor(java.util.concurrent.Executor,Params ...),并将THREAD_POOL_EXECUTOR用作第一个参数,以明确指定要在线程池中执行而不是单个线程中执行。这将提供类似于Honeycomb之前的行为。但请注意并行执行可能会产生副作用! - olik79

28

您可以使用Thread.sleep方法。

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

请参阅上面接受的答案中的评论。这样做有很大的缺点,特别是如果您还有其他AsyncTasks在运行 - 它也会使它们休眠。 - Andy Weinstein

7
@Override
        protected String doInBackground(String... params) {
            // TODO Auto-generated method stub
            try {
                Thread.currentThread();
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;

        }

3

如果您只是想推迟执行一个方法一段时间,一个好的选择是Handler.postDelayed()

定义处理程序和可运行对象...

private Handler handler = new Handler();
private Runnable runnable = new Runnable() {        
    finished();
};

并延迟执行...

handler.postDelayed(runnable, MIN_SPLASH_DURATION);

我正在显示一个启动画面的同时下载一些数据。当数据加载完成后,我想要启动一个活动,等待3秒钟后再启动。我的想法是使用两个异步任务线程,一个用于下载数据,另一个只需等待3秒钟,在两个onPostExecute方法中设置一个布尔值为true,并调用一个检查两个线程是否都已完成的方法。 - jul
我会创建一个带有AIDL的服务,完全独立于启动Activity代码和导航到下一个Activity所使用的代码,以下载数据。您想要延迟3秒钟,但如果数据下载时间略长于3秒钟,您不想取消数据下载。处理这种情况的唯一方法是将数据获取与Activities解耦。 - Rich
1
我认为为这个任务创建一个服务有些夸张了。我建议将AsyncTask与Splash活动解耦,并实现某种观察者模式,以便等待AsyncTask完成的下一个活动得到通知。 - Flo
@Rich:你不应该在runnable内部实现run方法吗?此外,在异步任务的doInBackground中使用handler会给我带来错误-"无法在未调用Looper.prepare()的线程中创建handler"。 - Basher51
1
@Basher51,是的,Runnable只是伪代码。完成的方法将在run方法内调用。您遇到的错误是因为Handler需要在UI线程上创建。如果您的AsyncTask是活动内部类,则只需将Handler作用域限定为Activity并在那里实例化即可。如果不是,则可能需要其他解决方案。也许可以将侦听器附加到AsyncTask,以便回调到Activity或类似的东西。 - Rich

1
请使用线程来实现。
public class SplashActivity extends Activity{

int splashTime = 5000;
private Thread splashThread;
private Context mContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    this.mContext = this;
    setContentView(R.layout.splash_layout);
    splashThread = new Thread(){
        public void run() {
            try{
                synchronized (this) {
                    wait(splashTime);
                }
            }catch(InterruptedException ex){
                ex.printStackTrace();
            }finally{
                Intent i = new Intent(mContext,LocationDemo.class);
                startActivity(i);
                stop();
            }
        }
    };

    splashThread.start();
}

public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        synchronized (splashThread) {
            splashThread.notifyAll();
        }
    }
    return true;
}

在触摸事件中,线程会得到通知.. 可根据您的需要进行更改。


你好!在 onTouchEvent 中的 splashThread.notifyAll() 之前是否需要加上 synchronized (splashThread) - Kobor42
是的。必须对wait()和notify()或notifyAll()进行同步调用。您还应该设置一个标志来检查两个事件的发生顺序是否与您期望的顺序不同(即,您应该在同步块内拥有类似“if(!splashDismissed)wait(splashTime);”和“splashDismissed = true; splashThread.notifyAll(); ”,否则很有可能由于某种原因初始化闪屏线程被延迟,并且notifyAll()调用实际上首先发生)。 - Jules

0

你可以使用asyntask和wait()来处理这个问题。

public class yourAsynctask extends AsyncTask<Void, Void, Void> {
    public boolean inWait;
    public boolean stopWork; 

    @Override
    protected void onPreExecute() {
        inWait = false;
        stopWork = false;
    }

    @Override
    protected Void doInBackground(Void... params) {
        synchronized (this) {
            while(true) {
                if(stopWork) return null;
                if(youHaveWork) {
                    //make some
                } else {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    public void mynotify() {
        synchronized (this) {
            if(inWait) {
                notify();
                inWait = false;
            }
        }
    }

    public void setStopWork() {
        synchronized (this) {
            stopWork = false;
            if(inWait) {
                notify();
                inWait = false;
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接