Posts Tagged SDK

Android’le Adım Adım Uygulama Geliştirme – III

Daha önceki iki bölümde döviz değerlerini gösteren basit bir uygulama geliştirmiştik. Ancak uygulamamız gibi düzenli aralıklarla güncellenmesi gereken değerlerinin olduğu uygulamalarda bu tarz güncelleme işlemlerinin kullanıcı etkileşimi olmadan arka planda çalışması beklenmektedir.
Android platformu arka planda çalışması beklenen iş akışları için “Service” altyapısı içermektedir. Bu yapı ile uygulamamız aktif olmasa bile çeşitli işlemlerin gerçekleştirilmesi sağlanabilmektedir. Çalışan service’ler, aktif olmayan/görüntülenmeyen Activity’lere daha yüksek bir önceliğe sahip olduğundan kaynak yönetimi tarafından daha etkin bir şekilde yönetilmektedirler.

Bir “Service” yaratabilmek için gerekli olan adımları 3 grupta toparlayabiliriz:

  • “Service” sınıfını extend eden bir sınıf hazırlanır
  • Bu sınıf içerisinde “onCreate”, “onStart” ve “onStop” metodları implement edilir
  • Uygulamanın AndroidManifest.xml dosyasında ilgili sınıf “service” tanımı içerisinde belirtilir
  • Bu adımlardan sonra hazırladığımız “Service” tanımını Context.startService ve Context.stopService metodları ile aktif ve pasif hale getirebiliriz.

    Bizde uygulamamız için döviz değerlerini alıp, veritabanına aktaran “CurrencyExchangeService” adında “Service” sınıfını extend eden bir sınıf hazırlayalım. Döviz değerlerini alıp, veritabanına aktarmak için “CurrencyExchangeActivity” sınıfındaki “getExchange” metodunu bu sınıf tanımında kopyalayalım (tabi içindeki gereksiz parçaları ayıklayarak).

    public class CurrencyExchangeService extends Service {
    	@Override
    	public void onCreate() {
    		super.onCreate();
    	}
     
    	@Override
    	public void onDestroy() {
    		super.onDestroy();
    	}
     
    	@Override
    	public void onStart(Intent intent, int startId) {
    		super.onStart(intent, startId);
    	}
     
    	@Override
    	public IBinder onBind(Intent intent) {
    		return null;
    	}
     
        public void getExchange() {
        ....
        }
     
        private class CurrencyExchangeHandler extends DefaultHandler {
        ....
        }
    }

    Bu “Service” tanımını aktif hale getirmek için ana Activity sınıfımızda “startService(new Intent(this,CurrencyExchangeService.class))” şeklinde bir metod çağrısı yeterli olacaktır. Bu metod ilk defa çağrıldığında “Service” sınıfındaki “onCreate” ve “onStart” metodlarını çalıştıracaktır. Sonraki çağrımlarında ise sadece “onStart” metodunu çalıştırır. Aktif haldeki “Service” tanımını iptal etmek için ise yine ana Activity sınıfımızda “stopService(new Intent(this,CurrencyExchangeService.class))” şeklinde bir metod çağrısı yapmamız yeterli olacaktır. Bu metod ilgili “Service” sınıfında “onStop” metodunu çalıştıracaktır.

    Ancak dikkat etmemiz gereken bir nokta, bu şekilde bir kullanımda “Service” sınıfında uygulamanın ana thread’i içinde çalıştığıdır. Mesela “onCreate” metodu içerisinde “getExchange” metodunu çalıştırdığım bir “Service” tanımının çalışması sırasındaki threadleri incelersek:

    public void onCreate() {
    	super.onCreate();
    	getExchange();
    }

    android-bolum3-1_ok

    Uygulamanın ana thread’i içerisinde çalıştığımızdan zaman isteyen işleyişlerde bu uygulamamızın kilitlenmesine neden olabilir.

    Bu durumu aşmak için yeni bir thread yaratıp, bu thread içerisinde iş akışını gerçekleştirmeyi deneyebiliriz. Yukarıdaki örnek için bunu şu şekilde gerçekleştirebiliriz:

    public void onCreate() {
    	super.onCreate();
        	Thread workerThread = new Thread(null,new Runnable(){
    			@Override
    			public void run() {
    				getExchange();
    			}},"currency_exchange");
        	workerThread.start();
    }

    Bu şekilde “Service” tanımını çalıştırıp, threadlerin durumunu incelersek, yeni thread’in yaratıldığını ve iş akışının bu yeni thread içerisinde çalıştırıldığını görebiliriz:

    android-bolum3-2_ok

    Yada eğer “Service” tanımı içerisinde belirli zaman aralıklarında tekrarlanmasını istediğimiz bir iş akışımız bulunuyor ise Android platformu ile gelen “Timer” sınıfını kullanabiliriz. Bu sınıf kendi içerisinde bir thread kullandığından bizim ekstra bir thread tanımı yapmamıza gerek kalmadan, iş akışımız Timer’a ait thread içerisinde çalışacaktır (Bu thread’i kapatmak için Timer.cancel metodunu kullanabilirsiniz).

    public void onCreate() {
    	super.onCreate();
    	Timer timer = new Timer();
    	timer.scheduleAtFixedRate(new TimerTask() {
    		@Override
    		public void run() {
    			getExchange();
    		}}, 0, 60*1000);
    }

    Bu şekilde “Service” tanımını çalıştırıp, threadlerin durumunu incelersek, iş akışının Timer’a bağlı thread içerisinde çalıştırıldığını görebiliriz:

    android-bolum3-3_ok

    Bir sonraki bölümde görüşmek üzere…

    , ,

    No Comments

    Objective-C’de Memory Problemlerinin Giderilmesi

    iPhone’da uygulama geliştirken en sık karşılaştığımız problemlerinden biri memory management ( hafıza yönetimidir ). Geliştirdiğimiz kodda hafıza ile ilgili herhanbi bir kaçak varsa bu bize, hafıza uyarısı, herhangi bir anda beklenmedik  şekilde uygulamanın kapatılması şeklinde geri dönecektir. Bu hataların çoğunluğu derleme zamanında yakanlan(a)mayıp, çalışma zamanında (runtime) ortaya çıkarlar.

    Bu hataların bir kısmı ( en güzel olanları :) ) ise simulator’de problem yaratmayıp , sadece telefon üzerinde çalışırken ortaya çıkanlarıdır.

    Bu yazıda memory yönetimi , alloc , retain , release metodlari ne zaman/nasıl yapılmalı üzerinde değil, yapılan hatalar nasıl bulunup, fark edilebilir onların üzerinde durulacaktır.

    Öncelikle, uygulamamız biz debug ederken-çalıştırırken , herhangi bir adımda EXC_BAD_ACCESS hatası alıp ta kapanıyorsa, ya da herhangi bir hata almadan beklenmedik şekilde kapanıyorsa , memory hatasından şüphelenmeliyiz. İlk yapmamız gereken Xcode > Run > Debugger menüsünden  uygulamamızın en son hangi işlemi yaparken hata aldığını bulmak olmalı.

    Geliştirme ortamımızı (yani Xcode’u ) memory hatalarını anlayıp, bize uyarı verir hale getirmemiz gerekiyor. Bunun için

    • NSZombieEnabled
    • NSAutoreleaseFreedObjectCheckEnabled
    • NSDebugEnabled
    • MallocStackLoggingNoCompact
    • MallocStackLogging
    • MallocScribble
    • MallocGuardEdges
    • MallocPreScribble
    parametrelerini set etmeliyiz.

    Xcode > Project > Edit Active Executable Environment > Arguments tabından aşağıdaki değerleri environment variable bölümünde kısmında değerleri “YES” olacak şekilde set edin.

    Bu değişkenler set edili iken iPhone üzerinde debug (device debug )işlemi yapamazsınız.

    xcode3

    Bu değişkenleri set ettiğimiz zaman uygulamamız herhangi bir anda çakıldığı zaman aşağıdaki şekilde  bize bir ipucu verecektir.

      debugger2

    Uygulamaki bugların bulunması için bir static analyzer kullanabilirsiniz. Objective-C için en uygun aday LLVM/Clang Static Analyzer uygulaması Uygulamayı taz.bz2 formatında indirip açtıktan sonra  yapmanız gerekenler kısaca şöyle:

    (Uygulamayı /Users/deniz/Downloads/checker-0.206 altına açtığınızı ve projenizin /Users/deniz/Projects/deneme altında olduğunu varsayalım)

    Yeni bir terminal penceresi açıp .

    • export PATH=$PATH:/Users/deniz/Downloads/checker-0.206/
    • export PATH=$PATH:/Users/deniz/Downloads/checker-0.206/bin/
    • cd /Users/deniz/Projects/deneme
    • xcodebuild clean ( Bu komut yerine Xcode > Build > Clean kullanılabilir )
    • scan-build -o problemler -k xcodebuild -configuration Debug

    En son olarak scan-build komutunun sonucunda belirtilen dizin için scan-view işlemi yapılır.

    • scan-view /Users/deniz/Projects/deneme/problemler/2009-05-18-1

    Son komutun çıktısı aşağıdaki gibi bir ekran olacaktır:

    result02

    Bu ekranda yer alan View Report linkleriyle o anki hatanın neden kaynaklandığına bakılabilir.

      result13

      , ,

      2 Comments

      Android’le Adım Adım Uygulama Geliştirme – II

      Bu bölümde bir önceki bölümde geliştirmeye başladığımız uygulama üzerinde çalışmaya devam ederek liste içindeki değerlerin compound (birleşik) kontroller ile göze hoş gelen bir şekilde göstermeye ve web servis aracılığı ile aldığımız döviz değerleri cihaz üzerinde veritabanında saklamaya çalışacağız.

      Hatırlayacağınız gibi uygulamamızda döviz değerlerini ile gösterim yapacağımız ListView’ı ilişkilendirebilmek için ListView sınıfındaki “setAdaptor” metodunu kullanmış ve bu şekilde hazırladığımız bir ArrayAdaptor instance’ını view ile ilişkilendirmiştik.

      Adaptörler, Android platformunda View ve gösterilecek veri arasındaki ilişkiyi kuran sınıflardır. Adaptörler hem her parçanın kullanıcıya nasıl gösterileceğini belirtir hemde arkadaki veri ile gerekli bağlantıyı sağlarlar. Android platformunun kendi içerisinde gelen adaptörler arasında en çok aşağıdaki iki adaptör tanımı kullanılmaktadır:

      1- ArrayAdaptor: İçerisindeki her parça için “toString” metodunu sonucunu, tanımında verilen layout içerisindeki TextView ile ilişkilendirir
      2- SimpleCursorAdaptor: İçerik sağlayıcılar (content provider) üzerinde çalıştırılan sorgulama sonuçlarını, tanımında verilen layout içerisindeki Viewlar ile ilişkilendirir

      Bizde uygulamamızda döviz değerlerinin gösterimi için yeni bir compound kontrolü ve bunu kullanabilecek yapıya sahip bir ArrayAdaptor sınıfı geliştireceğiz.

      Öncelikle okuduğumuz döviz değerlerini tutabilecek yeni bir ara sınıf yazalım (hatırlarsanız daha önce bunun için String yapısını kullanmıştık):

      package com.yazarbozar.currencyexchange;
       
      public class CurrencyItem {
      	private String name;
      	private String buy;
      	private String sell;
       
      	public CurrencyItem(String name,String buy,String sell) {
      		this.name = name;
      		this.buy  = buy;
      		this.sell = sell;
      	}
       
      	public String getName() {
      		return name;
      	}
       
      	public void setName(String name) {
      		this.name = name;
      	}
       
      	public String getBuy() {
      		return buy;
      	}
       
      	public void setBuy(String buy) {
      		this.buy = buy;
      	}
       
      	public String getSell() {
      		return sell;
      	}
       
      	public void setSell(String sell) {
      		this.sell = sell;
      	}
      }

      Web servisten gelen döviz değerlerini okuduğumuz kod bloğunda (CurrencyExchangeHandler) bu yapıyı kullanalım. Ayrıca gelen değerler arasında “Tarih” bilgisinide ayıralım:

      .    private class CurrencyExchangeHandler extends DefaultHandler {
          	private final int 				STATE_DUMMY  = 0;
          	private final int 				STATE_DOVIZ  = 1;
          	private final int				STATE_ALIS   = 2;
          	private final int				STATE_SATIS  = 3;
       
          	private Vector<CurrencyItem>	currenyItemVector;
          	private int					state;
          	private StringBuilder 			processDate;
      	private String 				currencyName;
          	private String				currencyBuy;
          	private String				currencySell;
       
          	public CurrencyExchangeHandler(StringBuilder processDate,Vector<CurrencyItem> currenyItemVector) {
          		this.currenyItemVector = currenyItemVector;
          		this.processDate	        = processDate;
          		this.state		  	= STATE_DUMMY;
          	}
       
      		@Override
      		public void endElement(String uri, String localName, String name) throws SAXException {
      			if("DOVIZ".equalsIgnoreCase(localName)) {
      				if("Tarih".equalsIgnoreCase(currencyName)) {
      					try {
      						processDate.append(currencyBuy);						
      					} catch (Exception e) {
      						//do nothing
      					}
      				} else {
      					currenyItemVector.add(new CurrencyItem(currencyName,currencyBuy,currencySell));					
      				}
      			}
      			state = STATE_DUMMY;
      		}
       
      		@Override
      		public void startElement(String uri, String localName, String name,Attributes attributes) throws SAXException {
      			if("DOVIZ".equalsIgnoreCase(localName)) {
      				state = STATE_DOVIZ;
      			} else if("ALIS".equalsIgnoreCase(localName)) {
      				state = STATE_ALIS;
      			} else if("SATIS".equalsIgnoreCase(localName)) {
      				state = STATE_SATIS;
      			}
      		}
       
      		@Override
      		public void characters(char[] ch, int start, int length) throws SAXException {
      			switch (state) {
      				case STATE_DOVIZ:
      					currencyName = new String(ch, start, length);
      					break;
      				case STATE_ALIS:
      					currencyBuy = new String(ch, start, length);
      					break;
      				case STATE_SATIS:
      					currencySell = new String(ch, start, length);
      					break;
      			}
      		} 
          }

      Şimdi de CurrencyItem’ı ListView içerisinde gösterirken kullanacağımız compound kontrol yapısını hazırlayalım. Bunun için öncelikle bir layout tanımı yaratıp, Android platformunun sistem servislerinden olan “LayoutInflate” servisi ile kullanarak istediğimiz View yapısını elde edeceğiz.

      Layout’ta kullanacağımız renk değerlerini “/res/values/” dizini altında “colors.xml” isimli bir dosya yaratıp tanımlayalım (burada ayrıca ListView içinde uygun renk tanımını hazırlayalım, bunu ileride ön ve arka plan arasındaki renk uyumsuzluklarını gidermede kullanacağız):

      <resources>
          <color  name="list_back_color">#FFFF00</color>
          <color  name="currency_name_text_color">#000000</color>
          <color  name="currency_buy_text_color">#000000</color>
          <color  name="currency_sell_text_color">#000000</color>
          <color  name="currency_name_back_color">#FF3300</color>
          <color  name="currency_buy_back_color">#FF9900</color>
          <color  name="currency_sell_back_color">#FFFF00</color>
      </resources>

      Sonrasında layout tanımımızı oluşturalım (”/res/layout/currency_item.xml”):

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      	android:layout_width="fill_parent"
      	android:layout_height="wrap_content"
      >
      	<TextView 
      		android:id="@+id/currencyName"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      		android:width="60px"
      		android:textColor="@color/currency_name_text_color"
      		android:background="@color/currency_name_back_color"
      	/>	
      	<TextView
      		android:id="@+id/currencyBuy"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      		android:width="60px"
      		android:textColor="@color/currency_buy_text_color"
      		android:background="@color/currency_buy_back_color"
      	/>
      	<TextView
      		android:id="@+id/currencySell"
      		android:layout_width="fill_parent"
      		android:layout_height="fill_parent"
      		android:textColor="@color/currency_sell_text_color"
      		android:background="@color/currency_sell_back_color"
      	/>
      </LinearLayout>

      Burada fark ettiğiniz gibi “CurrencyName” ve “CurrencyBuy” alanları için sabit bir alan ayırırken, “CurrencySell” alanının geri kalan tüm bölgeyi (satır bazında) kaplamasını sağlıyoruz. Bu şekilde satırlar listelendiğinde sutünların düzgün bir şekilde sıralanmasını sağlamış olacağız.

      Uygulamamızda kullandığımız ListView’ı içeren layout yapısını açıp, ListView için hazırladığımız arkaplan renk kodunu kullanmasınıda sağlayalım:

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          >
      <ListView
          android:id="@+id/currencyList"
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content" 
          android:background="@color/list_back_color"
          />
      </LinearLayout>

      Son olarak yukarıda yaptığımız tanımları destekleyen bir ArrayAdaptor yapısı oluşturmamız gerekecek. Bunun için ArrayAdaptor sınıfını extend eden yeni bir sınıf oluşturacağız. Ve bu sınıf içerisindeki “getView” metodunu override ederek bizim tasarladığımız View’ın kullanılmasını sağlayacağız.

      package com.yazarbozar.currencyexchange;
       
      import android.content.Context;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.ArrayAdapter;
      import android.widget.LinearLayout;
      import android.widget.TextView;
       
      public class CurrencyItemAdapter extends ArrayAdapter<CurrencyItem> {
       
      	public CurrencyItemAdapter(Context context, int textViewResourceId, CurrencyItem[] objects) {
      		super(context, textViewResourceId, objects);
      	}
       
      	@Override
      	public View getView(int position, View convertView, ViewGroup parent) {
      		CurrencyItem currencyItem = getItem(position);
      		View 		 currencyItemView;
       
      		if(convertView == null) {
      			LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      			currencyItemView              = layoutInflater.inflate(R.layout.currency_item, null);
      		} else {
      			currencyItemView = convertView;
      		}
       
      		TextView nameView = (TextView) currencyItemView.findViewById(R.id.currencyName);
      		TextView buyView   = (TextView) currencyItemView.findViewById(R.id.currencyBuy);
      		TextView sellView   = (TextView) currencyItemView.findViewById(R.id.currencySell);
       
      		nameView.setText(currencyItem.getName());
      		buyView.setText(currencyItem.getBuy());
      		sellView.setText(currencyItem.getSell());
       
      		return currencyItemView;
      	}
      }

      Burada aşağıdaki kullanım dikkatinizi çekebilir:

      Burada öncelikle tekrar kullanım için verilen view’ı (convertView) inceliyoruz. Eğer bu view “null” ise bu durumda LayoutInflater servisine yaratmış olduğumuz layout tanımını parametre olarak vererek ona ait bir instance’ını yaratmış oluyoruz (layout’un bir View extension’ı olduğunu unutmayalım).

      Bundan sonra yapmamız gereken tek şey, ListView’ın bu hazırladığımız adaptörü kullanmasını sağlamak olacak:

          public CurrencyItem[] getExchange() throws IOException, SAXException, ParserConfigurationException {
          	Vector<CurrencyItem> currenyItemVector        = new Vector<CurrencyItem>();    
          	StringBuilder		 processDate		= new StringBuilder();
          	SAXParserFactory 	 saxParserFactory 	= SAXParserFactory.newInstance();
          	SAXParser		 	 saxParser		  	= saxParserFactory.newSAXParser();
       
          	saxParser.parse(getResources().getString(R.string.currency_url), new CurrencyExchangeHandler(processDate,currenyItemVector));
          	return currenyItemVector.toArray(new CurrencyItem[currenyItemVector.size()]);
          }

      Uygulamamızı çalıştırırsak:

      1

      Hatırlarsanız web servis sonucunda gelen tarih bilgisini SAX parse işlemi sırasında ayırmıştık, bunuda listemizin başına bir TextView ile ekleyelim.

      İlk önce kullanacağımız sabit değerleri “/res/values” altındaki ilgili dosyalarda tanımlayalım:
      strings.xml

          <string name="header_text">Currency Exchange Values on</string>

      colors.xml

          <color  name="header_text_color">#000000</color>
          <color  name="header_back_color">#FFFFFF</color>

      ListView’ın tanımlı olduğu layout dosyasına bu bilgiyi göstereceğimiz bir TextView ekleyelim:

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          >
      <TextView
          android:id="@+id/currencyHeader"
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content" 
          android:textColor="@color/header_text_color"
          android:background="@color/header_back_color"
      	/>   
      <ListView
          android:id="@+id/currencyList"
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content" 
          android:background="@color/list_back_color"
          />
      </LinearLayout>

      En son olarak ilgili Activity tanımında bu TextView’ın içeriğinin hazırlanmasını sağlayan kısmı ekleyelim:

      public class CurrencyExchangeActivity extends Activity {
      	private ListView	listView;
      	private TextView	headerView;
       
          public void onCreate(Bundle savedInstanceState) {
              ....
              listView 	= (ListView) findViewById(R.id.currencyList);
              headerView	= (TextView) findViewById(R.id.currencyHeader);
              ....
          }
       
          public CurrencyItem[] getExchange() throws IOException, SAXException, ParserConfigurationException {
          	Vector<CurrencyItem>  currenyItemVector       = new Vector<CurrencyItem>();    
          	StringBuilder		 processDate		= new StringBuilder();
          	SAXParserFactory 	 saxParserFactory 	= SAXParserFactory.newInstance();
          	SAXParser		 	 saxParser		  	= saxParserFactory.newSAXParser();
       
          	saxParser.parse(getResources().getString(R.string.currency_url), new CurrencyExchangeHandler(processDate,currenyItemVector));
          	headerView.setText(getResources().getText(R.string.header_text)+" "+processDate.toString());    	
          	return currenyItemVector.toArray(new CurrencyItem[currenyItemVector.size()]);
          }
          ....
      }

      Uygulamamızı tekrar çalıştırdığımızda:

      21

      Şimdi uygulamamız bünyesinde web servisten aldığımız değerleri cihaz üzerindeki veritabanında saklama işlemini inceleyelim.

      Android platformu bünyesinde gelen SQLite kütüphanesi bize uygulamamız içerisinde kullanabileceğimiz bir ilişkisel veritabanı yapısı (RDBMS) sunmaktadır. Bu kütüphane aracılığı ile normal uygulamalarımızda yaptığımız tüm veritabanı işlemlerini gerçekleştirebiliriz (unutmamaız gereken bir nokta, uygulama içerisinde yarattığımız veritabanı sadece o uygulamaya özgüdür, uygulamalar arasında paylaşım yapmak için Android bünyesindeki Content Provider yapısı kullanılmalıdır).

      Öncelikle veritabanı üzerinde yaratma, açma ve kapama işlemleri için çekirdek bir sınıf yazıp, sonrada bu sınıfın üzerine CRUD işlemleri için ayrı bir sınıf hazırlayacağız. Veritabanı üzerindeki yaratma, açma ve kapama işlemlerini içerecek sınıfımız “SQLiteOpenHelper” sınıfından türeyecek, ve bu sınıf içerisindeki onCreate ve onUpdate metodlarını kullanarak uygulamamıza özgü bir veritabanı yapısı hazırlayacak.

      public class CurrencyExchangeDBHelper extends SQLiteOpenHelper{
      	private String createSQL;
      	private String updateSQL;
       
      	public CurrencyExchangeDBHelper(Context context, String dbName,CursorFactory factory, int dbVersion, String createSQL, String updateSQL) {
      		super(context, dbName, factory, dbVersion);
      		this.createSQL = createSQL;
      		this.updateSQL = updateSQL;
      	}
       
      	@Override
      	public void onCreate(SQLiteDatabase db) {
      		db.execSQL(createSQL);
      	}
       
      	@Override
      	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      		db.execSQL(updateSQL);
      	}
      }

      “onCreate” metodu, belirtilen isimde bir veritabanı (uygulamaya özgü) bulunmadığında, “onUpgrade” metodu ise bulunan veritabanının versiyonu ile constructor’da verilen versiyonun eşit olmaması durumunda çalıştırılmaktadır. Artık bu sınıf üzerinde “getWritableDatabase” metodunu kullanarak CRUD işlemlerimizi gerçekleştirebileceğimiz bir SQLiteDatabase instance’ı oluşturabiliriz (”getReadableDatabase” metodu ile sadece okuma amaçlı SQLiteDatabase instance’ıda oluşturabiliriz). Aslında Android platformu, SQLiteOpenHelper dışında “Context.openOrCreateDatabase” metodu ile de veritabanı yaratmamıza izin vermektedir, ancak bu kullanımda veritabanı içindeki tablo yaratımları ve gerekli tablo güncellemelerini bizim kontrol etmemiz gerekmektedir.

      Sonra hazırladığımız bu sınıfı kullanan ve CRUD işlemlerini gerçekleştirebilmemiz için veritabanımıza referans hazırlayan bir sınıf hazırlayalım:

      public class CurrencyExchangeDBAdaptor {
      	private static CurrencyExchangeDBAdaptor instance; 
      	private static SimpleDateFormat			 dateFormatFrom = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
      	private static SimpleDateFormat			 dateFormatTo   = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       
      	private SQLiteDatabase 				database;
      	private CurrencyExchangeDBHelper	currencyExchangeDBHelper;
      	private String		   				dbName    	      = "CurrencyExchangeDB";		 
      	private int							dbVersion	      = 1;
      	private String		   				tableName 	      = "CurrencyExchangeTable";		 
      	private String		   				dbKeyId	      = "id";
      	private String		   				dbKeyDate 	      = "date";	
      	private String		   				dbKeyCurrCode  = "code";	
      	private String		   				dbKeyCurrBuy    = "buy";	
      	private String		   				dbKeyCurrSell    = "sell";	
      	private String		   				createSQL        = "create table CurrencyExchangeTable(id integer primary key autoincrement, " +
      										   				" date date not null, code text not null, buy text not null, sell text not null)";
      	private String		   				updateSQL       = "";
       
      	private CurrencyExchangeDBAdaptor(Context context) {
       
      		currencyExchangeDBHelper = new CurrencyExchangeDBHelper(context,dbName,null,dbVersion,createSQL,updateSQL);
      		database                         = currencyExchangeDBHelper.getWritableDatabase();
      	}
       
      	public static CurrencyExchangeDBAdaptor getInstance(Context context) {
      		if(instance == null) {
      			instance = new CurrencyExchangeDBAdaptor(context);
      		}
      		return instance;
      	}
      }

      Fark ettiyseniz, yarattığımız tablo yapısında normal veri yapımızda bulunmayan “id” isimli bir alanda (unique index olarak) yarattık. Normal tablo yapılarında bu tarz bir alan zorunlu olmasada bu tarz bir yaratım tavsiye edilmektedir (Ayrıca ileride bu tabloyu Content Provider yapısı ile diğer uygulamalara açmak isterseniz bu tarz bir alan zorunludur). Ayrıca “Date” alanları içinde iki tane format instance’ı yarattık çünkü bize web servisten gelen tarih formatı “dd.MM.yyyy HH:mm:ss” şeklinde iken SQLite’ın desteklediği tarih formatı ise “yyyy-MM-dd HH:mm:ss” şeklindedir (SQLite’ın desteklediği diğer tarih formatları için : http://www.sqlite.org/cvstrac/wiki?p=DateAndTimeFunctions).

      CRUD işlemlemleri için SQLiteDatabase üzerindeki şu metodları kullanacağız:
      1- insert(String table, String nullColumnHack, ContentValues values)
      Burada özellikle “nullColumnHack” parametresi biraz kafa karıştırıcı olabilir, ContentValues olarak verilen yapı boş ise burada belirtilen sütun alanı “NULL” olarak atanır
      2- update(String table, ContentValues values, String whereClause, String[] whereArgs)
      3- delete(String table, String whereClause, String[] whereArgs)

      	public void insertCurrencyExchange(String date,CurrencyItem currencyItem) {
      		ContentValues values = new ContentValues();
      		values.put(dbKeyDate, date);
      		values.put(dbKeyCurrCode, currencyItem.getName());
      		values.put(dbKeyCurrBuy, currencyItem.getBuy());
      		values.put(dbKeyCurrSell, currencyItem.getSell());
       
      		database.insert(tableName, null, values);
      	}
       
      	public Cursor getCurrencyExchange(String date) {
      		return database.query(tableName, new String[]{dbKeyCurrCode,dbKeyCurrBuy,dbKeyCurrSell}, "date=?", new String[]{dateFormatTo.format(dateFormatFrom.parse(date))}, null, null, null, null);
      	}
       
      	public boolean isCurrencyExchangeExists(String date, String currencyCode) {
      		Cursor cursor = null;
      		try {
      			cursor = database.query(tableName, new String[]{dbKeyId}, "date=? and code=?", new String[]{dateFormatTo.format(dateFormatFrom.parse(date)),currencyCode}, null, null, null);
      			if(cursor.getCount() > 0) {
      				return true;
      			} else {
      				return false;
      			}
      		} finally {
      			cursor.close();
      		}
      	}

      Şimdi bu sınıfı, uygulamamız içerisinde iki temel amaç için kullanalım:
      1- Web servis aracılığı ile aldığımız döviz kur değerleri veritabanında bulunmuyorsa veritabanına yazalım
      2- Eğer varsa her döviz kur değerini en yakın zamandaki ilgili döviz kur değeri ile kıyaslayıp artma/azalma/aynı kalma durumlarını kullanıcıya gösterelim

      İlk işlem için

      public class CurrencyExchangeActivity extends Activity {
          ....
          public CurrencyItem[] getExchange() throws IOException, SAXException, ParserConfigurationException {
             ....
             for (CurrencyItem currencyItem : currenyItemVector) {
      		if(!CurrencyExchangeDBAdaptor.getInstance(this).isCurrencyExchangeExists(processDate.toString(), currencyItem.getName())) {
      			CurrencyExchangeDBAdaptor.getInstance(this).insertCurrencyExchange(processDate.toString(), currencyItem);
      		}
             }
             ....
          }
          ....
      }

      Uygulamamızı çalıştırdıktan sonra veritabanına yazılan kayıtları gözlemlemek için ADB (Android Debug Bridge) kullanılabilir. Bunun için:
      1- SDK kurulumunun altındaki adb.exe adlı dosya “adb shell” komutu ile çalıştırılır
      2- Uygulamaların veritabanı dosyaları şu dizin yapısı altında tutulmaktadır, “/data/data//databases”
      3- “sqlite3″ komutu veritabanı adı verilerek çalıştırılır
      4- Bu aşamadan sonra sql komutları çalıştırılarak yarattığımız kayıtları görebiliriz.

      31

      Şimdi ise döviz değerlerini daha önceki değerler ile karşılaştırarak değişimleri kullanıcıya gösterme işlemi yapalım. Hatırlarsanız, ListView içerisindeki parçaların gösterimi için bir Adaptor sınıfı hazırlamış ve bu sınıf içerisinde her parçanın nasıl kullanıcıya gösterileceğini belirtmiştik. Değişimleri gösterme işleminde de yine bu noktalarda işlem yapacağız:
      1- Değişikliği gösteren 3 adet imaj dosyası hazırlayacağız
      2- ListView içerisideki parçaların gösterimi için hazrıladığımız layout içerisine iki tane “ImageView” ekleyeceğiz
      3- Adaptor sınıfı içerisinde “getView” metodunda göstereceğimiz değeri veritabanındaki kayıtlar ile karşılaştırıp alış ve satış değerlerindeki değişimleri eklediğimiz iki ImageView içerisinde göstereceğiz.

      Öncelikle 3 tane imaj dosyası hazırlayıp, bunları “/res/drawables” dizini altına yerleştirelim.

      Sonra ListView içindeki parçaların gösterimi için hazırladığımız layout içerisine “ImageView” tanımlarını ekleyelim:

      colors.xml

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <color  name="list_back_color">#FFFF00</color>
          <color  name="header_text_color">#000000</color>
          <color  name="header_back_color">#FFFFFF</color>
          <color  name="currency_name_text_color">#000000</color>
          <color  name="currency_text_color">#000000</color>
          <color  name="currency_name_back_color">#FF3300</color>
          <color  name="currency_back_color">#FFFF00</color>
      </resources>

      currency_item.xml

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      	android:layout_width="fill_parent"
      	android:layout_height="wrap_content"
      >
      	<TextView 
      		android:id="@+id/currencyName"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      		android:width="60px"
      		android:textColor="@color/currency_name_text_color"
      		android:background="@color/currency_name_back_color"
      	/>	
      	<ImageView
      		android:id="@+id/currencyChangeBuy"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      	/>
      	<TextView
      		android:id="@+id/currencyBuy"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      		android:width="60px"
      		android:textColor="@color/currency_text_color"
      		android:background="@color/currency_back_color"
      	/>
      	<ImageView
      		android:id="@+id/currencyChangeSell"
      		android:layout_width="wrap_content"
      		android:layout_height="fill_parent"
      	/>
      	<TextView
      		android:id="@+id/currencySell"
      		android:layout_width="fill_parent"
      		android:layout_height="fill_parent"
      		android:textColor="@color/currency_text_color"
      		android:background="@color/currency_back_color"
      	/>
      </LinearLayout>

      Son olarak ise geliştirdiğimiz Adaptor sınıfı içerisindeki “getView” metodunda ilgili değişikliği yapacağız:

      public class CurrencyItemAdapter extends ArrayAdapter<CurrencyItem> {
      	private Context context;
      	private String  processDate;
       
      	public CurrencyItemAdapter(Context context, int textViewResourceId, CurrencyItem[] objects, String processDate) {
      		super(context, textViewResourceId, objects);
      		this.context 	 = context;
      		this.processDate   = processDate;
      	}
       
      	@Override
      	public View getView(int position, View convertView, ViewGroup parent) {
      		CurrencyItem currencyItem = getItem(position);
      		View 		   currencyItemView;
       
      		if(convertView == null) {
      			LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      			currencyItemView              = layoutInflater.inflate(R.layout.currency_item, null);
      		} else {
      			currencyItemView = convertView;
      		}
       
      		TextView  nameView          = (TextView) currencyItemView.findViewById(R.id.currencyName);
      		TextView  buyView            = (TextView) currencyItemView.findViewById(R.id.currencyBuy);
      		TextView  sellView             = (TextView) currencyItemView.findViewById(R.id.currencySell);
      		ImageView changeBuyView  = (ImageView) currencyItemView.findViewById(R.id.currencyChangeBuy);
      		ImageView changeSellView  = (ImageView) currencyItemView.findViewById(R.id.currencyChangeSell);
       
      		try {
      			CurrencyItem tempItem = CurrencyExchangeDBAdaptor.getInstance(context).getLatestCurrencyExchange(processDate, currencyItem.getName());			
      			if(tempItem == null) {
      				changeBuyView.setImageResource(R.drawable.same);
      				changeSellView.setImageResource(R.drawable.same);
      			} else {
      				if(Double.parseDouble(tempItem.getBuy()) > Double.parseDouble(currencyItem.getBuy())) {
      					changeBuyView.setImageResource(R.drawable.down);				
      				} else if(Double.parseDouble(tempItem.getBuy()) < Double.parseDouble(currencyItem.getBuy())) {
      					changeBuyView.setImageResource(R.drawable.up);				
      				} else if(Double.parseDouble(tempItem.getBuy()) == Double.parseDouble(currencyItem.getBuy())) {
      					changeBuyView.setImageResource(R.drawable.same);					
      				}	
      				if(Double.parseDouble(tempItem.getSell()) > Double.parseDouble(currencyItem.getSell())) {
      					changeSellView.setImageResource(R.drawable.down);				
      				} else if(Double.parseDouble(tempItem.getSell()) < Double.parseDouble(currencyItem.getSell())) {
      					changeSellView.setImageResource(R.drawable.up);				
      				} else if(Double.parseDouble(tempItem.getSell()) == Double.parseDouble(currencyItem.getSell())) {
      					changeSellView.setImageResource(R.drawable.same);									
      				}
      			}					
      		} catch (Exception e) {
      			e.printStackTrace();
      		}
       
      		nameView.setText(currencyItem.getName());
      		buyView.setText(currencyItem.getBuy());
      		sellView.setText(currencyItem.getSell());
       
      		return currencyItemView;
      	}
      }
      </java>

      Uygulamamızı çalıştırırsak:

      4

      Ve böylece uygulamamızın 2. adımınıda bitirmiş olduk, 3. adımda tekrar buluşmak üzere.

      , , ,

      3 Comments