【问题标题】:How to correctly use a Workerthread?如何正确使用 Workerthread?
【发布时间】:2015-03-27 16:12:38
【问题描述】:

我已经编写 Android 应用程序几个月了,现在我正在构建一个实际需要的应用程序。

因为我想让它工作得又快又好,所以我创建了一个 Workerthread 在后台执行各种任务,而 UI 可以...构建和工作等等。

它基于 Android Studio Drawer 应用蓝图。

在 Main.onCreate 中,我得到了我的 operator=new Operator(),它扩展了 Thread

现在,当加载一个新的 Fragment 时,它有时会调用 MainActivity.operator.someMethod()(我将 operator 设为静态,以便我可以在任何地方使用它),一段时间后我意识到,真正在后台运行的唯一任务是 operator 中的任务run() 方法和 Asynctask 我的登录片段运行。 UI 等待完成并因此由 UI 线程执行的所有其他操作。

所以我想:没问题!我的operator 得到一个handler,它内置在run() 中,我更改了这些任务:

public void run() {
    Looper.prepare();   //Android crashed and said I had to call this
    OpHandler = new Handler();
    LoadLoginData();
    [...Load up some Arrays with hardcoded stuff and compute for later use...]
}

public void LoadLoginData() {
    OpHandler.post(LoadLoginDataRunnable);
}
private Runnable LoadLoginDataRunnable = new Runnable() {
    @Override
    public void run() {
        if(sharedPreferences==null)
            sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
        sessionID=sharedPreferences.getString("sessionID", null);
        if(sessionID!=null) {
            postenID = sharedPreferences.getString("postenID", PID_STANDARD);
            postenName = sharedPreferences.getString("postenName", PID_STANDARD);
            context.QuickToast(sessionID, postenName, postenID);
        }
    }
};

context 是我的 MainActivity,我给了操作员一个参考,所以我可以发送 Toasts 进行调试。

但是现在,Runnables 似乎没有运行或完成,任何 Log.e 或 Log.d 的东西都没有到达控制台。

经过一些谷歌搜索和stackoverflow,每个人都在解释处理程序、异步任务和线程之间的区别。而且多任务示例总是只显示 new Thread(new Runnable{run(task1)}).start 乘以 3 的不同任务。

这成了我的大问题:

如何在更长的时间(~MainActivity 的生命周期)内,针对不同的任务正确地使用后台线程?

编辑:澄清一下,我也想直接解决我的特殊问题。

编辑 2:阅读 nikis 评论(谢谢)后,简单的答案似乎是“使用 HandlerThread 而不是线程”。我一回家就试试。

现在尝试 HandlerThread。似乎我的 OpHandler,在run() 中初始化,在run() 完成后被销毁或其他东西,不确定这里发生了什么(顺便说一句,我希望在这里得到解答的另一个谜团)。我在run() 完成后尝试使用它时立即收到 NullpointerException。

【问题讨论】:

  • 我建议您阅读 this tutorial 并重新访问您的代码。 AsyncTask 可能不是您想要的。 Android 首选的任务队列方式是IntentService
  • 别以为这是广告,而是blog.nikitaog.me/2014/10/11/…
  • 你需要在你的 operator#run 方法中最后做 Looper#loop。另一种选择是改用HandlerThread。完成后不要忘记退出线程。

标签: android multithreading android-handler


【解决方案1】:

让您的工作线程拥有一个任务队列。在run() 方法中,只需从队列中弹出一个任务并执行它。如果队列是空的,等待它填满。

class Operator extends Thread
{
     private Deque<Runnable> tasks;
     private boolean hasToStop=false;

     void run()
     {
          boolean stop=false;
          while(!stop)
          {
               sychronized(this)
               {
                    stop=hasToStop;
               }
               Runnable task=null;
               synchronized(tasks)
               {
                    if(!tasks.isEmpty())
                         task=tasks.poll();
               }
               if(task!=null)
                   task.run();
          }
     }

     void addTask(Runnable task)
     {
          synchronized(tasks)
          {
               tasks.add(task);
          }
     }

     public synchronized void stop()
     {
          hasToStop=true;
     }
}

【讨论】:

  • 我想知道投反对票的原因,以便我可以升级答案(或者至少学习一些我不知道的东西)。
  • 不是我的反对意见,也许它没有解决我的特殊示例或处理程序为什么不起作用的问题。 @topic:所以我的线程必须一直运行?我希望我可以让我的线程“按需”工作,这样它在不需要的时候就不需要 CPU 能力,也因为它,如前所述,只有在我切换片段或登录/注销或其他一些东西时才会得到任务。
  • @Marlon Regenhardt 如果您希望线程大部分时间保持空闲状态,只需让他在每次迭代时都睡一会儿,这样对队列的检查就不会那么频繁了。这应该使线程的开销可以忽略不计。
  • 同步布尔访问不会为您带来任何好处。此外,我建议使用LinckedBlockingQueue,它可以处理大部分工作。如果队列为空,take() 方法将阻塞。
  • 我更新了我的问题。 Cyroxis:我可以举一个咖啡 sn-p 的例子吗? (最好使用我的用例)。
猜你喜欢
  • 1970-01-01
  • 2020-12-04
  • 2012-07-12
  • 2013-01-21
  • 2021-10-24
  • 2011-12-22
  • 2020-10-14
  • 2015-12-10
  • 2021-01-15
相关资源
最近更新 更多