assets etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
assets etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Ders 20: Android External Database Connection

7 Mart 2012 Çarşamba
Evet,sonunda zaman buldum,yarattım. Ve kaldığımız yerden devam ediyoruz. Konumuz external database bağlantısını gerçekleştirmek.
Genel olarak önce kodları verip,daha sonra onların ne anlam ifade ettiğini açıkladım. Okurken şaşırmayın.

Öncelikli olarak SQLite Database Browser kullanarak, database içinde bulunan tabloları ve bu tabloların alanları ile verilerini (insert ile eklenen verileri) oluşturuyoruz. Bunu başka bir derste detaylı olarak anlatabilirim. Çok basit bir yapısı var bu programın. Add Table diyip yeni bir tablo oluşturabiliyorsunuz ve hiç insert cümleciği yazmadan bile veri ekleyebiliyorsunuz.

Main.xml içerisine gelip , tasarımımızı oluşturuyoruz. Ben bir listview kullanmayı uygun gördüm. Database üzerinden veri çekip bu listview’in elemanları olarak göstermek istedim. Listview’i daha önceden anlatmıştım,o yüzden onun da pek üzerinde durmuyorum. Kodları ise şöyle oluyor:


<?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" >
    <TextView
        android:layout_width="fill_parent"
       android:layout_height="wrap_content"
        android:text="@string/hello" />
  
<ListView
             android:id="@+id/contentlist"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"/>
</LinearLayout>

İsteyen buradaki TextView’i kaldırabilir tabiî ki. Ben üşendiğim için, direkt varolan main.xml içerisine ListView eklemesi yaptım.
 SQLite Database Browser ile oluşturduğumuz .db uzantılı database dosyaımızı , ilgili projemiz içindeki “Assets” klasörü altına atıyoruz.


   “src” klasörü içindeki com… adlı paketimizin altına DatabaseHelper adında bir java dosyası oluşturuyoruz.

İçini ise şöyle düzenliyoruz:

public class DatabaseHelper  extends SQLiteOpenHelper

SQLiteOpenHelper sınıfını extends ediyoruz,öncelikle. Böylece bu sınıfımızın database için kullanılacağını belirtmiş oluyoruz.


private static String DB_PATH = "/data/data/tr.edu.kelimeavcisi/databases/";
private static String DB_NAME = "tatliavcisi.db";

Path ve database adımızı belirtiyoruz. Db_Path değişkenini , database’imizin nerede olduğunu programa anlatmak için kullanıyoruz.Assets içine koyduğumuz tatliavcisi.db dosyası /data/data/packagename/databases/ dizini altında bulunduğu için,bunu kendimize göre ayarlıyoruz. Db_Name değişkenine de database dosyamızın adını yazıyoruz.


private SQLiteDatabase myDataBase;
private final Context myContext;

Şeklinde 2 değişken daha tanımlıyoruz, bunlardan myDatabase değişkenini, database üzerinde işlem yapabilmek için kullanacağız. myContext ise yine context değeri için kullanılacak.


public DatabaseHelper(Context context) {
      super(context, DB_NAME, null, 1);     
     this.myContext = context;
                                         }

Constructor metodumuzu yazıyoruz. Burada super metodu içindeki DB_NAME değişkenini yukarıda tanımlamıştık, 1 yazan değer ise bizim database’imizin sürümü.İstiyosanız 2,3 yazın,size kalmış.İlla 1 olucak diye bir kaide yok!


public void createDataBase() throws IOException{
        boolean dbExist =checkDataBase();
             if(dbExist){
             //bir şey yapma,zaten database mevcut.
              }else{
      
     this.getReadableDatabase();
             try {   
                copyDataBase();
             } catch (IOException e) {
       
            throw new Error("Error copying database");
      
     }  
   }
    }

Bu method kullanılarak default sistem pathi üzerinde boş bir database oluşturulacak, eğer bir database mevcut ise de o database bunun üzerine yazılacak. Burada Exception kullanarak, kodumuzu daha sağlıklı bir hale getirdik, eğer kopyalama (ya da siz buna üzerine yazma diyebilirsiniz) esnasında bir hata olursa,bize “Error copying database” şeklinde bir hata verecek. Buradaki checkDatabase()  ve copyDatabase() metodlarını da biz yazıyoruz. Dolayısıyla eğer şu anda bir hata mesajı alıyorsanız gayet normal. Tabiki gerekli importları da yapmadığınızı düşünürsek gene hata mesajı alabilirsiniz.


privat boolean checkDataBase(){
     SQLiteDatabase checkDB = null;
   try{
           String myPath = DB_PATH + DB_NAME;
       checkDB = SQLiteDatabase.openDatabase(myPath,null
SQLiteDatabase.OPEN_READONLY);
  
   }catch(SQLiteException e){
         //database does't exist yet.
       }
       if(checkDB != null){
             checkDB.close();
       }
       return checkDB != null ? true : false;
        }

checkDatabase() metodu sürekli olarak yeni bir database yaratılıp, onun üzerine kopyalama işleminin yapılmasını engelleyecek bir metot aslında. Daha önceden yaratılmış bir database olup olmadığına bakıyoruz ve varsa tamam, bir şey yapma diyoruz. Aslında olay bu kadar basit.

Burada /data/data/package_name/databases/database_name dizininde bir database olup olmadığına bakılıyor. Eğer sonuç olarak “null” dönerse, böyle bir database olmadığı anlaşılacak ve yeni bir database yaratılacak, eğer “null” değeri dönmezse , onun yerine bir database örneği dönerse , zaten böyle bir database’in var olduğunu anlayacağız ve yeni bir database yaratmamıza gerek kalmayacak.


private void copyDataBase() throws IOException{
   //Local database’imizi açıyoruz  
   InputStream myInput = myContext.getAssets().open(DB_NAME);
       // Yeni yaratılmış database’in pathini tam olarak algılıyoruz
   String outFileName = DB_PATH + DB_NAME;
   
   //Boş database’imizi açıyorz
      OutputStream myOutput = new FileOutputStream(outFileName);
  
   //Local database’imizi boş database üzerine kopyalıyoruz.
      byte[] buffer = new byte[1024];   
   int length;  
   while ((length =myInput.read(buffer))>0){
            myOutput.write(buffer, 0,length);  
   }  
   //Açılmış database’leri kapatıyoruz.  
   myOutput.flush();  
   myOutput.close();
  myInput.close();
    }

Sırasıyla , kod içinde de yazılan yorumlardan anlaşılacağı üzere, local database’imizi ve boş database’imizi açıp,kopyalama işlemini gerçekleştiriyoruz.Ardından da bu database’leri kapatıyoruz.


public void openDataBase() throws SQLException{   
   //Database’i Aç      
String myPath = DB_PATH + DB_NAME;  
   myDataBase = SQLiteDatabase.openDatabase(myPath,null,SQLiteDatabase.OPEN_READONLY);
  
}

Database’imize ulaşmamızı sağlayacak open metodunu yazıyoruz. Burada anlaşılmayan bir şey yok diye düşünüyorum.


@Override
       public synchronized void close() {
           if(myDataBase != null)
       myDataBase.close();  
       super.close();
       }

Aynı şekilde database üzerinde istenmeyen işlemlerin yapılmasını ya da dikkatsizlikten oluşacak hataları önlemekte kullanacağımız close metodunu da yazıyoruz. Yine burada anlaşılmayan bir şey yok diye düşünüyorum.


@Override
       public void onCreate(SQLiteDatabase db) {
       }
       @Override
       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       }

SQLiteOpenHelper sınıfını kullandığımızda zorunlu olarak yazmamız gereken metodları da ekliyoruz. Ama biz external bir database kullandığımız için bu metotlarla yapacağımız bir iş yok. İçlerini boş bırakıyoruz.


Cursor getAllAccounts()
         {
          this.close();
          this.openDataBase();
          SQLiteDatabase db=this.getReadableDatabase();
          Cursor cur=db.query("accounts", new String[] {"name"}, null , null , null , null , null);
          return cur;
         }


Bu illa kullanılması gereken bir metot değil. Yani kişiye,programa özel bir metot. Ben database’im içinde bulunan “accounts” tablosu içinden “name” kolonunda bulunan değerleri getirmek istediğim için , böyle bir metot oluşturdum. Burada , okunabilir bir database örneği çağırdıktan sonra query metodu ile sorgumu gerçekleştirdim. Dönen sonucu bir cursor üzerine atadım. Daha sonra activity içerisinden bunu çağırıp, istediğim gibi dönen verileri kullanacağım.



import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

Aslında tüm DatabaseHelper dosyamız bu kadar, ama hala hata alanlar varsa diye tüm importları da belirteyim istedim. Son satırdaki Log’u import etmeseniz de olabilir. Ben onu hatam nerede var, program nasıl işliyor diye görmek için LogCat üzerinde mesaj alabilmek için kullandım.


@Override
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
setContentView(R.layout.main);
        //DatabaseHelper sınıfından yeni bir örnek oluşturma
       
DatabaseHelper myDbHelper = new DatabaseHelper(this);
        myDbHelper = new DatabaseHelper(this);
      
//Database yaratma
        try {
             myDbHelper.createDataBase();
       } catch (IOException ioe) {
             throw new Error("Unable to create database");
       }
      
//Database açma
       try {
             myDbHelper.openDataBase();
       }catch(SQLException sqle){
             throw sqle;
       }
ListView listContent = (ListView)findViewById(R.id.contentlist);
Cursor cursor = myDbHelper.getAllAccounts();
    startManagingCursor(cursor);
    String [] values = new String [5] ;
   int i= 0;
  
while(cursor.moveToNext())
  {
      values[i] =cursor.getString(cursor.getColumnIndex("name"));
      i++;
    }
    cursor.close();
ArrayAdapter<String> adapter = new
ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, values);   
listContent.setAdapter(adapter);
    }           
    }


Activity dosyamız içinde yaptığımız şey, yeni bir database örneği yaratmak , bunu açmak , cursor üzerine accounts tablosundaki name kolonundaki verileri atmak ve bunları listview üzerinde göstermek. Hepsi bu.

Burada string array oluştururken boyutuna 5 verdim , çünkü geriye 5 tane verinin döneceğini biliyorum. Siz de size uygun olan bir boyutta array yaratmayı unutmayın. Yoksa hata alırsınız.

Görüntü ise şöyle oluyor. Hepsi empty olarak gözüküyor. Çünkü accounts tablomda name kolonu altında 5 tane empty yazıyor J



Sonuç olarak,Aykut Çayır’a, debug yapıp beni hatalardan kurtardığı için ve buradaki yazıyı yazana teşekkürü bir borç bilirim.

Biraz aceleye geldi yazı, eğer hatam,eksik aksağım varsa biri he desin. Ben de düzelteyim.

Ders 1: Android Klasör Yapısı

23 Ocak 2012 Pazartesi

Androidin klasör yapısını bilmek, android öğrenmenin en baş kuralı olsa gerek. Neyin nerede olduğunu bildikten sonra işler daha kolay, kod yazmak da öyle.

Androidin klasör yapısı şöyle oluşuyor:

·         src: Uygulamamızın kaynak kodlarını içeren klasör. Adı üstünde ‘source’ kısmı. Yazdığımız tüm java kodları, bu dizin altında paketler içinde bulunuyor. Aslına bakarsak, Eclipse, kaynak kodlarının bulunması/konumlandırılması için tek bir dizin sınırlandırması yapmıyor. Pek çok dizin kaynak kodu dizini olarak kullanılabilir, düzenlenebilir. Ama geleneksel olarak src altında tutulur. Bilinen kalıp budur.

·         gen: Eclipse ADT(Ada Development Tools)  tarafından otomatik olarak oluşturulan ve bizim içeriklerini değiştirmememiz gereken dosyaları içeren klasördür. İçeriğini değiştirmemiz, kodlarımızın bozulmasına, çalışmamasına sebebiyet verebilir. Bu klasör res (adı üstünde gene, resources) klasörü içine koyacağımız kaynak dosyalarına ait sabit değişkenler içeren R sınıfını barındırıyor sadece. Biz uygulamaya yeni kaynaklar ekledikçe bu sınıf da ADT tarafından otomatik olarak güncelleniyor. Bu sınıf, appt(Android Asset Packing Tool) tarafından oluşturuluyor. Eclipse ile uygulama geliştirenlerin appt’yi kullanmalarına gerek yok. Ayrıca R.java kodları üzerinde kullanıcının değişiklik yapması engellenmiştir.

·         assets: Uygulamamız ile birlikte paketlenmesini istediğimiz dosyalar varsa(tar.gz gibi-tar.gz : genelde Linux ortamında kullanılan.rar uzantısı gibi bir uzantı.) bunları koymamız gereken klasördür. Bu dizin altındaki dosyalara sadece ‘java.io.InputStream’ aracılığı ile erişilebiliyor. Mp3 dosyaları, pdfler, csv formatındaki data dosyalarını bu dizin altına atıp InputStream aracılığı ile okuyabiliriz. Yani kısacası, bu dizindeki kaynaklara erişmek için ilave kod yazılması gerekmekte.

·         res: İçerisinde birden fazla klasör barındıran kaynaklar(resources) klasörü. İçerisindeki ‘layout’ klasörüne arayüz tanımlamalarını içeren XML dosyalarını, ‘values’ klasörüne renk, string, çeşitli diziler vs. içeren XML dosyalarını, ‘drawables’ klasörüne ise kullandığımız resim ve ikonları koyabiliriz. Burada yaratılmış olan klasörlere ek olarak kendimiz de yeni klasörler ve XML dosyaları yaratıp, istediğimiz kadar kaynak tanımlaması yapabiliriz ve R sınıfı aracılığı ile bunlara ulaşabiliriz. ‘drawables’ klasörü yerine, drawable-hdpi,drawable-mdpi ve drawable-ldpi  klasörleri olabilir.drawable-hdpi(high definition) yüksek çözünürlüklü resimler için, drawable-mdpi(medium definition) orta kalite resimler için, drawable-ldpi(low definition) ise düşük çözünürlüklü resimler için kullanılır.

·         Strings.xml: res/values dizini altında tanımlayabileceğimiz kaynaklar arasında en fazla kullanılanı budur. Uygulama içerisinde geçen tüm metinleri strings.xml dosyasına kaydedip, bu metinlere java kodu içinden R sınıfı aracılığı ile ulaşabiliriz. Strings.xml içinde sabit metinler dışında değişkenler de tanımlayabiliriz. Değişken tanımlarken ‘java.util.Formatter’ kurallarına uymamız gerekir.XML dosyası içinde metin ve/ya değişken tanımlamak, değişiklikleri tek bir yerden yönetmemize olanak sağlar. Örneğin; telefonlarının yerel dil ayarı ‘FR’(Fransızca) olan kullanıcılar için uygulamaya Fransızca desteği eklemek istersek, strings.xml dosyasının bir kopyasını oluşturup Türkçe metin ve/ya değişkenleri Fransızca karşılıkları ile değiştirmemiz gerekir. Metinlerin Fransızca karşılıklarını içeren strings.xml dosyasını da res/values-fr dizini altına koymamız gerekir. Dili Fransızca olan telefonlarda metinlerin Fransızca karşılıklarını gösterme işini Android platformu bizim için yapacaktır.

·         Main.xml: Tasarımımızı bu dosya içerisinde yaratırız.İster grafiksel ara yüzü kullanıp sürükle bırak şeklinde ekranın nasıl gözükeceğini ayarlar, istersek de kod yazarak istediğimizi oluşturabiliriz.

·         Proguard.cfg : Uygulama kodlarına bir dizi performans optimizasyonu yapar.Kodun şifrelenmesini ve lisanslanmasını da sağlar.

·         Default.properties / Project.properties: Uygulamanın derlenip cihaza gönderilmesi sırasında çalışan ‘Ant’ scripti tarafından kullanılan özellik dosyasıdır.Burada uygulamanın hangi SDK versiyonu ile çalışacağı tutulur. Yazdığımız uygulamanın cihaza gönderilip çalışması sırasında arka planda kullanılan dosyadır. Elle değiştirilmesi genelde tavsiye edilmez.

·         AndroidManifest.xml: Bir Android uygulaması “AndroidManifest.xml” dosyası içinde tanımlanmaktadır. Uygulama içindeki tüm aktiviteler, servisler, ortak yayın alıcılar ve içerik sağlayıcılar bu dosya içerisinde belirtilmelidir. Uygulamaya ait izinler de bu dosya içinde belirtilir. Örneğin; internet erişimi gerektiren bir uygulamaya ait izinler bu dosya içinde tanımlanmalıdır. Uygulamanın önemli dosyalarından biridir.

Andoriddeki klasör yapısı hemen hemen böyle. Eksiğim, aksağım varsa bir babayiğit çıksın, söylesin. Doğrusunu ben de öğreneyim. Yoksa bilmeyenlere destek, bilenlere tekrar olsun, benim de sefam olsun.