programacion - manual android studio avanzado
Descargar varios archivos con una barra de progreso en ListView Android (2)
Esta es una muestra de trabajo , eche un vistazo.
Inicie la aplicación, presione el botón Atrás y luego regrese para probar el caso de iniciar otra Activity
y regresar.
Asegúrese de obtener PARTIAL_WAKE_LOCK
para su IntentService
para garantizar que la CPU siga funcionando.
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];
}
};
}
}
Diseño 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
solo contiene un 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" />
Nota: asegúrese de registrar DownloadingService
en AndroidManifest.xml de la siguiente manera:
<service android:name=".MainActivity$DownloadingService" />
ACTUALIZAR:
- Cancelar soporte añadido
Quiero crear un ListView
que permita al usuario descargar muchos archivos y mostrar un estado de barra de progreso en cada elemento de ListView
. Se parece a esto:
La descarga ListView
tiene algunas reglas:
- Cada tarea de descarga se muestra en un elemento
ListView
con una barra de progreso, porcentaje y estado actual (descarga, espera, finalizado). - Permita un máximo de 5 tareas de descarga, otras tareas tienen que esperar
- Los usuarios pueden cancelar la tarea de descarga y eliminar esta tarea del
ListView
-
ListView
está en la actividad de descarga. Los usuarios pueden cambiar a otra actividad. Cuando se vayan, continúe descargando en segundo plano. Cuando vuelvan, muestre el progreso actual y el estado de las tareas de descarga. - Mantenga las tareas de descarga completadas hasta que los usuarios quieran eliminarlas.
Traté de usar el ThreadPoolExcutor
en un Service
. Para cada tarea de descarga, puedo obtener el porcentaje completo, pero no sé cómo transmitirlo a un adaptador para mostrar el progreso. Y no sé cómo mantener todas las tareas ejecutándose en segundo plano y luego publicar el progreso cuando la actividad que contiene el ListView
está activa y mantener las tareas completadas
Sería genial si hubiera alguna biblioteca o ejemplo que pueda resolver mis problemas. ¡Gracias por adelantado! P / S: Ya he buscado preguntas similares, pero no puedo encontrar la solución para esta, así que tuve que crear mi propia pregunta en detalle. Así que por favor no lo marques como duplicate
. Gracias.
IMO - 4 cosas que te gustaría implementar:
Listener / Wrapper para HttpClient.Get.Exec (): para saber cuántos bytes se reciben en cada escritura ()
Transmitir / recibir para el oyente que mencionas puede ser redundante
Async HttpClient - no bloqueo es una DEBE
Puesta en común / puesta en cola para solicitudes
para el Listener puede ver el patrón del observador y cambiar el ''subir'' a abajo.
dado que ya tiene un patrón de observador, debe poder adaptarlo a sus requisitos de arquitectura. Cuando el oyente llama a la E / S dentro de get.exec (), solo necesita una interfaz que le permita hacer una devolución de llamada en la actividad / fragmento que tiene la interfaz de usuario y el adaptador para su lista, de modo que pueda ser notificado de la cambio en count-bytes-read-on-http-GET. La devolución de llamada de E / S tendrá que hacer referencia a la entrada de lista correcta en el adaptador o proporcionar alguna otra ID para que pueda vincularse a un GET en particular. He utilizado controladores y obtainMessageHandler() en obtainMessageHandler() para ese fin. Cuando el controlador proporciona ID a un GET específico ... entonces el oyente tendrá una referencia a la misma ID o arg cuando hace su devolución de llamada para contar bytes de E / S.
para los artículos 3 y 4, hay muchas cosas por ahí. Los httpclients nativos de Apache tienen pools / colas. Volley Android ofrece eso también. Más detalles here sobre la mecánica de los controladores y ''obtainMessage'' para las devoluciones de llamada.