在 AsyncTask 中等待异步网络任务的最佳方式

Best way to wait for an asychronous network task within AsyncTask

我正在通过我的 android 应用程序中的 Vimeo API 从我的 Vimeo Pro 帐户中提取特定 vimeo 视频的 link,然后通过异步任务。文件的 read/write 没问题,但我无法提取 link 并将其传递给该方法。

代码如下:

 class DownloadFileFromURL extends AsyncTask<String, String, String> {
    ...
    @Override
    protected String doInBackground(final String... args) {
        // args[1]/name is the output file name
        String name = args[1];
        ...
        // pos is the position within the vimeo channel's array
        final int pos = Integer.parseInt(args[3]);

        //Here is the main code, args[2] is the channel id for 
        //the specific vimeo channel that the code 
        //needs to pull the video files from.
        VimeoClient.getInstance().fetchNetworkContent(args[2], new ModelCallback<VideoList>(VideoList.class) {
                @Override
                public void success(VideoList videoList) {

                   //If the video exists, get the video object, 
                   //and then get the video files related to that object from download[].
                    if (videoList != null && videoList.data != null && !videoList.data.isEmpty()) {
                        Video video = videoList.data.get(pos);
                        ArrayList<VideoFile> videoFiles = video.download;

                        // Get the video file, and then get it's link, store as string.
                        if(videoFiles != null && !videoFiles.isEmpty()) {
                            VideoFile videoFile = videoFiles.get(2); // you could sort these files by size, fps, width/height
                            String link = videoFile.getLink();

                            **link = [test direct link to mp4];
                            DownloadFile(link, args[1]);**
                        }
                    }
                }
                ...
            });

            **//String link = [test direct link to mp4];
            //DownloadFile(link, args[1]);**
        }
        ...
    }

代码末尾的字符串变量和 DownloadFile(String link, string outputName) 行是我的主要问题。我从 videoFile.getLink() 中打印出 link,并将其用作代码的测试 link。当我 运行 字符串 link = xxx 和 vimeoClient.fetchNetworkContent 之外的 DownloadFile(如最后注释掉)时,代码在将它们放在 fetchNetworkContent() 方法中时工作,它会遇到 NetworkOnMainThreadException。

问题是我需要在 DownloadFile() 运行 之前检索 link。我有办法在 fetchNetworkContent 中解决这个问题吗?或者有没有办法让我强制系统在被注释掉的 DownloadFile() 之前等待,直到 networkFetchContent 完成?

编辑:所以我根据 cricket_007 对链接 AsyncTasks 的回答更新了我的代码。不过,我没有创建第二个 AsyncTask,而是决定使用逻辑系统在同一个任务中循环它。

首先 运行ning DownloadFileFromURL() 基本上会问,我得到了什么信息?

如果给定 url,它将 运行 DownloadFile(url, outputtedFileName)。 如果不是,如果它收到关键字 "vimeo",它会使用 vimeoClient 查找 link,然后 运行s DownloadFileFromURL(vimeoLinkURL, outputtedFileName) 从内部。我想我只是用了一个逻辑树。

    class DownloadFileFromURL extends AsyncTask<String, String, String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    /**
     * Downloading file in background thread
     */
    @Override
    protected String doInBackground(final String... args) {
        final String name = args[1];

        // Check if this is already a url link ending in .mp4
        if(FilenameUtils.isExtension(args[0], "mp4")){
                DownloadFile(args[0], args[1]);
        } 

        //If not, is it a vimeo video? Check with keyword "vimeo" 
        else if (args[0].contains("vimeo")){
            final int pos = Integer.parseInt(args[3]);
            VimeoClient.getInstance().fetchNetworkContent(args[2], new ModelCallback<VideoList>(VideoList.class) {
                @Override
                public void success(VideoList videoList) {
                    Log.d("VimeoClient", "Success in VimeoList Reading");

                    if (videoList != null && videoList.data != null && !videoList.data.isEmpty()) {

                        Video video = videoList.data.get(pos);
                        ArrayList<VideoFile> videoFiles = video.download;

                        if(videoFiles != null && !videoFiles.isEmpty()) {
                            VideoFile videoFile = videoFiles.get(2); // you could sort these files by size, fps, width/height

                            String link = videoFile.getLink();
                            new DownloadFileFromURL().execute(link, args[1], args[2], args[3]);
                        }
                    }
                }

                @Override
                public void failure(VimeoError error) {
                    Log.d("VimeoError", "Failure in VideoList Reading in VideoDownloader class");
                }
            });
            // return null so that the Toast is not created for completion 
            // since this ends in DownloadFile()
            return null;
        }
        return name;
    }

    @Override
    protected void onPostExecute(String fileName) {
        if(fileName != null) {
            Toast.makeText(mContext, "Completed download of " + fileName, Toast.LENGTH_LONG).show();
        }
    }
}

我将他的回答标记为正确,不是因为它是最终代码,而是我发现它比我使用的特定代码提供更多信息。我的代码确实特定于我在这个用例中的特定解决方案,但他的解释是真正的解决方案。

这是您可以尝试做的事情。

class DownloadFileFromURL extends AsyncTask<String, String, String> implements LinkReceivedListener{
...
LinkRecievedListener callbackListener;

@Override
protected void onPostExecute(String result) {
    // The result is the returned String link value from doInbackground()
    DownloadFile(result, args[1]);
}

@Override
protected String doInBackground(final String... args) {
    String name = args[1];
    final int pos = Integer.parseInt(args[3]);

    VimeoClient.getInstance().fetchNetworkContent(args[2], new ModelCallback<VideoList>(VideoList.class) {
            @Override
            public void success(VideoList videoList) {

               //If the video exists, get the video object, 
               //and then get the video files related to that object from download[].
                if (videoList != null && videoList.data != null && !videoList.data.isEmpty()) {
                    Video video = videoList.data.get(pos);
                    ArrayList<VideoFile> videoFiles = video.download;

                    // Get the video file, and then get it's link, store as string.
                    if(videoFiles != null && !videoFiles.isEmpty()) {
                        VideoFile videoFile = videoFiles.get(2); // you could sort these files by size, fps, width/height
                        String link = videoFile.getLink();
                        callbackListener.onSuccess(link);
                    }
                }
            }
            ...
        });
    }
    ...
}

public interface LinkRecievedListener {
    void onSuccess(String linkString);
}

尚不清楚 DownloadFile 方法的作用,但您可能需要一个辅助 AsyncTask。

旁注:我希望 VimeoClient.getInstance().fetchNetworkContent 无论如何都会 运行 在它自己的线程中,所以它不应该需要 AsyncTask,但我们会假装它不需要。

针对 AsyncTask "wait" 的推荐方法是小心地将调用链接在一起。例如,如果你给一个 AsyncTask 回调,那么 success 方法得到 运行。从那个 success 中,你可以开始一个新的任务,它将调用 DownloadFile(如果你想下载所有的,可能给它整个 ArrayList<VideoFile> 而不是一个 link links)

class DownloadFileFromURL extends AsyncTask<String, Void, Void> {
    private ModelCallback<VideoList> callback;

    public DownloadFileFromURL(ModelCallback<VideoList> callback) {
        this.callback = callback;
    }

    @Override
    protected Void doInBackground(final String... args) {

        //Here is the main code, args[0] is the channel id for 
        //the specific vimeo channel that the code 
        //needs to pull the video files from.
        VimeoClient.getInstance().fetchNetworkContent(args[0], callback);

    }
    ...
}

然后,无论您在哪里调用该任务,都传入要执行的操作的接口

// Need these values inside the callback - have to be final
final String arg1;
final int pos; 

// The callback that is hit from doInBackground()
ModelCallback<VideoList> callback = new ModelCallback<VideoList>(VideoList.class) {
    @Override
    public void success(VideoList videoList) {

       //If the video exists, get the video object, 
       //and then get the video files related to that object from download[].
        if (videoList != null && videoList.data != null && !videoList.data.isEmpty()) {
            Video video = videoList.data.get(pos);
            ArrayList<VideoFile> videoFiles = video.download;

            // Get the video file, and then get it's link, store as string.
            if(videoFiles != null && !videoFiles.isEmpty()) {
                VideoFile videoFile = videoFiles.get(2); // you could sort these files by size, fps, width/height
                String link = videoFile.getLink();

                // TODO: Might need to execute another AsyncTask
                DownloadFile(link, arg1);
            }
        }
    }
    ...
};

// pass the callback to the task 
new DownloadFileFromURL(callback).execute("channel_id");