Filedownloader: FetchDataTask.run timeout

Created on 9 Jun 2017  ·  15Comments  ·  Source: lingochamp/FileDownloader

06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: java.net.SocketTimeoutException: timeout
06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:593)
06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: at okhttp3.internal.http2.Http2Stream$FramingSource.waitUntilReadable(Http2Stream.java:374)
06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: at okhttp3.internal.http2.Http2Stream$FramingSource.read(Http2Stream.java:337)
06-09 19:14:28.193 23503-23503/com.wodi.who W/System.err: at okio.ForwardingSource.read(ForwardingSource.java:35)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at okio.RealBufferedSource$1.read(RealBufferedSource.java:409)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at java.io.InputStream.read(InputStream.java:162)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at com.liulishuo.filedownloader.download.FetchDataTask.run(FetchDataTask.java:106)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at com.liulishuo.filedownloader.download.DownloadRunnable.run(DownloadRunnable.java:101)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
06-09 19:14:28.194 23503-23503/com.wodi.who W/System.err: at java.lang.Thread.run(Thread.java:818)

当使用https://github.com/Jacksgong/filedownloader-okhttp3-connection来支持Http 302问题时,OPPO R9s上报了上述问题,操作是,执行了第一次下载,第二次下载就会报错,使用的filedownloader库是1.5.3

invalid

Most helpful comment

找到问题根源了:

该问题不是BUG,而是该资源网络连接十分不稳定。

经常回调started后,就要等待很久很久,然后就回调error超时了。这个过程将没有任何响应,原因是一直在建连。

started后,在与资源后台建连:

  1. 这个期间如果你再启动该任务,就会收到warn这个符合预期的,因为FileDownloader中已经有一个完全相同的任务正在执行;
  2. 只要有建立成功过连接过,本地都会有临时文件.temp这个是符合预期的,该文件是用于断点续传,下一次启动下载的时候会继续将数据写入该文件,直到下载完成后,会将该文件重命名为目标文件;
  3. 如果说采用okhttp3才有该问题,那么应该是okhttp3网络连接相关处理与默认的连接器有所不同,也可能是okhttp3的bug,针对这个,有三个建议: 1. 不使用okhttp3连接组件、2. 尝试使用最新版本okhttp3看是否已经解决了该问题、3. 到okhttp3中提issue

All 15 comments

感谢反馈。不过....

  1. 按照你的错误栈定位(FetchDataTask 106行 InputStream.read),这个应该是1.5.2版本。
  2. SocketTimeoutException 并非是302等问题,而是在弱网/远端服务器网络链路出现故障时无论任何链接都有可能遇到的socket 连接/读取超时报的错误。
  3. 这个应该是不会引发crash,而是按照预期的回调到FileDownloadListener#error中(代码中有catch了其父类型IOException的所有错误,也会覆盖这个catch),这是一个正确的反馈,因此这个不是bug,而是希望遇到该网络问题时,上层做对应的处理。

可是我下载成功一次后,再下载别的就一直报这个错,真的很奇怪,我不用Okhttp就可以一直下载@Jacksgong

好的,你做下以下验证:

  1. 有试过引用最新版本的okhttp3吗?直接引用filedownloader-okhttp3-connection,同时引用最新版本okhttp3即可(关于引用冲突可以这篇文章中的引用关系)
  2. 所有链接都必现这个问题吗?有试过别的链接吗?是否是你指的那个别的连接是存在问题的,换成别的连接,或者和第一个连接调换试试。
  3. 该问题是和进程有关吗,是否是下载成功一次,之后无论下载什么连接都是失败而且都是SocketTimeoutException,然后杀掉进程,又是第一次成功,后面又失败呢?

我刚刚在demo中验证:

  1. 我引入 cn.dreamtobe.filedownloader:filedownloader-okhttp3-connection:1.0.0
  2. 我在DemoApplication中注册: FileDownloader.init(this, new DownloadMgrInitialParams.InitCustomMaker().connectionCreator(new OkHttp3Connection.Creator()));
  3. 启动应用,然后进入单任务测试页面,依次下载每一个任务,每一个都成功。

你看看我上面的步骤是否与你的复现步骤相符合。

该问题,应该是你的 别的连接 那个连接存在问题。我这边按照你的复现步骤无法复现该问题。建议你也在demo中进行尝试,替换连接进行尝试。 有进一步跟进该Issue将被重新打开。

我的情况和 该问题是和进程有关吗,是否是下载成功一次,之后无论下载什么连接都是失败而且都是SocketTimeoutException,然后杀掉进程,又是第一次成功,后面又失败呢? 很吻合

public int downloadCocosUpdate(final Context context, final String downloadUrl, final String gamePath, final String passWord, final Map params) {
downloadId = 0;

    FragmentTransaction ft = ((BaseActivity) context).getSupportFragmentManager().beginTransaction();
    Fragment prev = ((BaseActivity) context).getSupportFragmentManager().findFragmentByTag(DOWNLOAD_DIALOG_TAG);
    if (prev != null) {
        ft.remove(prev);
    }
    final DownloadDialogFragment newFragment = DownloadDialogFragment.newInstance();
    newFragment.show(ft, DOWNLOAD_DIALOG_TAG);

    final String newGamePath = gamePath.contains("wolf") ? "wolf" : gamePath;

    // 首先删除文件夹下面的文件
    Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            // 首先删除文件
            File file = new File(BaseConfig.COCOS_FILE_DIR +
                    (TextUtils.isEmpty(newGamePath) ? "" : File.separator + newGamePath));
            FileUtil.deleteDirRecursive(file);

            subscriber.onCompleted();
        }
    })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<String>() {
                @Override
                public void onCompleted() {
                    // 执行下载过程
                    downloadId = FileDownloader.getImpl().create(downloadUrl)
                            .setPath(BaseConfig.COCOS_FILE_DIR + (TextUtils.isEmpty(newGamePath) ? "" : File.separator + newGamePath)
                                    + File.separator + COCOS_GAME_UPDATE)
                            .setListener(new FileDownloadListener() {
                                @Override
                                protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                                }

                                @Override
                                protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) {
                                }

                                @Override
                                protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                                    final int dl_progress = (int) (soFarBytes * 100L) / totalBytes;

                                    Timber.d("progress: " + dl_progress);

                                    ((Activity) context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            if (newFragment.getDownloadProgressBar() != null) {
                                                newFragment.getDownloadProgressBar().setProgress(dl_progress);
                                            }
                                        }
                                    });
                                }

                                @Override
                                protected void blockComplete(BaseDownloadTask task) {
                                }

                                @Override
                                protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) {
                                }

                                @Override
                                protected void completed(BaseDownloadTask task) {
                                    ((Activity) context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            if (newFragment.getDownloadProgressBar() != null) {
                                                newFragment.getDownloadProgressBar().setProgress(100);
                                            }
                                        }
                                    });

                                    // 同时, 对下载成功的文件进行解压
                                    Observable.create(new Observable.OnSubscribe<String>() {
                                        @Override
                                        public void call(Subscriber<? super String> subscriber) {
                                            try {
                                                unZipCocos(BaseConfig.COCOS_FILE_DIR +
                                                        (TextUtils.isEmpty(newGamePath) ? "" : File.separator + newGamePath), passWord + "_xiangbudao", params);
                                            } catch (ZipException e) {
                                                e.printStackTrace();
                                            } finally {
                                                DialogFragment prev = (DialogFragment) ((BaseActivity) context).getSupportFragmentManager().findFragmentByTag(DOWNLOAD_DIALOG_TAG);
                                                if (prev != null) {
                                                    prev.dismissAllowingStateLoss();
                                                }
                                            }
                                        }
                                    })
                                            .subscribeOn(Schedulers.io())
                                            .observeOn(AndroidSchedulers.mainThread())
                                            .subscribe();
                                }

                                @Override
                                protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                                }

                                @Override
                                protected void error(BaseDownloadTask task, Throwable e) {
                                    e.printStackTrace();

                                    ((Activity) context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            DialogFragment prev = (DialogFragment) ((BaseActivity) context).getSupportFragmentManager().findFragmentByTag(DOWNLOAD_DIALOG_TAG);
                                            if (prev != null) {
                                                prev.dismissAllowingStateLoss();
                                            }

                                            Toast.makeText(context, context.getResources().
                                                    getString(R.string.str_please_retry), Toast.LENGTH_SHORT).show();
                                        }
                                    });
                                }

                                @Override
                                protected void warn(BaseDownloadTask task) {
                                }
                            }).start();
                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(String s) {

                }
            });

    return downloadId;
}

代码是这样写的,然后Activity中调用

你在demo project中可以复现该问题吗,根据我的步骤: https://github.com/lingochamp/FileDownloader/issues/606#issuecomment-307375256 ,我刚刚按照这个步骤无法复现你的问题。

@或者简单点,我刚刚已经push上去了,你可以拉下来case/okhttp3分支,这个分支就是1.5.3,并且适配了okhttp3,

  • 然后你进入单任务测试,依次下载看看,如果没有问题,估计就是你连接的问题了,
  • 这时候你可以到SingleTaskTestActivity中替换createDownloadTask中的连接为你的连接进行测试看看。

有时下载下来的文件是.temp文件,而且貌似一出现这种情况,就会稳定复现,下不下来文件

cn.dreamtobe.filedownloader:filedownloader-okhttp3-connection:1.0.0

不加这个就不存在这个问题

@Jacksgong 这个可以给点改正的建议吗?

我明天给你看下,今晚有其他重要的事情要处理。

@wanghaihui

  • .temp文件是下载过程中的临时文件,文件只要没有完成下载都是.temp这是正常的情况。
  • 回调warn是当前已经有该任务在下载,又启动相同任务,新启动的任务就会接收到warn的回调,原下载不会受到影响。

FileDownloader并没有对你的连接做特殊处理,而okhttp3支持处理了连接层的事务。

找到问题根源了:

该问题不是BUG,而是该资源网络连接十分不稳定。

经常回调started后,就要等待很久很久,然后就回调error超时了。这个过程将没有任何响应,原因是一直在建连。

started后,在与资源后台建连:

  1. 这个期间如果你再启动该任务,就会收到warn这个符合预期的,因为FileDownloader中已经有一个完全相同的任务正在执行;
  2. 只要有建立成功过连接过,本地都会有临时文件.temp这个是符合预期的,该文件是用于断点续传,下一次启动下载的时候会继续将数据写入该文件,直到下载完成后,会将该文件重命名为目标文件;
  3. 如果说采用okhttp3才有该问题,那么应该是okhttp3网络连接相关处理与默认的连接器有所不同,也可能是okhttp3的bug,针对这个,有三个建议: 1. 不使用okhttp3连接组件、2. 尝试使用最新版本okhttp3看是否已经解决了该问题、3. 到okhttp3中提issue

好的,多谢~

Was this page helpful?
0 / 5 - 0 ratings

Related issues

itsmallsun picture itsmallsun  ·  5Comments

hu499019771 picture hu499019771  ·  3Comments

genkido picture genkido  ·  5Comments

logan62334 picture logan62334  ·  3Comments

asdf19910719 picture asdf19910719  ·  4Comments