Android:将回调AsyncTask传递给Fragment(而不是Activity)

8
我正在尝试在AsyncTask和Fragment之间实现回调,但找不到正确的信息如何实现。问题是所有回调实现都是在Activity和AsyncTask之间,但我需要在Fragment和AsyncTask之间。能否给我一个小的工作示例,说明如何在没有Activity的情况下实现它。
我的操作结构:Fragment调用DialogFragment -> 选择某些内容并向异步任务发送服务器请求 -> 异步任务处理所有内容并更新视图和一些变量。我的主要问题是,我只在onCreate中调用prepareData()一次,当我在其他片段之间切换并返回时,我看到的是旧数据。也就是说,在asynctask的onPost中仅更新视图是不够的。最好有一个回调函数来更新整个变量。
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnItemClickListener, onTaskListener {
 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    fm = getSherlockActivity().getSupportFragmentManager();


    prepareData();
}

public void prepareData() {
     termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity());
    termsAndConditions = termsAndConditionsM.getTermsAndConditions();

    if (termsAndConditions != null) {
                 int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
        if (totalPayments > 0) {
            paymentsData = termsAndConditionsM.getpayments();

            if (paymentsData != null) {

                payments = new ArrayList<Payment>();

                for (int i = 1; i <= totalPayments; i++) {
                    paymentValues = new Payment();
                    paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
                    paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
                    paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
                    payments.add(paymentValues);
                }
            }
        }
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    rootView = init(inflater, container);

    if (payments != null || termsAndConditions != null)
        updateTermsAndConditionsView();

    return rootView;
}

private View init(LayoutInflater inflater, ViewGroup container) {
    rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);

    ...
    return rootView;
}

public void updateTermsAndConditionsView() {
    etHowMuch.setText("£" + termsAndConditions.get(ServerAPI.AMOUNT_OF_CREDIT));
    etForHowLong.setText(Helpers.ConvertDays2Date(Integer.valueOf(termsAndConditions.get(ServerAPI.TERM_OF_AGREEMENT_IN_DAYS))));

    PaymentAdapter adapter = new PaymentAdapter(getSherlockActivity(), R.layout.custom_loan_item, payments);
    lvPayments.setAdapter(adapter);

    tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
    tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
    tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
}

@Override
public void onClick(View v) {
    ft = fm.beginTransaction();

    howMuch = etHowMuch.getText().toString();
    forHowLong = etForHowLong.getText().toString();

    switch (v.getId()) {
        case R.id.etHowMuch:
            f = new NumberPaymentsPickerFragment();
            args = new Bundle();
            args.putInt(Const.HOW_MUCH, Integer.valueOf(howMuch.replace("£", "")));
            args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
            args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
            f.setArguments(args);
            f.setTargetFragment(this, DIALOG_FRAGMENT);
            f.show(getActivity().getSupportFragmentManager(), Const.HOW_MUCH);
            break;
        case R.id.etForHowLong:
            f = new NumberPaymentsPickerFragment();
            args = new Bundle();
            args.putInt(Const.FOR_HOW_LONG, Integer.valueOf(Helpers.ConvertDate2Days(forHowLong)));
            args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
            args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
            f.setArguments(args);
            f.setTargetFragment(this, DIALOG_FRAGMENT);
            f.show(getActivity().getSupportFragmentManager(), Const.FOR_HOW_LONG);
            break;
        case R.id.tvPersonalDetails:
            sfm.saveCurFragment(ServerAPI.PERSONAL_DETAILS, 0);
            ft.replace(android.R.id.content, new PersonalDetailsFragment(), ServerAPI.PERSONAL_DETAILS).addToBackStack(null).commit();
            break;
        case R.id.tvAgreementDetails:
            sfm.saveCurFragment(ServerAPI.AGREEMENT_DETAILS, 0);
            ft.replace(android.R.id.content, new AgreementDetailsFragment(), ServerAPI.AGREEMENT_DETAILS).addToBackStack(null).commit();
            break;
        case R.id.bApply:

            break;
    }

@Override
public void onUpdateData() {
    Log.d(TAG, "Update data");

}

}

DialogFragment:

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

    Bundle args = getArguments();

    ...
}

public Dialog onCreateDialog(Bundle savedInstanceState) {

    ...

        return createDialog(v, R.string.for_how_long, etHowMuch, etForHowLong, etPromotionCode);
    }
    return null;
}
private Dialog createDialog(View view, int titleResID, final EditText howMuchField, final EditText forHowLongField, final EditText promotionCodeField) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(titleResID);
    builder.setView(view);
    builder.setPositiveButton(R.string.set, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int id) {

            doShowProgress();

        }

        private void doShowProgress() {


            ExecuteServerTaskBackground task = new
                    ExecuteServerTaskBackground(getActivity());
                task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
                onTaskListener listener = new onTaskListener() {

                @Override
                public void onUpdateData() {
                    Log.d(TAG, "Updaaate");

                }
            };
            task.setListener(listener);
            task.args = args;
            task.execute();
        }

    }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
        }
    });
    return builder.create();
}

AsyncTask:

    onTaskListener mListener;

public interface onTaskListener {
    void onUpdateData();
}

public void setListener(onTaskListener listener){
  mListener = listener;
}
public ExecuteServerTaskBackground(Activity activity) {
    this.mActivity = activity;
    this.mContext = activity.getApplicationContext();
}

@Override
protected void onPreExecute() {
    pb = (ProgressBar) mActivity.findViewById(R.id.progressBar1);
    pb.setVisibility(View.VISIBLE);
}

@Override
protected Void doInBackground(Void... params) {
    ServerAPI server = new ServerAPI(mContext);
    if (!args.isEmpty())
        server.serverRequest(action, args);
    else
        server.serverRequest(action, null);
    return null;
}

@Override
protected void onPostExecute(Void result) {
 mListener.onUpdateData();

//There is I just update view but how to update whole variables throughtout callback?        
//                tvNoOfPayments = (TextView) mActivity.findViewById(R.id.tvNoOfPaymentsValue);
//                tvFirstPayment = (TextView) mActivity.findViewById(R.id.tvFirstPaymentValue);
//                tvTotalRepayable = (TextView) mActivity.findViewById(R.id.tvTotalRepayableValue);
//
//                lvPayments = (ListView) mActivity.findViewById(R.id.lvData);
//
//                termsConditionsM = new TermsAndConditionsManager(mContext);
//
//                termsAndConditions = termsConditionsM.getTermsAndConditions();
//
//                int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
//
//                if (totalPayments > 0) {
//                    if (termsAndConditions != null) {
//                        tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
//                        tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
//                        tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
//                    }
//
//                    paymentsData = termsConditionsM.getpayments();
//
//                    if (paymentsData != null) {
//                        Log.d(TAG, paymentsData.toString());
//
//                        payments = new ArrayList<Payment>();
//
//                        for (int i = 1; i <= totalPayments; i++) {
//                            paymentValues = new Payment();
//                            paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
//                            paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
//                            paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
//                            payments.add(paymentValues);
//                        }
//
//                        PaymentAdapter adapter = new PaymentAdapter(mContext, R.layout.custom_loan_item, payments);
//                        lvPayments.setAdapter(adapter);
//                    }
    //             

   }

    pb.setVisibility(View.GONE);
    super.onPostExecute(result);
}
1个回答

26

不考虑您的代码,我将发布使回调函数正常工作所需的最重要部分。


TestFragment:

public class TestFragment extends Fragment {

    /* Skipping most code and I will only show you the most essential. */    
    private void methodThatStartsTheAsyncTask() {
        TestAsyncTask testAsyncTask = new TestAsyncTask(new FragmentCallback() {

            @Override
            public void onTaskDone() {
                methodThatDoesSomethingWhenTaskIsDone();
            }
        });

        testAsyncTask.execute();
    }

    private void methodThatDoesSomethingWhenTaskIsDone() {
        /* Magic! */
    }

    public interface FragmentCallback {
        public void onTaskDone();
    }
}

TestAsyncTask:

public class TestAsyncTask extends AsyncTask<Void, Void, Void> {
    private FragmentCallback mFragmentCallback;

    public TestAsyncTask(FragmentCallback fragmentCallback) {
        mFragmentCallback = fragmentCallback;
    }

    @Override
    protected Void doInBackground(Void... params) {
        /* Do your thing. */
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        mFragmentCallback.onTaskDone();
    }
}

这是我目前拥有的。这部分正在运作,但我想在调用AsyncTask的地方捕获消息而不是相同的位置。我的结构是MainFragment(这里我想捕获消息)->DialogFragment(调用asynctask)->asynctask(处理并在min fragment中返回结果)。现在,回调只适用于DialogFragment和AsyncTask之间的连接,但如何直接将结果发送到mainFragment? - Viktor M.
所有数据都暂时保存在SharedPreferences中。这就是为什么获取所有必要数据不是问题。问题只在于,当我转到另一个片段并再次回到MainFragment时,新数据(再次保存在sharedpreferences中,并在异步任务过程中替换了旧视图的数据)丢失了(视图的数据)。也就是说,当我回到MainFragment时,onCreate方法不会再次调用,只能是onStart或onResume。我想在AsyncTask完成后更新数据。 - Viktor M.
@user1376885 我了解了,那么你应该将数据保存到数据库中,而不是SharedPreferences中。当Fragment启动时,加载该数据并保持Fragments的“无状态”。当您的AsyncTask完成时,调用MainActivity启动MainFragment。 - Simon Zettervall
SharedPreferences适用于保存小数据,但我通常不喜欢它,因为好的编程实践应该考虑未来,这意味着你应该编写易于添加内容的代码。在数据库中处理数据比在SharedPreferences中更容易。但如果像你说的那样只需要保存很少的数据,那就使用SharedPreferences吧。我更多地使用数据库,因为我认为它们更安全,而且如果你有一个rooted的手机,导出和备份也很容易。简而言之,数据库提供了更多的功能。 - Simon Zettervall
@user1376885手机中不要存储敏感数据,因为正如您所说,它很容易被黑客攻击。您可以使用Proguard来混淆代码,使其更难解密。但是,如果使用已经root的手机,仍然有可能提取数据。因此,如果您有敏感信息,最安全的方式是将数据存储在服务器上,并连接到该服务器。关于性能,我认为数据库更好,因为它具有更多功能,而性能也不仅仅取决于手机的速度有多快,还取决于您的工作方式。通过使用数据库,您可以更轻松地工作,从而提高性能。 - Simon Zettervall
显示剩余6条评论

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