android - ListView एंड्रॉइड में प्रोग्रेस बार के साथ कई फाइलें डाउनलोड करें




android-listview android-service (2)

मैं एक ListView बनाना चाहता हूं जो उपयोगकर्ता को कई फ़ाइलों को डाउनलोड करने और प्रत्येक ListView आइटम में प्रगति बार स्थिति दिखाने की अनुमति देता है। यह इस तरह दिख रहा है:

डाउनलोड ListView में कुछ नियम हैं:

  • एक डाउनलोड ListView आइटम में प्रगति पट्टी, प्रतिशत और वर्तमान स्थिति (डाउनलोडिंग, प्रतीक्षा, समाप्त) के साथ प्रत्येक डाउनलोड कार्य प्रदर्शित होता है।
  • अधिकतम 5 डाउनलोड कार्यों की अनुमति दें, अन्य कार्यों को प्रतीक्षा करनी है
  • उपयोगकर्ता डाउनलोड कार्य को रद्द कर सकते हैं, और इस कार्य को ListView से निकाल ListView
  • ListView डाउनलोड गतिविधि में है। उपयोगकर्ता दूसरी गतिविधि में बदल सकते हैं। जब वे जाते हैं, पृष्ठभूमि में डाउनलोड करना जारी रखें। जब वे वापस आते हैं, तो डाउनलोड कार्यों की वर्तमान प्रगति और स्थिति प्रदर्शित करें।
  • पूरा डाउनलोड कार्य रखें जब तक कि उपयोगकर्ता उन्हें हटाना नहीं चाहते हैं।

मैंने एक Service में ThreadPoolExcutor का उपयोग करने की कोशिश की। प्रत्येक डाउनलोड कार्य के लिए, मैं प्रतिशत पूर्ण कर सकता हूं, लेकिन मुझे नहीं पता कि प्रगति को प्रदर्शित करने के लिए उन्हें एडाप्टर में कैसे प्रसारित किया जाए। और मुझे नहीं पता कि पृष्ठभूमि में चल रहे सभी कार्यों को कैसे रखा जाए और फिर प्रगति पोस्ट करें जब ListView वाली गतिविधि सक्रिय है और पूरा कार्य रखें
यह बहुत अच्छा होगा अगर कोई पुस्तकालय या उदाहरण था जो मेरी समस्याओं का समाधान कर सकता है। अग्रिम में धन्यवाद! पी / एस: मैंने पहले ही इसी तरह के प्रश्न खोजे हैं, लेकिन मुझे इस के लिए समाधान नहीं मिल रहा है, इसलिए मुझे अपना खुद का प्रश्न विस्तार से बनाना पड़ा। तो कृपया इसे duplicate रूप में चिह्नित न करें। धन्यवाद।


Answers

यह एक कामकाजी नमूना है, एक नज़र डालें।

ऐप लॉन्च करें, बैक बटन दबाएं और फिर फिर एक और Activity लॉन्च करने और वापस आने के मामले की जांच करने के लिए वापस आएं।

यह सुनिश्चित करने के लिए कि CPU चालू रहता है, सुनिश्चित करने के लिए अपने IntentService लिए PARTIAL_WAKE_LOCK प्राप्त करना सुनिश्चित करें।

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 में डाउनलोडिंग सेवा को पंजीकृत करना सुनिश्चित करें:

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

अद्यतन करें:

  • समर्थन जोड़ा रद्द करें

आईएमओ - 4 चीजें जिन्हें आप कार्यान्वित करना चाहते हैं:

  • HttpClient.Get.Exec () के लिए श्रोता / रैपर - ताकि आप जानते हैं कि प्रत्येक लेखन पर कितने बाइट प्राप्त हुए हैं ()

    आपके द्वारा उल्लेख किए गए श्रोता के लिए प्रसारण / प्राप्त करना वास्तव में अनावश्यक हो सकता है

    Async HttpClient - nonblocking एक जरूरी है

    अनुरोध के लिए पूलिंग / क्यूइंग

Listener आप पर्यवेक्षक पैटर्न देख सकते हैं और 'अपलोड' को नीचे स्विच कर सकते हैं।

चूंकि आपके पास पहले से ही एक पर्यवेक्षक पैटर्न है, इसलिए आप इसे अपनी वास्तुकला आवश्यकता में अनुकूलित करने में सक्षम होना चाहिए। जब श्रोता कॉल get.exec () के भीतर I / O पर बैक कॉल करता है, तो आपको बस आवश्यकता होती है और इंटरफ़ेस होता है जो आपको उस गतिविधि / खंड पर कॉलबैक करने की अनुमति देता है जिसमें आपकी सूची के लिए यूआई और एडाप्टर है ताकि इसे अधिसूचित किया जा सके गिनती-बाइट्स-रीड-ऑन-http-GET में बदलें। I / o कॉलबैक को या तो एडाप्टर में सही सूची प्रविष्टि का संदर्भ देना होगा या कुछ अन्य आईडी प्रदान करना होगा ताकि इसे किसी विशेष GET पर वापस जोड़ा जा सके। मैंने उस उद्देश्य के लिए हैंडलर हैंडलर obtainMessageHandler() करने वाले हैंडलर और obtainMessageHandler() उपयोग किया है। जब हैंडलर एक विशिष्ट जीईटी प्रदान करता है और आईडी ... तो श्रोता के पास एक ही आईडी या तर्क का संदर्भ होगा जब यह I / o बाइट्स को गिनने के लिए कॉलबैक बनाता है।

वस्तुओं 3 और 4 के लिए, वहां बहुत सारी चीजें हैं। मूल अपाचे httpclients के पास पूल / कतार हैं। एंड्रॉइड वॉली भी प्रदान करता है। कॉलबैक के लिए हैंडलर के मैकेनिक्स और 'getMessage' पर अधिक जानकारी।





android android-listview android-service