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

7 Ekim 2011 Cuma

AsyncTask class'tan dönen parametreyi handle etmek.

Merhaba

Android projelerinde genelde AsyncTask class, bir sub class olarak kullanıldığından o activity'e ait bir nesneyi set etmek çok fazla problem olmuyor. Bunun için OnPostExecute methodunda gerekli set işlemini yapmanız yeterli (konuyla ilgili bilgisi olmayanlar için güzel bir makale). Ancak bazı durumlarda network gibi UI thread üzerinde çalışmaya izin verilmeyen ancak bir çok activity tarafından kullanılacağı için inner class olarak tanımlanmasının da mantıklı olmadığı AsyncTask classlara ihtiyacımız olabilir. Bu durumda Context'i parametre olarak geçip AsyncTask class'ında o context'e ait gerekli field'ı ya da methodu set etmek mümkün gibi gözükse de bu çok kullanışlı olmayacaktır çünkü yine o methodu (veya field ya da context'i) cast edip içerisindeki set edeceğiniz field'ı manuel bir şekilde belirtmeniz gerekecektir.

Örneğimizde herhangi bir activity içerisinde soap request göndermek istenilen bir butona basıldığını varsayıyoruz.


public void onClick(View v) {
switch (v.getId()) {
case R.id.btnGonder:
String uri = "http://www.mustafaguven.com.tr/androidDenemeWebservice/Service1.asmx";
String soapAction = "http://tempuri.org/HelloWorld";
KeyValuePair[] parameters = new KeyValuePair[1];
parameters[0] = new KeyValuePair("a", "a parametresine atanan deger burada");
CallSoap soap = new CallSoap(uri, soapAction, parameters);
soap.setDataDownloadListener(new CallSoap.DataDownloadListener() {
public void dataDownloadedSuccessfully(String data) {
Log.e("GELEN DATA",data);
}
public void dataDownloadFailed() {

}
});
soap.execute("");
break;

default:
break;
}
}


Sıra geldi tüm bu istekleri hem dispatch edecek hem de işleyip geriye değer dönecek classımızı yazmaya. Hatırlayacağınız üzere yukarıda bir çok handikaptan bahsetmiştim, bu handikaplardan kurtulmak için kendi tanımlayacağımız bir Listener içeren AsyncTask class yazmak yeterli. Aşağıdaki class farklı activitylerden gelen soap isteklerini arkaplanda çalışan bir thread ile yönetip daha sonra bulduğu sonuç kümesini (result value) onPostExecute ile UI thread'e geçiyor. Buraya kadar yapılanlar, diğer asenkron classların kullanım metodolojisiyle zaten aynı. İşte tam bu noktada DataDownloadListener adlı listenerımız devreye giriyor. onPostExecute'da set edilen dataDownloadedSuccessfully methodu sayesinde bu class'ı call eden activity'de geri dönüş değeri kolaylıkla handle edilebiliyor.



package com.quadro.main.Util;

import org.apache.http.HttpEntity;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import BusinessObjects.KeyValuePair;
import android.os.AsyncTask;
import android.util.Log;

public class CallSoap extends AsyncTask {

private String methodName="";
private String uri="";
private String soapAction="";
private KeyValuePair[] parameters = null;
private String envelope = "";

DataDownloadListener dataDownloadListener;
   public void setDataDownloadListener(DataDownloadListener dataDownloadListener) {
       this.dataDownloadListener = dataDownloadListener;
   }

public CallSoap(String uri, String soapAction, KeyValuePair[] parameters){
this.uri=uri;
this.soapAction=soapAction;
this.parameters = parameters;
int iLastIndexOf = soapAction.lastIndexOf("/");
if(iLastIndexOf>0){
this.methodName=soapAction.substring(iLastIndexOf+1);
}
this.envelope = getSoapTemplate();
}

private String getSoapTemplate() {
String parameterXml="";
if(parameters!=null){
for (int i = 0; i < parameters.length; i++) {
KeyValuePair kv = parameters[i];
parameterXml+=String.format("<%s>%s",kv.getText(),kv.getValue(), kv.getText());
}

}
String envelope="<?xml version=\"1.0\" encoding=\"iso-8859-9\"?>"+
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"soap:encodingStyle=\"http://www.w3.org/2001/12/soap-encoding\">"+
"<soap:Body>"+
"<%s xmlns=\"http://tempuri.org/\">"+
"%s"+
"</%s>"+
"</soap:Body>"+
"</soap:Envelope>";
envelope = String.format(envelope, this.methodName, parameterXml, this.methodName);
return envelope;
}


@Override
protected String doInBackground(String... parameters) {
final DefaultHttpClient httpClient=new DefaultHttpClient();
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 10000);
HttpConnectionParams.setSoTimeout(params, 15000);

HttpProtocolParams.setUseExpectContinue(params, true);
HttpPost httppost = new HttpPost(uri);

httppost.setHeader("soapaction", soapAction);
httppost.setHeader("Content-Type", "text/xml; charset=iso-8859-9");

String responseString="";
try {
HttpEntity entity = new StringEntity(envelope, "iso-8859-9");
httppost.setEntity(entity);

ResponseHandler rh=new ResponseHandlerTr();
responseString=httpClient.execute(httppost, rh);
}
catch (Exception e) {
Log.e("exception", e.toString());
}

httpClient.getConnectionManager().shutdown();
return responseString;
}

@Override
protected void onPostExecute(final String responseString) {
       if(responseString != null){            
        dataDownloadListener.dataDownloadedSuccessfully(responseString);
       } else
        dataDownloadListener.dataDownloadFailed();
}

   public static interface DataDownloadListener {
       void dataDownloadedSuccessfully(String data);
       void dataDownloadFailed();
   }

}

Anlaşılmayan bir nokta olursa çekinmeden danışabilirsiniz.

İyi çalışmalar,
Mustafa Güven

23 Mayıs 2011 Pazartesi

Android Serisi 2. Bölüm - Activity, View, Toast kavramları

Merhaba arkadaşlar,
Android serimizin 2. bölümüyle tekrar beraberiz. Önceki bölümümüzde android'in temellerinden ve bu temel kavramlar içinde en önemlisi olan activity'nin yaşam döngüsünden bahsetmiştik. İkinci bölümde activity, view ve toast (messagebox) kavramlarıyla android ile program geliştirmeye girmiş olacağız.

Örnek bir uygulamanın yerini başka hiçbir şey tutamaz düşüncesiyle konuyu uzatmadan tüm bu kavramları tam manasıyla anlayabileceğimiz bir senaryo geliştiriyoruz. Senaryomuza göre kullanıcı, yazacağımız bir ekran ile uygulamaya login olmaya çalışacak, girilen bilgiler kontrol edilip kullanıcıya mesaj dönülecek.

Dilerseniz öncelikle login ekranımızın tasarım kısmını oluşturalım ve bu sayede de viewlara girmiş olalım. Hemen aşağıdaki gibi bir android projesi oluşturarak işe başlıyoruz.


Evet projemizi oluşturduk, artık tasarım ekranımızı yapmaya yani viewlarla tanışmaya hazırız. Yazılım literatüründe Control olarak bildiğimiz yapılar Android dünyasında View ismiyle anılıyor. Edittext (textbox), Textview (label), button, checkbox, radiobutton, vs... gibi bir çok view bulunmakta. http://www.basic4ppc.com/forum/basic4android-getting-started-tutorials/7319-android-views-controls-dialogs.html bağlantısına tıklayarak viewları tek tek inceleyebilirsiniz. Biz örneğimizde 2 edittext, 2 textview, 2 LinearLayout ve 1 tane de button kullanacağız.

Android'de bir activity'nin tasarım ekranını xml uzantılı layout dosyalarıyla gerçekleştirebiliyoruz. (Bunun yanında dilerseniz codebehind'dan da layout'unuzu oluşturabilirsiniz). Bu dosyalar sol taraftaki resimde de belirtildiği üzere Proje > res > layout dizininde bulunmaktadırlar. Dilerseniz tam bu noktada kısaca Res klasöründen bahsedelim. Res klasörü default olarak 5 klasör içerir. Bunlardan ilk üçü (drawable klasörleri) ikonların hangi çözünürlük tipine göre çizileceğini belirtir. Eğer yaptığınız uygulama ikon, resim, vb içeriyorsa ve bu uygulama android işletim sistemine sahip tüm aygıtlarda çalıştırılmak isteniyorsa o zaman tüm dpi türlerine göre bu dosyaları oluşturup ilgili klasörlerine atmak zorundasınız. Amacınız basit bir uygulama ise hepsini bir klasörde toplamanız da mümkün. Android ile IPhone arasındaki en büyük farklılıklardan biri android'de fragmantasyonun çok fazla olması. Bu da bu tür konularda uygulama geliştiriciler için büyük bir dezavantaj oluyor. Bu 3 drawable klasöründen hemen sonra Layout ve Values klasörlerini görüyoruz. Layout, az önce de belirttiğimiz gibi tasarım dosyalarının tutulduğu klasör. Values ise aslında yeri geldiğinde daha detaylıca değineceğiz ancak şimdilik hakkında bilmeniz gereken şey localization'dan tutun da hardcoded kötü kodlamanın önüne geçmenize imkan verecek kadar kullanışlı xml dosyalarımızın barındığı kısım olmasıdır.


main.xml dosyasına tıkladığınızda karşınıza bir tasarım editörü Graphical Layout çıkacak. Ancak biz tasarımımızı yaparken graphical layout yerine -bence- daha anlaşılır ve esnek olan (graphical layout'un hemen sağında göreceğiniz) main.xml tab sekmesini kullanacağız.

Aşağıdaki kodu main.xml ekranındaki default gelen kodla değiştirin.


<?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">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="50px"
android:weightSum="3"
android:layout_margin="10dp"
>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="2"
android:text="Kullanıcı Adı: "
android:gravity="right"
/>
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"
android:id="@+id/txtUsername"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="50px"
android:weightSum="3"
android:layout_marginTop="-10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="2"
android:text="Şifre: "
android:gravity="right"
/>
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"
android:id="@+id/txtPassword"
android:password="true"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="50px"
android:weightSum="3"
android:layout_marginTop="-10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="2"
android:text=""
/>
<Button
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"
android:id="@+id/btnGiris"
android:text="Giriş"
android:layout_gravity="right"
/>
</LinearLayout>
</LinearLayout>


Önce tüm ekranı kaplayacak bir LinearLayout daha sonra da onun içine satır mantığını oluşturacak diğer LinearLayoutları koyduk. Aralarındaki tek fark en dıştakinin orientation=vertical, diğerlerinin horizontal olması. LinearLayout'ı HTML elemanlarından birine benzetmemiz gerekirse en uygunu DIV olacaktır sanırım. Sonrasında sırasıyla TextView (label) ve EditText (textbox) kontrollerimizi ekledik. Atamış olduğum attributeleri değiştirerek nasıl çalıştığını daha iyi anlarsınız diye düşünüyorum. Burada dikkat etmeniz gereken ID atamasının nasıl yapıldığı keza kontrole bu atamış olduğumuz ID ile ulaşacağız.

Sonuç olarak ekran görüntümüz aşağıdaki gibi olmalı.


Artık kod tarafına geçebiliriz. Proje menümüzdeki src klasörünün altında bulunan Login.java isimli classımızı (activity) açıyoruz ve aşağıdaki kodları ekliyoruz.

package com.denemeapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class Login extends Activity {

private final String loginName="mustafaguven", loginPassword="123456";
private EditText txtUsername, txtPassword;
private Button btnGiris;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); //main.xml isimli dosyamızın çizilmesi gerektiği belirtildi.

//kontrollerimizi yakaladık
txtUsername = (EditText)findViewById(R.id.txtUsername);
txtPassword = (EditText)findViewById(R.id.txtPassword);
btnGiris = (Button)findViewById(R.id.btnGiris);

//button'ın onclick anında işlem yapmak için bir listener set ettik
btnGiris.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
//kullanıcı adı ve şifre kontrolümüzü yaptık
if(txtUsername.getText().toString().contentEquals(loginName)
&& txtPassword.getText().toString().contentEquals(loginPassword)){
//kullanıcıya mesaj vermek için Toast nesnesini kullandık
Toast.makeText(getApplicationContext(), "Hoşgeldiniz Sayın " + txtUsername.getText().toString(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Yanlış kullanıcı adı veya şifre", Toast.LENGTH_SHORT).show();
}
}
});
}
}


ve sonuç...



Dosya ismi: Activity_View_Toast.rar
Dosya Boyutu: 41.66 KB

21 Mayıs 2011 Cumartesi

Android Serisi 1. Bölüm - Android'in Temelleri

Merhaba, uzun zamandır baştan sona android ile ilgili bir set hazırlayıp, siz değerli yazılımcı arkadaşlarımla paylaşmak istiyordum. Ancak yoğun çalışma temposu, bilgisayarımın dandikliği ve bir şeyler yazmaktan erinecek kadar tembel birisi olmam bu isteğimin bir türlü gerçekleşmemesine neden oldu.

Aslında görsel bir paylaşımda bulunmak yerine makale hazırlamak -bence- son derece yanlış bir tercih. Görsel bir eğitimin yerini başka hiçbir şeyin tutmayacağını düşünüyorum. Ancak şu an bilgisayarım bir nedenden ötürü ses kaydı yapamıyor. Bundan dolayı -en azından şimdilik-, makalelerle idare etmemiz gerekmekte. Bu paylaşımı, çözüm bulur bulmaz videolarla daha katılımcı ve hızlıca anlaşılır bir şekle sokacağımdan emin olabilirsiniz. Neyse lafı çok uzattık dilerseniz hemen konumuza girelim.

ANDROID'İN TEMELLERİ:

Bir Android Uygulaması Temelde Hangi Kavramlardan Oluşur?
  1. Activity: Uygulamadaki her bir ekran aslında bir activity'dir ve Activity class'ından extend edilir. .NET tarafındaki ASPX dosyasının Android'deki karşılığıdır diyebiliriz. Activityler, View nesnelerini kullanarak işlem gerçekleştirirler. 
  2. Service: Arkaplanda çalışırlar ve bu esnada Activitylerle veya datasource gibi diğer nesnelerle etkileşimde bulunabilirler. 
  3. Content Provider: Uygulamalar arası data paylaşımını sağlamak için kullanılır. Kısaca paylaşılabilir veri depolarıdır diyebiliriz.
  4. Intent: Activityler, serviceler, vs gibi nesneler arası mesajlaşmayı sağlayan yapılardır. (Activityler ile birlikte en çok kullanılan nesnelerden biridir)
  5. Broadcast Receiver: Uygulamayla ilişkili intentlerin dinlenmesine imkan sağlayan yapılardır.

Bir Android Uygulamasının Yaşam Döngüsü Nasıldır?
  1. Aktiftir: An itibariyle kullanıcı tarafından kullanılmakta olan uygulamadır.
  2. Aktif değildir ancak "Visible" konumdadır: O an aktif olarak kullanılmamakla beraber memory'de yaşamaya devam etmektedir. Transparan konumdadır diyebiliriz.
  3. Visible değildir ancak "Arkaplandadır": An itibariyle hiçbir servisinin kullanılmadığı uygulamadır. Android tarafından otomatik olarak dispose edilirler.
  4. Pasiftir (Inactive): Uygulama, Android tarafından kill edilmiştir.
Bu döngüyü kafada daha iyi canlandırabilmek için iskambil kağıtlarını tutan bir el örneğinin çok da yanlış olmayacağını düşünüyorum. Evet lütfen gözünüzün önüne aşağıdaki resimdeki gibi iskambil kağıtlarını tutan bir el getirin. Bu resme göre bir analoji kuracak olursak diyebiliriz ki kupa as aktif olan activity'dir. Diğerleri visible konumdadırlar. Maça kızı kupa as'ın yerine geçtiğinde maça kızı aktif konuma gelmiş olacaktır, last active activity (en son aktif olan activity) ise kupa as olmuş olacaktır. Bu şekilde düşünmeye devam edecek olursak belli bir süre sonra Android tarafından ilk kill edilecek uygulama kupa papaz olacaktır. (bknz: last-seen-first-killed pattern, http://mobworld.wordpress.com/2010/07/05/memory-management-in-android/)

 


ACTIVITY YAŞAM DÖNGÜSÜ
Activityleri kullanmadan önce activitylerin nasıl bir yaşam döngüsüne sahip olduklarını bilmemiz çok önemli. Yaşamı boyunca hangi eventlerin hangi zamanlarda raise edildiği ve bu esnada uygulamanın ne aşamada olduğunu bilmemiz uygulama geliştirme aşamasında bizleri çok daha bilinçli hareket etmeye sevk edecektir.

Bir activity'nin yaşam döngüsü aşağıdaki gibidir.


Aslında yukarıdaki şema herşeyi son derece açık bir şekilde anlatıyor. Ancak yine de biz üstünden bir özet geçelim. Şimdi, siz bir uygulamayı çalıştırmak için tetiklediğinizde bilmelisiniz ki önce onCreate event'i raise edilmiş olacaktır. Yani activity initiliaze edilecektir. Buradan sonra onStart eventi fırlatılır. onStart eventinde activity artık visible'dır ancak hala aktiflik söz konusu değildir, UI bilgisi burada uygulanır. onResume eventinde ise activity artık aktiftir. onResume'dan sonra bu şemada gösterilmeyen onSaveInstanceState eventine girilir. Bu evente gelen Bundle (web uygulamalarındaki session veya hashtable nesnesinin benzeri bir yapıdır), eğer proses uçurulmuşsa (kill edilir) veya restart edilmişse onCreate eventine UI state değişikliklerini gönderir. Daha sonra onPause eventine geçilir. Activity önplanda değil askıya alınmıştır yani artık aktif değildir. onStop eventinde activity kill edilmek için beklemekte, onDestroy'da onCreate eventinde initiliaze edilmiş tüm kaynaklar, tüm connectionlar (network ve database connectionları gibi) dispose edilmektedir. Bazı durumlarda onDestroy methodu call edilmeden de proses terminate olabilir.

Artık ne yaptıklarını bildiğimize göre hangi aşamada hangi methodu kullanmamız gerektiğini de söyleyebiliriz.

onCreate; UI ve class değişkenlerini oluştururken, kontrollere data bağlarken ve service veya thread oluştururken

onStop: animasyonları, threadleri, sensör dinleyicilerini (sensor listeners), GPS aramalarını, zamanlayıcıları, servisleri durdururken

onStart: UI visible olduğunda onStop eventinde kapatılmış olanları tekrar çalıştırmak amacıyla (Ilk açılış haricinde)

onPause: Broadcast Receiverları durdururken

onResume: Broadcast Receiverları tekrar çalıştırırken kullanın.