简体   繁体   English

如果Activity在后台运行,则处理AsyncTask

[英]Handle AsyncTask if the task completes while the Activity is in the background

I've been using AsyncTasks for a while however, I've recently encountered a scenario where I'm unsure of how to handle correctly. 我使用AsyncTasks已经有一段时间了,但是最近我遇到了不确定如何正确处理的情况。 Since I thought it would be a somewhat common scenario I decided to ask the question here. 由于我认为这将是一个比较常见的情况,因此我决定在这里提出问题。

So, I'm trying to use an AsyncTask to make a simple call to sign a user in to the app. 因此,我尝试使用AsyncTask进行简单调用以使用户登录到该应用程序。 After the call completes, if it succeeds, the user should be taken to another activity. 呼叫完成后,如果成功,则应将用户带到另一个活动。 This logic is simple. 这个逻辑很简单。 The problem arrises when the user navigates away from the app before the sign in call returns. 当用户在登录呼叫返回之前离开应用程序时,问题就出现了。 In such a case, what should I do in onPostExecute() ? 在这种情况下,我应该在onPostExecute()做什么?

What I've seen some apps do is they continue with the call anyways, as long as the activity is still around, and will launch the next activity. 我所看到的一些应用程序所做的是,只要活动仍在进行中,它们仍将继续通话,并将启动下一个活动。 However this creates a weird experience where the user navigates away from the app, then several seconds later, the app just pops back up in their face. 但是,这会产生一种奇怪的体验,即用户导航离开该应用程序,然后在几秒钟后,该应用程序又突然弹出来。 Of course, I would like to avoid doing this. 当然,我想避免这样做。

Update Example code: 更新示例代码:

public class ExampleActivity extends Activity {
    private boolean mIsPaused;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
        btnSignIn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new SignInTask(ExampleActivity.this).execute();
            }
        });
        ...
    }

    @Override
    protected void onPause() {
        super.onPause();
        mIsPaused = true;
    }

    @Override
    protected void onResume() {
        super.onResume();
        mIsPaused = false;
    }

    private boolean isPaused() {
        return mIsPaused;
    }

    ...
    private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {

        private final WeakReference<ExampleActivity> mAct;

        public SignInTask(ExampleActivity act) {
            mAct = new WeakReference<ExampleActivity>(act);
        }

        @Override
        protected SomeResult doInBackground(Void... params) {
            return mApi.signIn(creds);
        }

        @Override
        protected void onPostExecute(SomeResult result) {
            if (result.getCode() == OK) {
                ExampleActivity act = mAct.get();
                if (act != null) {
                    if (act.isPaused()) {
                        // do something
                    } else {
                        startActivity(new Intent(act, NextActivity.class));
                    }
                } else {
                    // do something
                }
            }

        }
    }
}

使您的AsyncTask类成为静态内部类。

Pretty interesting problem... Going with what you've started by using booleans, you could save the response the Activity receives to the SharedPreferences in the event it is paused, or continue processing normally if it is not. 一个非常有趣的问题...从使用布尔值开始,您可以将Activity暂停时收到的响应保存到SharedPreferences中,如果没有,则继续正常处理。 If the Activity later resumes (or is recreated), check whether or not there is a saved response and handle accordingly. 如果活动稍后恢复(或重新创建),请检查是否存在已保存的响应并进行相应处理。 I was thinking something along the lines of: 我在考虑以下方面:

import org.json.JSONObject;

import android.app.Activity;
import android.os.Bundle;

public class TaskActivity extends Activity {

    private static final String KEY_RESPONSE_JSON = "returned_response";

    private boolean paused = false;

    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        // don't setup here, wait for onPostResume() to figure out what to do
    }

    @Override
    public void onPostResume(){
        super.onPostResume();
        paused = false;

        if(isSavedResponseAvailable()) processResponse(getSavedResponse());
        else setup();
    }

    @Override
    public void onPause(){
        paused = true;
        super.onPause();
    }

    private void setup(){ 
        // normal setup
    }

    public void onReceiveResponse(JSONObject response){
        if(paused) setSavedResponse(response);
        else processResponse(response); 
    }

    private void processResponse(JSONObject response){
        // Continue with processing as if they never left

        getSharedPreferences(this.getClass().getName(), 0).edit().clear().commit(); // Clear everything so re-entering won't parse old data
    }   

    private boolean isSavedResponseAvailable(){
        return getSavedResponse() != null;
    }

    private JSONObject getSavedResponse(){
        try{
            return new JSONObject(getSharedPreferences(this.getClass().getName(), 0).getString(KEY_RESPONSE_JSON, ""));
        }
        catch(Exception e){ }
        return null;
    }

    private void setSavedResponse(JSONObject response){
        getSharedPreferences(this.getClass().getName(), 0).edit().putString(KEY_RESPONSE_JSON, response.toString()).commit();
    }
}

Clearly that's assuming your response from the task is JSON, but there's no reason you couldn't extend that to save the data individually and rebuild the necessary response object from the saved preference data. 显然,这是假设您对任务的响应是JSON,但是没有理由您不能扩展它以单独保存数据并从保存的首选项数据中重建必要的响应对象。

As far as clean approaches go, though... I give this about a 3/10, but I can't think of anything better (well, other than making the TaskActivity abstract and forcing implementations to override setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse() , but that would only be mildly better for like a 4/10) 就干净的方法而言,虽然...我给出了3/10,但我想不到更好的方法了(好吧,除了使TaskActivity抽象化并强制实现重写setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse() ,但对于4/10而言,效果会稍好一些)

I would suggest putting a try/catch statement in the post execute - as far as I know what would happen in this situation is that you would get some kind of Window Manager exception. 我建议在执行后添加一条try / catch语句-据我所知,在这种情况下会发生某种窗口管理器异常的情况。

What I would STRONGLY recommend, however, is stopping any async tasks (with the cancel method) on the onPause method, meaning that you won't interrupt them. 但是,我强烈建议您在onPause方法上停止所有异步任务(使用cancel方法),这意味着您不会中断它们。

http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean) http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)

public final boolean cancel (boolean mayInterruptIfRunning) public final布尔取消(boolean mayInterruptIfRunning)

Added in API level 3 Attempts to cancel execution of this task. API级别3中已添加尝试取消执行此任务。 This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. 如果任务已经完成,已经被取消或由于某些其他原因而无法取消,则此尝试将失败。 If successful, and this task has not started when cancel is called, this task should never run. 如果成功,并且在调用cancel时此任务尚未开始,则该任务永远不要运行。 If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task. 如果任务已经开始,则mayInterruptIfRunning参数确定是否应中断执行该任务的线程以尝试停止该任务。

Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. 调用此方法将导致doInBackground(Object [])返回后,onCancelled(Object)在UI线程上被调用。 Calling this method guarantees that onPostExecute(Object) is never invoked. 调用此方法可确保永远不会调用onPostExecute(Object)。 After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible. 调用此方法后,应定期从doInBackground(Object [])检查isCancelled()返回的值,以尽早完成任务。

Parameters mayInterruptIfRunning 参数mayInterruptIfRunning
true if the thread executing this task should be interrupted; 如果应该中断执行此任务的线程,则为true;否则为false。 otherwise, in-progress tasks are allowed to complete. 否则,将允许正在进行的任务完成。 Returns false if the task could not be cancelled, typically because it has already completed normally; 如果无法取消任务(通常是因为它已经正常完成),则返回false;否则,返回false。 true otherwise See Also isCancelled() onCancelled(Object) 否则为true,另请参见isCancelled()onCancelled(Object)

boolean isRunning; //set it to true in onResume, and false in onStop
boolean isWaiting; // set it to true in onPostExecute, if "isRunning" is false

check in onResume whether isWaiting is true, if yes, take user to another screen. 检查onResume isWaiting是否为true,如果是,则将用户带到另一个屏幕。

   Use the cancel() of AsynchTask class onBackPress() of Activty class


 public class ExampleActivity extends Activity {
private boolean mIsPaused;
SignInTask singleTaskObj;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
    btnSignIn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
           singleTaskObj =  new SignInTask(ExampleActivity.this).execute();
        }
    });
    ...
}

@Override
protected void onPause() {
    super.onPause();
    mIsPaused = true;
}

@Override
protected void onResume() {
    super.onResume();
    mIsPaused = false;
}

protected void onBackPressed()
{
 singleTaskObj.cancel();
}

private boolean isPaused() {
    return mIsPaused;
}

...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {

    private final WeakReference<ExampleActivity> mAct;

    public SignInTask(ExampleActivity act) {
        mAct = new WeakReference<ExampleActivity>(act);
    }

    @Override
    protected SomeResult doInBackground(Void... params) {
        return mApi.signIn(creds);
    }

    @Override
    protected void onPostExecute(SomeResult result) {
        if (result.getCode() == OK) {
            ExampleActivity act = mAct.get();
            if (act != null) {
                if (act.isPaused()) {
                    // do something
                } else {
                    startActivity(new Intent(act, NextActivity.class));
                }
            } else {
                // do something
            }
        }

    }
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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