gpt4 book ai didi

android - 在 ListView Android 中下载多个带有进度条的文件

转载 作者:IT老高 更新时间:2023-10-28 22:16:09 25 4
gpt4 key购买 nike

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

enter image description here

下载ListView有一些规则:

  • 每个下载任务都显示在一个 ListView 项目中,带有进度条、百分比和当前状态(正在下载、等待、已完成)。
  • 最多允许 5 个下载任务,其他任务需要等待
  • 用户可以取消下载任务,并将该任务从ListView
  • 中移除
  • ListView 在下载 Activity 中。用户可以更改为另一个 Activity 。当他们离开时,继续在后台下载。当他们回来时,显示下载任务的当前进度和状态。
  • 保留已完成的下载任务,直到用户想要删除它们。

我尝试在 Service 中使用 ThreadPoolExcutor。对于每个下载任务,我可以获得完成百分比,但我不知道如何将它们广播到适配器以显示进度。而且我不知道如何让所有任务在后台运行,然后在包含 ListView 的 Activity 处于 Activity 状态时发布进度并保持已完成的任务
如果有任何库或示例可以解决我的问题,那就太好了。提前致谢!P/S:我已经搜索过类似的问题,但我找不到这个问题的解决方案,所以我不得不详细创建自己的问题。所以请不要将其标记为duplicate。谢谢。

最佳答案

这是一个工作示例,请看一下。

启动应用,按返回键,然后再次返回,测试启动另一个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" />

更新:

  • 添加了取消支持

关于android - 在 ListView Android 中下载多个带有进度条的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19875838/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com