重新打开应用程序时获取AsyncTask的状态

3

目前我有一个AsyncTask,它在我的RecyclerView.Adapter中被调用,在其中我从一个URL下载文件,并且进度在我的ViewHolder中显示。以下是我的AsyncTask的样子:

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

    int positionnumber;

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

    }

    @Override
    protected String doInBackground(String... f_url) {
        int count;
        String pathreference = f_url[0] + ",";

        positionnumber = Integer.parseInt(f_url[0]);
        isDownloading.set(positionnumber, true);
        try {
            URL url = new URL(f_url[1]);
            URLConnection conection = url.openConnection();
            conection.connect();

            // this will be useful so that you can show a tipical 0-100%
            // progress bar
            int lenghtOfFile = conection.getContentLength();

            // download the file
            InputStream input = new BufferedInputStream(url.openStream(),
                    8192);
            if (isCancelled()) {
                return null;
            }

            if (a.equals("one")) {
                // Output stream
                OutputStream output = new FileOutputStream(Environment
                        .getExternalStorageDirectory().toString()
                        + "/" + setDownloadedName + ".mp4");


                byte data[] = new byte[1024];

                long total = 0;

                while ((count = input.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    // After this onProgressUpdate will be called
                    if (isCancelled()) {
                        input.close();
                        return null;
                    }
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));

                    // writing data to file
                    output.write(data, 0, count);
                }

                // flushing output
                output.flush();
                pathreference = pathreference + Environment.getExternalStorageDirectory().toString() + "/" + setDownloadedName + ".mp4";
                // closing streams
                output.close();
                input.close();


            }
        } catch (Exception e) {
            Log.e("Error: ", e.getMessage());
        }

        return pathreference;
    }


    protected void onProgressUpdate(String... progress) {

        prog = progress[0];

        Log.d("Progress", progress[0]);
        bars.get(positionnumber).setProgress_(Float.parseFloat(progress[0]));
        float p = bars.get(positionnumber).getProgress_();
        if (p % 1 == 0)
            notifyItemChanged(positionnumber);

    }

    @Override
    protected void onPostExecute(String file_url) {

        String[] split = file_url.split(",");
        int index1 = Integer.parseInt(split[0]);

        try {

            videoHolderClass.get(index1).setImage(imgres[0]);
            bars.get(index1).setProgress_(0);
            manager.insertVideoPath(index1 + "", split[1]);
            RecyclerVideoAdapter.this.notifyItemChanged(index1);
            isDownloading.set(index1, false);
            Log.d("done", "filesaved");

        } catch (Exception e) {

            Log.e("Error: ", e.getMessage());
            Toast.makeText(c, "Network Error..", Toast.LENGTH_SHORT).show();

            prog = "";
            networkFailed = "yes";
            videoHolderClass.get(index1).setImage(imgres[1]);
            bars.get(index1).setProgress_(0);
            RecyclerVideoAdapter.this.notifyItemChanged(index1);
            isDownloading.set(index1, false);

        }
    }
}

RecyclerView在一个片段中,该片段填充在Activity - ViewPager 中。

现在,每当我关闭活动或转到新的活动并返回后,进度就不再显示了。

我已经看到人们这样做:

if(mtask.getStatus() == AsyncTask.Status.FINISHED){
    // My AsyncTask is done and onPostExecute was called
}

但问题是,AsyncTask 在我的 Adapter 中,因此我无法在片段中引用它。


我的问题:

请问有没有正确的方法来判断 AsyncTask 是否在运行,并在重新创建 Activity 时更新进度?


我认为你可以看一下“Android前台服务”。前台服务不会在用户关闭应用程序时被杀死。因此,您的异步任务应该在前台服务下执行,并且每次您的活动启动时,您都可以连接到服务以获取下载状态或进度。 - vsatkh
我认为ViewPager的OnCreateView()会使用某种ID切换异步任务并再次调用它。 - reinhard.lee
@vsatkh 我刚刚看到了这个与你的方法相矛盾的SO帖子- https://stackoverflow.com/a/38468455/8199772 - ClassA
@ClassA AsyncTask的故事很长,但讨论是正确的,只关注于使用AsyncTask。这并不意味着使用服务是一种不好的做法。下面有一条评论提到了大文件(从您的代码中可以看出您下载了mp4,视频可能非常大)。因此,最佳实践是使用AsyncTask执行短时间任务,并使用Thread Handler与Service或IntentService运行长时间任务。我建议使用前台服务与线程或AsyncTask一起使用的原因是因为我看到您可能会下载大文件。 - vsatkh
@ClassA 还有一些事情需要注意,对于AsyncTask,你需要小心onPostExecute,因为如果视图被销毁,你的应用程序可能会崩溃。另外,如果允许多个下载,你可能需要一个队列,否则它会减慢网络速度,用户将在其他应用程序上体验到较差的网络流量。最后一个是Android 8上后台任务的限制,如果用户离开应用程序,它可能会被杀死,当然,如果用户选择这样做。 - vsatkh
@vsatkh,我已经提供了我想要的答案。谢谢你的帮助。 - ClassA
1个回答

0

我知道这不是“正确”的做法,但我已经设法找到了一个解决方法,并且它对于我想要实现的目标有效。


我决定使用SharedPreferences来检查文件是否正在下载。在我的onPreExecute中,我执行了以下操作:

@Override
    protected void onPreExecute() {
        super.onPreExecute();
        SharedPreferences sharePrefss = c.getSharedPreferences("busyDownloading", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharePrefss.edit();
        editor.putString("isItBusy", "yes");
        editor.apply();
    }

我在上面存储了一个名为“isItBusy”的String,并将其设置为“是”。

然后在onPostExecute中,我将SharedPreferences更改为:

SharedPreferences sharePrefss = c.getSharedPreferences("busyDownloading", Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharePrefss.edit();
            editor.putString("isItBusy", "no");
            editor.apply();

在我的Activity中,当用户按下返回按钮时,我会执行以下操作:
@Override
public void onBackPressed() {
    SharedPreferences sharePrefs = getSharedPreferences("busyDownloading", Context.MODE_PRIVATE);
    String getSharedUser = sharePrefs.getString("isItBusy", "");
    if (getSharedUser.equals("yes")){
        // AsyncTask - onPostExecute was not called = moveTaskToBack
        moveTaskToBack(true);
    }else {
        //AsyncTask - onPostExecute was called and application can be destroyed
        finish();
    }
}

通过上述操作,我现在知道文件是否仍在忙于下载,并相应地处理返回按钮。
下一个问题是如何处理设备的旋转,因为当设备旋转时,Activity会被销毁并重新创建。
为了解决这个问题,我首先通过在我的片段的onCreateView中添加setRetainInstance(true);来保留片段状态。然后在我的清单中,我将android:configChanges="orientation|screenSize"添加到我的Activity中。
这样做解决了旋转问题。
@vsatkh提到的最后一个问题是将下载排队。我要下载的文件不是很大(最多只有2-5MB)。因此,我决定一次只启用一个下载,并通过检查下面显示的SharedPreferences来实现:
holder.setItemClickListener(new ItemClickListener() {

        @Override
        public void onItemClick(View v, int pos) {

            SharedPreferences sharePrefs = c.getSharedPreferences("busyDownloading", Context.MODE_PRIVATE);
            String getSharedUser = sharePrefs.getString("isItBusy", "");
            if (getSharedUser.equals("yes")){
                Toast.makeText(c, "You can only download one file at a time...", Toast.LENGTH_SHORT).show();

            }else {
             DownloadFileFromURL p = new DownloadFileFromURL();
             p.execute(pos + "", downloadLink);

            }

        }
    });

并不是我使用的所有方法(例如检查文件是否已经存在)都在上面包含了,只有与我提出的问题相关的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接