2012-01-09 59 views
5

关于ThreadsHandlersAsyncTask的正确使用有很多问题。 (如here & here处理程序与线程是否有任何真正的性能影响?

这些问题很好地解决了何时使用什么问题。我的问题更多的是关于某些类型案件的绩效影响。

作为一个例子,我经常会看到其他人编写代码,他们使用Threads只是为了能够为将来安排一些代码执行。无论何时,当我看到这一点时,我本能地想要重构代码以使用Handler,并且只是推迟了runnable

下面是一个例子,其中一个线程用于更新一些媒体的搜索栏,用mediaplayer进行播放,然后是我会这样做的方式。

我看到了很多:

if (positionTracker != null && positionTracker.isAlive() 
       && !positionTracker.isInterrupted()) { 
      return; 
     } 

     positionTracker = new Thread(new Runnable() { 

      public void run() { 
       int currentPosition = 0; 
       int total = player.getDuration(); 
       while (player != null && CurrentPosition < total) { 
        try { 
         Thread.sleep(1000); 
         currentPosition = player.getCurrentPosition(); 
        } catch (InterruptedException e) { 
         return; 
        } catch (Exception e) { 
         return; 
        } 


        if (someListener != null) { 
         someListener.onEvent(); 
        } 

       } 
      } 

     }, "position tracker thread"); 

     positionTracker.start(); 

,我喜欢做的方式:

Runnable trackPositionRunnable = new Runnable() { 
      @Override 
      public void run() { 
       currentPosition = player.getCurrentPosition(); 
       if (someListener != null) { 
        someListener.onEvent(); 
        mHandler.postDelayed(this, 1000); 
       } 
      } 
     }; 

     mHandler.post(trackPositionRunnable); 

很显然,我的首选方法是有点更容易阅读和更多简洁。 但是性能影响是什么?在性能方面,单向方法比其他方法更好吗?如果是这样,为什么?

回答

1

正确性:你的第一个例子是充满了危险,因为MediaPlayer必须在一个线程中创建了自己的Looper和运营从任何其他线程可能会导致错误。同样,由于您的someListener.onEvent()大概是更新了UI,因此最好知道是发布到UI线程上的处理程序。

性能:我没有测量要提供,但在您的示例中,运行时成本是(线程切换)+(处理器开销),而不仅仅是(处理器开销)。因此,对于任何线程切换开销> 0,线程更昂贵。另一方面,如果您的整个应用程序都以您喜欢的风格编码,并且任何一段代码都很慢并且同步,那么您的应用程序就会感觉迟缓。

这就是为什么任何潜在的缓慢或同步需要朝向线程(或服务)风格的原因,尽管感觉更复杂且容易出错。您的特殊MediaPlayer示例不是用于制作此案例的完美海报。

+0

谢谢!所以,你说的同步开始时是一个你无法避免使用线程/服务的问题,但对于像我的例子这样的小任务来说,处理程序可能是最简单的方法呢? – LuxuryMode 2012-01-09 20:50:08

+1

我会说有点不同。将所有耗时的工作从UI线程中移走。在主UI线程上保留低成本的用户界面。请记住,在多个HandlerThreads之间发布是一个相当干净的下一步; post接口是一种干净的方式来请求事物,而少量的工作线程可能是异步的。但要非常注意确保您的所有UI操作都发生在主UI线程上。 – 2012-01-09 21:07:34

+0

至于“开关开销”:因为有一个线程在任何情况下运行,我没有看到任何区别。 “已处理”线程也将以任何其他常规线程的方式进入和退出。 – 2013-09-04 14:10:38

4

每种方法都取决于您计划在该Runnable中执行的操作,以确定它是否有用。它们之间最大的区别在于你是否打算接触UI。在Android中,您无法触摸UI线程中的UI组件(您的媒体播放器示例正在使用原始线程打破此规则)。因为这个规则立即将你可以或不可以做的每种方法分开。这些方法之间的性能差异可以忽略不计,因为运行后台作业所花费的时间将超过它们之间的差异。

处理程序通常使用另一个后台线程来执行逻辑,但它取决于哪个线程构造了处理程序。如果处理程序是在UI线程上构建的(作为对回调onSomething的响应),那么您的Runnable将在UI线程内运行,从而可以触摸UI组件。但是,如果您创建的UI线程无法触及UI组件,则Runnables会将其发布到它。处理程序在UI线程上创建的缺点意味着您不在后台执行这些操作,因此如果作业需要很长时间才能运行,则会锁定用户界面直至完成。虽然处理程序从非UI线程运行将解决任何锁定用户界面的问题。他们需要更多的工作来设置,并且您仍然必须应对如何安全地更新UI以响应您的后台工作(即,如果要更新UI,您仍然必须将另一个可运行内容发布回UI线程)。

原始线程不会锁定UI,因为它们独立于UI线程运行,但无法触及UI组件。这意味着您必须执行任何代码才能在UI线程上更新UI,这意味着需要编写更多代码才能让UI线程运行它。这可能非常复杂。由于使用它们的复杂性,真正应该避免原始线程。

后台任务最常见的例子是等待来自服务器的响应。大多数库会阻塞,直到服务器发送响应,这意味着您无法在UI线程上调用它们,否则在服务器返回呼叫之前,您的用户将被阻止执行任何操作。它们不仅会被阻塞,而且UI也无法自我更新以显示微调或其他外观。这最好推到后台线程。技术上的处理程序和线程可以做到这一点,但处理程序必须专门构建,以便它们将使用真正的后台线程。

这是AsyncTask胜过处理程序的地方,因为它同时处理真正的后台作业和UI更新。它有一个部分用于在后台执行一些长时间运行的操作,并且它有一个用于在UI线程完成时更新UI的部分。它甚至有一个可选的进度部分,因此您可以在任务运行时向UI提供任何中间进度。 AsyncTask的缺点是它们必须结束。后台作业继续运行以定期检查是否发生了某些事情,睡眠和检查更多,这些都不利于AsyncTask模型。然而,这并不是说你不能使用Handler来定期启动AsyncTask,但是为​​了讨论的完整性,我提到了这一点。

最后,使用原始线程并不是那么容易,甚至“更好”,因为处理程序几乎可以做任何线程可以用较少代码执行的任何操作。但是,处理程序在确定Runnable在哪个线程上执行时非常棘手。通常它是UI线程,并且在技术上设置它来使用非UI线程是棘手的。这两个选项都会遇到UI更新问题,因为您必须在真正的后台作业结束时执行额外的工作才能运行UI作业。 AsyncTask是我做后台作业的首选方法。

-2

如果你正在处理的线程,我建议你同时使用一个处理程序与它:

Handler handle = new Handler(); 
Thread new Thread() 
{ 
    @Override 
    public void run() 
    { 
    try 
    { 
     handle.postDelayed(new Runnable() 
     { 
      @Override 
      public void run() 
      {   
      "your code goes here" 
      } 
     },delay); 
} 
     catch(Exception e) 
     { 
     e.printStackTrace(); 
     } 
    }; 
    } 

这样可以延迟执行的,只要你愿意,也可以使用postThread.sleep(delay),我更喜欢这些日子。

3

它不是Handler vs Threads。他们是完全不同的东西:

线程:是旧的Java类实现thread of execution。作为Java API的其他部分,它们也可以在Android上使用。注意,在Java语言的后期版本中,它们被Executors framework所取代,所以推荐的做法是使用Executor/Runnable,但由于它的简单性,线程有时仍被使用。

处理器:这个类是仅在Android中使用,它主要是与现有Thread沟通的机制。您发送目标线程Messages或Runnables,并且您还可以安排此通信。

当您需要将某些内容发送给某个主题时,您通常需要一个Handler。例如,这个“东西”可以是封装的数据进行处理,或者可以在该线程中执行的Runnable。每个处理程序通常都在实例化时与当前线程关联,除非使用更奇特的构造函数。一个典型的用例是在主线程(这是UI线程)中安排重复任务。请注意,对于安排一次性任务,有一个最简单的方法:Activity.runOnUithread

现在对于需要在一个线程中运行比主一个不同的后台任务:在这两种方法,你就会有一个线程在运行,但创建一个处理程序意味着Android将启动一个新的消息队列的线程,这是普通线程不需要的东西,因此会有一些开销。因此,如果你需要启动一个可以在不接收信息的情况下运行的线程,我会说简单的线程是首选。但是如果你需要一个执行队列来调度Runnables,你可以在Timer,Executor,“处理过的”线程或者AlarmManager之间进行选择。处理程序的优点是它们可以附加到应用程序中任何已经存在的线程,而定时器和执行程序将在它们设置时在内部启动一个新的专用线程。

相关问题