Android - Как загрузить большое количество изображений (большое количество http-адресов) в фоновом процессе

Я получаю список URL-адресов изображений с сервера, число которых составляет примерно от 400 до 500. Как я могу загрузить это изображение в фоновом режиме в локальную папку устройства?

До сих пор я запускал службу переднего плана, в которой я использую ExecutorService для запуска потока. Мой сервисный код ниже

public class SaveImageService extends Service {

    private Context context;
    public static final String NOTIFICATION_CHANNEL_ID = "10001";
    public SaveImageService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }



    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        context = this;
        super.onStartCommand(intent, flags, startId);
        List<Callable<String>> saveDataThreads = new ArrayList<>();
        SaveTaskImage saveTaskImage = new SaveTaskImage(this);
        saveDataThreads.add(saveTaskImage);
        ExecutorService executor = Executors.newFixedThreadPool(saveDataThreads.size());
        try {
            List<Future<String>> aaa = executor.invokeAll(saveDataThreads);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executor.shutdown();
        if (executor.isShutdown()) {
            stopForeground(true);
            stopSelf();
        }
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void startMyOwnForeground() {
        String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp";
        String channelName = "My Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.mipmap.talentify_logo_red)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }
}

В потоке SaveTask я запускаю задачу синхронизации, как показано ниже.

new SaveImageAsync ("file_path").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,url);

Ниже приведен код моей асинхронной задачи:

public class SaveImageAsync extends AsyncTask<String, Void, String> {
    private MediaSaver mediaSaver;

    public SaveImageAsync(MediaSaver mediaSaver){
        this.mediaSaver = mediaSaver;
    }
    @Override
    protected String doInBackground(String... params) {
        Bitmap bmp = null;
        try{
            System.out.println("Save image url --> "+params[0].replaceAll(" ", "%20"));
            URL url = new URL(params[0].replaceAll(" ", "%20"));
            InputStream fileInputStream = url.openStream();
            if(fileInputStream != null) {
                mediaSaver.save(fileInputStream);
                System.out.println("Saving  image --> ");
            }else{
                System.out.println("Not Saving  image --> ");

            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    @Override
    protected void onPostExecute(String string) {
    }
}

Но при таком подходе я могу сохранить только 50-60 изображений в фоновом режиме. Он не работает до конца. Что мне делать подскажите пожалуйста


person Feroz Siddiqui    schedule 08.11.2018    source источник
comment
Каковы симптомы вашей проблемы? Сбой фоновой задачи? У вас есть трассировка стека? Служба убита или нормально завершается? Процесс вашего приложения убит? Ваш сервис воссоздан (из-за START_STICKY)?   -  person Pawel    schedule 08.11.2018
comment
да, кажется, процесс убивается между ними. до конца не доходит.   -  person Feroz Siddiqui    schedule 08.11.2018
comment
есть ли способ сделать очередь этих загрузок?   -  person Feroz Siddiqui    schedule 08.11.2018
comment
Запуск 200 одновременных потоков, скорее всего, никогда не сработает. Вам нужно использовать разумное количество потоков и ставить задачи в очередь.   -  person Pawel    schedule 08.11.2018
comment
для этого какой подход я должен следовать? время здесь не при чем. Я просто хочу скачать все изображения.   -  person Feroz Siddiqui    schedule 08.11.2018
comment
Ограничьте потоки в вашем ExecutorService примерно до 8 и не shutdown сразу, так как это очистит задачи в очереди.   -  person Pawel    schedule 08.11.2018
comment
Должен ли я использовать диспетчер работ или исполнителя пула потоков??   -  person Feroz Siddiqui    schedule 08.11.2018


Ответы (1)


Используйте библиотеку ниже -

implementation 'com.amitshekhar.android:android-networking:1.0.2'

Реализация этой библиотеки -

for (int j = 0; j < imageArrayList.size(); j++) {
        downloadImage(imageArrayList.getImagePath(j), imageArrayList.get(j),getImageName);
    }


 private void downloadImage(String imageURL, String imagename) {

    AndroidNetworking.download(imageURL, getCacheDir().getPath() + "/" + Constant.FOLDER_NAME + "/", imagename)
            .setPriority(Priority.HIGH)
            .build()
            .setDownloadProgressListener(new DownloadProgressListener() {
                @Override
                public void onProgress(long bytesDownloaded, long totalBytes) {
                    // do anything with progress
                }
            })
            .startDownload(new DownloadListener() {
                @Override
                public void onDownloadComplete() {

                    // do anything after completion


                }

                @Override
                public void onError(ANError error) {
                    // handle error

                }
            });

}

Вы также можете использовать Android DownloadManager API. Android: как использовать класс диспетчера загрузки?

person Farman Ali Khan    schedule 08.11.2018