片段能够启动在配置更改期间未中断的任务

时间:2014-03-13 10:37:57

标签: android android-fragments android-configchanges

抱歉我的英文

这不是问题。我发布它是因为这可能对某人有帮助。我的解决方案基于几个月前放置here的解决方案,但后来作者将其删除了。但是他的解决方案中存在一些错误,我试图修复它们。

即使您在应用中执行了长时间执行的任务时打开了另一个应用,然后在另一个应用中更改了屏幕方向,并在完成任务后返回到您的应用,我的解决方案也能正常工作。

如果您在我的解决方案中发现错误,请告知我们。

看看:

public interface AbleToStartTask {
    void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task);
    void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task, Integer titleId);
}


public interface TaskResultReceiver {
    void onTaskResult(int requestCode, int resultCode, Serializable result);
}


public abstract class TaskNotBeInterruptedDuringConfigurationChange extends AsyncTask<Void, Void, Serializable> {

    private TaskFragment fragment;

    final void setFragment(TaskFragment fragment) {
        this.fragment = fragment;
    }

    @Override
    protected final void onPostExecute(Serializable result) {
        if (fragment != null) {
            fragment.onTaskFinished(result);
        }
    }
}



public class TaskFragment extends android.support.v4.app.DialogFragment {

    public static final Integer NO_TITLE_ID = null;

    private boolean isNeedToReturnResult = false;
    private boolean isResultReturned = false;
    private TaskNotBeInterruptedDuringConfigurationChange task;

    private int resultCode = Activity.RESULT_CANCELED;
    private Serializable result = null;
    private Integer titleId;


    public void setTask(TaskNotBeInterruptedDuringConfigurationChange task) {
        task.setFragment(this);
        this.task = task;
    }

    public void setTitle(Integer titleId) {
        this.titleId = titleId;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        if (savedInstanceState == null && task != null) {
            task.execute();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        initDialog();
        return inflater.inflate(R.layout.fragment_task, container);
    }

    private void initDialog() {
        if (titleId == NO_TITLE_ID) {
            getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
            getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        } else {
            getDialog().setTitle(titleId);
        }
        getDialog().setCanceledOnTouchOutside(false);
    }

    @Override
    public void onDestroyView() {
        if ((getDialog() != null) && getRetainInstance()) {
            getDialog().setDismissMessage(null);
        }
        super.onDestroyView();
    }


    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        if (task != null) {
            task.cancel(false);
        }
        tryReturnResult();
    }

    private void tryReturnResult() {
        task = null;
        if (isResultReturned) {
            return;
        }
        if (getTargetFragment() != null) {
            returnResult();
        }
    }

    private void returnResult() {
        isResultReturned = true;
        FragmentAbleToStartTask ableToStartTask = (FragmentAbleToStartTask) getTargetFragment();
        if (ableToStartTask.isAbleToReceiveResult()) {
            ableToStartTask.onResult(FragmentAbleToStartTask.TASK_FRAGMENT_TARGET, resultCode, result);
            isNeedToReturnResult = false;
            finishFragment();
        } else {
            isNeedToReturnResult = true;
        }
    }

    private void finishFragment() {
        if (isResumed()) {
            dismiss();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (task == null) {
            dismiss();
        }
    }

    public void onTaskFinished(Serializable result) {
        setResult(Activity.RESULT_OK, result);
        tryReturnResult();
    }

    private void setResult(int resultCode, Serializable result) {
        this.resultCode = resultCode;
        this.result = result;
    }

    public void getResultIfItReturnedDuringPause() {
        if (isNeedToReturnResult) {
            returnResult();
        }
    }
}



public abstract class FragmentAbleToStartTask extends android.support.v4.app.Fragment
        implements AbleToStartTask, TaskResultReceiver {

    public static final int TASK_FRAGMENT_TARGET = 123;

    protected static final String TAG_TASK_FRAGMENT = FragmentAbleToStartTask.class.getName() + "TAG_TASK_FRAGMENT";

    private static final String KEY_TASK_REQUEST_CODE = "KEY_TASK_REQUEST_CODE";

    private FragmentManager fragmentManager;
    private int taskRequestCode;

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_TASK_REQUEST_CODE, taskRequestCode);
    }

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

        if (savedInstanceState != null) {
            restoreState(savedInstanceState);
        }

        fragmentManager = getFragmentManager();

        TaskFragment taskFragment = getTaskFragment();
        if (taskFragment != null) {
            taskFragment.setTargetFragment(this, TASK_FRAGMENT_TARGET);
        }
    }

    private void restoreState(Bundle savedInstanceState) {
        taskRequestCode = savedInstanceState.getInt(KEY_TASK_REQUEST_CODE);
    }

    private TaskFragment getTaskFragment() {
        return (TaskFragment) fragmentManager.findFragmentByTag(TAG_TASK_FRAGMENT);
    }

    public void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task) {
        startTask(requestCode, task, TaskFragment.NO_TITLE_ID);
    }

    public void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task, Integer title) {
        this.taskRequestCode = requestCode;
        TaskFragment taskFragment = new TaskFragment();
        taskFragment.setTitle(title);
        taskFragment.setTask(task);
        taskFragment.setTargetFragment(this, TASK_FRAGMENT_TARGET);
        startFragment(taskFragment);
    }

    private void startFragment(TaskFragment taskFragment) {
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        Fragment previousTaskFragment = fragmentManager.findFragmentByTag(TAG_TASK_FRAGMENT);
        if (previousTaskFragment != null) {
            transaction.remove(previousTaskFragment);
        }
        transaction.add(taskFragment, TAG_TASK_FRAGMENT);
        transaction.commit();
    }

    public final void onResult(int requestCode, int resultCode, Serializable result) {
        if ((requestCode == TASK_FRAGMENT_TARGET)) {
            onTaskResult(this.taskRequestCode, resultCode, result);
        }
    }

    public abstract void onTaskResult(int requestCode, int resultCode, Serializable result);

    public boolean isAbleToReceiveResult() {
        return isResumed();
    }

    @Override
    public void onResume() {
        super.onResume();
        TaskFragment taskFragment = getTaskFragment();
        if (taskFragment != null) {
            taskFragment.getResultIfItReturnedDuringPause();
        }
    }

}

因此FragmentAbleToStartTask的每个继承者都必须实现onTaskResult(requestCode, resultCode, result)。在onTaskResult() - requestCode中传递给startTask(requestCode, ...)的值。请注意,此片段只能同时执行一项任务。

TaskNotBeInterruptedDuringConfigurationChange子类的示例:

public class SomeLongTask extends TaskNotBeInterruptedDuringConfigurationChange {
    private final Context context;
    private final Worker worker;

    public SomeLongTask (Worker worker, Context context) {
        this.worker= worker;
        this.context = context;
    }

    @Override
    protected Serializable doInBackground(Void... voids) {
        return (Serializable) worker.work(context);
    }
}

FragmentAbleToStartTask子类的例子:

public class NameListFragment extends FragmentAbleToStartTask {

    private static final int TASK_REQUEST_CODE_RECEIVING_NAMES = 461;

    private static final String KEY_PATTERN = "KEY_PATTERN";

    private static final String KEY_NAMES = "KEY_NAMES";


    private List<String> names;

    private ListView listView;


    public static Fragment newInstance(String patternToSearchNames) {
        Fragment fragment = new NameListFragment();
        Bundle args = new Bundle();
        args.putString(KEY_PATTERN, patternToSearchNames);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.example_list_fragment, null);
        listView = (ListView) root.findViewById(R.id.listView);
        return root;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (savedInstanceState == null) {
            startTaskReceivingNames();
        } else {
            restoreState(savedInstanceState);
        }
    }

    // !!! Key Point
    private void startTaskReceivingNames() {
        String pattern = getArguments().getString(KEY_PATTERN);
        TaskNotBeInterruptedDuringConfigurationChange task = new NamesReceiverTask(getActivity(), pattern);
        startTask(TASK_REQUEST_CODE_RECEIVING_NAMES, task);
    }

    private void restoreState(Bundle savedInstanceState) {
        names = (List<String>) savedInstanceState.getString(KEY_NAMES);
        if (names != null) {
            initList();
        }
    }

    private void initList() {
        setEmptyListView();
        ArrayList<String> adapter = new ArrayList<String>(getActivity(), android.R.layout.simple_list_item_1, names);
        listView.setAdapter(adapter);
    }

    private void setEmptyListView() {
        View emptyListView = getView().findViewById(R.id.emptyListView);
        listView.setEmptyView(emptyListView);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putSerializable(KEY_NAMES, (Serializable) names);
    }

    // !!! Key Point
    @Override
    public void onTaskResult(int requestCode, int resultCode, Serializable result) {
        if (requestCode == TASK_REQUEST_CODE_RECEIVING_NAMES) {
            onReceivingNamesFinished(resultCode, result);
        }
    }

    private void onReceivingNamesFinished(int resultCode, Serializable result) {
        if (resultCode != Activity.RESULT_OK) {
            getActivity().finish();
            return;
        }
        this.names = (List<String>) result;
        initList();
    }
}

0 个答案:

没有答案