Android Web Servisleri Kullanımı

Android uygulamaları herhangi bir web servisi doğrudan kullanabilmektedir.Android işletim sistemi için genel olarak XML ya da JSON formatı tercih edilebilir. Bir XML verisini ayrıştıran sınıflar genel olarak tüm XML veriyi önce belleğe okuduklarından mobil dünya için genel olarak JSON kullanılır.

JSON(JavaScript Object Notation) formatı javaScript programlama dilinin 1999 Aralık standartlarında belirtilen nesne yapısından hareketle oluşturulmuştur.Bu tip formatlar genel olarak kolay algılanabilir ve oluşturulabilir yazısal kaynaklardır.

JSON formatı genel olarak isim ve değer çiftlerinden oluşmaktadır. Bu anlamda bakıldığında Map Collection sınıflarına benzetilmektedir.Değerlerin peş peşe yazılması açısından dinamik büyüyen dizilere de benzetilmektedir.

JSON ve ya XML gibi veri yapılarının temel amacı okunabilir bir text formatında taşınmaktadır.Verilerin yazı olarak iletilmesi her ortamda ve her teknolojide kullanılmasına olanak sağlar.Şüphesiz programcı başka yazısal formatları kendisi de oluşturabilir.Fakat JSON ve XML formatları veri iletiminde en çok kullanılan ve kabul gören formatlardır.Bu formatların çok kullanımından dolayı hemen hemen her kütüphane ve framework içerisinde bu formatlarla hazır işlem yapan sınıflar ya da metodlar(fonksiyonlar) bulunmaktadır.

Web Servis İstemci Programlarının Yapısı

Web Servisleri kullanmak için (Android 6.0)APILEVEL23'den önce HttpClient isimli bir sınıf kullanılmaktaydı fakat bu sınıf depricated ve APILEVEL23'den sonra silindiği için bunun yerine HttpUrlConnection isimli bir sınıf kullanılmaktadır.Bu sınıf ile kendisine gönderilen bir servise Url olarak bağlanılır.Bağlantı alındıktan sonra bir stream alınır ve bu stream ile okuma işlemleri yapılır.Bu işlem kısaca download olarak da düşünülebilir.Ayrıca upload yapmak istenirse stream'e yazma yapmak gereklidir.

JSON Formatında Servislerin Kullanılması

[Dropbox]081-SimpleWebServiceJSON

081-SimpleWebServiceJSON Ne yapar?

EditText'e girilen postakodunu butona basıldığında api.geonames.org üzerinden istek gönderir ve json data alır.

Url işlemleri için URL sınıfı kullanılmaktadır.Bu sınıfın başlangıç metodu java.net.MalformedURLException türünden Exception throw etmektedir.URL sınıfı java.net paketi içerisindedir.

Herhangi bir urlye bağlantı için URL sınıfının openConnection metodu kullanılabilir.Bağlantıyı kesmek için ise HttpConnection sınıfının disconnect metodu çağrılmalıdır.

Web Servis bağlantıları zaman olarak uzama ihtimaline karşı client programın asenkron bir akış ile bu işlemi gerçekleştirmesi gerekmektedir.

Bir connection elde edildiği zaman o connectiona ilişkin inputStream ve outputStream programcı tarafından alınabilir.Bunun için getInputStream ve getOutputStream metodlarından faydalanılabilir.

Bu işlemler için INTERNET izni gerekmektedir.

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

Programcı JSON formatındaki veriyi String olarak aldıktan sonra JSONXXX metodlarıyla ayrıştırılır. Bir JSON formatındaki bir yazıyı JSONObject isimli bir sınıf ile sarmalamak gerekir.Herhangi bir Json elemanına ilişkin bilgiyi JsonArraylerle alabiliriz.

JsonArray nesnesine alınan bir data aşağıdaki gibi ele alınabilir.

geoNames Apiden elde edilen JSON'ın parse edilmesi http://api.geonames.org/postalCodeSearchJSON?postalcode=34387&maxRows=1&username=demo

{  
   "postalCodes":[  
      {  
         "adminCode2":"8521538",
         "adminCode1":"34",
         "adminName2":"Şişli",
         "lng":29,
         "countryCode":"TR",
         "postalCode":"34387",
         "adminName1":"İstanbul",
         "ISO3166-2":"34",
         "placeName":"Mecidiyeköy",
         "lat":41.066667
      }
   ]
}
try {
                JSONObject object = new JSONObject(result);
                JSONArray items = new JSONArray(object.getString("postalCodes"));

                String placeNames = "";

                for (int i = 0; i < items.length(); ++i) {
                    JSONObject curObject = items.getJSONObject(i);

                    if (!placeNames.isEmpty())
                        placeNames += "-";

                    placeNames += curObject.getString("placeName");
                }
                Toast.makeText(MainActivity.this, placeNames, Toast.LENGTH_LONG).show();
            }
            catch (Exception ex) {
                Toast.makeText(MainActivity.this, ex.getMessage(), Toast.LENGTH_SHORT).show();
            }

Burada programcının Json formatında gelen datayı isimleriyle birlikte bilmesi gerekir.

Text Formatında Olmayan Dataların Download Edilmesi

[Dropbox]BinaryDataDownload

BinaryDataDownload Ne Yapar?

Butona basıldığında linki verilen görseli Asynctask ile indirir ve imageView içerisinde gösterir.

Bir Url üzerinden text olmayan formatta veri indirmesi işlemi yine HttpURLConnection sınıfı kullanılarak yapılabilir.Bunun için indirilecek formatın ne olduğunun bilinmesi gerekir.Örneğin resim formatındaki data şu şekilde indirilir.

connection = Util.openHttpConnection(params[0]);
InputStream in = new BufferedInputStream(connection.getInputStream());
result = BitmapFactory.decodeStream(in);

Burada BitmapFactory sınıfı resim olmayan fakat resmin datalarını içeren datadan resim elde etmek için kullanılan bir sınıftır.

Burada henüz bir save işlemi yapılmamıştır.Yanlızca bitmap formatı ImageView üzerinde resim olarak gösterilmiştir.

XML Formatında Servislerin Kullanılması

JSON formatına benzer olarak XML formatı da yaygın olarak kullanılmaktadır.Java'da aynı zamanda Androidde de bulunan XMLParser sınıfları ile bu işlem gerçekleştirilebilir.

Anahtar Notlar:Bir XML içerisinde bir eleman açısal parantezler içerisinde <tag> bildirilir.ve </tag> şeklinde kapatılabilir.

Bu elemanların herbirine node denilmektedir. XML formatını ayrıştıran sınıflar nodelar üzerinde işlem yapmaktadırlar.Nodelar kendi içerisinde hiyeraşik bir yapıya sahiptir.

Basit bir XML formati aşağıdaki gibi düşünülebilir.

<players>
  <player>
    <name>Oguz</name>
    <notes>100</notes>
  </player>

  <player>
    <name>Kaan</name>
    <notes>100</notes>
  </player>

  <player>
    <name>Burak</name>
    <notes>100</notes>
  </player>
  .
  .
  .
</players>

Burada players tepe (route) node olarak düşünülebilir.Diğerleri bunların childleri (çocukları)dır.

XML Formatını Ayrıştıran Sınıflar

[Dropbox]083-SimpleWebServiceXML

Android işletim sistemi için XML ayrıştıran sınıflar daha çok Java içerisinden gelen sınıflardır.

XML ayrıştırmak için önce Document isimli sınıftan faydalanılır.Document oluşturmak için öncellikle DocumentBuilder isimli bir sınıf gerekmektedir.Bu nesne için DocumentBuilderFactory sınıfının newInstance metodu gerekmektedir.

Document sınıfı org.w3c.dom altında bildirilmiştir.

Dökümanın kendisine ilişkin sınıf Document sınıfıdır.XML içerisinde herhangi bir elemana (tag'e) ilişkin düğüm listesi getElementsByTagName isimli metod ile elde edilebilir.

Bir DocumentBuilder nesnesi DocumentBuilderFactory sınıfının newDocumentBuilder sınıfıyla alınabilir.

DocumentBuilder sınıfının parseXXX metoduyla bir Document nesnesi alınabilir.

Document sınıfının getElementByTagName metoduyla bir düğüme ilişkin tüm alt listeler bir liste olarak elde edilebilir.

Anahtar Notlar: Boşluk ve standart ASCII olmayan karakterler ve bunun gibi özel karakterler için bir url'in ayrıştırılması UrlEncoder isimli bir sınıfla yapılabilir.Bu sınıfın encode metodu encode edilecek yazıyı ve hangi tablolama yöntemiyle encode edeceği bilgisini almaktadır.Tek parametreli encode metodu deprecated durumdadır.Default olarak UTF-8 kullanılabilir

str = URLEncoder.encode(str , "UTF-8")

Bu metod encoding parametresi geçersizse UnsportedEncodingException fırlatır.

WebServis uygulamalarında genel olarak client uygulamanın encode ederek url vermesi iyi bir tekniktir.

Örnek XML service linki:http://services.aonaware.com/dictservice/dictservice.asmx/Define?word=ankara

Örnek XML Kodu

<?xml version="1.0" encoding="UTF-8"?>
<WordDefinition xmlns="http://services.aonaware.com/webservices/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Word>ankara</Word>
   <Definitions>
      <Definition>
         <Word>ankara</Word>
         <Dictionary>
            <Id>wn</Id>
            <Name>WordNet (r) 2.0</Name>
         </Dictionary>
         <WordDefinition>Ankara
     n : the capital of Turkey; located in west-central Turkey;
         formerly known as Angora and is the home of Angora goats
         [syn: {Turkish capital}, {capital of Turkey}]</WordDefinition>
      </Definition>
   </Definitions>
</WordDefinition>
 public void onFindButtonClicked(View v)
    {
        String str = m_editTextWord.getText().toString();
        try { 
        str = URLEncoder.encode(str, "UTF-8");
        }
        catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        new DictionaryTask().execute("http://services.aonaware.com/DictService/DictService.asmx/Define?word=" + str);
    }

    private class DictionaryTask extends AsyncTask<String, String, String> {
        @Override
        protected String doInBackground(String... params)
        {
            String result = "";
            HttpURLConnection connection = null;

            try {
                connection = Utils.openConnection(params[0]);

                InputStream in = new BufferedInputStream(connection.getInputStream());

                Document doc = null;
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

                DocumentBuilder db = null;

                db = dbf.newDocumentBuilder();

                doc = db.parse(in);

                NodeList definitionElements = doc.getElementsByTagName("Definition");

                for (int i = 0; i < definitionElements.getLength(); ++i) {
                    Node itemNode = definitionElements.item(i);

                    if (itemNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (Element)itemNode;

                        NodeList wordDefList = element.getElementsByTagName("WordDefinition");

                        for (int k = 0; k < wordDefList.getLength(); ++k) {
                            NodeList textNode = wordDefList.item(k).getChildNodes();

                            result += ((Node)textNode.item(0)).getNodeValue() + "\n";
                        }
                    }
                }
            }
            catch (Exception ex) {
                publishProgress(ex.getMessage());
            }
            finally {
                if (connection != null)
                    connection.disconnect();
            }
            return result;
        }

        @Override
        protected void onPostExecute(String result)
        {
            //Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
            m_textViewResult.setText(result.isEmpty() ? "No word in db" : result);
            super.onPostExecute(result);
        }

        @Override
        protected void onProgressUpdate(String... values)
        {
            Toast.makeText(MainActivity.this, "EXCEPTION:" + values[0], Toast.LENGTH_LONG).show();
            super.onProgressUpdate(values);
        }
    }