Posts Tagged SDK
Android’le Adım Adım Uygulama Geliştirme – III
Posted by Serkan Doğantekin in Android, Genel on May 21st, 2009
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:
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(); }

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:

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:

Bir sonraki bölümde görüşmek üzere…
Objective-C’de Memory Problemlerinin Giderilmesi
Posted by Deniz Seçilir in Genel, iPhone on May 19th, 2009
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
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.

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

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:

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

Android’le Adım Adım Uygulama Geliştirme – II
Posted by Serkan Doğantekin in Android, Genel on May 3rd, 2009
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:

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:

Ş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.

Ş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:

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