Android'de PRDownloader Kullanımı

Android'de PRDownloader Kullanımı

Herkese merhaba. Yaklaşık 20 günlük vize rezaletim ardından bugün bir blog yazısı yazmak istedim. İçerik olarak ise herkesin ihtiyacı olabileceğini düşündüğüm için daha önce birkaç projemde kullandığım ve açık kaynak olan bir kütüphanenin kullanımını göstermeyi tercih ettim. Açıkçası bu kütüphane açık kaynak değil de ücretli olsa dahi kullanılabilecek kadar değerli bir kütüphane diyebilirim. Çünkü bir yandan eşzamanlı dosya indirme desteği sunarken diğer yandan herhangi bir sorunla karşılaşılmasıyla dosya indirme işleminin yarıda kalması durumunda ya da kullanıcı isteğiyle indirmenin duraklatılma durumunda kalınan yerden devam edebilme desteği sunuyor. Evet ben de dökümantasyonu ilk okuduğumda sizin gibi “Yok yaw!” demiştim ama sonradan alıştım açık kaynağın geldiği bu noktaya. Her neyse lafı daha fazla uzatmadan kodlara geçelim, bakalım bu kütüphane nasıl kullanılıyormuş.

Öncelikle aşağıdaki satırı app düzeyindeki gradle dosyamızın içine ekliyoruz ki kütüphane proje içerisine dahil olsun.

implementation 'com.mindorks.android:prdownloader:0.5.0'

Daha sonra indirme işlemi yapacağımızdan dolayı internet erişim iznini almak için aşağıdaki satırları manifest dosyamıza ekliyoruz.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission-sdk-23 android:name="android.permission.INTERNET" />

Sonrasında aşağıdaki activity dizaynını oluşturuyoruz. Bu dizaynı oluşturduğumuzda tasarım yukarıdaki gibi görünecektir. Görüldüğü gibi her bir indirme için bir progress bar, indirme durumunu gösterecek olan bir TextView ve kontrolleri sağlayan iki buton bulunmakta.

<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">

            <ProgressBar
                android:id="@+id/progressBarOne"
                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:progressTint="@color/colorPrimaryDark" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/progressBarOne"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewProgressOne"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:layout_weight="1"

                    android:text="@string/not_started_download_size"
                    android:textAlignment="center" />

                <Button
                    android:id="@+id/buttonCancelOne"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:enabled="false"
                    android:text="@string/cancel" />

                <Button
                    android:id="@+id/buttonOne"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:text="@string/start" />
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">

            <ProgressBar
                android:id="@+id/progressBarTwo"
                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:progressTint="@color/colorPrimaryDark" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/progressBarTwo"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewProgressTwo"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:layout_weight="1"

                    android:text="@string/not_started_download_size"
                    android:textAlignment="center" />

                <Button
                    android:id="@+id/buttonCancelTwo"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:enabled="false"
                    android:text="@string/cancel" />

                <Button
                    android:id="@+id/buttonTwo"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:text="@string/start" />
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">

            <ProgressBar
                android:id="@+id/progressBarThree"
                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:progressTint="@color/colorPrimaryDark" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/progressBarThree"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewProgressThree"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:layout_weight="1"

                    android:text="@string/not_started_download_size"
                    android:textAlignment="center" />

                <Button
                    android:id="@+id/buttonCancelThree"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:enabled="false"
                    android:text="@string/cancel" />

                <Button
                    android:id="@+id/buttonThree"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:text="@string/start" />
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">

            <ProgressBar
                android:id="@+id/progressBarFour"
                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:progressTint="@color/colorPrimaryDark" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/progressBarFour"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewProgressFour"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:layout_weight="1"

                    android:text="@string/not_started_download_size"
                    android:textAlignment="center" />

                <Button
                    android:id="@+id/buttonCancelFour"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:enabled="false"
                    android:text="@string/cancel" />

                <Button
                    android:id="@+id/buttonFour"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:text="@string/start" />
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">

            <ProgressBar
                android:id="@+id/progressBarFive"
                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:progressTint="@color/colorPrimaryDark" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/progressBarFive"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewProgressFive"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:layout_weight="1"

                    android:text="@string/not_started_download_size"
                    android:textAlignment="center" />

                <Button
                    android:id="@+id/buttonCancelFive"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:enabled="false"
                    android:text="@string/cancel" />

                <Button
                    android:id="@+id/buttonFive"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="4dp"
                    android:layout_weight="1"
                    android:text="@string/start" />
            </LinearLayout>
        </RelativeLayout>

    </LinearLayout>

</ScrollView>

Yukarıdaki adımları tamamladıktan sonra sıra geldi activity classımızın içerisine.
Aşağıda da gördüğünüz gibi dirPath adında bir değişken tanımlıyoruz, bu değişken dosyaları indireceğimiz yeri tutacak içerisinde.
Sonrasındaki 5 değişken ise indireceğimiz dosyaların linklerini içermekte. Ben örnek olarak github reposuna upload ettiğim zip dosyalarını kullanacağım.
Sonrasında ise dizayn için gereken viewlerin tanımlamalarını yapıyoruz.
En altta ise her bir indirmenin ID değerini tutması için 5 adet integer değer oluşturuyoruz. Bu değerler aracılığıyla indirmemizin durumunu öğrenebilecek ya da indirmeyi duraklatıp devam ettirebileceğiz. Kısacası bu değerlere flag diyebiliriz.

    private static String dirPath;

    final String URL1 = "https://raw.githubusercontent.com/CagriAldemir/AndroidFileDownloaderExample/master/Downloadable_Files/File1.zip";
    final String URL2 = "https://raw.githubusercontent.com/CagriAldemir/AndroidFileDownloaderExample/master/Downloadable_Files/File2.zip";
    final String URL3 = "https://raw.githubusercontent.com/CagriAldemir/AndroidFileDownloaderExample/master/Downloadable_Files/File3.zip";
    final String URL4 = "https://raw.githubusercontent.com/CagriAldemir/AndroidFileDownloaderExample/master/Downloadable_Files/File4.zip";
    final String URL5 = "https://raw.githubusercontent.com/CagriAldemir/AndroidFileDownloaderExample/master/Downloadable_Files/File5.zip";


    Button buttonOne, buttonTwo, buttonThree, buttonFour, buttonFive;

    Button buttonCancelOne, buttonCancelTwo, buttonCancelThree, buttonCancelFour, buttonCancelFive;

    TextView textViewProgressOne, textViewProgressTwo, textViewProgressThree, textViewProgressFour, textViewProgressFive;

    ProgressBar progressBarOne, progressBarTwo, progressBarThree, progressBarFour, progressBarFive;

    int downloadIdOne, downloadIdTwo, downloadIdThree, downloadIdFour, downloadIdFive;

Gerekli olan tüm değişkenleri ve viewleri oluşturduğumuza göre sıra geldi metodları yazmaya. Öncelikle initViews metodunu oluşturuyoruz. initViews metodu zaten sizlerin de bildiği gibi sadece yukarıda tanımladığımız viewleri activity tasarımında tanımladıklarımızla eşleştiriyor.
Asıl önemli olan initDownloader metodu ise kullanacağımız kütüphanenin initialize işlemlerini gerçekleştiriyor.
Aşağıda da gördüğünüz gibi öncelikle bahsi geçen kütüphanede bulunan PRDownloaderConfig classından bir obje oluşturuyoruz. Daha sonra indirme işleminin durdurma/devam etme durumlarını takip edebilmek için uygulamanın database özelliğini aktif ediyoruz. Sonrasında ise indirme için bağlantı ve okuma gecikmelerini set ediyoruz. Set etmezsek kütüphane kendi default değerlerini (20.000) kullanacaktır.

    private void initViews() {
        buttonOne = findViewById(R.id.buttonOne);
        buttonTwo = findViewById(R.id.buttonTwo);
        buttonThree = findViewById(R.id.buttonThree);
        buttonFour = findViewById(R.id.buttonFour);
        buttonFive = findViewById(R.id.buttonFive);

        buttonCancelOne = findViewById(R.id.buttonCancelOne);
        buttonCancelTwo = findViewById(R.id.buttonCancelTwo);
        buttonCancelThree = findViewById(R.id.buttonCancelThree);
        buttonCancelFour = findViewById(R.id.buttonCancelFour);
        buttonCancelFive = findViewById(R.id.buttonCancelFive);

        textViewProgressOne = findViewById(R.id.textViewProgressOne);
        textViewProgressTwo = findViewById(R.id.textViewProgressTwo);
        textViewProgressThree = findViewById(R.id.textViewProgressThree);
        textViewProgressFour = findViewById(R.id.textViewProgressFour);
        textViewProgressFive = findViewById(R.id.textViewProgressFive);

        progressBarOne = findViewById(R.id.progressBarOne);
        progressBarTwo = findViewById(R.id.progressBarTwo);
        progressBarThree = findViewById(R.id.progressBarThree);
        progressBarFour = findViewById(R.id.progressBarFour);
        progressBarFive = findViewById(R.id.progressBarFive);
    }


    private void initDownloader() {
        PRDownloaderConfig mConfig = PRDownloaderConfig.newBuilder()
                .setDatabaseEnabled(true) //İndirme işlemi uygulama kill olduktan sonra da devam edebilsin diye database desteğini aktif ediyoruz.
                .setConnectTimeout(30000).setReadTimeout(30000) //İndirme için bağlantı ve okuma gecikmelerini set ediyoruz. Set etmezsek default değerler (20.000) kullanılacaktır.
                .build();
        PRDownloader.initialize(this, mConfig);
    }

Kütüphanenin initialize işlemini de gerçekleştirdiğimize göre sıra geldi butonların neler yapacağını yazmaya. Yukarıdaki dizaynda da gördüğünüz üzere uygulama 5 farklı indirme işlemi için tasarlanmış durumda ancak ben sadece ilki için olan bölümü açıklayacağım. Zira diğerlerinde linkler ve dosya isimleri hariç işleyiş olarak her şey aynı.

Metod içerisinde buttonOne isimli butonun onClickListenerini oluşturuyoruz. Bu buton yukarıdaki uygulama görselinde ilk indirme bölümünde sağdaki butonu ifade etmekte ve işlev olarak BAŞLAT ve DURAKLAT işlevlerini yerine getirmekte. Listener içerisinde öncelikle if koşulu içerisinde ilk indirmenin durumunun devam edip etmediğini kontrol ediyoruz ve eğer devam ediyorsa indirmeyi ID’si aracılığıyla duraklatıp return ile click metodundan çıkıyoruz.

Yok eğer indirme işlemi devam etmiyorsa önce tıkladığımız butonu pasif ediyoruz ki işlemler arasında ikinci bir tıklama ile yapı birbirine girmesin. Sonrasında progress barın indetermine durumunu aktif ediyoruz çünkü indirme işlemini henüz başlatmadığımız ya da devam ettirmediğimiz için doğru bir değer gösteremeyiz. Sonrasındaki satır sadece dizayn için yazılmış bir satır, konumuzla ilgisi olmadığı için bahsetmeyeceğim.
Daha sonra indirme işleminin duraklatılıp duraklatılmadığını kontrol ediyoruz ve eğer duraklatıldıysa yine ID aracılığıyla indirme işlemini devam ettiriyoruz ve return ile metod içerisinden çıkıyoruz.

Ancak indirme işleminin henüz hiç başlamadığı durumu hala kontrol etmedik. Kodun devamı da zaten bu bölümü içermekte. Kütüphanenin içerisindeki download metoduyla bir indirme işlemi başlatıyoruz ve bu metodu ID’ye eşitliyoruz ki sonrasında işlemi takip edebilelim. Bu metod içerisine indirilecek dosyanın adresini, indirme pathini ve indirilecek dosyanın adını alıyor. Sonrasında ise diğer listenerleri ekliyoruz. Bu listenerler sırasıyla şöyle;

  • OnStartOrResumeListener

  • OnPauseListener

  • OnCancelListener

  • OnProgressListener

  • OnDownloadListener

Bu listenerlerin gövdelerini tek tek anlatmayacağım çünkü bakıldığında fazlasıyla anlaşılır olduğunu görebilirsiniz zaten. Ancak yine de takıldığınız yer olursa yorum olarak bildirirseniz ayrıca yardımcı olabilirim.

Kod bloğunun en sonunda ise soldaki diğer butonun listenerinin set edildiğini görebilirsiniz. Bu buton sadece indirme işlemini durduruyor. cancel metoduna ID’yi gönderdiğimizde de bu işlem gerçekleşiyor. Tabii burda indirme tamamen durdurulduğu için daha önce indirilmiş olan yarım kalmış veriler de hafızadan tamamen siliniyor. O nedenle bu bölümü duraklatma ile karıştırmamakta fayda var.

    private void onClickListenerOne() {
        buttonOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (Status.RUNNING == PRDownloader.getStatus(downloadIdOne)) {
                    PRDownloader.pause(downloadIdOne);
                    return;
                }

                buttonOne.setEnabled(false);
                progressBarOne.setIndeterminate(true);
                progressBarOne.getIndeterminateDrawable().setColorFilter(
                        Color.RED, android.graphics.PorterDuff.Mode.SRC_IN);

                if (Status.PAUSED == PRDownloader.getStatus(downloadIdOne)) {
                    PRDownloader.resume(downloadIdOne);
                    return;
                }

                downloadIdOne = PRDownloader.download(URL1, dirPath, "File1.zip")
                        .build()
                        .setOnStartOrResumeListener(new OnStartOrResumeListener() {
                            @Override
                            public void onStartOrResume() {
                                progressBarOne.setIndeterminate(false);
                                buttonOne.setEnabled(true);
                                buttonOne.setText(R.string.pause);
                                buttonCancelOne.setEnabled(true);
                            }
                        })
                        .setOnPauseListener(new OnPauseListener() {
                            @Override
                            public void onPause() {
                                buttonOne.setText(R.string.resume);
                            }
                        })
                        .setOnCancelListener(new OnCancelListener() {
                            @Override
                            public void onCancel() {
                                buttonOne.setText(R.string.start);
                                buttonCancelOne.setEnabled(false);
                                progressBarOne.setProgress(0);
                                textViewProgressOne.setText(R.string.not_started_download_size);
                                downloadIdOne = 0;
                                progressBarOne.setIndeterminate(false);
                            }
                        })
                        .setOnProgressListener(new OnProgressListener() {
                            @Override
                            public void onProgress(Progress progress) {
                                long progressPercent = progress.currentBytes * 100 / progress.totalBytes;
                                progressBarOne.setProgress((int) progressPercent);
                                textViewProgressOne.setText(Utils.getProgressDisplayLine(progress.currentBytes, progress.totalBytes));
                                progressBarOne.setIndeterminate(false);
                            }
                        })
                        .start(new OnDownloadListener() {
                            @Override
                            public void onDownloadComplete() {
                                buttonOne.setEnabled(false);
                                buttonCancelOne.setEnabled(false);
                                buttonOne.setText(R.string.completed);
                            }

                            @Override
                            public void onError(Error error) {
                                buttonOne.setText(R.string.start);
                                Toast.makeText(getApplicationContext(), getString(R.string.some_error_occurred) + "\n İndirme ID Numarası: " + downloadIdOne, Toast.LENGTH_SHORT).show();
                                textViewProgressOne.setText(R.string.not_started_download_size);
                                progressBarOne.setProgress(0);
                                downloadIdOne = 0;
                                buttonCancelOne.setEnabled(false);
                                progressBarOne.setIndeterminate(false);
                                buttonOne.setEnabled(true);
                            }
                        });
            }
        });

        buttonCancelOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PRDownloader.cancel(downloadIdOne);
            }
        });
    }

Gerekli tüm metodlar da yazıldığına göre son olarak bu metodları onCreate içerisinde çağırmak kaldı demektir. Ancak ondan önce dirPath’i set ediyoruz. Ben oluşturduğum Utils classı içerisindeki metoddan dönen değerle set ediyorum bu değişkeni burada. Yine konuyla ilgili olmadığı için ayrıntı vermeyeceğim ancak aşağıdaki github linkinden kodlara bakarak rahatlıkla anlayabilirsiniz olanları. Bu projede ise dosyalar “ROOT/Android/data/PACKAGE_NAME/files” pathine inmekte. Bu klasör içerisinden indirilen dosyaları kontrol edebilirsiniz.
Bu tanımlamadan sonra ise dediğim gibi yazılan metodları geçerli olması için çağırıyoruz sadece.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dirPath = Utils.getRootDirPath(getApplicationContext());

        initViews();
        initDownloader();

        onClickListenerOne();
        onClickListenerTwo();
        onClickListenerThree();
        onClickListenerFour();
        onClickListenerFive();
    }

Tüm bu aşamaları eksiksiz tamamladığınızda kütüphane üzerine düşen tüm görevleri yerine getirmek için hazır demektir. Dilerseniz kütüphanenin github adresine de buradan erişebilir ve güncel sürümlerini kullanabilirsiniz.

Bugünlük anlatacaklarım bu kadar, umarım faydalı olmuştur.
Bir sonraki içerikte görüşmek üzere...