FETCH2: Uma alternativa ao DownloadManager

Se você já tentou se aprofundar no universo mobile e tentou realizar algum tipo de download dentro de sua aplicação, com certeza já se deparou com a classe DownloadManager, um serviço dentro do Android que é chamado pela sua aplicação para executar o download.

O gerenciador de download é um serviço de sistema que lida com downloads HTTP de longa duração. Os clientes podem solicitar que um URI seja baixado para um arquivo de destino específico. O gerenciador de download conduzirá o download em segundo plano, cuidando das interações HTTP e tentando novamente os downloads após falhas ou entre alterações de conectividade e reinicializações do sistema.

No entanto, o DownloadManager contem alguns problemas que podem lhe dar algumas dores de cabeça. Quando comecei a utiliza-lo me deparei com bugs que faziam com que meu download nunca começasse até problema de bloqueio de IP já que a rede que meu aplicativo utilizava bloqueava alguns IP’s da Google já que o seu download acaba passando pela rede da Google.

A solução foi buscar alternativas para o Download Manager e logo de cara me deparei com o repositório do Fetch2 que acabou solucionando todos os meus problemas com uma implementação simples e com vários métodos já prontos que com o Download Manager tive que fazer. Abaixo estarei utilizando o exemplo do próprio projeto.

São necessárias as permissões de leitura e escrita no armazenamento e de acesso a internet para o seu aplicativo, então insira no seu AndroidManifest:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

Para utilizar o Fetch, adicione a implementação no seu build.gradle:

implementation "com.tonyodev.fetch2:fetch2:3.0.12" 
// OU
implementation "androidx.tonyodev.fetch2:xfetch2:3.1.6"
 // PARA ANDROIDX

E agora vamos ao código!

Comece declarando a variável onde guardaremos a instancia do Fetch:

public class TestActivity extends AppCompatActivity {

    private Fetch fetch;
     
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          ...
}

Em seguida vamos definir as configurações do Fetch e inseri-los na instancia

public class TestActivity extends AppCompatActivity {

    private Fetch fetch;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

       //Builder para criação de configurações 
       FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this) 
                .setDownloadConcurrentLimit(3) // Setamos o limite de downloads paralelos para 3
                .build();

        // Devemos utilizar o getInstance e passar as configurações desejados para salvar na variável Fetch
        fetch = Fetch.Impl.getInstance(fetchConfiguration);

        String origem = "http:www.example.com/test.txt";
        String destino = "/downloads/test.txt";
        
        // Por fim montamos a requisição instanciando um Request
        final Request request = new Request(origem, destino);
        request.setPriority(Priority.HIGH); // Setamos a prioridade
        request.setNetworkType(NetworkType.ALL); // Limitações de rede (WI-FI ou Dados Moveis)
        request.addHeader("clientKey", "SD78DF93_3947&MVNGHE1WONG"); // Exemplo de cabeçalho
        
        // Adicionado o request na fila de downloads
        fetch.enqueue(request, updatedRequest -> {
            // Callback para caso o download tenha sido inserido na fila com sucesso
        }, error -> {
            // Callback para caso tenha ocorrido algum problema ao inserir na fila
        });
    }
}

Com apenas esse trecho de código o download já é realizado, porém, temos a possibilidade de adicionar um Listener para acompanhar com mais detalhes o andamento do download. É através do Listener que conseguimos acessar métodos que retornam o tempo aproximado para o fim de download, quantidade de bytes já baixados e etc…

Adicionando o Listener

Dentro do nosso método onCreate criaremos uma classe anônima que será filha da classe FetchListener. Em seguida, teremos que implementar os seguintes métodos e adicionar o fetchListener em nosso objeto fetch.

FetchListener fetchListener = new FetchListener() {
    @Override
    public void onQueued(@NotNull Download download, boolean waitingOnNetwork) {
        if (request.getId() == download.getId()) {
            showDownloadInList(download);
        }
    }

    @Override
    public void onCompleted(@NotNull Download download) {

    }

    @Override
    public void onError(@NotNull Download download) {
        Error error = download.getError();
    }

    @Override
    public void onProgress(@NotNull Download download, long etaInMilliSeconds, long downloadedBytesPerSecond) {
        if (request.getId() == download.getId()) {
            updateDownload(download, etaInMilliSeconds);
        }
        int progress = download.getProgress(); // Porcentagem do download
    }

    @Override
    public void onPaused(@NotNull Download download) {

    }

    @Override
    public void onResumed(@NotNull Download download) {

    }

    @Override
    public void onCancelled(@NotNull Download download) {

    }

    @Override
    public void onRemoved(@NotNull Download download) {

    }

    @Override
    public void onDeleted(@NotNull Download download) {

    }
};
// Adiciona o listener 
fetch.addListener(fetchListener);

// Remove o listener ao concluir o download.
fetch.removeListener(fetchListener);

Por fim, temos a implementação completa do Fetch2 e basta realizar as alterações para que se encaixe no projeto.

E você, conhece outra biblioteca que pode ajudar a resolver um problema parecido? Deixe nos comentários!