Android Volley
Volley android için geliştirilmiş Networking kütüphanesi. Networking kütüphanesi diyorum çünkü network üzerinden yapmak isteyebileceğiniz hemen herşeyi yapabiliyor. Yapabileceklerinin başında ise URL üzerinden resim indermek ve REST servislere bağlanmak geliyor.
Aktif iÅŸ hayatımda Java EE uygulamaları geliÅŸtiriyor olsam bile, boÅŸ zamanlarımda ufak Freelance iÅŸleri alarak Android uygulamaları geliÅŸtiriyorum. Android konusunda uzman olduÄŸumu söyleyemem ama bu kütüphaneyi gördüğümde vuruldum. En son projemde ağırlıklı olarak resim indirilen bir uygulama geliÅŸtiriyordum. Okadar ki hemen hemen her menüde, listede ya da sayfada resim indirmem gerekiyordu. Her ne kadar AsyncTask’lar ile iÅŸimi hallediyor olasam da bu durum hiç mi hiç içime sinmiyordu. Her sayfa yüklenirken bir loading gösterip arka tarafta resimleri çekmek zaten ne kadar güzel olabilir ki.
Bende bu rahatsızlığımı gidermek için ve birazda merakla açık kaynak android projelerini incelemeye baÅŸladım. Proje proje gezerken Google IO 2013 için geliÅŸtirilmiÅŸ mobile projesinde Volley diye bir kütüphane kullandıklarını gördüm. Nedir ne deÄŸildir diye araÅŸtırırken Google IO 2013’teki sunumu izledim ve aradığım kütüphanenin bu olduÄŸunu anladım.
Volley
Volley networking kütüphanesi. İlk başta aklıma gelen temel yetenekleri şu şekilde,
- HTTP üzerinden resimlerinizi indirebilirsiniz. Hatta indirdiÄŸiniz resimleri bellekte ya da disk üzerinde cache’leyebilirsiniz. Resimleriniz inerken, ImageView içerisinde otomatik olarak bir “loading” simgesi gösterebilir hatta hata durumları için (404 vs) resim atayabilirsiniz.
- REST servislerinize bağlanabilir ve kendi yazacağınız çeviricilerle JSON ya da XML cevaplarını otomatik olarak istediğiniz sınıflara çevirebilirsiniz.
- Tüm networking işlemlerini belirli bir sırada yaptığı gibi, mutithread yapıda çalışmaktadır.
- İstekleri kuyruk yapısında tutar ve eÄŸer isteÄŸin cevabına artık ihtiyaç kalmadıysa otomatik olarak isteÄŸi iptal eder. (İsteÄŸin yapıldığı activity’nin sonlanması gibi durumlarda isteÄŸi otomatik olarak iptal eder.)
- Ve tüm herÅŸeyi aktif olan Thread’i bloklamadan yapar.
Kısaca aklıma gelen yapılar bunlar. Daha detay ve örnek isterseniz sunumu incelemenizi tavsiye ederim.
Volley.jar
Öncelikle Volley.jar’ı nasıl elde edebileceÄŸinize bakalım. Ne yazıkki ben bu yazıyı yazdığım sıralarda bir maven respository’sinde volley.jar kütüphanesini bulamadım. Bundan dolayı volley kütüphanesini kendizin indirip JAR’laması gerekmektedir. Bunu anlatırken Android SDK’nızın kurulu olduÄŸunu, ANDROID_HOME sistem deÄŸiÅŸkeninizin tanımlı olduÄŸunu ve ANDROID_HOME\tools klasörünüzün sistem PATH’nize ekli olduÄŸunu varsayıyorum. Ayrıca ANT geliÅŸtirme aracınızın da kurulu olduÄŸunu varsayıyorum.
Son olarak JAR oluşturmaya başlamadan önce GIT istemcinizin bulunması gerekmektedir. Eğer daha önce GIT istemcisi kurmadıysanız bu adresten indirip kurabilirsiniz.
Artık aşağıdaki komutları takip ederek JAR dosyanızı oluşturabilirsiniz.
- git clone https://android.googlesource.com/platform/frameworks/volley
- cd volley
- android update project -p .
- ant jar
Bu işlemleri yaptıktan sonra bin klasörünün altında volley.jar dosyanızın oluşmuş olması gerekmekedir. Eğer bir hata ile karşılaştıysanız adımlarınızı ve araçlarınızı kontrol edip tekrar deneyin. Bu arada özellikle üçüncü adıma dikkat etmenizi istiyorum orada bir nokta işareti var. Komutun düzgün çalışması için nokta işaretini atlamamanız gerekmektedir.
Örnek Volley Uygulaması
Burdan sonra örnek bir volley uygulaması yapabiliriz. Ben örneğimi Eclipse ADT üzerinden yapacağım. Sizede aynısını kullanmanızı öneririm.
İlk olarak Android Application Project yaratıyoruz. Yaratırken aşağıdaki değerleri kullanıyorum.
- Application Name: VolleyTutorial
- Project Name:Â Â VolleyTutorial
- Package Name:Â com.bahadirakin.volley
- Minimum Required SDK: API 8
- Target SDK: API 18
- Compile With: API 18
- Theme: None
Burdan sonra geri kalan tüm deÄŸerleri Default’ta bırakıyorum. Arzu ettiÄŸinizi deÄŸiÅŸtirebilirsiniz. Bu ÅŸekilde projeyi yarattığımda bana içerisinde MainActivity.java olan bir proje yaratıyor. Burada bir dakika duralım ve Volley kütüphanesini projemize ekleyelim.
Bunun için az önce oluÅŸturduÄŸumuz Volley.jar kütüphanesini projemizin libs klasörüne atıyoruz. EÄŸer jar olarak oluÅŸturamadıysanız proje olarak eclipse’ ekleyipte kullanabilirsiniz.
RequestQueue ve ImageLoader
Yazımızın başında Volley uygulamasının, http isteklerini kuyruk yapısı üzerinden gönderdiğinden bahsetmiştik. Genel olarak bu kuyruk yapısının sistemde bir tane olması gerekmektedir. Tüm http requestleri tabi bunlara yüklenecek resimlerde dahil olmak üzere bu sınıf üzeirnden gönderilecek. Bunun için Singleton yapıda bir sınıf yaratmamız gerekecek. Bunun için com.bahadirakin.volley paketinin altına VolleyApplication isminde Singleton bir sınıf yaratıyoruz.
VolleyApplicaiton.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 | package com.bahadirakin.volley; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; import android.app.ActivityManager; import android.content.Context; public final class VolleyApplication { private static VolleyApplication instance = null; public static final VolleyApplication getInstance() { if (instance == null) { instance = new VolleyApplication(); } return instance; } private RequestQueue requestQueue; private ImageLoader imageLoader; private boolean initialized = false; private VolleyApplication() { } public void init(final Context context) { if (initialized) { return; } requestQueue = Volley.newRequestQueue(context); int memory = ((ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); int cacheSize = 1024 * 1024 * memory / 8; imageLoader = new ImageLoader(requestQueue, new BitmapLruCache(cacheSize)); } public RequestQueue getRequestQueue() { if (requestQueue == null) { throw new RuntimeException("Init first"); } return requestQueue; } public ImageLoader getImageLoader() { if (imageLoader == null) { throw new RuntimeException("Init first"); } return imageLoader; } } |
Bu sınıfı kullanmadan önce “init” methodunun çaÄŸrılması ve içerisinde Andorid Context’inin gönderilmesi gerekiyor. Bu iÅŸlemin bir kere yapılması yeterli olacaktır. Ben tercihen SplashScreen’te bu iÅŸlmi gerçekleÅŸtiriyorum. Ama bu seferlik bu iÅŸlemi MainActivity sınıfının içerisinde yapacağım.
Burada iÅŸaretli olan satıra dikkar ederseniz BitmapLruCache sınıfının kullanıldığını göreceksiniz. Bu sınıfı daha yazmadığımızdan hata verecektir. Kabaca bu sınıf bize yüklenen resimlerin cache’lenmesinde yardımcı olarak. Adından da alyacağınız üzere LRU tekniÄŸiyle cache’leme yapacak ve belliÄŸin sekizde birini kullanacak. Sınıfın detayına aÅŸağıdan ulaÅŸabilirsiniz.
BitmapLruCache.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 | package com.bahadirakin.volley; import com.android.volley.toolbox.ImageLoader.ImageCache; import android.graphics.Bitmap; import android.support.v4.util.LruCache; public class BitmapLruCache extends LruCache<String, Bitmap> implements ImageCache { public BitmapLruCache(int maxSize) { super(maxSize); } @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override public Bitmap getBitmap(String url) { return get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { put(url, bitmap); } } |
Image Lazy Loading
Volley kısmını projeye eklemeyi bitirdik. Şimdi lazy load yapacağımız resim alanını MainActivity sınıfına ekleyim. Ve lazy load işlemini gerçekleştirelim.
HTTP üzerinden yükleyeceğimiz resim adresi: http://www.bahadirakin.com/wp-content/uploads/2013/10/android_volley.png
Resim yüklenene kadar göstereceğimiz resim: loading.png Bu resmi indirip projenizin res/drawable-mdpi klasörüne yüklemenizi istiyorum.
Resim yükleme sırasında hata çıkarsa göstereceğimiz resim: error.png Bu resmide indirip aynı yere kaydetmenizi istiyorum.
Resim indireceğiz bunun için andorid uygulamamızın izinlerini ayarlamamız gerekiyor. AndroidManifest.xml dosyasına aşağıdaki satırları eklemeniz yeterli olacaktır.
AndroidManifest.xml
1 2 | <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
ImageView yapsınızı MainActivty’e ekledikten sorna xml dosyası aÅŸağıdaki gibi olacaktır.
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <RelativeLayout 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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ImageView android:id="@+id/lazyImage" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> |
Sayfamızın onCreate adımında ise resmi yüklüyoruz. Bu sizi ÅŸaşırta bilir ama bu iÅŸlem sayfanızı kitlemeyecektir. Yazının başında Volley’i tanıtırken de söylediÄŸim gibi, volley iÅŸlemlerini thread’leri bloklamadan yapıyor.
MainActivity.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 | package com.bahadirakin.volley; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.widget.ImageView; import com.android.volley.toolbox.ImageLoader; public class MainActivity extends Activity { private static final String IMAGE_URL = "http://www.bahadirakin.com/wp-content/uploads/2013/10/android_volley.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Init Volley application --- Only Once final VolleyApplication volleyApplication = VolleyApplication .getInstance(); volleyApplication.init(getApplicationContext()); // Lazy Loading Image final ImageView lazyImage = (ImageView) findViewById(R.id.lazyImage); volleyApplication.getImageLoader().get( IMAGE_URL, ImageLoader.getImageListener(lazyImage, R.drawable.loading, R.drawable.error)); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } } |
Eğer hiç bir hata ile karşılaşmazsanız resminiz başarıyla yüklenecektir. Hatta yüklenmeden önce Loading.png resmini göreceksiniz. Burada isterseniz başka Drawable yapılarıda kullanabilirsiniz.
Rest Servisleri
Şimdi diğer bahsettiğimiz konuyla ilgili bir örnek yapalım. Bu arada Rest servislerine bağlanıp bilgi çekelim. Ben örnek için hali hazırda ayakta olan facebook servislerinden yararlanacağım. Facebook grap api kullanarak facebook android ile ilgili bilgi çekeceğiz. Bunun için kullanacağımız URL aşağıda verilmiştir.
- http://graph.facebook.com/android
İlk örneğimizi basit bir istek üzerinden yapacağız. Bunun için StringRequest kullanacağız. Bu request ile döndn değerin içeriği doğrudan string olarak kullanılabiliyor. Bu bilgiler ışığında MainActivity.java sınıfının içeriği aşağıdaki gibi değiştiriliyor.
MainActivity.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 | package com.bahadirakin.volley; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.widget.ImageView; import android.widget.Toast; import com.android.volley.Request.Method; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.StringRequest; public class MainActivity extends Activity { private static final String IMAGE_URL = "http://www.bahadirakin.com/wp-content/uploads/2013/10/android_volley.png"; private static final String GRAPH_FACEBOOK_ANDOROID = "http://graph.facebook.com/android"; @Override protected void onCreate(Bundle savedInstanceState) { ... // Init Volley application --- Only Once ... // Lazy Loading Image ... // String Request volleyApplication.getRequestQueue().add( new StringRequest(Method.GET, GRAPH_FACEBOOK_ANDOROID, createSuccessListener(), createErrorListener())); } ... private Response.Listener<String> createSuccessListener() { return new Response.Listener<String>() { @Override public void onResponse(String response) { Toast.makeText(MainActivity.this, response, Toast.LENGTH_LONG) .show(); } }; } private Response.ErrorListener createErrorListener() { return new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show(); } }; } } |
Bu şekilde projenizi çalıştırdığınızda Toast mesajının içerisinde JSON sonucunu göreceksiniz.
Gson Request
Peki bu Json sonucu bizim için yeterli mi? Tabiki de hayır. Bizim için kullanılmaz durumda. Bunun illaki yakalanıp bir nesneye çevrilmesi gerekiyor. Bizde bunun için GSON kütüphanesini kullanacağız. Fakat tüm istekleri tek tek GSON ile çevirmek yerine kendi request sınıfımızı geliştireceğiz. Bunu yapmadan önce GSON kütüphanesini bu linkten indirin ve libs klasörünün altına atın.
Öncelikle GsonRequest sınıfını oluşturuyoruz.
GsonRequest.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 | package com.bahadirakin.volley; import java.io.UnsupportedEncodingException; import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import com.android.volley.toolbox.HttpHeaderParser; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; public class GsonRequest<T> extends Request<T> { private final Class<T> clazz; private final Listener<T> listener; public GsonRequest(int method, String url, ErrorListener errorListener, Class<T> clazz, Listener<T> listener) { super(method, url, errorListener); this.clazz = clazz; this.listener = listener; } @Override protected void deliverResponse(T arg0) { listener.onResponse(arg0); } @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { try { final Gson gson = new Gson(); final String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JsonSyntaxException e) { return Response.error(new ParseError(e)); } } } |
Şimdi ise Json yapsını çevirirken kullanacağımız sınıfı yaratıyoruz.
FacebookAndroid.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 | package com.bahadirakin.volley; public class FacebookAndroid { private String name; private String description; private String link; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } @Override public String toString() { return "FacebookAndroid [name=" + name + ", description=" + description + ", link=" + link + "]"; } } |
Son olarak MainActivity sınıfında gerekli değişiklikleri yapıp sonucu test ediyoruz.
MainActivity.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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package com.bahadirakin.volley; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.widget.ImageView; import android.widget.Toast; import com.android.volley.Request.Method; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.StringRequest; public class MainActivity extends Activity { private static final String IMAGE_URL = "http://www.bahadirakin.com/wp-content/uploads/2013/10/android_volley.png"; private static final String GRAPH_FACEBOOK_ANDOROID = "http://graph.facebook.com/android"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Init Volley application --- Only Once final VolleyApplication volleyApplication = VolleyApplication .getInstance(); volleyApplication.init(getApplicationContext()); // Lazy Loading Image final ImageView lazyImage = (ImageView) findViewById(R.id.lazyImage); volleyApplication.getImageLoader().get( IMAGE_URL, ImageLoader.getImageListener(lazyImage, R.drawable.loading, R.drawable.error)); // String Request // volleyApplication.getRequestQueue().add( // new StringRequest(Method.GET, GRAPH_FACEBOOK_ANDOROID, // createSuccessListener(), createErrorListener())); // Gson Request volleyApplication.getRequestQueue().add( new GsonRequest<FacebookAndroid>(Method.GET, GRAPH_FACEBOOK_ANDOROID, createErrorListener(), FacebookAndroid.class, createFacebookListener())); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private Response.Listener<FacebookAndroid> createFacebookListener() { return new Response.Listener<FacebookAndroid>() { @Override public void onResponse(FacebookAndroid arg0) { final String toString = arg0.toString(); Toast.makeText(MainActivity.this, toString, Toast.LENGTH_LONG) .show(); } }; } private Response.Listener<String> createSuccessListener() { return new Response.Listener<String>() { @Override public void onResponse(String response) { Toast.makeText(MainActivity.this, response, Toast.LENGTH_LONG) .show(); } }; } private Response.ErrorListener createErrorListener() { return new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show(); } }; } } |
Kodumuzu çalıştırdığımızda, FacebookAndroid sınıfı içerisinde yazdığımız toString methodunun sonucunu göreceğiz. Burada listener içerisinde isterseniz istediğiniz View alanlarınıda güncelleyebilirsiniz.
Son
Baştada bahsettiğim gibi Volley kütüphanesi ilk kullanmaya başladığımdan beri beni kendine hayran bıraktı. Umarım sizin içinde bu şekilde olur.
Referanslar
End Of Line