396

我对 Android和Android 之间Handlers的区别感到有些困惑。我在 StackOverflow 上阅读了很多博客和问题。AsyncTaskThreads

Handler是为您提供与 UI 通信的后台线程。例如,更新进度条应该通过Handler. 使用 Handlers 你有优势MessagingQueues,所以如果你想安排消息或更新多个 UI 元素或有重复的任务。

AsyncTask类似,实际上它们都使用Handler,但不在 UI 线程中运行,因此它适用于获取数据,例如获取 Web 服务。稍后您可以与 UI 交互。

Thread但是无法与 UI 交互,提供更多“基本”线程,并且您错过了AsyncTask.

但是,我希望在服务中运行一个套接字连接。这应该在处理程序或线程中运行,甚至是AsyncTask? 根本不需要 UI 交互。它对我使用的性能有影响吗?

同时,文档也得到了重大改进。

4

13 回答 13

357

如果我们查看源代码,我们会看到AsyncTaskHandler是纯粹用 Java 编写的。(不过也有一些例外。但这不是重点)

AsyncTask所以or没有魔法Handler。这些课程使我们作为开发人员的生活更轻松。

例如:如果程序 A 调用方法 A(),则方法 A() 可以与程序 A 在不同的线程中运行。我们可以通过以下代码轻松验证:

Thread t = Thread.currentThread();    
int id = t.getId();

为什么我们要为某些任务使用新线程?你可以google一下。很多很多原因,例如:举重,长时间运行的工作。

Thread那么,AsyncTask、 和之间有什么区别Handler

AsyncTaskandHandler是用 Java 编写的(在内部他们使用 a Thread),所以我们可以用Handleror做的所有事情AsyncTask,我们也可以使用 a 来实现Thread

什么可以Handler而且AsyncTask真正有帮助?

最明显的原因是调用者线程和工作线程之间的通信。(调用者线程:调用工作线程以执行某些任务的线程。调用者线程不一定是 UI 线程)。当然,我们可以通过其他方式在两个线程之间进行通信,但是由于线程安全,存在许多缺点(和危险)。

这就是我们应该使用Handlerand的原因AsyncTask。这些类为我们完成了大部分工作,我们只需要知道要覆盖哪些方法。

Handler和之间的区别AsyncTask是:AsyncTaskCaller 线程UI Thread时使用。这是android文档所说的:

AsyncTask 允许正确和轻松地使用 UI 线程。此类允许在 UI 线程上执行后台操作并发布结果,而无需操作线程和/或处理程序

我想强调两点:

1)UI线程的易用性(所以,当调用者线程是UI线程时使用)。

2)无需操纵处理程序。(意味着:您可以使用 Handler 代替 AsyncTask,但 AsyncTask 是一个更简单的选择)。

这篇文章中有很多东西我还没有说,例如:什么是 UI Thread,或者为什么它更容易。你必须知道每个类背后的一些方法并使用它,你就会完全理解其中的原因。

@:当你阅读Android文档时,你会看到:

Handler 允许您发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象

这个描述起初可能看起来很奇怪。我们只需要了解每个线程都有各自的消息队列(就像一个待办事项列表),线程会拿走每条消息,一直执行到消息队列为空(就像我们完成工作上床睡觉一样)。所以,当Handler通信时,它只是给调用者线程一个消息,它会等待处理。

复杂?请记住,Handler可以安全地与调用者线程通信。

于 2012-03-21T08:19:25.213 回答
61

正如Vogella 网站上关于使用 Handlers、AsyncTask 和 Loaders 进行 Android 后台处理的教程所说:

该类Handler可用于注册到线程并提供一个简单的通道来向该线程发送数据。

该类AsyncTask封装了后台进程的创建以及与主线程的同步。它还支持报告正在运行的任务的进度。

aThread基本上是多线程的核心元素,开发人员可以使用它,但有以下缺点:

如果您使用 Java 线程,您必须在自己的代码中处理以下要求:

  • 如果您将结果回发到用户界面,则与主线程同步
  • 取消线程没有默认值
  • 没有默认线程池
  • 在 Android 中处理配置更改没有默认值

关于AsyncTask,正如Android Developer's Reference所说:

AsyncTask允许正确和轻松地使用 UI 线程。此类允许在 UI 线程上执行后台操作并发布结果,而无需操作线程和/或处理程序。

AsyncTask被设计为一个辅助类ThreadHandler 并且不构成通用线程框架。AsyncTasks 最好用于短时间的操作(最多几秒钟)。如果您需要保持线程长时间运行,强烈建议您使用 java.util.concurrent 包提供的各种 API,例如Executor、ThreadPoolExecutor 和 FutureTask。

2015 年 5 月更新:我找到了涵盖该主题的一系列精彩讲座。

这是谷歌搜索:Douglas Schmidt 讲座 android 并发和同步

这是YouTube上第一堂课的视频

所有这些都是CS 282(2013)的一部分:范德比尔特大学Android 系统编程。这是YouTube 播放列表

道格拉斯·施密特似乎是一位出色的讲师

重要提示:如果您正在考虑使用它AsyncTask来解决线程问题,则应首先检查ReactiveX/RxAndroid可能更合适的编程模式。一个很好的概览资源是Learning RxJava 2 for Android by example

于 2015-04-15T16:18:55.973 回答
56

深入研究后,它是直截了当的。

AsyncTask

这是一种在不了解 java 线程模型的情况下使用线程的简单方法。 AsyncTask为工作线程和主线程提供各种回调。

用于小型等待操作,如下所示:

  1. 从 Web 服务获取一些数据并显示在布局上。
  2. 数据库查询。
  3. 当您意识到正在运行的操作将永远不会被嵌套。

Handler

当我们在 android 中安装应用程序时,它会为该应用程序创建一个名为 MAIN UI Thread 的线程。所有活动都在该线程内运行。根据 android 单线程模型规则,我们不能直接访问该 Activity 中定义的另一个线程的 UI 元素(位图、文本视图等)。

处理程序允许您从其他后台线程与 UI 线程进行回通信。这在 android 中很有用,因为 android 不允许其他线程直接与 UI 线程通信。处理程序可以发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例都与单个线程和该线程的消息队列相关联。当一个新的处理程序被创建时,它被绑定到创建它的线程的线程/消息队列中。

它最适合:

  1. 它允许您进行消息队列。
  2. 消息调度。

Thread

现在是时候谈谈线程了。

Thread 是 和 的父AsyncTaskHandler。它们都在内部使用线程,这意味着您也可以创建自己的线程模型,例如AsyncTaskand Handler,但这需要对Java 的多线程实现有很好的了解。

于 2014-05-09T07:39:27.510 回答
22

AnAsyncTask用于执行一些后台计算并将结果发布到 UI 线程(带有可选的进度更新)。既然你不关心 UI,那么 a HandlerorThread似乎更合适。

您可以使用'方法生成背景Thread并将消息传递回主线程 。Handlerpost

于 2011-08-06T00:57:17.113 回答
9

线

Android 支持标准 Java线程。您可以使用标准线程和“<code>java.util.concurrent”包中的工具将操作置于后台。唯一的限制是您不能直接从后台进程更新 UI。

如果您需要从后台任务更新 UI,您需要使用一些 Android 特定的类。您可以为此使用“<code>android.os.Handler”类或“<code>AsyncTask”类</p>

处理程序

“<code>Handler”类可以更新 UI。句柄提供了接收消息和可运行对象的方法。要使用处理程序,您必须对其进行子类化并覆盖handleMessage()以处理消息。要处理Runable,您可以使用方法post();您只需要一个处理程序实例在您的活动中。

您的线程可以通过 方法sendMessage(Message msg)或发布消息sendEmptyMessage

异步任务

如果您Activity需要下载内容或执行可以在后台完成的操作,则可以AsyncTask维护响应式用户界面并将这些操作的进度发布给用户。

有关更多信息,您可以查看这些链接。

http://mobisys.in/blog/2012/01/android-threads-handlers-and-asynctask-tutorial/

http://www.slideshare.net/HoangNgoBuu/android-thread-handler-and-asynctask

于 2014-03-24T11:03:10.973 回答
8

Thread

您可以将新Thread的用于长时间运行的后台任务,而不会影响 UI 线程。从 java Thread,你不能更新 UI Thread。

由于普通的Thread对 Android 架构没有多大用处,因此引入了线程辅助类。

您可以在线程性能文档页面中找到您的查询的答案。

处理程序

AHandler允许您发送和处理Runnable与线程关联的消息和对象MessageQueue。每个Handler实例都与单个线程和该线程的消息队列相关联。

a 有两个主要用途Handler

  1. 安排消息和可运行文件在未来某个时间点执行;

  2. 将要在与您自己的线程不同的线程上执行的操作排入队列。

异步任务

AsyncTask允许正确和轻松地使用 UI 线程。此类允许您在 UI 线程上执行后台操作并发布结果,而无需操作线程和/或处理程序。

缺点:

  1. 默认情况下,应用程序会将AsyncTask其创建的所有对象推送到单个线程中。因此,它们以串行方式执行,并且与主线程一样,特别长的工作包会阻塞队列。由于这个原因,使用 AsyncTask 处理持续时间小于5ms 的工作项。

  2. AsyncTask对象也是隐式引用问题最常见的问题。AsyncTask对象也存在与显式引用相关的风险。

处理线程

您可能需要一种更传统的方法来在长时间运行的线程上执行工作块(与 AsyncTask 不同,它应该用于 5ms 工作负载),以及手动管理该工作流的能力。处理程序线程实际上是一个长时间运行的线程,它从队列中获取工作并对其进行操作。

线程池执行器

此类管理一组线程的创建,设置它们的优先级,并管理工作在这些线程之间的分配方式。随着工作负载的增加或减少,该类会启动或销毁更多线程以适应工作负载。

如果工作量比较大,单身HandlerThread不够,可以去ThreadPoolExecutor

但是,我希望在服务中运行一个套接字连接。这应该在处理程序或线程中运行,甚至是 AsyncTask 吗?根本不需要 UI 交互。它对我使用的性能有影响吗?

由于不需要 UI 交互,您可能不会选择AsyncTask. 普通线程没有多大用处,因此HandlerThread是最佳选择。由于您必须维护套接字连接,因此主线程上的 Handler 根本没有用。创建 aHandlerThreadHandlerHandlerThread.

 HandlerThread handlerThread = new HandlerThread("SocketOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());
 requestHandler.post(myRunnable); // where myRunnable is your Runnable object. 

如果您想与 UI 线程进行通信,您可以使用另外一个 Handler 来处理响应。

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Foreground task is completed:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

在你的Runnable,你可以添加

responseHandler.sendMessage(msg);

有关实施的更多细节可以在这里找到:

Android:线程中的吐司

于 2017-08-27T19:35:07.720 回答
5

AsyncTask设计用于在后台执行不超过几秒的操作(不推荐用于从服务器下载兆字节文件或计算 cpu 密集型任务,例如文件 IO 操作)。如果您需要执行长时间运行的操作,强烈建议您使用 java 本地线程。Java 为您提供了各种与线程相关的类来满足您的需求。用于Handler更新 UI 线程。

于 2014-03-24T09:46:24.737 回答
5

在我看来,线程并不是进行套接字连接的最有效方式,但它们确实在运行线程方面提供了最多的功能。我这么说是因为从经验来看,长时间运行线程会导致设备非常热和资源密集。即使是简单while(true)的也能在几分钟内加热手机。如果你说 UI 交互不重要,也许一个AsyncTask很好,因为它们是为长期流程设计的。这只是我对它的看法。

更新

请无视我上面的回答!我在 2011 年回答了这个问题,当时我在 Android 方面的经验远不如现在。我上面的回答具有误导性,被认为是错误的。我把它留在那里是因为很多人在下面评论它并纠正我,我已经吸取了教训。

在这个线程上有更好的其他答案,但我至少会给我更正确的答案。使用常规 Java 没有任何问题Thread;但是,您应该非常小心如何实现它,因为做错可能会占用大量处理器资源(最显着的症状可能是您的设备发热)。AsyncTasks 非常适合您希望在后台运行的大多数任务(常见示例是磁盘 I/O、网络调用和数据库调用)。但是,AsyncTasks 不应用于在用户关闭您的应用程序或将其设备置于待机状态后可能需要继续的特别长的进程。我会说在大多数情况下,任何不属于 UI 线程的东西都可以在AsyncTask.

于 2011-08-06T00:43:09.070 回答
2
public class RequestHandler {

    public String sendPostRequest(String requestURL,
                                  HashMap<String, String> postDataParams) {

        URL url;

        StringBuilder sb = new StringBuilder();
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode = conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                sb = new StringBuilder();
                String response;
                while ((response = br.readLine()) != null){
                    sb.append(response);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

}
于 2017-07-10T06:30:42.100 回答
2

这取决于根据要求选择哪一个

处理程序主要用于从其他线程切换到主线程,处理程序附加到一个循环器,它在队列中发布其可运行任务。因此,如果您已经在其他线程中并切换到主线程,那么您需要句柄而不是异步任务或其他线程

如果在不是循环器的主线程之外创建的处理程序不会在线程创建句柄时给出错误,则该线程需要成为一个循环器

AsyncTask用于执行在后台线程上运行的代码几秒钟并将其结果提供给主线程 ** *AsyncTask 限制 1. Async Task 不附加到活动的生命周期,即使其活动被破坏,它也会继续运行,而加载程序没有没有这个限制 2. 所有异步任务共享相同的后台线程来执行,这也会影响应用程序的性能

线程在应用程序中也用于后台工作,但它在主线程上没有任何回调。如果要求适合某些线程而不是一个线程并且需要多次执行任务,那么线程池执行器是更好的选择。例如,需要从多个 url 加载图像,如 glide。

于 2019-08-07T09:57:30.017 回答
1

让我试着用一个例子来回答这里的问题:) - MyImageSearch [请在此处参考主活动屏幕的图像 - 包含编辑文本/搜索按钮/网格视图]

我的图像搜索

MyImageSearch 的描述-一旦用户在编辑文本字段中输入详细信息并单击搜索按钮,我们将通过 flickr 提供的网络服务在互联网上搜索图像(您只需要在那里注册以获得密钥/秘密令牌) - 对于搜索,我们发送一个 HTTP 请求和 GET JSON 数据作为响应,其中包含单个图像的 url,然后我们将使用它们来加载网格视图。

我的实现-在主要活动中,我将定义一个内部类,它扩展 AsyncTask 以在 doInBackGround 方法中发送 HTTP 请求并获取 JSON 响应并更新我将用于通过 FlickrAdapter 更新我的 GridView 的 FlickrItems 的本地 ArrayList (扩展 BaseAdapter)并在 AsyncTask 的 onPostExecute() 中调用 adapter.notifyDataSetChanged() 以重新加载网格视图。请注意,这里的 HTTP 请求是一个阻塞调用,因此我通过 AsyncTask 完成了它。而且,我可以在适配器中缓存项目以提高性能或将它们存储在 SDCard 上。我将在 FlickrAdapter 中膨胀的网格在我的实现中包含一个进度条和图像视图。您可以在下面找到我使用的 mainActivity 的代码。

现在回答问题- 因此,一旦我们有了用于获取单个图像的 JSON 数据,我们就可以实现通过 Handlers 或 Threads 或 AsyncTask 在后台获取图像的逻辑。我们应该在这里注意,由于我的图像一旦下载必须显示在 UI/主线程上,我们不能简单地使用线程,因为它们无法访问上下文。在 FlickrAdapter 中,我能想到的选择:

  • 选择 1:创建 LooperThread [扩展线程] - 并通过保持该线程打开 [looper.loop()] 继续在一个线程中按顺序下载图像
  • 选择 2:利用线程池并通过 myHandler 发布可运行文件,其中包含对我的 ImageView 的引用,但由于 Grid View 中的视图被回收,因此索引 4 处的图像显示在索引 9 处可能会再次出现问题[下载可能多花点时间]
  • 选择3【我用过这个】:利用线程池向myHandler发送消息,其中包含与ImageView的索引和ImageView本身相关的数据,因此在执行handleMessage()时,我们将仅在currentIndex与索引匹配时更新ImageView我们尝试下载的图像。
  • 选择4:使用AsyncTask在后台下载图片,但这里我无法访问线程池中我想要的线程数,它因不同的android版本而异,但在选择3中我可以做出有意识的决定线程池的大小取决于所使用的设备配置。

这里是源代码:

public class MainActivity extends ActionBarActivity {

    GridView imageGridView;
    ArrayList<FlickrItem> items = new ArrayList<FlickrItem>();
    FlickrAdapter adapter;

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

        imageGridView = (GridView) findViewById(R.id.gridView1);
        adapter = new FlickrAdapter(this, items);
        imageGridView.setAdapter(adapter);
    }

    // To avoid a memory leak on configuration change making it a inner class
    class FlickrDownloader extends AsyncTask<Void, Void, Void> {



        @Override
        protected Void doInBackground(Void... params) {
            FlickrGetter getter = new FlickrGetter();

            ArrayList<FlickrItem> newItems = getter.fetchItems();

            // clear the existing array
            items.clear();

            // add the new items to the array
            items.addAll(newItems);

            // is this correct ? - Wrong rebuilding the list view and should not be done in background
            //adapter.notifyDataSetChanged();

            return null;
        }

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

            adapter.notifyDataSetChanged();
        }

    }

    public void search(View view) {
        // get the flickr data
        FlickrDownloader downloader = new FlickrDownloader();
        downloader.execute();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

我希望我的回答虽然很长,但有助于理解一些更精细的细节。

于 2014-08-24T18:43:52.250 回答
0

线

当您启动应用程序时,会创建一个进程来执行代码。为了有效地使用计算资源,可以在进程内启动线程,以便同时执行多个任务。因此,线程允许您通过有效利用 CPU 而无需空闲时间来构建高效的应用程序。

在 Android 中,所有组件都在一个称为主线程的单个线程上执行。Android系统对任务进行排队,并在主线程上一一执行。执行长时间运行的任务时,应用程序变得无响应。

为了防止这种情况,您可以创建工作线程并运行后台或长时间运行的任务。

处理程序

由于 android 使用单线程模型,UI 组件是非线程安全的,这意味着只有它创建的线程才能访问它们,这意味着 UI 组件应该只在主线程上更新。由于 UI 组件运行在主线程上,因此在工作线程上运行的任务无法修改 UI 组件。这就是 Handler 发挥作用的地方。处理程序在 Looper 的帮助下可以连接到新线程或现有线程,并在连接的线程上运行它包含的代码。

处理程序使线程间通信成为可能。使用 Handler,后台线程可以将结果发送给它,连接到主线程的处理程序可以更新主线程上的 UI 组件。

异步任务

android 提供的 AsyncTask 使用线程和处理程序来使在后台运行简单的任务并将结果从后台线程更新到主线程变得容易。

有关示例,请参阅android 线程、处理程序、异步任务和线程池

于 2018-03-28T07:19:39.283 回答
-2

Handler- 是线程之间的通信媒介。在android中,它主要用于通过处理程序创建和发送消息来与主线程通信

AsyncTask- 用于在后台线程中执行长时间运行的应用程序。使用 nAsyncTask您可以在后台线程中执行操作并在应用程序的主线程中获取结果。

Thread- 是一个轻量级进程,实现并发和最大cpu利用率。在 android 中,您可以使用线程来执行不触及应用程序 UI 的活动

于 2017-02-09T13:39:16.337 回答