10 Aralık 2011 Cumartesi

Derinlemesine Android

Merhaba,

Bugün birer android geliştirici olarak kullandığımız bu platformun Türkçe kaynaklarda bariz eksikliğinin göründüğüne inandığım temellerine inmeye başlayacağız. Pek tabi konu Android'in temelleri olunca, ister istemez linux literatüründen de bazı kavramlara girmemiz kaçınılmaz olacak. Elimden geldiğince yalın dille anlatmaya çalışacağım bu yazı umuyorum tüm Android ile ilgilenen yazılımcı arkadaşlar için faydalı olur. Evet herşey hazırsa, kemerlerinizi takın ve arkanıza yaslanın keza android'in temellerine olan yolculuğumuz şu andan itibaren başlamak üzere.

Android Stack Yapısı

Android; mobil cihazlar için geliştirilmiş, genel manada 4 farklı katmandan oluşan bir işletim platformudur. Bu katmanların en altında, platformun abstraction layer'ı olma görevini de üstlenecek şekilde konumlandırılmış olan linux kernel bulunmaktadır. HAL'de (hardware abstraction layer) linux kernel'in kullanılmasının temelde üç önemli amacı vardır.
  1. Fragmantasyonun android'de fazla olması (Andy Rubin'in belirttiğine göre her gün 500.000 yeni androidli cihaz piyasaya çıkmaktadır) ve linux kernel'in farklı donanımlarda kolayca compile edilebilmesi münasebetiyle yaşanması muhtemel uyum problemlerinin önüne geçmek. Bu sıkıntıyı gidermek maksadıyla bir çok farklı donanıma ait sürücülerin linux kernel içerisinde implemente edilmiş halde bulunduğunu söyleyebiliriz.
  2. Güvenlik, hız, proses ve network yönetimi konularında yadsınmaz başarısı.
  3. Linux kernel'in de hali hazırda açık kaynak olması.
Belirtmeden geçmeyelim, android linux kernel'i kullanmaktadır, linux değildir. Ayrıca linux'ın tüm standartlarını içermez. Bununla beraber Android için linux kernel'e ilave edilmiş alarm, power management, kernel debugger, low memory killer, ashmem, binder ve logger gibi nesneler mevcuttur. Bu noktada Binder'a biraz değinmekte fayda var; Android'de uygulamalar ve servisler ayrı prosesler üzerinde çalışırlar yani bir proses başka bir prosesin memory'sine erişemez ancak bunun yanında aralarında haberleşme ve data paylaşımına da ihtiyaç duyarlar. İşte Android'de iki proses arasında data aktarımını-mesaj gönderimini sağlayan yani interproses communication çağrıları (IPC Call) yöneten yapı Binder'dır.

Android'de iki farklı prosesin birbirleriyle haberleşmesini Binder sağlar

İkinci katmanda, android geliştiricilerinin çok nadiren direk olarak erişmeye çalıştığı genel manada ise üst katmanların ihtiyaç duyacağı işlevselliklerin yerine getirilebilmesini sağlayacak kütüphanelerin (databasesel ilişkilerin yönetilmesine imkan veren sqlite, opengl için 2D-3D grafik işleme, bitmap ve vektör font render ederken kullanılan freetype, html render etmede kullanılan webkit gibi) implemente edildiği native libraries bulunmaktadır. 

Dalvik Virtual Machine (DVM)
Android uygulamalarını compile ederken Android için özel olarak yazılmış Dalvik Virtual Machine kullanılır. Uygulamalar genellikle Java ile geliştirildiğinden, kodlar önce JVM (Java Virtual Machine) ile bytecode'a compile edilir (.java => .class). Bu işlem sonrası JVM ile uyumlu hale gelen .class dosyaları uygulama çalıştırılmaya başlamadan önce bir de Android'in anlayabileceği ve çalıştırabileceği Dalvik compiled executable file olan .dex formatına çevrilir. Aşağıdaki resimde, JVM ile çalışan cihaz ile Android arasındaki fark gösterilmektedir. Burada şu soruyu sorabilirsiniz "neden direk Dalvik byte code'a çevrilmiyor da önce Java byte code'a daha sonra Dalvik byte code'a çevriliyor?" Bu sorunun temelde iki nedeni var.
  1. DVM registerbased virtual machine JVM ise stack-based virtual machine'dir. Stack-based vmler register-based vmler'e göre ham kodu generate etmede daha hızlı ve kolaydırlar. Register-basedler ise  yüksek ölçekte optimize edilmiş kodun generate edilmesinde ve hızlı implementasyon yaratımlarında daha başarılıdırlar. Dolayısıyla compile etme işleminin tekrar tekrar yapılması yerine önceden jvm ile makinenin anlayabileceği dile çok yakın şekilde çevrilmiş byte code'tan dvm'nin ürettiği dex tipine çevirim kullanılır.
  2. DVM, .NET framework'ün virtual machine'i olan CLR (Common Language Runtime) mantığında çalışır. CLR, IL (intermediate language) adında ara bir dil vasıtasıyla uygulamayı hem dilden hem de işletim sisteminden bağımsız hale getirir. DVM'de de mantık aynı şekildedir, her ne kadar teoride gibi gözükse de Phyton'la yazdığınız kodu dalvik ile compile edebiliyorsanız android bu uygulamanızı başarıyla çalıştırabilecek demektir.
Android'de java kodu 2 kere compile edilmiş olur

Dex dosyasının bir diğer oluşturulma amacı hafıza ve işlemci hızının daha optimum seviyelere indirgenmesinin sağlanmasıdır keza Dalvik Virtual Machine'in geliştiricisi Dan Bornstein 2008 yılında yaptığı bir sunumda %100 işlemci kaynaklarını kullanan sıkıştırılmamış (uncompressed) bir java kodunun, sıkıştırıldığında %56'lara, Dalvik'li sıkıştırılmamış halinin ise %48'lere kadar indiğini ifade etmiştir. Yani arada neredeyse yarı yarıya bir fark vardır ki bu bir çok açıdan sınırlı olan mobil cihazlar için çok önemli bir ayrıntıdır. Bu iyileştirmeyi sağlamada en büyük etkenlerden biri -DVM'nin JVM'den en büyük farkı olarak da gösterilmesine neden olan- önceden de belirttiğimiz gibi DVM'nin stack based yerine register based olarak geliştirilmesinde yatmaktadır.

 Ayrıca JVM, aygıtın tipine bakmaksızın tüm cihazlar için bir tanedir (one size fits all). Yani bir mobil cihaz için kullanılan JVM ile mobil olmayan bambaşka bir cihaz (örneğin süper bilgisayar) için kullanılan JVM'de hiç bir farklılık yoktur. Bunun yanında DVM spesifik olarak mobil cihazlar için geliştirildiğinden JVM ile oluşan JAR dosyasından çok daha küçük ve mobil cihazın ihtiyaç duymayacağı (kaynak kod ile ilgili bir sürü gerekli gereksiz bilgi -verbose data- jar dosyasının içerisine yazılmaktadır) bilgilerin atılarak derlendiği bir dosya çıktısı oluşturmaktadır. Tüm bu teknik özelliklerin yanında Google'ı DVM'yi yazmaya iten nedenlerden bir diğeri de Oracle ile geçmişte yaşamış olduğu lisans problemleridir.

Hepimizin bildiği gibi Android bir açık kaynak kod geliştirme platformudur ve açık kaynak kod projelerinde, geliştiricilerin platformun tüm öğelerinden istedikleri miktarda erişim sağlaması birincil amaçtır. Bu amaç doğrultusunda, oldukça kapsamlı ve geniş kütüphaneler hazırlanarak kullanıcıların maksimum fayda edinebilecekleri bir altyapı oluşturulması önemlidir. Stack'te bulunan üçüncü kısım yani application framework (uygulama altyapısı) bu ihtiyaç için oluşturulmuştur. Application framework'te bulunan nesneler, geliştiricilerin native librarylere ulaşmasında kilit rol oynarlar. Bu nesneler, native'de bulunan kütüphane elemanlarıyla iletişime geçerek, normal şartlarda ancak taklalar atarak ulaşabileceğimiz bilgileri (örneğin bir koordinat bilgisi) son derece basit bir şekilde elde etmemize imkan verirler. Kısaca kodlarımızda kullandığımız neredeyse tüm nesneler burada bulunan nesnelerin soyutlandırılmış halleridir.

Yapının en üst bölümünde yazmış olduğumuz applicationlar (uygulamalar) bulunmaktadır. Application, bir veya birden çok apk (application package file) adı verilen paketlerin birleşiminden meydana gelir. Apk dosyaları .jar dosyaları gibi sıkıştırılmış dosyalardır. Android platformundaki yükleme ve dağıtım gibi işlevleri yerine getirdikleri için android'teki exe dosyaları şeklinde adlandırılabilirler. Apk dosyaları içerilerinde, yazmış olduğumuz kodları (ki bu kodlar apk oluşumu esnasında derlenerek dex formatına çevrilirler), resource, manifest, sertifika ve diğer uygulamayla ilgili tüm bilgileri barındırırlar.

Güvenlik
Android, Inter-Component Communication (Komponentler Arası İletişim) yapısına göre çalışır. Her komponent tipi sadece kendi tipindekilerle iletişime geçebilir ve yine her uygulama sadece kendisine atanan tekil bir kullanıcı kimliği aracılığıyla kendi linux prosesi üzerinde çalıştırılır. Bu sayede uygulamadan kaynaklanması muhtemel kusurlar nedeniyle oluşacak hasarlar önlenmiş olur. Yine her uygulama diğer uygulamalardan kendi izolasyonunu sağlamak amacıyla veritabanı ve diğer konfigürasyon arabirimlerinin belirtilmiş olduğu bir sandbox (çocuk parklarında çocukların güvenliğini sağlamak amacıyla oluşturulan kum havuzu gibi düşünebilirsiniz) dosya sistemine sahiptir. Bu izolasyon yapısı sayesinde, uygulama izin verdiğini belirtmedikçe asla diğer uygulamalar tarafından erişilemez. (Google mühendislerinden Dianne Hackborn'ün konuyla ilgili açıklaması: Android had a number of very different original design goals than iOS did. A key goal of Android was to provide an open application platform, using application sandboxes to create a much more secure environment that doesn’t rely on a central authority to verify that applications do what they claim. To achieve this, it uses Linux process isolation and user IDs to prevent each application from being able to access the system or other application in ways that are not controlled and secure. https://plus.google.com/105051985738280261832/posts/XAZ4CeVP6DC)

Her uygulama kendisine ait tekil bir kimlik bilgisi ile çalıştırılarak çevresinden izole edilmiş olur.

Aslında olan biteni şöyle bir analojiyle açıklayabiliriz; bir otel düşünün, içinde kalan her insan için ayrı bir oda tesis edilmiş olsun. Güvenlikten sorumlu bir otel görevlisi herkes için ayrı ayrı yetkilerinin yazılı olduğu bir yaka kartı hazırlıyor ve hazırlanan bu yaka kartlarının takılması zorunlu. İnsanlar birbirleriyle görüşmek istediğinde otel görevlisi görüşüp görüşemeyeceklerine bu yetki kartlarına bakarak onay veriyor ya da vermiyor. 

Android: otel
Otel odaları: tekil proses idler
İnsanlar: uygulama ve komponentler
Güvenlikten sorumlu otel görevlisi: referans monitör
Yaka kartları: permission labels (yetki etiketleri)

Android'de Başlangıç Mekanizması (Startup)
  1. Bootloader linux kernel'i yükler ve init prosesi başlatır. (/system/init)
  2. Init, çoğu /system/bin adresinde bulunan, android native programda implemente edilmiş bazı servisleri (usbd [usb bağlantılarını yönetmek için], adbd [android debug bridge-adb bağlantılarını yönetmek için], debuggerd [dump memory gibi debug proses taleplerini yönetmek için], rild [radio interface layer daemon - radyo ile bağlantıyı yönetmek için], vold, netd, installd, qemud) başlatır. Yeri gelmişken belirtelim unix'te (veya multitask çalışan diğer işletim sistemlerinde) init aşamasında arka planda çalıştırılmaya başlanan servislere daemon denir ve genelde sona koyulan d ile belirtilirler.)  
  3. Init, zygote (zigot) prosesi başlatır. Zygote classları yükleyen ve sanal makine için soketten gelecek istekleri dinleyen çekirdek prosestir. Aslında zygote'un yaptığı şey tüm uygulamaları çalışmaya hazır hale getirmektir şöyle ki; bir futbol takımında maç öncesi ilk 11'de oynasın oynamasın tüm futbolcular ısınır değil mi? Neden? Çünkü her an görev alma olasılığı mevcuttur. Aniden maçta oynanması istendiğinde hocam 2 dk bekleyin ben bir ısınıp geleyim dediğini düşünsenize. Zygote bu ısınma durumunu sağlar. Tüm uygulamaları, ileride çalışsın çalıştırılmasın farketmeksizin belli bir seviyeye kadar ayağa kaldırır (uykudan uyandırır, android uygulamayı çağırdığında gidip bir de onu uyandırmayla uğraşmaz).
  4.  Init runtime prosesi, runtime proses de service manager'ı başlatır. Servis manager, uygulamanın ihtiyaç duyacağı servisleri yönetecek (TelephonyService, LocationService, vb), gerektiğinde yeni yazılan servisin kaydedileceği, gerektiğinde ise kullanılması gereken servisin çekilebileceği kısacası servislerle ilgili tüm içeriğin yönetiminin sorumlu olduğu yapıdır.
  5. Zygote dalvik vm'yi çalıştırmaya başlarken bir diğer yandan da kendisine system server'ın çalıştırılması yönünde bir istek yollanır, zygote system service için yeni bir sanal makine (vm) kopyalayarak (forking) servisi çalıştırır. Burada forking denen hadiseyi biraz açıklayalım; forking bir diğer anlamıyla daemonizing, programın yeni bir child proses ile arka planda yürütülmesi demektir. Linux'ta yeni bir proses yaratılmak istendiğinde fork() kullanılır. Mitoz bölünme gibi düşünebiliriz keza bütün proseslerin atası Init'tir. Son olarak ekleyelim fork() çalıştırıldığında kopyalanan proses önceki prosesin kaldığı yerden devam eder. Biraz daha basite indirgeyelim; bir proses var, bu prosesten başka bir proses oluşturulması veya bir diğer deyişle child'i oluşturulması gerekiyor ki bunu ancak  fork() ile yapabiliyoruz. fork() geriye bir proses id dönüyor. Bu prosesId > 0 ise parent üzerinden işleme prosesId = 0 ise child üzerinden prosese devam ediliyor. 
  6. System server, surface flinger ve audio flinger'ın bulunduğu native system serverları başlatır. Native sistem sunucuları sistem manager'a IPC servis hedefleri olarak kaydedilir. Bu andan itibaren, çalışma zamanı prosesi (runtime process, service manager, system server (sistem sunucusu) ve native sunucuların hepsi android'in native programı olarak implemente edilmiş olur.
  7. Sistem servisi (system service) window manager, activity manager, location manager gibi tüm yönetim işlemini üstlenen android managed services'i başlatır. Android managed services service manager'a kaydedilir.
yeşiller native kod tarafını, maviler java tarafını göstermektedir.

26 Ekim 2011 Çarşamba

Failed to allocate memory: 8 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

[2011-10-26 22:31:49 - Emulator] Failed to allocate memory: 8
[2011-10-26 22:31:49 - Emulator] 
[2011-10-26 22:31:49 - Emulator] This application has requested the Runtime to terminate it in an unusual way.
[2011-10-26 22:31:49 - Emulator] Please contact the application's support team for more information.

Problemi gidermek için
(To resolve the problem)
  1. Eclipse > Window > Avd Manager
  2. Değişiklik yapacağınız virtual device'ı seçin ve Edit'e basın (Select virtual device which you want to edit and click Edit)
  3. Device Ram Size'ı 512 Mb olarak ayarlayın. (Set Device Ram Size as 512 Mb)

This Android SDK requires Android Developer Toolkit version 14.0.0 or above. Current version is 12.0.0.v201106281929-138431. Please update ADT to the latest version.

Sdk 14'ü kurarken aşağıdaki hatayla karşılaşmanız, ADT (Android Development Tools) ve SDK'nızın senkronize olmamasından kaynaklanmaktadır.
(This error happens when there is an incompatibility between ADT and SDK)


Problemi gidermek için
(To resolve the problem)
  1. Eclipse > Help
  2. Install New Software
  3. Work With bölümüne https://dl-ssl.google.com/android/eclipse/ yazıp enter'a basın. (Add https://dl-ssl.google.com/android/eclipse/ to Work With section and press enter) 
  4. Developer Tools'un listede belirdiğini gördükten sonra seçip Next'e tıklayın. (After developer tools appears on the list, check it and click Next)
  5. İşlem bittikten sonra Eclipse'i yeniden başlatın. (Restart eclipse once download finished)

23 Ekim 2011 Pazar

Android ile dataset dönen bir .NET webmethod'unu çağırmak. How to call a .NET webmethod which returns dataset using Android

Arkadaşlar merhaba,
(Hello guys)

10 yılı aşkın bir süredir profesyonel olarak yazılım sektörünün içinde olmam sebebiyle rahatlıkla söyleyebilirim ki gerçek hayat projelerinin neredeyse hiç birinde işler yazılımcının istediği şekilde ilerlemez.  Lafı uzatmadan android'de pek çok kişinin karşılaştığı ancak koca internette "1" tane bile örneğin bulunmadığı bu tür durumlardan birine değineceğim: Android ile .NET'te yazılmış dataset dönen bir webservise erişmek. Aslında hiçbirimiz dataset veya datatable gibi belli bir platforma (.NET) özel oluşturulmuş kompleks bir nesnenin webmethod gibi globallik arz eden bir yapıda dönüş değeri olarak kullanılmasını doğru bulmayacağız, bundan eminim. Fakat sizden böyle bir webmethod'la iletişime geçmenizin istendiğini ve bunun alternatifi olmadığını düşünün, ne yapacaksınız? Bu tamamen yanlış demeniz muhatabınız açısından ne kadar gerçekçi ve kabul edilebilir bir cevap olarak karşılanacak?

(As a professional developer who develops, plans and executes for 10 years, I can conveniently say without any doubt that in real life projects the things rarely comply with our plan. This example is one of them: Today we are going to mention about a case that there can not be found any tutorial on the internet about it, reaching a webservice which returns .NET dataset. I'm sure nobody wants to use these kind of complex objects like dataset or datatable -which runs only in .NET- as a parameter with a webmethod. However let's suppose that you are facing with a case that you need to call a webmethod like this and there is no any alternative way to do. Do they accept it as a reasonable answer when you say this is impossible? Or just think how they react when you say that.)

Gelin nasıl bu işin altından kalkacağımıza bakalım.
(Let's look how do we resolve that.)

Dediğimiz gibi bir webmethodumuz var ve dataset dönüyor. Önceki yazılarımızda 3. parti kütüphane kullanmadan (ksoap2 gibi) android ile bir webservise nasıl soap request atacağımızı ve geri dönüşü nasıl handle edeceğimizi anlatmıştık. Tüm bu bilgiler üzerine dataset dönen bir webmethod'un nasıl bir xml şemasına sahip olduğunu da bilirsek geriye kalan tek şey uygun yapıyı hazırlamak olacaktı ve öyle de oldu. Yeri gelmişken söylemekte fayda var bu tür envelope'un doğru oluşup oluşmadığını deneme amaçlı testlerde soapUI size büyük kolaylık sağlayacaktır.

(As I said there is a webmethod which returns dataset. I mentioned on previous tutorials that we need to know some key facts like how do we send soap request on android without using any third party library like ksoap2 and how do you handle return parameter which comes from AsyncClass on android -at the moment it supports turkish only-. In addition to this, if we know the xml schema of the soap response that comes from a webmethod which returns dataset then we can write the code very easily what we need.)

Bu noktadan sonra şu aşamada olduğumuzu varsayıyorum. (I suppose that we are here at this point)
  1. Soap envelope'umuz hazır,(we prepared the soap envelope)
  2. Webmethod'a başarıyla request attık. (we can call the webmethod without exception)
  3. Response'umuzu XML olarak aldık ve parse edilmeyi bekliyor. (we got the response as xml and it is waiting for parsing)
private DataTable parseAsDataTable(String x, String methodName){  
    //Datatable benim android için geliştirdiğim, .NET'teki DataTable nesnesinin içerdiği neredeyse bütün method ve özellikleri barındıran bir nesne. Siz bunun yerine 2 boyutlu bir object array kullanabilirsiniz. 
    //Datatable is an object that I created for android. It supports a lot of methods and properties of .NET datatable. But you can use 2 dimensional array instead of using this object -Object[row][column]-) 
    DataTable dt = new DataTable(Gonderim.this);
    try {
        String sMethodBaslangic = "<"+methodName+" xmlns=\"\">";
        String sMethodBitis = "";
        int artiBaslangic = sMethodBaslangic.indexOf(">");
        artiBaslangic++;
        int baslangic = x.indexOf(sMethodBaslangic);
        if(baslangic>-1){
            baslangic+=artiBaslangic;
            x = x.substring(baslangic);
            int bitis = x.indexOf(sMethodBitis);
            x = x.substring(0, bitis).replace(":", "_");
            x = new StringBuffer(x).insert(0,"<"+methodName+">").toString();
            x += "";        
            byte[] bytes = x.getBytes();
            ByteArrayInputStream bInputStream = new ByteArrayInputStream(bytes);


            // gelen string'i bir xml haline getiriyoruz.
            // converting string to xml
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            org.w3c.dom.Document doc = docBuilder.parse (bInputStream);
            org.w3c.dom.Element element = doc.getDocumentElement();
            NodeList nl = element.getChildNodes();
            boolean takenColumnNames=false;
            String[] columnArray;
            String columnNames = "";
            int rowIndex =  nl.getLength();
            int colIndex = nl.item(0).getChildNodes().getLength();
            Object[][] dataSource = new Object[rowIndex][colIndex];


            for (int i = 0; i < rowIndex; i++) {
                NodeList nlChildren = nl.item(i).getChildNodes();
                for (int j = 0; j < nlChildren.getLength(); j++) {
                    Node node = nlChildren.item(j);
                    if(!takenColumnNames){
                        columnNames+=node.getNodeName()+",";
                    }
                    dataSource[i][j] = node.getChildNodes().item(0).getNodeValue();
                }
                takenColumnNames = true;
            }


           columnArray = columnNames.split(",");
           dt.setColumns(columnArray);
           dt.setDataSource(dataSource);
        }
    } catch (Exception e) {
        Util.setException(Gonderim.this, e);
    }
    return dt;
    }
Anlaşılmayan bir nokta olursa çekinmeden mesaj atabilirsiniz. İyi çalışmalar.
(Please feel free to contact me if you have any questions)


mgNet Library kullanımı
örnek video



20 Ekim 2011 Perşembe

How to pass a complex object from one activity to another in Android

implement your class with Serializable. Let's suppose that this is your entity class:
import java.io.Serializable;
@SuppressWarnings("serial") //with this annotation we are going to hide compiler warning
public class Deneme implements Serializable {
public Deneme(double id, String name){
    this.id = id;
    this.name = name;
}
public double getId() {
    return id;
}
public void setId(double id) {
    this.id = id;
}
public String getName() {
    return this.name;
}
public void setName(String name) {
    this.name = name;
}
private double id;
private String name;
}
we are sending the object called dene from X activity to Y activity. Somewhere in X activity;
Deneme dene = new Deneme(4,"Mustafa");
Intent i = new Intent(this, Y.class);
i.putExtra("sampleObject", dene);
startActivity(i);
In Y activity we are getting the object.
Intent i = getIntent();
Deneme dene = (Deneme)i.getSerializableExtra("sampleObject");
that's it.