android trigger配置 - Handler vs AsyncTask vs Thread




gerrit触发jenkins jenkins+gerrit+git搭建ci系统 (11)

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

UPDATE

请忽略我的上面的答案! 我在2011年回答了这个问题,当时我对Android的经验远不及现在。 我上面的答案是误导性的,被认为是错误的。 我要离开它,因为很多人在纠正我的意见后对它进行了评论,并且我吸取了教训。

在这个线程上还有其他更好的答案,但我至少会给我更正确的答案。 使用常规Java Thread没有任何问题; 然而,你应该真的小心你的实现方式,因为做错了可能是处理器密集型的(最显着的症状可能是你的设备正在升温)。 AsyncTask对于你想在后台运行的大多数任务非常理想(常见的例子是磁盘I / O,网络调用和数据库调用)。 但是, AsyncTask不应该用于在用户关闭应用程序或将其设备置于待机状态后可能需要继续的特别长的进程。 我会说在大多数情况下,任何不属于UI线程的东西都可以在AsyncTask

我对Android中的HandlersAsyncTaskThreads之间的差异略有困惑。 我在这里阅读了很多博客和问题。

Handler是后台线程,可让您与UI进行通信。 例如更新进度条应通过Handler完成。 使用处理程序,您具有MessagingQueues的优势,所以如果您想要安排消息或更新多个UI元素或者有重复任务。

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

但是Thread不能与UI交互,提供更多的“基本”线程,并且你错过了AsyncTask所有抽象。

不过,我想要在服务中运行套接字连接。 应该在一个处理程序或线程中运行,还是在AsyncTask ? UI交互不是必需的。 它在我使用的性能方面有所不同吗?

同时, documentation得到了重大改进。


AsyncTask被设计为在后台执行不超过几秒钟的操作(不建议从服务器下载数兆字节的文件或计算cpu密集型任务,如文件IO操作)。 如果您需要执行长时间运行的操作,强烈建议您使用Java本机线程。 Java为你提供了各种线程相关的类来完成你所需要的。 使用Handler程序更新UI线程。


Thread

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

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

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

Handler

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

处理程序有两个主要用途:

  1. 安排消息和可运行子程序在将来的某个点执行;

  2. 排队一个动作,以便在不同于你自己的线程上执行。

documentation

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

缺点:

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

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

HandlerThread

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

ThreadPoolExecutor

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

如果工作负载更大并且单个HandlerThread不够用,则可以使用ThreadPoolExecutor

不过,我想要在服务中运行套接字连接。 应该在一个处理程序或线程中运行,还是在AsyncTask中运行? UI交互不是必需的。 它在我使用的性能方面有所不同吗?

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

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

如果你想回到UI线程,你可以使用一个更多的处理程序来处理响应。

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:在一个线程中敬酒


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

你可以产生一个后台Thread并通过使用Handlerpost方法将消息传递回你的主线程。


线

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

在Android中,所有组件都在一个被调用的主线程上执行。 Android系统队列任务并在主线程上逐个执行它们。 当执行长时间运行的任务时,应用程序变得无响应。

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

处理器

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

处理程序使得可以进行线程间通信。 使用Handler,后台线程可以向其发送结果,并且连接到主线程的处理程序可以更新主线程上的UI组件。

的AsyncTask

由android提供的AsyncTask使用线程和处理程序在后台运行简单的任务,并将后台线程的结果更新为主线程。

有关示例请参阅android线程,处理程序,asynctask和线程池


Thread

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

如果您需要从后台任务更新UI,则需要使用一些Android特定的类。 你可以使用类“ android.os.Handler ”或类“ AsyncTask

Handler

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

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

documentation

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

欲了解更多信息,你可以看看这些链接。

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

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


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();
    }

}

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

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

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

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

  • 选择1:创建一个LooperThread [extends线程] - 继续在一个线程中继续下载图像,方法是保持此线程打开[looper.loop()]
  • 选择2:利用线程池并通过包含对我的ImageView的引用的myHandler发布可运行子,但由于网格视图中的视图被回收,所以再次出现索引为9时显示索引4处的图像的问题[可以下载花更多的时间]
  • 选择3 [我使用过这个]:利用线程池并向myHandler发送消息,其中包含与ImageView索引和ImageView本身相关的数据,因此在执行handleMessage()时,只有当currentIndex匹配我们尝试下载的图像。
  • 选择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);
    }
}

我希望我的答案能够帮助你理解一些细节。


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

所以在AsyncTaskHandler没有魔法。 作为开发人员,他们只是让你的工作更轻松。

例如:如果程序A调用方法A(),则方法A()可以与程序A在不同的线程中运行。您可以使用以下方法轻松验证它:

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

为什么你应该使用新的线程? 你可以谷歌的。 许多原因。

那么, ThreadAsyncTaskHandler之间的区别是什么?

AsyncTaskHandler是用Java编写的(在内部它们使用一个Thread ),所以您可以使用HandlerAsyncTask执行所有AsyncTask ,您也可以使用Thread来实现。

HandlerAsyncTask真的可以帮助你吗?

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

这就是为什么你应该使用HandlerAsyncTask 。 他们为你做了大部分工作,你只需要知道要覆盖哪些方法。

HandlerAsyncTask之间的区别是:当调用者线程UI线程时使用AsyncTask 。 这是android文档所说的:

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

我想强调两点:

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

2)不需要操作处理程序。 (意思是:你可以使用Handler而不是AsyncTask,但AsyncTask是一个更简单的选项)。

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

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

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

起初他们可能看起来很奇怪。 只要理解每个线程都有每个消息队列(就像一个待办事项列表),线程就会接收每条消息并执行,直到消息队列为空(就像完成工作并上床睡觉一样)。 所以,当Handler通信时,它只是给调用者线程发送消息,并等待处理。 复杂? 请记住, Handler可以以安全的方式与调用者线程进行通信。


正如沃格拉网站上的处理程序,AsyncTask和Loaders背景处理教程所说:

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

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

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

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

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

关于AsyncTask ,正如documentation指出documentation那样:

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

AsyncTask被设计成围绕ThreadHandler的辅助类,并不构成通用线程框架。 理想情况下,AsyncTasks应该用于短操作(最多几秒)。如果您需要让线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,例如执行器,ThreadPoolExecutor和FutureTask。

2015年5月更新:我发现了一系列涵盖此主题的优秀系列讲座

这是谷歌搜索: 道格拉斯施密特讲座android并发和同步

这是YouTube上第一场演讲的视频

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

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

重要说明:如果您正在考虑使用AsyncTask来解决线程问题,那么您应该首先查看ReactiveX/RxAndroid以获取更适合的编程模式。 通过示例学习RxJava 2 for Android是获得概述的非常好的资源。


进程是应用程序的执行实例。

那是什么意思? 那么,例如,当您双击Microsoft Word图标时,就会启动运行Word的进程。

线程是进程内的执行路径。 另外,一个进程可以包含多个线程。

当您启动Word时,操作系统将创建一个进程并开始执行该进程的主线程。





android multithreading android-asynctask android-handler