Android,AsyncTask中的实用程序类和松散耦合请指教

时间:2013-12-26 19:46:12

标签: android android-fragments android-asynctask

我试图搜索有关此主题的任何讨论,但到目前为止我还没有找到任何有用的内容。因此,我决定继续发布这个。

所以我的查询是关于Android最佳做法的。我正在创建一个调用RESTful端点的简单应用程序,解析下载的JSON,并在活动中包含的某些片段中显示结果。

我有一个自定义的“实用程序”类,它扩展了AsyncTask。在doInBackground方法中,我发出请求,将响应存储在String中等等(非常简单的东西)。

现在,我知道AsyncTask还有另外两个方法 - onPreExecute和onPostExecute。如果我在网上研究的是正确的,这些方法是我应该能够与UI交互的地方,通过将我的上下文传递给这个实用程序类,通过id查找视图,设置字段文本,或者我想要的任何内容要做。

所以这是我的问题。围绕这个的最佳做法是什么?

目前,扩展AsyncTask并使Web服务调用的实用程序类具有私有成员变量:

    private FragmentActivity mActivityContext;

然后,为了与UI进行交互,我做了类似的事情:

@Override
protected  void onPreExecute()
{
    super.onPreExecute();
    android.support.v4.app.FragmentManager fm = mActivityContext.getSupportFragmentManager();
    MainActivity.PlaceholderFragment fragment = (MainActivity.PlaceholderFragment)fm.findFragmentByTag("PlaceholderFragment");

    if (fragment != null)
    {
        TextView textView = (TextView)fragment.getView().findViewById(R.id.my_custom_field);
        textView.setText("This field was updated through onPreExecute!");
    }
}

虽然到目前为止看起来似乎没问题,但我担心的是 - 如果实用程序类正在访问它引用的活动的片段管理器吗?它甚至应该参考活动吗?通过使用片段管理器,实用程序类必须知道片段标记。这不是一种非常模块化或松散耦合的方法。当我创建实用程序类的实例时,解决方案就像传递fragment标记一样简单而不是像我现在那样硬设置吗?还是有更好的替代路线?

2 个答案:

答案 0 :(得分:9)

这就是我所做的,我是从StackOverflow中学到的:

假设我们正在从服务器数据库下载类别

首先,创建一个界面

public interface OnCategoriesDownloadedListener {
    public void onCategoriesDownloadSuccess(ArrayList<String> categories);
    public void onCategoriesDownloadFailure(String reason);
}

然后,在等待下载这些类别的活动中,我实现了这个界面:

public class MyActivity extends Activity implements  OnCategoriesDownloadedListener {

    @Override
    onCategoriesDownloadSuccess(ArrayList<String> categories) {
        //get the categories and do whatever my soul desire
    }

    @Override
    onCategoriesDownloadFailure(String reason) {
       //get the reason and come up with appropriate failure excuse
    }

}

在我的asynctask类中,我执行以下操作

public class GetCategoriesFromServerAsync extends AsyncTask<String, String, ArrayList<String>> {


private OnCategoriesDownloadedListener listener;
private ArrayList<String> categoryNamesArray;
private String reason;

public GetCategoriesFromServerAsync(OnCategoriesDownloadedListener listener) {
    this.listener = listener;
            //usually I pass more data here, for example when Im registering a user in the server database, Im using the constructor to pass their data.
}

@Override
protected ArrayList<String> doInBackground(String... args) {
    int success;

    try {
        //JSON stuff

            return categoryNames;

        } else if (success == 2) {
            reason = "failure";
            return null;
        } else {
            reason = "Connection error";
            return null;
        }
    } catch (JSONException e) {
        e.printStackTrace();
        reason = "Connection error";
        return null;
    }
}

@Override
protected void onPostExecute(Object[] categoryNamesAndLogos) {

    if (listener != null) {
        if (categoryNames!=null) {
            listener.onCategoriesDownloadSuccess(categoryNames);
        } else if (reason.contentEquals(TAG_FAILURE)) {
            listener.onCategoriesDownloadFailure(reason);
        } else if (reason.contentEquals(TAG_CONNECTION_ERROR)) {
            listener.onCategoriesDownloadFailure(reason);
        }

    }
    super.onPostExecute(categoryNames);
}

}

从实现监听器的活动开始,我使用它的构造函数启动asynctask:

new GetCategoriesFromDatabase(this).execute();

因此注意到该活动正在实施监听器并正在等待结果。

在asynctask postExecute中我只处理结果,将它们发送到等待它们的活动并在onCategoriesDownloadSuccess方法中处理它们

如果要显示Loader,可以在启动活动的asynctask的同时将其显示在onSuccess和onFailure方法中。此时我在AsyncTask的onPreExecute和onPostExecute中执行此操作,但我需要花一些时间或建议并考虑哪种方法更好。

不确定这是否是最佳做法,因为它是我所知道的唯一一个,但它确实适合我。

答案 1 :(得分:2)

@J。 Kowalski:这确实是一个很好的做法,虽然onPost和onPre AsyncTasks方法是在UI线程上执行的,但最好这样做。

只是一个小编辑,不要在任务构造函数中传递活动/接口实例,而是使用onResume和onPause的活动来设置和删除(nullify)对AsyncTask的接口引用,这样你甚至可以保存响应,如果活动在任务运行时进入暂停状态,供以后使用,如果任务花费太多时间,GC可以刷新活动对象。

伪代码

<强>的AsyncTask

public class GetCategoriesFromServerAsync extends AsyncTask<String, String, ArrayList<String>> {

private OnCategoriesDownloadedListener listener;
private ArrayList<String> categoryNamesArray;
private String reason;

public GetCategoriesFromServerAsync() {
    //usually I pass more data here, for example when Im registering a user in the server database, Im using the constructor to pass their data.
}

@Override
protected ArrayList<String> doInBackground(String... args) {
    int success;

    try {
        //JSON stuff
        return categoryNames;

        } else if (success == 2) {
            reason = "failure";
            return null;
        } else {
            reason = "Connection error";
            return null;
        }
    } catch (JSONException e) {
        e.printStackTrace();
        reason = "Connection error";
        return null;
    }
}

@Override
protected void onPostExecute(Object[] categoryNamesAndLogos) {

    if (listener != null) {
        if (categoryNames!=null) {
            listener.onCategoriesDownloadSuccess(categoryNames);
        } else if (reason.contentEquals(TAG_FAILURE)) {
            listener.onCategoriesDownloadFailure(reason);
        } else if (reason.contentEquals(TAG_CONNECTION_ERROR)) {
            listener.onCategoriesDownloadFailure(reason);
        }

    } else {
        //listener is null, activity gone in onPause, either save to file, or run the task again
    }
    super.onPostExecute(categoryNames);
}

public void setInterface(OnCategoriesDownloadedListener listener) {
    this.listener = listener;
}
}

<强>活动

public class SampleActivity extends Activity {
GetCategoriesFromServerAsync mTask;
boolean isDataLoaded = false;

protected void onResume() {
    if(mTask != null) {
        switch(mTask.getStatus()) {
        case Status.PENDING:
        case Status.RUNNING:
            mTask.setInterface(this);
        break;

        case Status.FINISHED:
            if(isDataLoaded) {
                //success case, do nothing...
            } else {
                //load from file, or start the task again
            }
        break;
        } 
    }
}

protected void onPause() {
    if(mTask != null) mTask.setInterface(null);
}
}

希望有所帮助。