Android列表视图中的多个下载暂停恢复,包含进度更新

时间:2014-11-15 10:45:35

标签: android android-asynctask download

我正在尝试使用进度条下载listview中的多个文件。我取得的成就是,我可以开始特定下载,使用AsyncTask暂停/恢复它并更新进度条(对于单个文件),这部分效果很好

我的问题是我无法同时下载多个文件,当我将listview留到另一个屏幕时,虽然我的下载是在后台进行但进度没有更新,进度条显示0进度,好像它没有下载但它一直在后台下载。

3 个答案:

答案 0 :(得分:4)

最后我发现答案比我想象的要简单得多,这里的内容如下

  1. 创建一个service Asynctask用于下载和hashtable值(url,Asynctask)
  2. 单击列表项时传递值(url,Asynctask)并检查该哈希表是否已包含该值,如果是,则取消Asynctask任务,如果没有将其添加到哈希表并启动Asynctask
  3. 现在用于更新adapter中的进度,我运行了一个迭代hashtable的线程,并使用BroadcastListener传递值。
  4. 并且在活动中拦截broadcast并取决于ListItem可见更新进度
  5. PS:如果有人需要一些代码,我可以提供上述说明的基本代码

    public class DownloadingService extends Service {
    public static String PROGRESS_UPDATE_ACTION = DownloadingService.class.getName() + ".progress";
    
    private static final long INTERVAL_BROADCAST = 800;
    private long mLastUpdate = 0;
    private Hashtable<String, DownloadFile> downloadTable;
    
    private LocalBroadcastManager broadcastManager;
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        MessageEntity entityRecieved = (MessageEntity) intent.getSerializableExtra("ENTITY");
        queueDownload(entityRecieved);
        return super.onStartCommand(intent, flags, startId);
    }
    
    private void queueDownload(MessageEntity entityRecieved){
        if (downloadTable.containsKey(entityRecieved.getPacketID())) {
            DownloadFile downloadFile = downloadTable.get(entityRecieved.getPacketID());
            if (downloadFile.isCancelled()) {
                downloadFile = new DownloadFile(entityRecieved);
                downloadTable.put(entityRecieved.getPacketID(), downloadFile);
                startDownloadFileTask(downloadFile);
            } else {
                downloadFile.cancel(true);
                downloadTable.remove(entityRecieved.getPacketID());
            }
    
        } else {
            DownloadFile downloadFile = new DownloadFile(entityRecieved);
            downloadTable.put(entityRecieved.getPacketID(), downloadFile);
            startDownloadFileTask(downloadFile);
        }
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        downloadTable = new Hashtable<String, DownloadFile>();
        broadcastManager = LocalBroadcastManager.getInstance(this);
    }
    
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    void startDownloadFileTask(DownloadFile asyncTask) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        else
            asyncTask.execute();
    }
    
    private void publishCurrentProgressOneShot(boolean forced) {
        if (forced || System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) {
            mLastUpdate = System.currentTimeMillis();
            int[] progresses = new int[downloadTable.size()];
            String[] packetIds = new String[downloadTable.size()];
            int index = 0;
            Enumeration<String> enumKey = downloadTable.keys();
            while (enumKey.hasMoreElements()) {
                String key = enumKey.nextElement();
                int val = downloadTable.get(key).progress;
                progresses[index] = val;
                packetIds[index++] = key;
            }
        Intent i = new Intent();
        i.setAction(PROGRESS_UPDATE_ACTION);
        i.putExtra("packetIds", packetIds);
        i.putExtra("progress", progresses);
        mBroadcastManager.sendBroadcast(i);
    }
    
    class DownloadFile extends AsyncTask<Void, Integer, Void> {
        private MessageEntity entity;
        private File file;
        private int progress;
    
        public DownloadFile(MessageEntity entity) {
            this.entity = entity;
        }
    
        @Override
        protected Void doInBackground(Void... arg0) {
            String filename = entity.getMediaURL().substring(entity.getMediaURL().lastIndexOf('/') + 1);
            file = new File(FileUtil.getAppStorageDir().getPath(), filename);
            downloadFile(entity.getMediaURL(), file); 
            return null;
        }
    
        public String downloadFile(String download_file_path, File file) {
            int downloadedSize = 0;
            int totalSize = 0;
            try {
                // download the file here
                while ((bufferLength = inputStream.read(buffer)) > 0 && !isCancelled()) {
    
                    progress = percentage;
                    publishCurrentProgressOneShot(true);
                }
    
    
            } catch (final Exception e) {
                return null;
            }
    
            return file.getPath();
        }
    
    }
    

答案 1 :(得分:1)

关于AsynchTask的一个大问题是当你完成它的活动时,AsynchTask会用你的UI失去它的轨道。之后,当您返回到该活动时,即使下载进度仍在后台运行,progressBar也不会更新。实际上,AsynchTask不属于您发布的新Activity,因此新Activity中的新进度条实例不会更新。 为了解决这个问题,我建议你:

1-在onResume()中运行带有timerTask的线程,该线程使用从AsyncTask运行后台更新的值更新你的进度条。像这样:

private void updateProgressBar(){
    Runnable runnable = new updateProgress();
    background  = new Thread(runnable);
    background.start();
}

public class updateProgress implements Runnable {
    public void run() {
        while(Thread.currentThread()==background)
            try {
                Thread.sleep(1000); 
                Message msg = new Message();
                progress = getProgressPercentage();        
                handler.sendMessage(msg);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (Exception e) {
          }
      }
}
private Handler handler =  new Handler(){
    @Override
    public void handleMessage(Message msg) {
        progress.setProgress(msg.what);
    }
};

当您的活动不可见时,您必须销毁该主题:

private void destroyRunningThreads()
{
    if(background!=null)
    {
        background.interrupt();
        background=null;
    }
}

2-定义全局静态布尔变量。在onPreExecute中设置为true,在onPostExecute中将其设置为false。它显示您是否正在下载,因此您可以检查变量是否等于true,显示上一个进度条对话框。(您可以使用整数值或整数数组执行此类操作 - 以显示更新每个下载进度的百分比)。

3-我个人使用的最后一种方式是在通知栏中显示下载进度,在我的列表视图中,我只是表明它现在正在下载。(使用带有布尔值的第二种方法)。这样,即使您完成了活动,通知栏仍会随着下载进度进行更新。

答案 2 :(得分:0)

当你离开你的活动时,asynctask显示进度条的活动被杀死,因此当你回到新活动时,progressBar不再显示,因为asynctask不知道你的新活动。 一般解决方案,适用于任何情况,例如当您的用户关闭您的应用并再次打开它并想要知道progressBar完全分离您的演示文稿时。这意味着您可以创建sharedPreferences或数据库表,并在下载asynctask时将文件的状态放入其中。例如,每500毫秒更新sharedPreferences或数据库表,从总文件大小下载多少。然后,当用户回到您的新活动时,您会从DB或sharedPreferences中读取以显示progressBar并每次更新它,例如1000毫秒。通过这种方式,即使用户关闭应用程序并再次打开它,您的用户也会知道progressBar。我知道这需要更多的工作,但它肯定会让你的用户感到高兴。

要以固定费率阅读和更新,您可以使用scheduleAtFixedRate