最佳实践:方向更改期间的AsyncTask


151

AsyncTask 在另一个线程中运行复杂的任务是一件很棒的事情。

但是,如果AsyncTask仍在运行时发生方向更改或其他配置更改,则电流将Activity被销毁并重新启动。并且由于的实例AsyncTask连接到该活动,因此失败并导致“强制关闭”消息窗口。

因此,我正在寻找某种“最佳实践”来避免这些错误并防止AsyncTask失败。

到目前为止,我所看到的是:

  • 禁用方向更改。(请确保不要使用此方法。)
  • 让任务继续存在并通过以下方式使用新的活动实例对其进行更新 onRetainNonConfigurationInstance
  • 只需在Activity销毁销毁任务时取消该任务,并Activity在再次创建销毁任务时重新启动它即可。
  • 将任务绑定到应用程序类而不是活动实例。
  • 在“货架”项目中使用的某些方法(通过onRestoreInstanceState)

一些代码示例:

屏幕旋转期间的Android AsyncTasks,第一部分第二部分

ShelvesActivity.java

您能帮助我找到最好的方法,该方法可以最好地解决问题并且也易于实施吗?代码本身也很重要,因为我不知道如何正确解决这个问题。



这来自Mark Murphy的Blog ... AsyncTask和ScreenRotation可能有帮助... 链接
Gopal

尽管这是一则旧文章,但 IMO却是一种更容易(更好)的方法。
DroidDev

我只是想知道为什么文档没有谈到如此琐碎的情况。
Sreekanth Karumanaghat

Answers:


140

难道使用android:configChanges来解决这个问题。这是非常不好的做法。

千万不要使用Activity#onRetainNonConfigurationInstance()两种。这不是模块化的,也不适合Fragment基于应用程序的应用程序。

您可以阅读我的文章,文章描述了如何使用reserved来处理配置更改Fragment。它AsyncTask很好地解决了保持旋转变化的问题。你基本上需要托管AsyncTaskFragment,通话setRetainInstance(true)Fragment,而报告AsyncTask的进度/结果返回给它Activity通过保留Fragment


26
好主意,但不是每个人都使用Fragments。在选择Fragments之前,有很多遗留代码。
Scott Biggs

14
@ScottBiggs片段可通过支持库从Android 1.6一直使用。您能否举一个仍在积极使用的遗留代码的示例,这些代码在使用支持库片段时会遇到麻烦?因为老实说,我认为这不是问题。
亚历克斯·洛克伍德

4
@tactoth我认为答案中并不需要解决这些问题,因为99.9%的人不再使用TabActivity。老实说,我不确定我们为什么还要谈论这个……每个人都同意Fragments是必经之路。:)
Alex Lockwood 2014年

2
如果必须从嵌套Fragment调用AsyncTask怎么办?
爱德华多·纳维达

3
@AlexLockwood-“每个人都同意,碎片是必经之路。” Squared的开发人员会不同意!
JBeckton 2015年

36

我通常通过让AsyncTasks在.onPostExecute()回调中广播Intent来解决此问题,因此它们不会修改直接启动它们的Activity。活动使用动态的BroadcastReceiver收听这些广播并采取相应的措施。

这样,AsyncTasks不必关心处理其结果的特定Activity实例。他们只是在完成时大声喊叫,并且如果某个活动在那个时间(处于活动状态且处于集中状态/处于恢复状态)对任务的结果感兴趣,那么它将被处理。

这涉及更多的开销,因为运行时需要处理广播,但是我通常不介意。我认为使用LocalBroadcastManager而不是默认的系统范围的速度会加快速度。


6
如果您可以在答案中添加示例,则将对您有所帮助
Sankar V

1
我认为这是在活动和片段之间提供较少耦合的解决方案
Roger Garzon Nieto

7
这可能是解决方案的一部分,但似乎无法解决方向更改后重新创建AsyncTask的问题。
miguel

4
如果您很不幸并且在广播期间没有任何活动怎么办?(即您处于中等旋转状态)
山姆

24

这是AsyncTask的另一个示例,该示例使用Fragment来处理的运行时配置更改(如用户旋转屏幕时)setRetainInstance(true)。还显示了确定的(定期更新)进度条。

该示例部分基于官方文档“ 在配置更改期间保留对象”

在此示例中,需要后台线程的工作仅是将图像从Internet加载到UI中。

Alex Lockwood似乎是对的,当涉及使用AsyncTasks使用“保留的片段”来处理运行时配置更改时,这是最佳实践。onRetainNonConfigurationInstance()在Android Studio中的Lint中被弃用。官方文档警告我们android:configChanges不要使用“自行处理配置更改” ...

自己处理配置更改会使使用替代资源变得更加困难,因为系统不会自动为您应用它们。当您必须避免由于配置更改而导致重启时,应将这种技术视为万不得已的方法,建议不要将其用于大多数应用程序。

然后是一个问题,即是否应该对后台线程完全使用AsyncTask。

AsyncTask官方参考警告...

理想情况下,应该将AsyncTasks用于较短的操作(最多几秒钟)。如果需要使线程长时间运行,则强烈建议您使用java.util.concurrent pacakge提供的各种API,例如执行程序,ThreadPoolExecutor和FutureTask。

或者,可以使用服务,加载程序(使用CursorLoader或AsyncTaskLoader)或内容提供程序来执行异步操作。

我将其余的帖子分解为:

  • 步骤; 和
  • 以上过程的所有代码。

步骤

  1. 从基本的AsyncTask开始,作为活动的内部类(它不一定是内部类,但可能很方便)。在此阶段,AsyncTask不处理运行时配置更改。

    public class ThreadsActivity extends ActionBarActivity {
    
        private ImageView mPictureImageView;
    
        private class LoadImageFromNetworkAsyncTask
                              extends AsyncTask<String, Void, Bitmap> {
    
            @Override
            protected Bitmap doInBackground(String... urls) {
                return loadImageFromNetwork(urls[0]);
            }
    
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                mPictureImageView.setImageBitmap(bitmap);
            }
        }
    
        /**
         * Requires in AndroidManifext.xml
         *  <uses-permission android:name="android.permission.INTERNET" />
         */
        private Bitmap loadImageFromNetwork(String url) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream((InputStream)
                                              new URL(url).getContent());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            mPictureImageView =
                (ImageView) findViewById(R.id.imageView_picture);
        }
    
        public void getPicture(View view) {
            new LoadImageFromNetworkAsyncTask()
                .execute("http://i.imgur.com/SikTbWe.jpg");
        }
    
    }
  2. 添加一个嵌套类RetainedFragment,该类扩展了Fragement类,并且没有自己的UI。将setRetainInstance(true)添加到此Fragment的onCreate事件中。提供设置和获取数据的过程。

    public class ThreadsActivity extends Activity {
    
        private ImageView mPictureImageView;
        private RetainedFragment mRetainedFragment = null;
        ...
    
        public static class RetainedFragment extends Fragment {
    
            private Bitmap mBitmap;
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
    
                // The key to making data survive
                // runtime configuration changes.
                setRetainInstance(true);
            }
    
            public Bitmap getData() {
                return this.mBitmap;
            }
    
            public void setData(Bitmap bitmapToRetain) {
                this.mBitmap = bitmapToRetain;
            }
        }
    
        private class LoadImageFromNetworkAsyncTask
                        extends AsyncTask<String, Integer,Bitmap> {
        ....
  3. 在最外层的Activity类的onCreate()中处理RetainedFragment:如果已经存在,则对其进行引用(以防Activity重新启动)。创建并添加它(如果不存在);然后,如果已经存在,请从RetainedFragment中获取数据,并使用该数据设置UI。

    public class ThreadsActivity extends Activity {
    
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            final String retainedFragmentTag = "RetainedFragmentTag";
    
            mPictureImageView =
                      (ImageView) findViewById(R.id.imageView_picture);
            mLoadingProgressBar =
                    (ProgressBar) findViewById(R.id.progressBar_loading);
    
            // Find the RetainedFragment on Activity restarts
            FragmentManager fm = getFragmentManager();
            // The RetainedFragment has no UI so we must
            // reference it with a tag.
            mRetainedFragment =
              (RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
    
            // if Retained Fragment doesn't exist create and add it.
            if (mRetainedFragment == null) {
    
                // Add the fragment
                mRetainedFragment = new RetainedFragment();
                fm.beginTransaction()
                    .add(mRetainedFragment, retainedFragmentTag).commit();
    
            // The Retained Fragment exists
            } else {
    
                mPictureImageView
                    .setImageBitmap(mRetainedFragment.getData());
            }
        }
  4. 从UI初始化AsyncTask

    public void getPicture(View view) {
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }
  5. 添加并编写一个确定的进度条:

    • 将进度条添加到UI布局;
    • 在Activity oncreate();中获得对它的引用;
    • 在过程的开始和结束时使其可见且不可见;
    • 定义进度以在onProgressUpdate中报告给UI。
    • 将AsyncTask 2nd Generic参数从Void更改为可以处理进度更新的类型(例如Integer)。
    • 在doInBackground()中的常规点处进行publishProgress。

以上过程的所有代码

活动布局。

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mysecondapp.ThreadsActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <ImageView
            android:id="@+id/imageView_picture"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="@android:color/black" />

        <Button
            android:id="@+id/button_get_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@id/imageView_picture"
            android:onClick="getPicture"
            android:text="Get Picture" />

        <Button
            android:id="@+id/button_clear_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/button_get_picture"
            android:layout_toEndOf="@id/button_get_picture"
            android:layout_toRightOf="@id/button_get_picture"
            android:onClick="clearPicture"
            android:text="Clear Picture" />

        <ProgressBar
            android:id="@+id/progressBar_loading"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/button_get_picture"
            android:progress="0"
            android:indeterminateOnly="false"
            android:visibility="invisible" />

    </RelativeLayout>
</ScrollView>

Activity具有:子类化的AsyncTask内部类;RetainedFragment内部类的子类,用于处理运行时配置更改(例如,当用户旋转屏幕时);并定期更新确定的进度条。...

public class ThreadsActivity extends Activity {

    private ImageView mPictureImageView;
    private RetainedFragment mRetainedFragment = null;
    private ProgressBar mLoadingProgressBar;

    public static class RetainedFragment extends Fragment {

        private Bitmap mBitmap;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // The key to making data survive runtime configuration changes.
            setRetainInstance(true);
        }

        public Bitmap getData() {
            return this.mBitmap;
        }

        public void setData(Bitmap bitmapToRetain) {
            this.mBitmap = bitmapToRetain;
        }
    }

    private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
            Integer, Bitmap> {

        @Override
        protected Bitmap doInBackground(String... urls) {
            // Simulate a burdensome load.
            int sleepSeconds = 4;
            for (int i = 1; i <= sleepSeconds; i++) {
                SystemClock.sleep(1000); // milliseconds
                publishProgress(i * 20); // Adjust for a scale to 100
            }

            return com.example.standardapplibrary.android.Network
                    .loadImageFromNetwork(
                    urls[0]);
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            mLoadingProgressBar.setProgress(progress[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            publishProgress(100);
            mRetainedFragment.setData(bitmap);
            mPictureImageView.setImageBitmap(bitmap);
            mLoadingProgressBar.setVisibility(View.INVISIBLE);
            publishProgress(0);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_threads);

        final String retainedFragmentTag = "RetainedFragmentTag";

        mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
        mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);

        // Find the RetainedFragment on Activity restarts
        FragmentManager fm = getFragmentManager();
        // The RetainedFragment has no UI so we must reference it with a tag.
        mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
                retainedFragmentTag);

        // if Retained Fragment doesn't exist create and add it.
        if (mRetainedFragment == null) {

            // Add the fragment
            mRetainedFragment = new RetainedFragment();
            fm.beginTransaction().add(mRetainedFragment,
                                      retainedFragmentTag).commit();

            // The Retained Fragment exists
        } else {

            mPictureImageView.setImageBitmap(mRetainedFragment.getData());
        }
    }

    public void getPicture(View view) {
        mLoadingProgressBar.setVisibility(View.VISIBLE);
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }

    public void clearPicture(View view) {
        mRetainedFragment.setData(null);
        mPictureImageView.setImageBitmap(null);
    }
}

在此示例中,库函数(上面使用显式包前缀com.example.standardapplibrary.android.Network引用)确实有效。

public static Bitmap loadImageFromNetwork(String url) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
                .getContent());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return bitmap;
}

将您的后台任务所需的任何权限添加到AndroidManifest.xml中。

<manifest>
...
    <uses-permission android:name="android.permission.INTERNET" />

将您的活动添加到AndroidManifest.xml ...

<manifest>
...
    <application>
        <activity
            android:name=".ThreadsActivity"
            android:label="@string/title_activity_threads"
            android:parentActivityName=".MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.mysecondapp.MainActivity" />
        </activity>

大。您应该为此写博客。
圆弧Akh

2
@AKh。您是要建议我的答案在Stackoverflow上占用太多空间吗?
约翰·本特利

1
我认为他只是意味着您有一个很棒的答案,您应该写一个博客!=)@JohnBentley
Sandy D.

@ SandyD.yesterday感谢您的积极解释。我确实希望他或他有此意图。
John Bentley

我也认为这是一个很棒的答案,我也这样解释。像这样非常完整的答案很棒!
LeonardoSibela

3

最近,我在这里找到了一个很好的解决方案。它基于通过RetainConfiguration保存任务对象。以我的观点,该解决方案非常优雅,对于我来说,我已经开始使用它。您只需要从基本任务中嵌套asynctask即可,仅此而已。


非常感谢您提供这个有趣的答案。除了相关问题中提到的解决方案之外,这也是一个很好的解决方案。
caw 2012年

5
不幸的是,该解决方案使用了不推荐使用的方法。
Damien 2014年

3

基于@Alex Lockwood的答案以及基于@William和@quickdraw的mcgraw答案:暂停活动/片段时如何处理处理程序消息,我编写了一个通用解决方案。

通过这种方式处理轮换,并且如果活动在异步任务执行期间进入后台,则该活动一旦恢复就将收到回调(onPreExecute,onProgressUpdate,onPostExecute和onCancelled),因此不会引发IllegalStateException(请参阅如何处理Handler)。活动/片段暂停时显示的消息)。

拥有相同但具有通用参数类型(例如AsyncTask)(例如:AsyncTaskFragment <Params,Progress,Result>)会很好,但是我没有迅速做到这一点,并且现在没有时间。如果有人想做改进,请放心!

代码:

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class AsyncTaskFragment extends Fragment {

    /* ------------------------------------------------------------------------------------------ */
    // region Classes & Interfaces

    public static abstract class Task extends AsyncTask<Object, Object, Object> {

        private AsyncTaskFragment _fragment;

        private void setFragment(AsyncTaskFragment fragment) {

            _fragment = fragment;
        }

        @Override
        protected final void onPreExecute() {

            // Save the state :
            _fragment.setRunning(true);

            // Send a message :
            sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
        }

        @Override
        protected final void onPostExecute(Object result) {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_POST_EXECUTE_MESSAGE, result);
        }

        @Override
        protected final void onProgressUpdate(Object... values) {

            // Send a message :
            sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
        }

        @Override
        protected final void onCancelled() {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_CANCELLED_MESSAGE, null);
        }

        private void sendMessage(int what, Object obj) {

            Message message = new Message();
            message.what = what;
            message.obj = obj;

            Bundle data = new Bundle(1);
            data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
            message.setData(data);

            _fragment.handler.sendMessage(message);
        }
    }

    public interface AsyncTaskFragmentListener {

        void onPreExecute(String fragmentTag);
        void onProgressUpdate(String fragmentTag, Object... progress);
        void onCancelled(String fragmentTag);
        void onPostExecute(String fragmentTag, Object result);
    }

    private static class AsyncTaskFragmentPauseHandler extends PauseHandler {

        @Override
        final protected void processMessage(Activity activity, Message message) {

            switch (message.what) {

                case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
                case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
                case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
                case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
            }
        }
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Attributes

    private Task _task;
    private AsyncTaskFragmentListener _listener;
    private boolean _running = false;

    private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
    private static final int ON_PRE_EXECUTE_MESSAGE = 0;
    private static final int ON_POST_EXECUTE_MESSAGE = 1;
    private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
    private static final int ON_CANCELLED_MESSAGE = 3;

    private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Getters

    public AsyncTaskFragmentListener getListener() { return _listener; }
    public boolean isRunning() { return _running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Setters

    public void setTask(Task task) {

        _task = task;
        _task.setFragment(this);
    }

    public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
    private void setRunning(boolean running) { _running = running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Fragment lifecycle

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void onResume() {

        super.onResume();
        handler.resume(getActivity());
    }

    @Override
    public void onPause() {

        super.onPause();
        handler.pause();
    }

    @Override
    public void onAttach(Activity activity) {

        super.onAttach(activity);
        _listener = (AsyncTaskFragmentListener) activity;
    }

    @Override
    public void onDetach() {

        super.onDetach();
        _listener = null;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Utils

    public void execute(Object... params) {

        _task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }

    public void cancel(boolean mayInterruptIfRunning) {

        _task.cancel(mayInterruptIfRunning);
    }

    public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {

        FragmentManager fm = activity.getSupportFragmentManager();
        AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);

        if (fragment == null) {

            fragment = new AsyncTaskFragment();
            fragment.setListener( (AsyncTaskFragmentListener) activity);
            fm.beginTransaction().add(fragment, fragmentTag).commit();
        }

        return fragment;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */
}

您将需要PauseHandler:

import android.app.Activity;
import android.os.Handler;
import android.os.Message;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
 *
 * /programming/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused
 */
public abstract class PauseHandler extends Handler {

    /**
     * Message Queue Buffer
     */
    private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());

    /**
     * Flag indicating the pause state
     */
    private Activity activity;

    /**
     * Resume the handler.
     */
    public final synchronized void resume(Activity activity) {
        this.activity = activity;

        while (messageQueueBuffer.size() > 0) {
            final Message msg = messageQueueBuffer.get(0);
            messageQueueBuffer.remove(0);
            sendMessage(msg);
        }
    }

    /**
     * Pause the handler.
     */
    public final synchronized void pause() {
        activity = null;
    }

    /**
     * Store the message if we have been paused, otherwise handle it now.
     *
     * @param msg   Message to handle.
     */
    @Override
    public final synchronized void handleMessage(Message msg) {
        if (activity == null) {
            final Message msgCopy = new Message();
            msgCopy.copyFrom(msg);
            messageQueueBuffer.add(msgCopy);
        } else {
            processMessage(activity, msg);
        }
    }

    /**
     * Notification message to be processed. This will either be directly from
     * handleMessage or played back from a saved message when the activity was
     * paused.
     *
     * @param activity  Activity owning this Handler that isn't currently paused.
     * @param message   Message to be handled
     */
    protected abstract void processMessage(Activity activity, Message message);
}

用法示例:

public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {

    private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
    private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Button testButton = (Button) findViewById(R.id.test_button);
        final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);

        testButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if(!fragment.isRunning()) {

                    fragment.setTask(new Task() {

                        @Override
                        protected Object doInBackground(Object... objects) {

                            // Do your async stuff

                            return null;
                        }
                    });

                    fragment.execute();
                }
            }
        });
    }

    @Override
    public void onPreExecute(String fragmentTag) {}

    @Override
    public void onProgressUpdate(String fragmentTag, Float percent) {}

    @Override
    public void onCancelled(String fragmentTag) {}

    @Override
    public void onPostExecute(String fragmentTag, Object result) {

        switch (fragmentTag) {

            case ASYNC_TASK_FRAGMENT_A: {

                // Handle ASYNC_TASK_FRAGMENT_A
                break;
            }
            case ASYNC_TASK_FRAGMENT_B: {

                // Handle ASYNC_TASK_FRAGMENT_B
                break;
            }
        }
    }
}

3

您可以为此使用装载程序。在这里检查文件


2
从Android API 28开始,不再推荐使用加载程序(如链接所示)。
萨米特

加载程序并没有被弃用,只是您如何称呼它们已更改
EdgeDev

2

对于那些想避开片段的人,您可以使用onRetainCustomNonConfigurationInstance()和一些连接来保留在方向更改时运行的AsyncTask 。

(请注意,此方法是不推荐使用的onRetainNonConfigurationInstance()的替代方法)。

似乎这种解决方案虽然很少被提及。我写了一个简单的运行示例来说明。

干杯!

public class MainActivity extends AppCompatActivity {

private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    result = (TextView) findViewById(R.id.textView_result);
    run = (Button) findViewById(R.id.button_run);
    asyncTaskHolder = getAsyncTaskHolder();
    run.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            asyncTaskHolder.execute();
        }
    });
}

private AsyncTaskHolder getAsyncTaskHolder() {
    if (this.asyncTaskHolder != null) {
        return asyncTaskHolder;
    }
    //Not deprecated. Get the same instance back.
    Object instance = getLastCustomNonConfigurationInstance();

    if (instance == null) {
        instance = new AsyncTaskHolder();
    }
    if (!(instance instanceof ActivityDependant)) {
        Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
    }
    return (AsyncTaskHolder) instance;
}

@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
    return asyncTaskHolder;
}

@Override
protected void onStart() {
    super.onStart();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.attach(this);
    }
}

@Override
protected void onStop() {
    super.onStop();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.detach();
    }
}

void updateUI(String value) {
    this.result.setText(value);
}

interface ActivityDependant {

    void attach(Activity activity);

    void detach();
}

class AsyncTaskHolder implements ActivityDependant {

    private Activity parentActivity;
    private boolean isRunning;
    private boolean isUpdateOnAttach;

    @Override
    public synchronized void attach(Activity activity) {
        this.parentActivity = activity;
        if (isUpdateOnAttach) {
            ((MainActivity) parentActivity).updateUI("done");
            isUpdateOnAttach = false;
        }
    }

    @Override
    public synchronized void detach() {
        this.parentActivity = null;
    }

    public synchronized void execute() {
        if (isRunning) {
            Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
            return;
        }
        isRunning = true;
        new AsyncTask<Void, Integer, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 100; i += 10) {
                    try {
                        Thread.sleep(500);
                        publishProgress(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
                }
            }

            @Override
            protected synchronized void onPostExecute(Void aVoid) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI("done");
                } else {
                    isUpdateOnAttach = true;
                }
                isRunning = false;
            }
        }.execute();
    }
}

0

我已经实现了可以解决任务执行时活动暂停和娱乐问题的

您应该实现AsmykPleaseWaitTaskAsmykBasicPleaseWaitActivity。即使您要旋转屏幕并在应用程序之间切换,您的活动和后台任务也可以正常工作


-9

快速替代(不推荐)

避免Activity销毁并创建自身就是在清单文件中声明您的Activity: android:configChanges =“ orientation | keyboardHidden | screenSize

  <activity
        android:name=".ui.activity.MyActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:label="@string/app_name">

正如在文档中提到的

屏幕方向已更改-用户已旋转设备。

注意:如果您的应用程序以API级别13或更高级别(由minSdkVersion和targetSdkVersion属性声明)为目标,则还应声明“ screenSize”配置,因为当设备在纵向和横向之间切换时,它也会更改。


1
最好避免这种情况。developer.android.com/guide/topics/resources/… “注意:您自己处理配置更改会使使用替代资源更加困难,因为系统不会自动为您应用这些资源。该技术应被认为是最后一种当您必须避免由于配置更改而重新启动并且不建议大多数应用程序使用时,请采取这种措施。”
David
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.