BG MVC Model View Controller eğitim serisi yayında...

Ana sayfa > Programlama > Android Programlama > Ağ işlemleri

Ağ işlemleri

Bu bölümde aşağıdaki ağ işlemlerini incelemeye çalışacağız:

Bir ağa bağlanma

Şimdi, bir ağa bağlanmak için basit bir uygulama oluşturmaya çalışalım. Bu uygulamadaki ağ işlemlerini gerçekleştirmek için, uygulamanın manifest dosyasında aşağıdaki izinlerin yer alması gerekir:

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

HTTP Client seçimi

Ağ bağlantılı Android uygulamalarının çoğu veri gönderip almak için HTTP kullanır. Android işletim sisteminde 2 HTTP istemci bulunur:

HttpURLConnection (Gingerbread ve daha üst sürümler için yazılan uygulamalarda kullanılması önerilir.)

Apache HttpClient

Her iki istemcide HTTPS, akış yükleme ve indirme, zaman aşımı düzenleme, IPv6 ve bağlantı havuzunu destekler.

Ağ bağlantı kontrolü

Bir uygulama bir ağa bağlanmadan önce getActiveNetworkInfo() ve isConnected() metotlarını kullanarak ağ bağlantısının olup olmadığını kontrol etmelidir. Kullanılan cihaz ağın kapsama alanı dışındadır veya kullanıcı Wi-Fi ve mobil veri erişimini devre dışı bırakmış olabilir.

public void myClickHandler(View view) {
    ...
    ConnectivityManager connMgr = (ConnectivityManager)
	    getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
        // Veri kullanımı
    } else {
        // Hata gösterme
    }
    ...
}

Ağ işlemlerini ayrı bir program parçacığında gerçekleştirme

Ağ işlemlerinde önceden tahmin edilemeyen gecikmeler meydana gelebileceğinden, ağ işlemlerini program arayüzünden farklı bir program parçacığında gerçekleştirmeniz önerilir. AsyncTask sınıfı program arayüzünden yeni bir işlem başlatmak için kullanılabilir.

Aşağıdaki kod içinde, myClickHandler() metodu new DownloadWebpageText().execute(stringUrl) komutunu çağırır. DownloadWebpageText sınıfı alt sınıf olarak AsyncTask sınıfından türetilmiştir. DownloadWebpageText sınıfı aşağıdaki AsyncTask sınıfı metotlarını kullanır:

  • doInBackground() metodu downloadUrl() metodunu çalıştırır. Web sayfası URL'sini parametre olarak geçirir. downloadUrl() metodu web sayfası içeriğini alır ve işlem yapar. İşlemi bitirdiğinde bir sonuç karakter dizisini geri döndürür.
  • onPostExecute() metodu geri döndürülen karakter dizisini alır ve arayüz penceresinde gösterir.
public class HttpExampleActivity extends Activity {
    private static final String DEBUG_TAG = "HttpExample";
    private EditText urlText;
    private TextView textView;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);   
        urlText = (EditText) findViewById(R.id.myUrl);
        textView = (TextView) findViewById(R.id.myText);
    }

    // Kullanıcı butona tıkladığında, AsyncTask sınıfını çağırır.
    // URL değerini almadan önce, ağ bağlantısı olduğunu kontrol edin.
    public void myClickHandler(View view) {
        // Arayüzde bulunan metin alanı URL değerini alır.
        String stringUrl = urlText.getText().toString();
        ConnectivityManager connMgr = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            new DownloadWebpageText().execute(stringUrl);
        } else {
            textView.setText("Ağ başlantısı mevcut değil!");
        }
    }

    // AsyncTask sınıfı arayüz thread'den bir görev oluşturmak için kullanılır.
    // Bu görev kendisine aktarılan URL değeri bir HttpUrlConnection oluşturmak için 
    // kullanır. Bağlantı kurulduktan sonra, AsyncTask sınıfı web sayfasının 
    // içeriğini indirir ve karakter dizisine çevirir. onPostExecute() metodu bu 
    // karakter dizisini program arayüzü ekranında gösterir.	
    private class DownloadWebpageText extends AsyncTask {
        @Override
        protected String doInBackground(String... urls) {
            // Parametreler execute() çağrısından gelir: params[0] değeri url değeri.
            try {
                // Bu metodun içeriği aşağıda yer almaktadır.
                return downloadUrl(urls[0]);
            } 
            catch (IOException e) {
                return "Web sayfası içeriğini alamıyor. URL geçersiz olabilir..";
            }
        }
        // AsyncTask sınıfının sonuçlarını ekranda gösterir.
        @Override
        protected void onPostExecute(String result) {
            textView.setText(result);
       }
    }
    ...
}

Kod içindeki işlemler aşağıdaki sırayla gerçekleşir:

  1. Kullanıcı myClickHandler() metodunu çağıran butona tıkladığında, uygulama tanımlanan URL değerini AsyncTask sınıfından türetilen DownloadWebpageText sınıfına aktarır.
  2. AsyncTask sınıfındaki doInBackground() metodu downloadUrl() metodunu çağırır.
  3. downloadUrl() metodu URL değerini parametre olarak alır ve bir URL nesnesi oluşturmak için kullanır.
  4. URL nesnesi bir HttpURLConnection oluşturmak için kullanılır.
  5. Bağlantı kurulduktan sonra, HttpURLConnection nesnesi web sayfası içeriğini alır.
  6. Alınan içerik karakter dizisine çevrilmek üzere readIt() metoduna aktarılır.
  7. AsyncTask onPostExecute() metodu karakter dizisini ana aktivite arayüzünde gösterir.

Bağlantı kurma ve veri indirme

Ağ işlemlerini gerçekleştiren thread içinde, verileri elde etmek ve indirmek için HttpURLConnection nesnesini kullanabilirsiniz. connect() metodunu çağırdıktan sonra, veriyi getInputStream() metodu ile alabilirsiniz.

Aşağıdaki kod içinde, doInBackground() metodu downloadUrl() metodunu çağırır. downloadUrl() metodu verilen URL değerini alır ve HttpURLConnection nesnesi ile ağa bağlanmak için kullanır. Bağlantı kurulduktan sonra, uygulama veriyi almak için getInputStream() metodunu kullanır.

// Verilen URL değeri ile bir HttpUrlConnection bağlantısı kurar, web sayfa içeriğini 
// alır ve karakter dizisine çevirdikten sonra geri döndürür.
private String downloadUrl(String myurl) throws IOException {
    InputStream is = null;
    // Alınan web sayfası içeriğinin sadece ilk 500 karakterini gösterir.
    int len = 500;
        
    try {
        URL url = new URL(myurl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milisaniye */);
        conn.setConnectTimeout(15000 /* milisaniye */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Sorgulamayı başlatır.
        conn.connect();
        int response = conn.getResponseCode();
        Log.d(DEBUG_TAG, "The response is: " + response);
        is = conn.getInputStream();

        // Web sayfa içeriğini karakter dizisine çevirir.
        // Bu metodun içeriği aşağıda yer almaktadır.
        String contentAsString = readIt(is, len); 
        return contentAsString;
        
    // Uygulama tarafından kullanılması sona erdikten sonra InputStream 
    // değerinin kapatıldığını kontrol eder.
    } finally {
        if (is != null) {
            is.close();
        } 
    }
}

getResponseCode() metodu bağlantı durum kodu döndürür. Bu kodun 200 değerini alması işlemin başarılı olduğunu gösterir.

Web site içeriğini karakter dizisine çevirme

InputStream değeri byte değerlerinden oluşur. InputStream değeri alındıktan sonra, istenilen veri türüne çevrilir. Örneğin, eğer resim indirildiyse, veri aşağıdaki şekilde çevrilir ve ekranda gösterilir:

InputStream is = null;
...
Bitmap bitmap = BitmapFactory.decodeStream(is);
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageBitmap(bitmap);

Yukarıda verilen örnekte, InputStream değeri bir web sayfasının metin içeriğini gösterir. Aşağıdaki kod InputStream değerini karakter dizisi değerine çevirir:

// Web sayfa içeriğini InputStream olarak alır ve 
// karakter dizisine çevirir.
public String readIt(InputStream stream, int len) throws IOException, 
                                                      UnsupportedEncodingException {
    Reader reader = null;
    reader = new InputStreamReader(stream, "UTF-8");        
    char[] buffer = new char[len];
    reader.read(buffer);
    return new String(buffer);
}

Ağ kullanımını yönetme

Şimdi, ağ kaynaklarını hassas bir şekilde kullanan ve kontrol eden uygulamalar geliştirmeye çalışacağız. Eğer uygulamanız çok fazla ağ işlemi gerçekleştiriyorsa, kullanıcının uygulamanızın veri ayarlarını kontrol etmesini sağlayan bir ayar sistemini uygulamanıza eklemeniz önerilir. Böylelikle kullanıcı uygulamanızın verileri ne sıklıkla senkronize edeceğini, yükleme ve indirme işlemlerini sadece Wi-Fi üzerinden yapıp yapmayacağını, gezinme yaparken verileri kullanıp kullanmayacağı gibi ayarlamaları yapabilir.

Bir aygıtın ağ bağlantısını kontrol etme

Bir aygıtta farklı türlerde ağ bağlantısı bulunabilir. Burada, Wi-Fi veya mobil ağ bağlantısı üzerinde işlem yapacağız.

Wi-Fi daha hızlı bir bağlantı türü olduğundan büyük boyutlu verileri almak için Wi-Fi bağlantı tercih edilmelidir.

Ağ işlemlerini gerçekleştirmeden önce, ağ bağlantılarının durumunu kontrol edilmesi önerilir. Bu işlem uygulamanızın yanlış sinyali kullanmasını önler. Eğer bir ağ bağlantısı yoksa, uygulamanızın düzgün bir şekilde işlem yapması gerekir. Ağ bağlantılarını kontrol etmek için, aşağıdaki sınıfları kullanabilirsiniz:

  • ConnectivityManager: Ağ bağlantılarının durumu hakkında yapılan sorgulamaları cevaplar. Ağ bağlantılarında meydana gelen değişikliklerde uygulamalara bilgi verir.
  • NetworkInfo: Verilen Mobil veya Wi-Fi ağ arayüzünün durumunu gösterir.

Aşağıdaki kod Wi-Fi ve Mobil ağ bağlantılarını kontrol eder. Bu ağ arayüzlerinin varlığını ve/veya bağlı olup olmadığını belirler:

private static final String DEBUG_TAG = "NetworkStatusExample";
...      
ConnectivityManager connMgr = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 
boolean isWifiConn = networkInfo.isConnected();
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
Log.d(DEBUG_TAG, "Wifi bağlı: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobil bağlı: " + isMobileConn);

Ağ işlemlerini gerçekleştirmeden önce, isConnected() metodunu kullanarak ağ bağlantılarını kontrol etmeniz önerilir.

Bir ağ arayüzünün varlığını kontrol etmek için getActiveNetworkInfo() metodu kullanılır. Bu metod bulduğu ilk ağ arayüzünü gösteren bir NetworkInfo instance değeri veya bağlanılabilecek herhangi internet bağlantısı olmadığında null bir değer geri döndürür.

public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}  

Ağ bağlantısı durumu hakkında daha fazla bilgi almak için NetworkInfo.DetailedState değerini kullanabilirsiniz.

Ağ kullanımını yönetme

Kullanıcıların uygulamanızın ağ kaynakları kullanımını kontrol etmek üzere uygulamanıza bir tercihler aktivitesi ekleyebilirsiniz. Böylelikle; cihaz sadece Wi-Fi bir ağa bağlı olduğunda, kullanıcıların video yüklemelerine veya ağ bağlantısı olup olmadığına bağlı olarak senkronize işlemler gerçekleştirmeye izin verebilirsiniz.

Uygulamanızın ağ girişini ve ağ kullanımını yönetebilmesi, manifest dosyasında gerekli izin ve intent filtre tanımlarını yapmanız gerekir.

android.permission.INTERNET: Uygulamaların ağ soketlerini açmalarını sağlar.

android.permission.ACCESS_NETWORK_STATE: Uygulamaların ağ hakkındaki bilgilere erişimini sağlar.

Android 4.0 sürümünden itibaren, uygulamanızın veri kullanımının kontrolünü sağlayacak bir aktivite tanımlamanızı sağlayacak ACTION_MANAGE_NETWORK_USAGE değeri için bir intent filtre oluşturabilirsiniz. ACTION_MANAGE_NETWORK_USAGE değeri belirli bir uygulamanın ağdaki veri kullanımını yönetmek için hgerekli ayarları gösterir. Eğer uygulamanızda kullanıcının ağ kullanımını kontrol etmesi için bir ayar aktivitesi varsa, bu intent filtre bildirimini yapmanız gerekir.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>

    <uses-sdk android:minSdkVersion="4" 
           android:targetSdkVersion="14" />
        
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        ...>
        ...
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
             <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
        </activity>
    </application>
</manifest>

Tercihler uygulaması gerçekleştirme

Yukarıdaki manifest dosyasında SettingsActivity aktivitesinde ACTION_MANAGE_NETWORK_USAGE işlemi için bir intent filtresi bulunmaktadır. SettingsActivity, PreferenceActivity sınıfının bir alt sınıfıdır. Kullanıcıların aşağıdaki tercihleri yapmasını sağlayan ekranı gösterir:

  • Her bir XML girişi için özet bilgi gösterme veya her giriş için sadece bir bağlantı seçenekleri arasında tercih
  • XML indirme işlemini herhangi bir ağ ya da sadece Wi-Fi bağantısı durumunda indirme tercihi

SettingsActivity sınıfı OnSharedPreferenceChangeListener sınıfını uygular. Kullanıcı bir tercihi değiştirdiğinde, onSharedPreferenceChanged() metodunu çalıştırır. Bu metod refreshDisplay değişkenine true değeri atar. Böylece; kullanıcı ana aktiviteye geri döndüğünde ekranın yenilenmesini sağlar:

public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        // XML tercih dosyasını yükler
        addPreferencesFromResource(R.xml.preferences);
    }
  
    @Override
    protected void onResume() {
        super.onResume();
        // Bir tuş değişikliğinde bir listener kaydı yapar.
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }
  
    @Override
    protected void onPause() {
        super.onPause();
        // onResume() metodu içinde kaydı yapılan Listener kaydını iptal eder.
        // Uygulamanız Listener kullanmadığında sistemin gereksiz yere meşgul
        // edilmemesi için iptal edilmesi önerilir.
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);    
    }
  
    // Kullanıcı tercih seçimini değiştirdiğinde,
    // onSharedPreferenceChanged() metodu ana aktiviteyi yeni bir görev olarak
    // yeniden başlatır. Ana aktivitenin ekranını güncelleştirmesini sağlayan
    // refreshDisplay değerini "true" olarak ayarlar. Ana aktivite son ayarları
    // elde etmek için PreferenceManager'i sorgular.
    
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {    
        // refreshDisplay değerini "true" olarak ayarlar.
        NetworkActivity.refreshDisplay = true;
    }
}

Tercih değişikliklerine işlem yapma

Kullanıcı ayarlar ekranında tercihleri değiştirdiğinde, uygulamayı etkiler. Aşağıdaki kod ile, uygulama onStart() metodu içinde tercih ayarlarını kontrol eder. Eğer ayarlar ile cihazın ağ bağlantısı arasında uyum varsa (örneğin, eğer ayar "Wi-Fi" ise ve cihazın Wi-Fi bağlantısı varsa), uygulama indirme yapar ve görüntüyü yeniler.

public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
   
    // Wi-Fi bağlantısını gösteren değişken
    private static boolean wifiConnected = false; 
    // Mobile bağlantıyı gösteren değişken
    private static boolean mobileConnected = false;
    // Ekran yenileme belirleyen değişken
    public static boolean refreshDisplay = true;
    
    // Kullanıcının ön tanımlı ağ tercih ayarı
    public static String sPref = null;
    
    // Ağ bağlantı değişikliklerini izleyen BroadcastReceiver değeri.
    private NetworkReceiver receiver = new NetworkReceiver();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Ağ bağlantı değişikliklerini izleyen BroadcastReceiver değerini kaydeder.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }
    
    @Override 
    public void onDestroy() {
        super.onDestroy();
        // Uygulama sona erdiğinde BroadcastReceiver kaydını iptal eder.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }
    
    // Eğer ağ bağlantısı ve tercih ayarları izin verirse ekranı yeniler.
    
    @Override
    public void onStart () {
        super.onStart();  
        
        // Kullanıcının ağ tercih ayarlarını alır.
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        
        // Tercihler için bir karakter dizisi alır. İkinci 
        // parametre tercih değeri bulunamadığında kullanılacak ön tanımlı değerdir.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");

        updateConnectedFlags(); 
       
        if(refreshDisplay){
            loadPage();    
        }
    }
    
    // Ağ bağlantısını kontrol eder, wifiConnected ve mobileConnected
    // değerlerini ayarlar. 
    public void updateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager) 
                getSystemService(Context.CONNECTIVITY_SERVICE);
        
        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } else {
            wifiConnected = false;
            mobileConnected = false;
        }  
    }
      
    // stackoverflow.com sitesinden XML indirmek için AsyncTask alt sınıfını kullanır.
    public void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask alt sınıfı
            new DownloadXmlTask().execute(URL);
        } else {
            showErrorPage();
        }
    }
...    
}

Bağlantı değişikliklerini algılama

Cihazın ağ bağlantısı değiştiğinde, NetworkReceiver sınıfı CONNECTIVITY_ACTION işleminde araya girerek, ağ bağlantı durumunu belirler ve buna göre wifiConnected ve mobileConnected değerlerini true/false olarak ayarlar. Sonuç olarak, kullanıcı uygulama bir sonraki girişinde, uygulama sadece en son veriyi indirir ve eğer NetworkActivity.refreshDisplay değeri true ise ekranı güncelleştirir .

Gereksiz olarak çağrılan bir BroadcastReceiver oluşturmak sistem kaynaklarını tüketebilir. Örnek uygulama BroadcastReceiver NetworkReceiver'ı onCreate() metodu içinde kaydeder, onDestroy() metodu içinde kaydı siler. Bu işlem manifest dosyasında bildirimi yapmaktan daha hafif bir yöntemdir. Manifest dosyasında bildirimi yaptığınızda, uygulamanızı herhangi bir zamanda çalıştırabilir. Ana aktivite içinde NetworkReceiver kaydı yapmak ve kaydı silmek, kullanıcı uygulamayı kapattıktan sonra, uygulamanın açılmasını engeller. Eğer manifest dosyasında bildirimi yapar ve nerede ihtiyacınız olduğunu tam olarak bilirseniz, setComponentEnabledSetting() metodunu kullanarak uygun olduğunda devreye sokar veya devre dışı bırakabilirsiniz.

public class NetworkReceiver extends BroadcastReceiver {   
      
@Override
public void onReceive(Context context, Intent intent) {
    ConnectivityManager conn =  (ConnectivityManager)
        context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = conn.getActiveNetworkInfo();
       
    // Kullanıcı tercihlerini ve ağ bağlantısını kontrol eder. Sonuca bağlı olarak,
    // ekranı yenilemeyi veya olduğu gibi bırakmaya karar verir. Eğer kullanıcı tercihi 
    // "sadece Wi-Fi" ise, cihazın Wi-Fi bağlantısı olup olmadığını kontrol eder.
    if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
        // Eğer cihazın Wi-Fi bağlantısı varsa, refreshDisplay
        // değişkenine "true" değeri verir.
        refreshDisplay = true;
        Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
    } 
	else if (ANY.equals(sPref) && networkInfo != null) {
        // Eğer ayar "ANY network" ise ve bir ağ bağlantısı varsa
        // refreshDisplay değişkenine "true" değeri verir.
    	refreshDisplay = true;                 
    } 
	else {
        // Aksi takdirde, uygulama içeriği indiremez--ya ağ bağlantısı olmadığı
        // (mobile veya Wi-Fi), ya da tercih WIFI olup Wi-Fi bağlantısı olmadığı için.
        // refreshDisplay değişkenine "false" değeri verir.
        refreshDisplay = false;
        Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
    }
}

XML verilere işlem yapma

Extensible Markup Language (XML) dökümanları bilgisayar tarafından okunabilir hale getiren komutlardan oluşur. XML internet üzerinden veri paylaşımı için yaygın olarak kullanılan bir yapıdır. Sık sık içeriklerini güncelleyen web siteleri takipçilerinin içeriğe ulaşmalrı için XML formatını kullanır. Ağ bağlantılı olarak çalışan uygulamalarda XML işlmeleri yaygın olarak kullanılır.

Parser (XML işlemcisi) seçme

XML işlemleri için XmlPullParser arayüzünü kullanmanız önerilir. Android için bu arayüzün 2 uygulaması vardır:

KXmlParser (XmlPullParserFactory.newPullParser() yoluyla kullanılır.)

ExpatPullParser (Xml.newPullParser() yoluyla kullanılır.)

Buradaki örneklerde ExpatPullParser kullanılacaktır.

Verileri inceleme

İlk olarak XML veri içinde işlem yapılacak alanlar belirlenir. Parser sadece söz konusu alanlardan veri alır. Diğer alanları dikkat almaz.

//****************
//****************
//****************
//****************
//****************
//****************

Bu bölümde HttpURLConnection HTTP istemcisini kullanarak bir web sitesindeki XML verileri almaya ve üzerinde işlem yapmaya çalışacağız.

Öncelikle, uygulamada internet erişimini ve ağ girişini sağlamak için uygulamanın manifest dosyasına aşağıdaki izinleri eklememiz gerekir:

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

NetwokActivity.java

package com.example.android.networkusage;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.webkit.WebView;
import android.widget.Toast;

import com.example.android.networkusage.R;
import com.example.android.networkusage.StackOverflowXmlParser.Entry;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";

    // Wi-Fi bağlantısını gösteren değişken
    private static boolean wifiConnected = false;
    // Mobile bağlantıyı gösteren değişken
    private static boolean mobileConnected = false;
    // Ekran yenileme belirleyen değişken
    public static boolean refreshDisplay = true;

    // Kullanıcının ön tanımlı ağ tercih ayarı
    public static String sPref = null;

    // Ağ bağlantı değişikliklerini izleyen BroadcastReceiver değeri.
    private NetworkReceiver receiver = new NetworkReceiver();

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

        // Ağ bağlantı değişikliklerini izleyen BroadcastReceiver değerini kaydeder.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }

	// Eğer ağ bağlantısı ve tercih ayarları izin verirse ekranı yeniler.
    @Override
    public void onStart() {
        super.onStart();

        // Kullanıcının ağ tercih ayarlarını alır.
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        // Tercihler için bir karakter dizisi alır. İkinci 
        // parametre tercih değeri bulunamadığında kullanılacak ön tanımlı değerdir.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");

        updateConnectedFlags();

        // Sadece refreshDisplay değeri true ise sayfayı yükler. Aksi takdirde, önceki ekranı korur.
        if (refreshDisplay) {
            loadPage();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Uygulama sona erdiğinde BroadcastReceiver kaydını iptal eder.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }

    // Ağ bağlantısını kontrol eder, wifiConnected ve mobileConnected
    // değerlerini ayarlar.
    private void updateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } 
        else {
            wifiConnected = false;
            mobileConnected = false;
        }
    }

    // stackoverflow.com sitesinden XML indirmek için AsyncTask alt sınıfını kullanır.
    private void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask alt sınıfı
            new DownloadXmlTask().execute(URL);
        } 
        else {
            showErrorPage();
        }
    }

    // Eğer uygulama içeriği yükleyemezse hata gösterir.
    private void showErrorPage() {
        setContentView(R.layout.main);

        // Tanımlanan ağ bağlantısı mevcut olmadığından hata mesajı gösterir.
        WebView myWebView = (WebView) findViewById(R.id.webview);
        myWebView.loadData(getResources().getString(R.string.connection_error), "text/html", null);
    }

    // Aktivite seçenek menüsünü doldurur.
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.mainmenu, menu);
        return true;
    }

    // Kullanıcı menü seçeneğine işlem yapar.
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.settings:
                Intent settingsActivity = new Intent(getBaseContext(), SettingsActivity.class);
                startActivity(settingsActivity);
                return true;
        case R.id.refresh:
                loadPage();
                return true;
        default:
                return super.onOptionsItemSelected(item);
        }
    }

    // stackoverflow.com'dan XML verilerini indirmek için kullanılan AsyncTask uygulaması
    private class DownloadXmlTask extends AsyncTask {

        @Override
        protected String doInBackground(String... urls) {
            try {
                return loadXmlFromNetwork(urls[0]);
            } 
            catch (IOException e) {
                return getResources().getString(R.string.connection_error);
            } 
            catch (XmlPullParserException e) {
                return getResources().getString(R.string.xml_error);
            }
        }

        @Override
        protected void onPostExecute(String result) {
            setContentView(R.layout.main);
            // WebView yoluyla HTML karakter dizisini arayüzünde gösterir.
            WebView myWebView = (WebView) findViewById(R.id.webview);
            myWebView.loadData(result, "text/html", null);
        }
    }

    // stackoverflow.com'dan XML verilerini alır, işlem yapar ve HTML karakter dizisi ile geri verir.
    private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
        InputStream stream = null;
        StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
        List<Entry> entries = null;
        String title = null;
        String url = null;
        String summary = null;
        Calendar rightNow = Calendar.getInstance();
        DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");

        // Kullanıcının özet metni içerecek şekilde tercihleri ayarlayıp ayarlamadığını kontrol eder.
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        boolean pref = sharedPrefs.getBoolean("summaryPref", false);

        StringBuilder htmlString = new StringBuilder();
        htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
        htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
                formatter.format(rightNow.getTime()) + "</em>");

        try {
            stream = downloadUrl(urlString);
            entries = stackOverflowXmlParser.parse(stream);        
        } 
        // Uygulama kullandıktan sonra InputStream'in kapatıldığını kontrol eder.
        finally {
            if (stream != null) {
                stream.close();
            }
        }

        // StackOverflowXmlParser "entries" adlı bir liste geri verir.
        for (Entry entry : entries) {
            htmlString.append("<p><a href='");
            htmlString.append(entry.link);
            htmlString.append("'>" + entry.title + "</a></p>");
            // Eğer kullanıcı özet metni içerecek şekilde tercih ayarı yapmışsa, ekrana ekler.
            if (pref) {
                htmlString.append(entry.summary);
            }
        }
        return htmlString.toString();
    }

    // Verilen bir URL değeri ile bağlantı kurar ve veri alır.
    private InputStream downloadUrl(String urlString) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milliseconds */);
        conn.setConnectTimeout(15000 /* milliseconds */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Sorgulamayı başlatır.
        conn.connect();
        InputStream stream = conn.getInputStream();
        return stream;
    }

    /*
     * Bu BroadcastReceiver bağlantı değişikliğini gösteren 
     * android.net.ConnectivityManager.CONNECTIVITY_ACTION işleminin arasına girer.
     * Bağlantı türünün TYPE_WIFI olup olmadığını kontrol eder.
     * Eğer öyleyse, Wi-Fi'nin bağlı olduğunu kontrol eder ve
     * ana aktivite içindeki wifiConnected değerini ayarlar.
    */
    public class NetworkReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

            // Kullanıcı tercihlerini ve ağ bağlantısını kontrol eder. Sonuca bağlı olarak,
            // ekranı yenilemeyi veya olduğu gibi bırakmaya karar verir. Eğer kullanıcı tercihi 
            // "sadece Wi-Fi" ise, cihazın Wi-Fi bağlantısı olup olmadığını kontrol eder.
            if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                // Eğer cihazın Wi-Fi bağlantısı varsa, refreshDisplay değişkenine "true" değeri verir.                refreshDisplay = true;
                Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
            } 
            else if (ANY.equals(sPref) && networkInfo != null) {
                // Eğer ayar "ANY network" ise ve bir ağ bağlantısı varsa refreshDisplay değişkenine "true" değeri verir.
                refreshDisplay = true;
            } 
            else {
                // Aksi takdirde, uygulama içeriği indiremez--ya ağ bağlantısı olmadığı
                // (mobile veya Wi-Fi), ya da tercih WIFI olup Wi-Fi bağlantısı olmadığı için.
                // refreshDisplay değişkenine "false" değeri verir.
                refreshDisplay = false;
                Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
            }
        }
    }
}

SettingsActivity.java

package com.example.android.networkusage;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.PreferenceActivity;

public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {

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

        // XML tercih dosyasını yükler.
        addPreferencesFromResource(R.xml.preferences);
    }
    
    @Override
    protected void onResume() {
        super.onResume();

        // Bir tercih değişikliğinde bir listener kaydı yapar.
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();

        // onResume() metodu içinde kaydı yapılan Listener kaydını iptal eder.
        // Uygulamanız Listener kullanmadığında sistemin gereksiz yere meşgul
        // edilmemesi için iptal edilmesi önerilir.
		
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }

    // Kullanıcı bir tercih değişikliği yaptığında çalışır.
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // refreshDisplay değerini "true" olarak ayarlar.
        NetworkActivity.refreshDisplay = true;
    }
}

StackOverflowXmlParser.java

package com.example.android.networkusage;

import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class StackOverflowXmlParser {
    private static final String ns = null;

    public List parse(InputStream in) throws XmlPullParserException, IOException {
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(in, null);
            parser.nextTag();
            return readFeed(parser);
        } 
        finally {
            in.close();
        }
    }

    private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
        List entries = new ArrayList();

        parser.require(XmlPullParser.START_TAG, ns, "feed");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();
            if (name.equals("entry")) {
                entries.add(readEntry(parser));
            } 
            else {
                skip(parser);
            }
        }
        return entries;
    }

    public static class Entry {
        public final String title;
        public final String link;
        public final String summary;

        private Entry(String title, String summary, String link) {
            this.title = title;
            this.summary = summary;
            this.link = link;
        }
    }

    private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
        parser.require(XmlPullParser.START_TAG, ns, "entry");
        String title = null;
        String summary = null;
        String link = null;
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();
            if (name.equals("title")) {
                title = readTitle(parser);
            } 
            else if (name.equals("summary")) {
                summary = readSummary(parser);
            } 
            else if (name.equals("link")) {
                link = readLink(parser);
            } 
            else {
                skip(parser);
            }
        }
        return new Entry(title, summary, link);
    }

    // title etiketlerine işlem yapar.
    private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "title");
        String title = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, "title");
        return title;
    }

    // link etiketlerine işlem yapar.
    private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
        String link = "";
        parser.require(XmlPullParser.START_TAG, ns, "link");
        String tag = parser.getName();
        String relType = parser.getAttributeValue(null, "rel");
        if (tag.equals("link")) {
            if (relType.equals("alternate")) {
                link = parser.getAttributeValue(null, "href");
                parser.nextTag();
            }
        }
        parser.require(XmlPullParser.END_TAG, ns, "link");
        return link;
    }

    // summary etiketlerine işlem yapar.
    private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "summary");
        String summary = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, "summary");
        return summary;
    }

    // title ve summary etiketlerinin metin değerlerini alır.
    private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
        String result = "";
        if (parser.next() == XmlPullParser.TEXT) {
            result = parser.getText();
            parser.nextTag();
        }
        return result;
    }

    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
            case XmlPullParser.END_TAG:
                    depth--;
                    break;
            case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14" />

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

    <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" >

        <activity android:name=".NetworkActivity" android:label="@string/app_name" >
            <intent-filter> 
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
</LinearLayout>