在ListView Android中使用进度条下载多个文件

我想要创build一个ListView ,允许用户下载多个文件,并在每个ListView项目中显示进度条状态。 它看起来像这样:

在这里输入图像说明

下载ListView有一些规则:

  • 每个下载任务在一个ListView项目中显示一个进度条,百分比和当前状态(下载,等待,完成)。
  • 最多允许5个下载任务,其他任务必须等待
  • 用户可以取消下载任务,并从ListView删除此任务
  • ListView处于下载活动中。 用户可以更改为其他活动。 他们离开时,继续在后台下载。 当他们回来时,显示下载任务的当前进度和状态。
  • 保持已完成的下载任务,直到用户想要删除它们。

我试图在Service使用ThreadPoolExcutor 。 对于每个下载任务,我可以得到完整的百分比,但我不知道如何将它们广播到适配器来显示进度。 而且我不知道如何保留在后台运行的所有任务,然后在包含ListView的活动处于活动状态时发布进度并保留已完成的任务
如果有任何图书馆或例子能解决我的问题,那将是非常好的。 提前致谢! P / S:我已经search了类似的问题,但是我找不到解决scheme,所以我必须详细地提出自己的问题。 所以请不要把它标为duplicate 。 谢谢。

Solutions Collecting From Web of "在ListView Android中使用进度条下载多个文件"

这是一个工作样本,看看。

启动应用程序,按返回button,然后再回来testing启动另一个Activity并回来的情况。

确保为您的IntentService获取PARTIAL_WAKE_LOCK以确保CPU保持运行。

 import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.app.Activity; import android.app.IntentService; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.content.LocalBroadcastManager; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends Activity { public static final String ID = "id"; private ListView mListView; private ArrayAdapter<File> mAdapter; private boolean mReceiversRegistered; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView = mListView = (ListView) findViewById(R.id.list); long id = 0; File[] files = {getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id++), getFile(id)}; listView.setAdapter(mAdapter = new ArrayAdapter<File>(this, R.layout.row, R.id.textView, files) { @Override public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent); updateRow(getItem(position), v); return v; } }); if (savedInstanceState == null) { Intent intent = new Intent(this, DownloadingService.class); intent.putParcelableArrayListExtra("files", new ArrayList<File>(Arrays.asList(files))); startService(intent); } registerReceiver(); } private File getFile(long id) { return new File(id, "https://someurl/" + id); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(); } private void registerReceiver() { unregisterReceiver(); IntentFilter intentToReceiveFilter = new IntentFilter(); intentToReceiveFilter .addAction(DownloadingService.PROGRESS_UPDATE_ACTION); LocalBroadcastManager.getInstance(this).registerReceiver( mDownloadingProgressReceiver, intentToReceiveFilter); mReceiversRegistered = true; } private void unregisterReceiver() { if (mReceiversRegistered) { LocalBroadcastManager.getInstance(this).unregisterReceiver( mDownloadingProgressReceiver); mReceiversRegistered = false; } } private void updateRow(final File file, View v) { ProgressBar bar = (ProgressBar) v.findViewById(R.id.progressBar); bar.setProgress(file.progress); TextView tv = (TextView) v.findViewById(R.id.textView); tv.setText(file.toString()); v.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent i = new Intent(); i.setAction(DownloadingService.ACTION_CANCEL_DOWNLOAD); i.putExtra(ID, file.getId()); LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(i); } }); } // don't call notifyDatasetChanged() too frequently, have a look at // following url http://stackoverflow.com/a/19090832/1112882 protected void onProgressUpdate(int position, int progress) { final ListView listView = mListView; int first = listView.getFirstVisiblePosition(); int last = listView.getLastVisiblePosition(); mAdapter.getItem(position).progress = progress > 100 ? 100 : progress; if (position < first || position > last) { // just update your data set, UI will be updated automatically in next // getView() call } else { View convertView = mListView.getChildAt(position - first); // this is the convertView that you previously returned in getView // just fix it (for example:) updateRow(mAdapter.getItem(position), convertView); } } protected void onProgressUpdateOneShot(int[] positions, int[] progresses) { for (int i = 0; i < positions.length; i++) { int position = positions[i]; int progress = progresses[i]; onProgressUpdate(position, progress); } } private final BroadcastReceiver mDownloadingProgressReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals( DownloadingService.PROGRESS_UPDATE_ACTION)) { final boolean oneShot = intent .getBooleanExtra("oneshot", false); if (oneShot) { final int[] progresses = intent .getIntArrayExtra("progress"); final int[] positions = intent.getIntArrayExtra("position"); onProgressUpdateOneShot(positions, progresses); } else { final int progress = intent.getIntExtra("progress", -1); final int position = intent.getIntExtra("position", -1); if (position == -1) { return; } onProgressUpdate(position, progress); } } } }; public static class DownloadingService extends IntentService { public static String PROGRESS_UPDATE_ACTION = DownloadingService.class .getName() + ".progress_update"; private static final String ACTION_CANCEL_DOWNLOAD = DownloadingService.class .getName() + "action_cancel_download"; private boolean mIsAlreadyRunning; private boolean mReceiversRegistered; private ExecutorService mExec; private CompletionService<NoResultType> mEcs; private LocalBroadcastManager mBroadcastManager; private List<DownloadTask> mTasks; private static final long INTERVAL_BROADCAST = 800; private long mLastUpdate = 0; public DownloadingService() { super("DownloadingService"); mExec = Executors.newFixedThreadPool( /* only 5 at a time */5); mEcs = new ExecutorCompletionService<NoResultType>(mExec); mBroadcastManager = LocalBroadcastManager.getInstance(this); mTasks = new ArrayList<MainActivity.DownloadingService.DownloadTask>(); } @Override public void onCreate() { super.onCreate(); registerReceiver(); } @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mIsAlreadyRunning) { publishCurrentProgressOneShot(true); } return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) { if (mIsAlreadyRunning) { return; } mIsAlreadyRunning = true; ArrayList<File> files = intent.getParcelableArrayListExtra("files"); final Collection<DownloadTask> tasks = mTasks; int index = 0; for (File file : files) { DownloadTask yt1 = new DownloadTask(index++, file); tasks.add(yt1); } for (DownloadTask t : tasks) { mEcs.submit(t); } // wait for finish int n = tasks.size(); for (int i = 0; i < n; ++i) { NoResultType r; try { r = mEcs.take().get(); if (r != null) { // use you result here } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } // send a last broadcast publishCurrentProgressOneShot(true); mExec.shutdown(); } private void publishCurrentProgressOneShot(boolean forced) { if (forced || System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) { mLastUpdate = System.currentTimeMillis(); final List<DownloadTask> tasks = mTasks; int[] positions = new int[tasks.size()]; int[] progresses = new int[tasks.size()]; for (int i = 0; i < tasks.size(); i++) { DownloadTask t = tasks.get(i); positions[i] = t.mPosition; progresses[i] = t.mProgress; } publishProgress(positions, progresses); } } private void publishCurrentProgressOneShot() { publishCurrentProgressOneShot(false); } private synchronized void publishProgress(int[] positions, int[] progresses) { Intent i = new Intent(); i.setAction(PROGRESS_UPDATE_ACTION); i.putExtra("position", positions); i.putExtra("progress", progresses); i.putExtra("oneshot", true); mBroadcastManager.sendBroadcast(i); } // following methods can also be used but will cause lots of broadcasts private void publishCurrentProgress() { final Collection<DownloadTask> tasks = mTasks; for (DownloadTask t : tasks) { publishProgress(t.mPosition, t.mProgress); } } private synchronized void publishProgress(int position, int progress) { Intent i = new Intent(); i.setAction(PROGRESS_UPDATE_ACTION); i.putExtra("progress", progress); i.putExtra("position", position); mBroadcastManager.sendBroadcast(i); } class DownloadTask implements Callable<NoResultType> { private int mPosition; private int mProgress; private boolean mCancelled; private final File mFile; private Random mRand = new Random(); public DownloadTask(int position, File file) { mPosition = position; mFile = file; } @Override public NoResultType call() throws Exception { while (mProgress < 100 && !mCancelled) { mProgress += mRand.nextInt(5); Thread.sleep(mRand.nextInt(500)); // publish progress publishCurrentProgressOneShot(); // we can also call publishProgress(int position, int // progress) instead, which will work fine but avoid broadcasts // by aggregating them // publishProgress(mPosition,mProgress); } return new NoResultType(); } public int getProgress() { return mProgress; } public int getPosition() { return mPosition; } public void cancel() { mCancelled = true; } } private void registerReceiver() { unregisterReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(DownloadingService.ACTION_CANCEL_DOWNLOAD); LocalBroadcastManager.getInstance(this).registerReceiver( mCommunicationReceiver, filter); mReceiversRegistered = true; } private void unregisterReceiver() { if (mReceiversRegistered) { LocalBroadcastManager.getInstance(this).unregisterReceiver( mCommunicationReceiver); mReceiversRegistered = false; } } private final BroadcastReceiver mCommunicationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals( DownloadingService.ACTION_CANCEL_DOWNLOAD)) { final long id = intent.getLongExtra(ID, -1); if (id != -1) { for (DownloadTask task : mTasks) { if (task.mFile.getId() == id) { task.cancel(); break; } } } } } }; class NoResultType { } } public static class File implements Parcelable { private final long id; private final String url; private int progress; public File(long id, String url) { this.id = id; this.url = url; } public long getId() { return id; } public String getUrl() { return url; } @Override public String toString() { return url + " " + progress + " %"; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(this.id); dest.writeString(this.url); dest.writeInt(this.progress); } private File(Parcel in) { this.id = in.readLong(); this.url = in.readString(); this.progress = in.readInt(); } public static final Parcelable.Creator<File> CREATOR = new Parcelable.Creator<File>() { public File createFromParcel(Parcel source) { return new File(source); } public File[] newArray(int size) { return new File[size]; } }; } } 

row.xml布局:

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" /> <Button android:id="@+id/cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:text="Cancel" /> </LinearLayout> 

activity_main.xml只包含一个ListView

 <?xml version="1.0" encoding="utf-8"?> <ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> 

注意:确保在AndroidManifest.xml中注册DownloadingService ,如下所示:

 <service android:name=".MainActivity$DownloadingService" /> 

更新:

  • 取消支持添加

国际海事组织 – 4件事情,你会想要实现:

  • HttpClient.Get.Exec()的监听器/包装器 – 所以你知道每个write()接收多less个字节,

    广播/收听你提到的听众实际上可能是多余的

    asynchronousHttpClient – 非阻塞是必须的

    请求的联合/排队

对于Listener,您可以看到观察者模式,并将“上传”切换为closures。

因为你已经有了观察者模式,所以你应该能够适应你的架构需求。 当侦听器在get.exec()中的I / O上调用BackBack时,只需要和接口,它允许您callback具有UI和适配器的活动/片段,以便可以通知在count-bytes-read-on-http-GET中改变。 I / Ocallback将不得不引用适配器中正确的列表条目,或者提供一些其他的ID,以便将其绑定回特定的GET。 我已经使用了处理程序和用于此目的的obtainMessageHandler()中的参数。 当处理程序提供了一个特定的GET …并且ID …那么监听器将在它的callback计数I / O字节时引用相同的ID或arg。

对于项目3和4,这里有很多东西。 本地Apache httpclient具有池/队列。 Android volley也提供这个function。 更多的细节在这里关于处理程序的机制和“获取消息”的callback。