Volley, Android uygulamalarımızda kullandığımız AsyncTask’ın alternatifi olarak ortaya çıkarılan, Google I/O 2013 sunumunda duyurulan bir Network(Http) kütüphanesidir.
Volley kütüphanesi, hazırladığınız bir REST servisi ya da JSON dosyası varsa, servis URL’ine bağlanıp sonucu size daha hızlı bir şekilde döndürür. AsyncTask kullananlar bilir. doInBackground() metodunda ekran işlemleri yapılmazdı ve tasklar bazen diğerinden önce çalışırdı. Activity bittikten sonra AsyncTask kapatılırsa hata alınırdı. Volley ile bu sorunlar ortadan kalkmış oldu. Tüm işlemler asenkron olarak yapıldığı için zaten bu işi yapan AsyncTask’lara da gerek kalmadı.
Volley kütüphanesi, performansı arttırmak için en önemli işlev olarak önbellekleme özelliğini kullanır. Eğer bilgi Cache’de varsa doğrudan sonuç gösterilir. Yoksa yeni bir istek oluşturulur.
Volley kütüphanesinin avantajları :
- Ağ isteklerinin zamanlamasını otomatik olarak yapar.
- Cache yapısı olduğundan hızlı işlem yapar. Özellikle resim indirme ve gösterme işlemlerinde disk ya da bellekte cache’leme yapılır.
- Eş zamanlı olarak çoklu bağlantıya izin verir.
- İstekleri asenkron olarak işleyebildiği için AsyncTask’taki sorunlar görülmez.
- AsyncTask’taki gibi karışık ve zorlu kontrollere gerek yoktur.
- JSON ve resim işlemlerinde güçlü desteği mevcuttur.
- Yapılan bir istek, istenildiğinde iptal edilebilir. İşi biten istekler otomatik olarak iptal edilir.
- Büyük boyuttaki veri çekme işlemleri için uygun değildir. Cache sistemi yüksek boyutta veri tutmaya yetmeyecektir.
RequestQueue Sınıfı
Volley bünyesindeki RequestQueue sınıfı, önbelleğe yazma ve önbellekten okuma işlemlerini, istekleri sıraya koyma ve istek sonuçlarını dönme gibi işlemleri yapar. Her ağ isteğinden önce, bu sınıftan bir nesne oluşturulması ve daha sonrasında istenilen bağlantının sağlanması gerekir. Yapılan her isteğin, istekten sonra bu nesneye add() ile eklenmesi gerekir. Böylece istek kuyruğuna isteği eklemiş oluruz ve bu sınıf bizim için kuyruktaki isteklerin yönetimini ve cache’lemelerini yapar. AsyncTask’a göre en önemli avantajı budur.
Her activity için RequestQueue sınıfından nesne oluşturmak yerine, tüm uygulama boyunca tek bir Singleton nesne oluşturup, onun üzerinden kuyruğa ekleme ve çıkarma işlemleri yapılması Google tarafından öneriliyor. Biz uygulamalarımızda Singleton nesne kullanacağız fakat manuel olarak RequestQueue nesnesi oluşturmayı da bilelim. Konunun sonunda UYARI başlığı altında bunun detayları anlatılacaktır.
Yeni Proje Oluşturma
Volley hakkında bilgi verdikten sonra ufaktan proje oluşturalım ve Volley kullanımını görelim.
Proje oluşturduktan sonra ilk olarak build.gradle dosyasında Volley kütüphanesini tanıtıyoruz. Kütüphane ilk çıktığında jar oluşturma işlemini manuel olarak yapıyorduk. Sağolsun maven imdadımıza yetişti.
1 2 3 4 5 |
dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.volley:volley:1.1.0' // volley için bu satır eklenmeli } |
build.gradle dosyasının tamamı ise şu şekilde olacaktır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "com.mehmetkirazli.volleykullanimi" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.volley:volley:1.1.0' // volley için bu satır eklenmeli } |
Şimdi ise AppController sınıfını ekleyelim. Bu sınıf, tüm uygulama boyunca isteklerin takibini ve asenkron çalışmasını sağlar.
Activity tabanlı değil de Application tabanlı bu sınıfı kullanmak, uygulamanın yaşam döngüsü boyunca yönetimin tek bir sınıftan ve tek bir RequestQueue nesnesi üzerinden olması amacıyla daha avantajlıdır. Bu şekilde sınıfı yazıp Manifest içinden de tanımını yapacağız.
AsyncTask kullanırken cihaz döndürüldüğünde, Activity tekrar çalıştığı için AsyncTask da tekrar başlatılıyordu. Volley’de ise Application sınıfından türetilen AppController sınıfı üzerinden, yaşam döngüsü boyunca kuyruğa ekleme çıkarma işlemleri yapılır. Konu başında bundan bahsetmiştik.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.mehmetkirazli.volleykullanimi; import android.app.Application; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; public class AppController extends Application { public static final String TAG = AppController.class.getSimpleName(); private RequestQueue mRequestQueue; private static AppController mInstance; private ImageLoader mImageLoader; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized AppController getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } // kuyruya istek eklemek için kullanılır (istek adıyla beraber) public <T> void addToRequestQueue(Request<T> request, String tag) { request.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(request); } // kuyruya istek eklemek için kullanılır (istek adı olmadan) public <T> void addToRequestQueue(Request<T> request) { request.setTag(TAG); getRequestQueue().add(request); } // resimleri yükleme ve cache işlemleri için çağrılır public ImageLoader getImageLoader() { getRequestQueue(); if (mImageLoader == null) { mImageLoader = new ImageLoader(this.mRequestQueue, new BitMapCache()); } return this.mImageLoader; } // volley isteğini iptal etmek için çağrılır public void cancelPendingRequests(Object obj) { if (mRequestQueue != null) { mRequestQueue.cancelAll(obj); } } } |
AppController sınıfı, Application sınıfından türetilir. Konunun sonunda resim indirme işlemi de yapacağımız için getImageLoader() metodunu da ekledik. Siz kullanmayacaksanız kaldırabilirsiniz.
Resim işlemleri için Cache’leme görevini yapacak olan BitMapCache sınıfımızı da ekleyelim. getImageLoader() metodunda bu sınıfı kullanıyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.mehmetkirazli.volleykullanimi; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import com.android.volley.toolbox.ImageLoader; public class BitMapCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache { public static int getDefaultLruCacheSize() { final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; return cacheSize; } public BitMapCache() { this(getDefaultLruCacheSize()); } public BitMapCache(int sizeInKiloBytes) { super(sizeInKiloBytes); } @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight() / 1024; } @Override public Bitmap getBitmap(String url) { return get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { put(url, bitmap); } } |
Daha sonra AndroidManifest.xml dosyasında İnternet izni ekliyoruz. Uygulamada kullanacağımız 3 adet aktiviteyi tanımlıyoruz. Son olarak da application tag’inde name kısmında AppController‘i tanımlıyoruz.
AppController kullanmayıp her istek için RequestQueue kullansaydık bu name tanımını yapmayacaktık.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mehmetkirazli.volleykullanimi"> <uses-permission android:name="android.permission.INTERNET" /> <application android:name="com.mehmetkirazli.volleykullanimi.AppController" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".ActivityMain"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ActivityMarkalar"/> <activity android:name=".ActivityKullanicilar"/> <activity android:name=".ActivityResimler"/> </application> </manifest> |
Giriş Ekranı
activity_main.xml dosyasına 3 adet buton ekleyelim. 1.sinde basit olarak JSON dosyasını okuyup listeye basalım. 2.sinde biraz daha karışık olan JSON dosyasını parse edip sonuçları bir listede gösterelim. Son butonda da Volley ile çekilen resimleri ImageView üzerinde gösterelim.
Bu işlemler yeni Activity üzerinde yapılacak o yüzden her bir buton için yeni bir Activity oluşturacağız ve her tıklamada yeni Activity açacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btnMarka" android:layout_width="183dp" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="180dp" android:text="Markaları Göster" /> <Button android:id="@+id/btnKullanici" android:layout_width="183dp" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="230dp" android:text="Kullanıcıları Göster" /> <Button android:id="@+id/btnResim" android:layout_width="183dp" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="280dp" android:text="Resimleri Göster" /> </RelativeLayout> |
Bu ekrana bağlı olan ActivityMain.java sınıfımız ise şu şekilde olacaktır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.mehmetkirazli.volleykullanimi; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class ActivityMain extends AppCompatActivity { Button btnKullanici, btnMarka, btnResim; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnKullanici = (Button) findViewById(R.id.btnKullanici); btnMarka = (Button) findViewById(R.id.btnMarka); btnResim = (Button) findViewById(R.id.btnResim); btnMarka.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(getApplicationContext(), ActivityMarkalar.class)); } }); btnKullanici.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(getApplicationContext(), ActivityKullanicilar.class)); } }); btnResim.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(getApplicationContext(), ActivityResimler.class)); } }); } } |
Markalar Ekranı
Buraya kadar Volley kullanarak bir Http isteği yapmadık. Bundan sonra istek gönderme işlemlerine başlıyoruz. İlk olarak elimizde markaları gösteren bir JSON dosyası olsun ve bu dosyayı çağırarak dönen sonucu bir listede gösterelim.
Önce markalar.json dosyasının içeriğini verelim sonra bunu Volley ile çağırıp dönen sonuçları Parse ederek gösterelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{ "markalar":[ "Alfa Romeo", "Bmw", "Dacia", "Fiat", "Ford", "Honda", "Hyundai", "Jaguar", "Lada", "Mazda", "Mercedes", "Nissan", "Opel", "Peugeot", "Renault", "Seat", "Skoda", "Subaru", "Suzuki", "Tofaş", "Toyota", "Volkswagen", "Volvo" ] } |
Şimdi ActivityMarkalar sınıfını kodlamaya başlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
package com.mehmetkirazli.volleykullanimi; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.StringRequest; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; public class ActivityMarkalar extends AppCompatActivity { ListView listViewMarka; ProgressDialog dialog; String urlMarkalar = "https://www.mehmetkirazli.com/Dosya/markalar.json"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_markalar); listViewMarka = (ListView) findViewById(R.id.lwMarka); // istek bitene kadar dialog gösterilir. istek sonuçlanınca dialog kapatılacak dialog = new ProgressDialog(this); dialog.setMessage("Veriler Okunuyor..."); dialog.setCancelable(false); dialog.show(); /* StringRequest yerine JsonObjectRequest veya JsonArrayRequest de kullanılabilir. Fakat StringRequest hepsini kapsadığı için bunu kullandık. */ // VOLLEY İSTEĞİ BURADA YAPILIYOR. StringRequest request = new StringRequest(Request.Method.GET, urlMarkalar, new Response.Listener<String>() { @Override public void onResponse(String string) { // sonuç başarılı dönerse onResponse çağrılır okunanlariParseEt(string); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { // hata olursa volleyError nesnesinde hata sebebi yazar Toast.makeText(getApplicationContext(), "Veriler Okunurken Hata Oluştu", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }); // isteği kuyruğa ekledik. "markalar" etiketini ise isteği iptal etmek istediğimizde kullanacağız. Zorunlu değildir AppController.getInstance().addToRequestQueue(request, "markalar"); } void okunanlariParseEt(String okunanJson) { // dönen sonucu parse ediyoruz try { JSONObject jsonObj = new JSONObject(okunanJson); JSONArray arrayMarka = jsonObj.getJSONArray("markalar"); ArrayList<String> sonucList = new ArrayList<>(); for (int i = 0; i < arrayMarka.length(); ++i) { // markaları tek tek listeye ekledik sonucList.add(arrayMarka.getString(i)); } // oluşturduğumuz sonucList listesini, listview'ın adaptörüne verdik ve listede gösterilmesini sağladık ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, sonucList); listViewMarka.setAdapter(adapter); } catch (JSONException e) { e.printStackTrace(); } dialog.dismiss(); } } |
Burada yapılan işlemleri açıklama satırlarında anlatmaya çalıştım. Yine üzerinden geçelim.
Bir StringRequest nesnesi oluşturduk. İlk parametresi, isteğin tipidir. Biz GET isteğinde bulunduk. Sonraki parametre, bağlanacağımız URL‘dir. Diğer 2 parametre ise başarılı ve başarısız sonuç döndüğünde çalışacak Listener‘lardır.
Sonuç dönerse onResponse() metodu çalışır. Bu metotta dönen JSON değeri parse edilir ve değerler tek tek listeye eklenir. Son olarak liste, ListView‘a adaptör olarak verilir ve ekranda gösterilir.
Eğer dönen JSON değeri, doğrudan bir JSON Array‘i ise StringRequest yerine JsonArrayRequest de kullanabilirsiniz. O zaman Listener’a ait metotlar da değişecektir. JSON Objesi ise JsonObjectRequest sınıfı kullanılabilir. StringRequest ikisini de kapsadığı için ben bunu kullandım.
Bu sınıfa ait xml dosyasını da oluşturalım.
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lwMarka" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> |
Markaları Göster butonuna tıkladığımızda ekran görüntüsü şu şekilde olacaktır.
Kullanıcılar Ekranı
ActivityKullanicilar.java sınıfında ise biraz daha detaylı bir JSON dosyası okuyacağız ve parse edip ListView’da göstereceğiz.
Öncelikle URL’imizde bulunan JSON dosyasının içeriğini paylaşalım. JSON dosyasında şu bilgiler bulunmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
[{ "id": 1, "ad": "Mehmet", "soyad": "Kirazli", "sehir": "Istanbul", "cinsiyet": "Erkek", "ip_adres": "36.48.233.2" }, { "id": 2, "ad": "Faruk", "soyad": "Kalkan", "sehir": "Edirne", "cinsiyet": "Erkek", "ip_adres": "212.111.4.222" }, { "id": 3, "ad": "Salih", "soyad": "Aydin", "sehir": "Samsun", "cinsiyet": "Erkek", "ip_adres": "120.33.112.111" }, { "id": 4, "ad": "Okan", "soyad": "Eren", "sehir": "Giresun", "cinsiyet": "Erkek", "ip_adres": "77.77.222.11" }] |
Şimdi bu JSON dosyasına Volley ile erişip okuma işlemi yapalım.
Şuan elimde JSON bilgisi dönen bir servis olmadığı için hazır bir JSON dosyasına erişiyorum. Eğer JSON dönen www.mehmetkirazli.com/apis/getusers.php gibi bir servis olsaydı, URL kısmında bunu yazacaktım. Fakat siz test amaçlı olarak :
https://maps.googleapis.com/maps/api/place/textsearch/xml?query=restaurants+in+Sydney&key=API_KEY
URL’ini test edebilirsiniz. Api key girerek servisten dönen JSON değerini görebilirsiniz.
ActivitiyKulanicilar.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
package com.mehmetkirazli.volleykullanimi; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.StringRequest; import org.json.JSONArray; import org.json.JSONException; import java.util.ArrayList; public class ActivityKullanicilar extends AppCompatActivity { ListView listViewMarka; String urlKullanicilar = "https://www.mehmetkirazli.com/Dosya/kullanicilar.json"; ProgressDialog dialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_kullanicilar); listViewMarka = (ListView) findViewById(R.id.lwMarka); // istek bitene kadar dilaog gösterilir. istek sonuçlanınca dialog kapatılacak dialog = new ProgressDialog(this); dialog.setMessage("Veriler Okunuyor..."); dialog.setCancelable(false); dialog.show(); /* StringRequest yerine JsonObjectRequest veya JsonArrayRequest de kullanılabilir. Fakat StringRequest hepsini kapsadığı için bunu kullandık. */ // VOLLEY İSTEĞİ BURADA YAPILIYOR. StringRequest request = new StringRequest(Request.Method.GET, urlKullanicilar, new Response.Listener<String>() { @Override public void onResponse(String sonuc) { // sonuç başarılı dönerse onResponse çağrılır ArrayList<Kullanicilar> kullaniciList = okunanlariParseEt(sonuc); // parse metodu çağrıldı ArrayAdapter<Kullanicilar> adapter = new ArrayAdapter<>(ActivityKullanicilar.this, android.R.layout.simple_list_item_1, kullaniciList); listViewMarka.setAdapter(adapter); } }, new Response.ErrorListener() { // hata oluşursa burası çağrılır @Override public void onErrorResponse(VolleyError volleyError) { Toast.makeText(getApplicationContext(), "Veriler Okunurken Hata Oluştu", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }); AppController.getInstance().addToRequestQueue(request, "kullanicilar"); } ArrayList<Kullanicilar> okunanlariParseEt(String okunanJson) { ArrayList<Kullanicilar> kullaniciList = new ArrayList<>(); try { JSONArray arrayKullanici = new JSONArray(okunanJson); for (int i = 0; i < arrayKullanici.length(); ++i) { kullaniciList.add(new Kullanicilar(Integer.valueOf(arrayKullanici.getJSONObject(i).get("id").toString()), arrayKullanici.getJSONObject(i).get("ad").toString(), arrayKullanici.getJSONObject(i).get("soyad").toString(), arrayKullanici.getJSONObject(i).get("sehir").toString(), arrayKullanici.getJSONObject(i).get("cinsiyet").toString(), arrayKullanici.getJSONObject(i).get("ip_adres").toString())); } } catch (JSONException e) { e.printStackTrace(); } dialog.dismiss(); return kullaniciList; } } |
Genel olarak Markalar sınıfındaki benzer işlemleri yaptık. Parse işlemleri biraz daha karışık oldu. AppController sınıfında addToRequestQueue() metoduna, isteğimizi parametre olarak verdik ve kuyruğa ekledik. Yanında bir de “kullanicilar” metnini verdik. Bunu, isteği iptal etmek istediğimizde kullanacağız ve bu isimle iptal edeceğiz. Bunu kendisi yapıyor fakat örnek olması açısından yazdım. Bu parametreyi yazmak zorunlu değildir.
Burada Markalar sınıfından farklı olarak, dönen JSON’ı parse edip Kullanicilar adında bir model sınıfına ekledik. Kullanicilar sınıfını da verelim.
Kullanicilar Model sınıfı:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package com.mehmetkirazli.volleykullanimi; public class Kullanicilar { private int id; private String ad; private String soyad; private String sehir; private String cinsiyet; private String ip_adres; public Kullanicilar(int id, String ad, String soyad, String sehir, String cinsiyet, String ip_adres) { this.id = id; this.ad = ad; this.soyad = soyad; this.sehir = sehir; this.cinsiyet = cinsiyet; this.ip_adres = ip_adres; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAd() { return ad; } public void setAd(String ad) { this.ad = ad; } public String getSoyad() { return soyad; } public void setSoyad(String soyad) { this.soyad = soyad; } public String getSehir() { return sehir; } public void setSehir(String sehir) { this.sehir = sehir; } public String getCinsiyet() { return cinsiyet; } public void setCinsiyet(String cinsiyet) { this.cinsiyet = cinsiyet; } public String getIp_adres() { return ip_adres; } public void setIp_adres(String ip_adres) { this.ip_adres = ip_adres; } @Override public String toString() { return ad + " " + soyad + "\n" + cinsiyet + "-" + sehir + "-" + ip_adres; } } |
Bu model sınıfında en son toString() metodunu override ettik. Çünkü ListView’a bu modele ait listeyi parametre olarak verdiğimizde, her satırda görülmesi istenen metni yazdık. Custom bir Adapter‘e gerek kalmadı.
activity_kullanicilar.xml dosyası :
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lwMarka" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> |
“Kullanıcıları Göster” butonuna tıkladığımızda ActivityKullanicilar ekranı açılacak ve sonuç alınıp ekrana aşağıdaki gibi bilgiler yazılacak. Tabi bu arada ProgressBar da dönecek ve sonuç döndüğünde progress kapanacak.
POST İSTEĞİ GÖNDERME
Bu zamana kadar örneklerde GET isteği gönderdik.
1 |
StringRequest request = new StringRequest(Request.Method.GET, urlKullanicilar, new Response.Listener<String>() { |
Satırında da GET isteği gönderdiğimizi belirttik. Hazırladığınız servisin özelliğine göre POST isteği de göndermek isteyebilirsiniz. POST isteklerinde servise parametre gönderirsiniz ve size sonuç döner. URL kısmında ise gönderdiğiniz parametreler yazmaz. GET isteğinde ise bu parametreler yazar. Parametreden kastımız örnek olarak id = 1 gönderirsiniz ve id değeri 1 olan kayıtlar serviste okunur ve sonuç JSON olarak size döner.
Yani GET işleminde URL’de parametreleri yazarız (Örneklerimizde parametre yoktu). POST işleminde ise parametreleri URL’e yazmayız, arkaplanda göndeririz.
Volley ile POST isteği oluşturduğunuzda, her şey aynı sadece StringRequest‘i oluşturduğunuz satırda aşağıdaki gibi ufak bir değişiklik yapıyorsunuz.
1 |
StringRequest request = new StringRequest(Request.Method.POST, urlKullanicilar, new Response.Listener<String>() { |
Daha sonra da isteğin sonuna parametreleri ekliyorsunuz. Parametre ekleme metodu olan getParams() metodundaki süslü parantezlerin sırasına dikkat ediniz. Çünkü bu bölüm biraz karışık. Kod bloğumuz son olarak şu şekilde olur:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
StringRequest request = new StringRequest(Request.Method.POST, urlKullanicilar, new Response.Listener<String>() { @Override public void onResponse(String string) { ArrayList<Kullanicilar> kullaniciList = okunanlariParseEt(string); ArrayAdapter<Kullanicilar> adapter = new ArrayAdapter<>(ActivityResimler.this, android.R.layout.simple_list_item_1, kullaniciList); listViewMarka.setAdapter(adapter); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { Toast.makeText(getApplicationContext(), "Veriler Okunurken Hata Oluştu", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }) { @Override protected Map<String, String> getParams() { Map<String, String> params = new HashMap<>(); params.put("id", "2"); return params; } }; AppController.getInstance().addToRequestQueue(request); |
Burada id değeri 2 ye eşit olan kaydı bize getirir. Tabi servisin de buna uygun kontrolleri yapmış olması gerekiyor. Örnek olarak eğer servisimiz PHP ile hazırlanmışsa şu şekilde kontrol olacaktır.
1 2 3 |
if($_SERVER['REQUEST_METHOD']=="POST"){ $id= @$_POST['id']; } |
Bunu GET ile yapsaydık urlKullanicilar URL’inin sonunda ?id=2 gibi bir ekleme yapılacaktı ve getParams() olmayacaktı.
Header Ekleme
Eğer POST isteğinize bir Header bilgisi eklemek istiyorsanız, isteğinizi aşağıdaki gibi güncellemeniz gerekmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
StringRequest request = new StringRequest(Request.Method.POST, urlKullanicilar, new Response.Listener<String>() { @Override public void onResponse(String string) { ArrayList<Kullanicilar> kullaniciList = okunanlariParseEt(string); ArrayAdapter<Kullanicilar> adapter = new ArrayAdapter<>(ActivityResimler.this, android.R.layout.simple_list_item_1, kullaniciList); listViewMarka.setAdapter(adapter); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { Toast.makeText(getApplicationContext(), "Veriler Okunurken Hata Oluştu", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }) { @Override public Map<String, String> getHeaders() throws AuthFailureError { HashMap<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("apiKey", "abcdefghijklmn"); return headers; } }; |
ÖNEMLİ BİR UYARI
Konu başında belirtmiştim. Volley isteği oluşturduktan sonra isteği, AppController üzerinden kuyruğa ekliyoruz. Bu işlem şu şekilde de yapılabiliyor fakat önerilmiyor.
İsteği oluşturmadan önce :
1 |
RequestQueue rQueue = Volley.newRequestQueue(ActivityMarkalar.this); |
Kod bloğu ile bir kuyruk oluşturuluyor. İstek oluşturulduktan sonra da aşağıdaki gibi isteğimiz kuyruğa ekleniyor.
İsteği oluşturduktan sonra :
1 |
rQueue.add(request); |
Eğer uygulamada çokça Volley isteği olacaksa, bu işlemlerin tek bir SingletonClass üzerinden yapılması (AppController) daha doğrudur ve Google da bunu önermektedir. Fakat az sayıda istek olması durumunda manuel olarak her istek için RequestQueue nesnesi de oluşturulabilir. Bu şekilde bir yapı kullanılacaksa, RequestQueue nesnesi, istek yapılmadan önce oluşturulmalıdır.
Resim İşlemleri
Son olarak Volley ile resim bilgisini okuyup ImageView üzerinde gösterim işlemini yapalım. Okunan resim bilgisini Bitmap‘e çevirip ImageView içerisine set edeceğiz.
İlk olarak xml tarafını yazalım. Basit bir ImageView olması yeterli. activity_resimler:
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/imgView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" /> </RelativeLayout> |
Şimdi de ActivityResimler sınıfını kodlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package com.mehmetkirazli.volleykullanimi; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.ImageView; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; public class ActivityResimler extends AppCompatActivity { String urlResimler = "https://www.mehmetkirazli.com/wp-content/uploads/2017/03/islami-hafiza-oyunu-310x165.png"; ProgressDialog dialog; ImageView imgView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_resimler); imgView = (ImageView) findViewById(R.id.imgView); dialog = new ProgressDialog(this); dialog.setMessage("Veriler Okunuyor..."); dialog.setCancelable(false); dialog.show(); ImageLoader imageLoader = AppController.getInstance().getImageLoader(); // resim indirilirken ve hata olması durumunda gösterilecek resimleri ayarladık. imageLoader.get(urlResimler, ImageLoader.getImageListener(imgView, R.drawable.yukleniyor, R.drawable.hata)); // resim alınıyor imageLoader.get(urlResimler, new ImageLoader.ImageListener() { @Override public void onErrorResponse(VolleyError error) { dialog.dismiss(); // hata olursa dialog kapansın ve hata mesajı loga basılsın Log.d("hata",error.toString()); } @Override public void onResponse(ImageLoader.ImageContainer response, boolean arg1) { if (response.getBitmap() != null) { imgView.setImageBitmap(response.getBitmap()); // bitmap'e çevirip set ettik } dialog.dismiss(); } }); } } |
Burada resim indirilirken aşağıdaki YÜKLENİYOR resmi ImageView’da gösterilecek.
Eğer resim indirilirken hata alınırsa da ImageView’da aşağıdaki hata resmi gösterilecek. Volley ile bunları ayarlayabilmek iyi oluyor.
Ekran çıktımız ise şu şekilde olacaktır.
Cache Okuma ve Silme
Volley kütüphanesinde Cache mekanizması olduğunu ve bu yüzden hız konusunda avantaj sağladığını söylemiştik. Cache’de tutulan bilgileri görmek, eğer cache boşsa yeni bir istek yapmak isteyebiliriz. Bunun kullanımı ise şu şekildedir.
1 2 3 4 5 6 7 8 9 10 11 |
Cache cache = AppController.getInstance().getRequestQueue().getCache(); Cache.Entry entry = cache.get("https://www.mehmetkirazli.com/Dosya/markalar.json"); if (entry != null) { try { String cacheBilgisi = new String(entry.data, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else { // cache boş. yeni istek yapılabilir } |
Daha önce gidilmiş bir URL varsa, Cache‘de tutulan dönüş bilgisini bu şekilde görebiliriz. Debug yapıldığında, cacheBilgisi değişkeninde, henüz istek yapılmadan JSON değeri görülür.
Url’deki JSON bilgisinin değiştiğini düşünürsek, Cache bilgisini silmek için ise aşağıdaki kod kullanılabilir.
1 |
AppController.getInstance().getRequestQueue().getCache().remove("https://www.mehmetkirazli.com/Dosya/markalar.json"); |
İstek İptal Etme
Örneklerde kuyruğa eklerken bir parametre daha eklemiştik hatırlarsanız. Bunu daha sonra istek iptal etmek istediğimizde KEY olarak kullanacağımızı söylemiştik.
Eğer kullanıcıları aldığımız isteği herhangi bir sebeple iptal etmek istersek:
1 |
AppController.getInstance().getRequestQueue().cancelAll("kullanicilar"); |
Komutunu kullanmalıyız. Volley bunu bizim için yapıyor fakat bir yerde işleyişe müdahale etmek istediğimizde işimize yarayacaktır.
Volley kütüphanesinin kullanımı bu şekildedir. Uzun bir yazı oldu umarım anlatabilmişimdir.
Geliştiriciler için Volley bir nimettir. En azından AsyncTask belasından kurtulmuş olduk 🙂 Görüşmek üzere…
Merhaba Mehmet Hocam,
Makale için öncelikle teşekkürler. Çok güzel bir yazı olmuş. Fakat programı çalıştırdığımda sanal cihazda
“Unfourtanely Json data parsing has stopped” hatası veriyor. Acaba sanal cihaz diyemi böyle yapıyor.
Yoksa kodlarda herhangi bir hata vermiyor.
Teşekkürler.
Merhaba. Sanal cihazda değil de kendi cihazınızda da dener misiniz ? Sanal cihazın versiyonu eskiyse hata veriyor olabilir.