【问题标题】:Java - Android : Thread being called (run) twiceJava - Android:线程被调用(运行)两次
【发布时间】:2014-05-26 10:17:03
【问题描述】:

我想要一些关于 Java - Android 多线程的帮助

在学习以多线程方式开发我的应用程序以利用不断增长的多核设备市场份额(现在大多数设备是四核,有些甚至是八核)时,我跑了一个我的线程被调用两次或运行两次的情况。

我只是不知道为什么以及如何。

[编辑 3] 好吧,我缩小了问题范围:我从 onResume() 方法中调用了 AsyncTask。虽然我的应用程序在测试期间没有失去焦点(这意味着在返回焦点时调用onPause() 然后返回onResume(),在这种情况下我的线程将运行两次),我通过移开调用解决了这个问题到FetchFriendsList 到另一个地方。 到目前为止一切都很好,但是由于在我的测试中该应用程序没有失去焦点,或者它可能没有但我无法目睹它(!),我认为背后还有另一个原因,所以我会说我的问题没有完全解决.. . 至少目前。不过它确实有效。也许我确实解决了这个问题,但我不知道如何:( [编辑 3 结束]

我正在实现最后一个 Facebook SDK,并使用它来获取最终用户朋友列表,这似乎可以完成工作。 由于我在 AsyncTask 中运行此操作,因此我没有使用 request.executeAsync()。 相反,我使用的是request.executeAndWait()。 Facebook JavaDoc 确实声明只有当我不在主 UI 线程中时才必须使用此方法,这是我的情况,否则我会得到 NetworkOnMainThreadException

无论如何,这就是奇怪行为发生的地方。

private final ArrayList<GraphUser> userFriendsList = new ArrayList<GraphUser>(); 
public final void fetchFriendsList() {
    if (this.session != null && this.session.isOpened()) {
        final Request requestUserFriendsList = Request.newMyFriendsRequest(
                this.session, new Request.GraphUserListCallback()                                         
                    public final void onCompleted(final List<GraphUser> users, final Response response) {
                        if (users != null && users.size() > 0) {
                            Log.v("Retrieved Friends List -> ", String.valueOf(users.size()));
                            userFriendsList.addAll(users);
                        }
                    }
                }
        );
        if (this.asyncFlag)
            requestUserFriendsList.executeAsync();
        else
            requestUserFriendsList.executeAndWait();
    }
}

就我而言,asyncFlag 设置为 false,因为我需要按特定顺序同步执行操作:

  1. 获取用户好友列表(不在主 (UI) 线程上)
  2. 在设备上保存好友列表(单独的新线程)
  3. 在服务器上保存好友列表(单独的新线程)

按照这种模式,userFriendsList.addAll(users); 行被调用两次。 在logcat中,Log.v也显示了两次,最后用调试器查看,用户好友列表的内容是重复的。

但这还不是全部……第 2 步和第 3 步确实是两个独立的线程,它们都是在同一个方法中创建和生成的:public final void asyncSaveFacebookFriendsList()

你猜怎么着,这个方法甚至被称为两次

为什么?

一开始我是这样调用第 2 步和第 3 步的方法的:

[...]
userFriendsList.addAll(users);
asyncSaveFacebookFriendsList(); // it was private before
[...]

这就是问题开始的地方,因为两条线路都运行了两次。

所以我想,好吧,我以后会这样称呼它:

[...]
fetchFriendsList();
asyncSaveFacebookFriendsList(); // it is now public
[...]

但问题仍然存在。 如果我不打电话给public final void asyncSaveFacebookFriendsList(),那么什么都不会运行两次

为什么会出现这个问题?有什么我在 Java 线程中没有得到的东西吗?

我不认为这与 Facebook SDK 有某种关系,因为遵循相同的模式(同时也这样做),我在获取和存储最终用户 Twitter 好友列表时遇到了同样的问题。

所以我相信我做错了什么。有人知道在什么情况下线程被调用两次?

注意:所有线程都以这种方式启动:thread.start()。我没有使用任何ThreadPoolExecutorService

如果您需要更多背景上下文: AsyncTask的内容:(不用问为什么要Void和Long,相关的无关代码我都去掉了)

private final class FetchFriendsLists extends AsyncTask<Long, Integer, Void> {
        protected final Void doInBackground(final Long... params) {
            if (params[0] != Long.valueOf(-1)) {
                [...]
                twitterAPI.fetchUserFriendsList();
                publishProgress(1, -1);
            }
            if (params[1] == Long.valueOf(0)) {
                [...]
                facebookAPI.fetchFriendsList();
                publishProgress(-1, 0);
            }
            return null;
        }

        protected final void onProgressUpdate(Integer... flags) {
            super.onProgressUpdate(flags);
            if (flags[0] != -1)
                twitterAPI.asyncSaveFacebookFriendsList();
            if (flags[1] == 0)
                facebookAPI.asyncSaveFacebookFriendsList();
        }
    }

如您所见,我在 onPublishProgress() 中开始第 2 步和第 3 步,它在主 UI 线程上运行。之前它在 doInBackground() 方法中:这两种情况都会出现问题!

[编辑] 经过进一步测试,似乎任何类型的代码实际上都运行了两次。 我创建了一个名为 test 的简单方法,其中打印了一个计数器。计数器也会增加两倍!

【问题讨论】:

  • 这里没有什么可以解释你的问题。关键问题是如何启动和停止线程、如何同步它们以及它们拥有哪些共享内存。这些都在这里不可见。
  • @david.pfx 我同意你的看法。但正如我在第二次编辑中所述,问题可能来自 Android 生命周期。从onCreate() 方法调用的AsyncTask,即使用户不交互(锁屏、应用失去焦点等)也会被调用,因此我的任务可能会被调用两次。我解决了这个问题,现在它工作得很好。但是,我并不完全相信,所以我会做一些进一步的测试并在稍后提供完整的答案。

标签: android multithreading android-asynctask thread-safety


【解决方案1】:

为什么要使用 onProgressUpdate?¿?

onProgressUpdate(进度...),[...]。此方法用于显示任何形式的进度 后台计算仍在执行时的用户界面。 例如,它可以用于动画进度条或显示登录 一个文本字段。

这不是在请愿结束时使用,而是在进度增加时使用。
阅读:
http://developer.android.com/reference/android/os/AsyncTask.html

你需要使用:

 protected void onPostExecute(Long result) {

【讨论】:

  • 感谢您的回答。我同意它,但是,我试图了解我的代码在何处、何时以及如何运行两次,因此我使用了在主 UI 线程上运行的onProgressUpdate() 来查看会发生什么。这绝对不是正确的用法,但它仅用于调试。我向你保证我后来删除了代码。
  • @Mackovich 如果 FetchFriendsList 类不是您真正的类,请提供原始类,提供原样的代码,以便我们阅读并查找错误。
  • 正如我的 OP 上次编辑中所解释的,我将问题缩小到处理 Android Activity 生命周期。到目前为止一切顺利:我的线程被注意运行两次:我从onResume() 方法中移走了FetechFriendsList 的调用。我很傻,但我仍然处于学习发展模式。感谢您的帮助!
  • 欢迎您!欢迎来到安卓;)。如果对您有帮助,您可以接受答案。
  • 谢谢@Sulfkain!你真好,虽然......在我发表评论这么久之后这么说有点奇怪^^现在我在 Xamarin 上! :)
猜你喜欢
  • 1970-01-01
  • 2013-11-22
  • 2017-09-24
  • 1970-01-01
  • 1970-01-01
  • 2021-03-04
  • 1970-01-01
  • 2017-04-08
  • 2016-01-27
相关资源
最近更新 更多