Android强大的线程架构? Asynctask vs线程/处理程序vs服务/线程

时间:2014-05-20 21:03:12

标签: java android multithreading service android-asynctask

假设您正在为应用设计线程架构 - >主要目的是你的应用程序将有很多需要在后台线程上完成的任务,有时候是UI线程上的结果任务,或者某些事情没有(尽管更多次,结果需要在UI线程上运行)。为简单起见,假设任务将是这样的:下载文件并显示弹出窗口,登录用户并转到其他页面,处理图像并将结果存储在数据库中(很多流行任务)应用程序))

我已经研究了很多关于细微差别的内容,但是我真的希望深入了解什么样的架构更好,以及考虑的因素。

以下是考虑的三个模型:

  1. AsyncTask模型:每个操作(如下载文件和显示弹出窗口)都是AsyncTask,或父类的一些派生,它们提取了常见的功能。
  2. 线程/处理程序模型:我总是创建一个new Handler(Looper.getMainLooper());,每次我需要执行任务时,我使用线程工厂来分离任务,使用UI线程上的处理程序(或任何自定义处理程序)
  3. 服务/线程模型:我使用基于某些操作代码负责操作的通用Service类。有一堆ServiceTask衍生对象可以执行某些操作,但Service类在启动/完成任务时与每个ServiceTask进行通信。
  4. 我稍微倾向于使用整个服务/线程模型,因为我已经阅读了一些与AsyncTask / Threads相比非常尴尬的细微差别:

    • AsyncTask有一个private static handler,如果类加载器在错误的时间调用它(例如包含一个在应用程序之前使用它的库)那么你的所有onPostExecute将在错误的时间发生,因为您的处理程序不是主处理程序
    • 很容易忘记检查onPostExecute中的一堆内容,例如是否有配置更改,或者您的活动已被销毁,或者在调用onPostExecute时应用程序是后置/暂停的(导致崩溃)
    • AsyncTask更改了其在不同API上的串行/并行执行行为
    • 如果您使用线程/处理程序模型,在较旧的设备上,线程优先级实际上非常低。我听说有一个优先级为1-15的东西,这样你的线程自动获得低优先级,如果系统资源不足,你的线程就会停止运行(而因为服务是独立于你的活动运行的线程优先级更高?)

    设计强大的线程架构的最佳方法是什么,这种架构不会轻易导致崩溃/意外行为,同时还能保持良好的性能?

    如果这个问题过于含糊,如果你需要实际的代码(我害怕发布代码,因为它会超出问题的长度而不是已经过的代码),请在评论中告诉我。

4 个答案:

答案 0 :(得分:3)

我认为你不会在这里找到一种适合所有人的方法。

  • 正在下载文件?使用DownloadManager
  • 登录用户并转到下一个屏幕?可能AsyncTask最好。
  • 处理图像并存储? Service可能是一个不错的选择,因为您不希望将操作附加到任何特定的Activity
  • Handlers更加棘手,如果它们附加到在后台线程上运行的Looper,那么当您完成后,需要在quit()上调用Looper 。当您需要延迟操作时,处理程序很好,postDelayed()非常适合。当您需要从后台线程回传到UI线程时,它们也很好。

但是,你是正确的,每个人都有你提到的陷阱。 Android是一个复杂的野兽,看起来他们可以做得更好,防止开发人员在脚下射击自己,特别是在一个Activity被销毁后调用AsyncTask!

答案 1 :(得分:1)

我使用Java的旧学校方法创建了一个派生自Java的Thread的类(我称之为ThreadRunner)。构造函数看起来像:

public ThreadRunner (Object [] params, AbstractCallback callBack) {...}

AbstractCallback是一个实现单个“onCall”的课程。方法,主要用于通知主叫方有关事件,例如"完成任务的执行"。

我已经用它来从互联网上获取内容并运行其他耗时的操作。它没有造成任何问题并按预期工作。

但是,我多次听说AsyncTask是一种Android-ish方式。我不知道为什么也没有任何改变的意图,因为如果它没有被破坏,我就不会修改它。"方法

我还看到了你需要用AsyncTask编写更少代码的评论,但在我使用传统Java威胁的方法中,编码量也很小,所以我把它排除在外#39;只是个人喜好和经验问题。

关于你的第三种方法 - 我认为你应该在编写一直运行的服务时使用它,监听请求并永不停止。当您只需要异步执行单个任务时,应该使用Java Threads或AsyncTask。

答案 2 :(得分:1)

我认为AsyncTask是列出目的的好工具。但它需要包装AsyncTask以便于使用。我的这种包装的变体(带有进度指示器)如下:

用于在应用程序活动中扩展它的主类AsyncActivity:

public abstract class AsyncActivity extends Activity{
// Поле нужно обязательно объявить как статическое!
private static AsyncConnect asyncConnect =  null;

protected void runBackgroundTask(String progressInscription, RequestTask task){
    asyncConnect = new AsyncConnect(this, responseListener, progressInscription, task);
    asyncConnect.execute();
}

protected abstract void onBackgroundTaskEnd(boolean result);

@Override
protected void onResume(){
    super.onResume();

    // Перерегистрируем текущий контекст этой формы 
    // для корректной работы слушателя ответа с сервера
    responseListener.registerCurrentContext( this );

    if (asyncConnect != null){
        asyncConnect.onResume(this);
    }
}

@Override
protected void onPause(){
    super.onPause();

    if (asyncConnect != null){
        asyncConnect.onPause();
    }
}

/**
 * Чтобы диалоги не вызывались из устаревшего контекста 
 * и по этой причине не исчезали при повороте экрана,
 * слушателя ответа с сервера необходимо сделать статическим полем класса,
 * в котором должен быть зарегистрирован текущий контекст
 */
private static final OnServerResponseListener responseListener = new OnServerResponseListener(){
    private AsyncActivity context = null;

    @Override
    public void registerCurrentContext(AsyncActivity context){this.context = context; }

    @Override
    public void onResponse(boolean result){
        // Если никакой контекст не был зарегистрирован, ничего не делаем
        if (context == null) return;

        // Освождаем статическое поле для сборщика мусора (но делать это не обязательно!)
        asyncConnect = null;

        // Вызываем колбэк о завершении фоновой задачи
        context.onBackgroundTaskEnd(result); 
    }
};
}

附加类和一对接口:

public class AsyncConnect {
private final Activity context;
private final RequestTask task;
private final String progressInscription;
private final OnServerResponseListener responseListener;
private boolean isDone = false;

private ProgressDialog progressDialog;

public AsyncConnect(Activity context, OnServerResponseListener responseListener,
        String progressInscription, RequestTask task){
    this.context = context;
    this.task = task;
    this.progressInscription = progressInscription;
    this.responseListener = responseListener;

    progressDialog = null;

    isDone = false;
}

public void execute(){
    if (isDone) return;

    new ConnectTask().execute();
}

public void onPause(){
    if (isDone) return;

    if (progressDialog != null){
        if (progressDialog.isShowing()){
            progressDialog.dismiss();
            progressDialog = null;
        } 
    }
}

public void onResume(Activity context){
    if (isDone) return;

    progressDialog = ProgressDialog.show( context, null, (CharSequence)progressInscription, 
        true, false);
}

private class ConnectTask extends AsyncTask<Object, Void, Boolean> {

    @Override
    protected void onPreExecute( ) {
        super.onPreExecute();

        progressDialog = ProgressDialog.show( context, null, 
            (CharSequence)progressInscription, true, false);
    }

    @Override
    protected Boolean doInBackground(Object... messages) {
        return task.call();
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);

        if (progressDialog != null){
            if (progressDialog.isShowing()){
                progressDialog.dismiss();
                progressDialog = null;
            } 
        }

        // Делаем невозможным повторное использование этого объекта
        isDone = true; 

        responseListener.onResponse(result);
    }
}

}

public interface OnServerResponseListener {
    public void registerCurrentContext(AsyncActivity context);
    public void onResponse(boolean result);
}

public interface RequestTask {
    public boolean call();
}

对于使用AsyncActivity,我们只需要调用runBackgroundTask并在目标活动中实现onBackgroundTaskEnd。根据这个想法,可以创建不同类型的AsyncTask包装。

答案 3 :(得分:1)

您也可以查看Needle;它是一个开源,简单但功能强大的Android多线程库。有了它,你可以说:

Needle.onMainThread().execute(new Runnable() {
    @Override
    public void run() {
        // e.g. change one of the views
    }
});

Needle.onBackgroundThread().execute(new UiRelatedTask<Integer>() {
    @Override
    protected Integer doWork() {
        int result = 1+2;
        return result;
    }

    @Override
    protected void thenDoUiRelatedWork(Integer result) {
        mSomeTextView.setText("result: " + result);
    }
});
  • 非常简单的API
  • 固定线程池大小
  • 可自定义的线程池大小
  • 支持UI交互(“工作然后在UI线程上使用结果”)
  • android 1.5 +
  • 在所有平台版本上的行为相同

在GitHub上查看:https://github.com/ZsoltSafrany/needle