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

6 Ekim 2011 Perşembe

3. party kütüphane kullanmadan (ksoap2) Android ile soap request işlemleri. Soap request on Android without using any third party library like ksoap2


Uzun bir aradan sonra tekrar merhaba.
(Hello again after a long time)

Büyük bir çoğunluk, android ile webservice'e bağlanmak istediğinde ksoap2'yi kullanıyor. Bunun zaman zaman dezavantajlarıyla karşılaşmış biri olarak size nasıl 3. party kütüphane kullanmadan kendi soap requestiniz ile bir webservice'e bağlanacağınızı açıklayacağım.
(A large majority of the android developers are using ksoap2 library when they want to connect to a webservice. As a man who encountered with disadvantages and advantages of these kind of libraries I will explain you how to send soap request without using any third party library -like soap2- on android)

HelloWorld adında bir webmethodumuz var, String türünde a değişkeni alıyor ve geriye a parametresinin değerine "türkçe karakterlerle doğru çalışıyor:" ibaresini ekleyerek dönüyor. Bilmeyenler .NET ile nasıl webservice yapılacağı konusunu anlatan ilgili örneğe buradan ulaşabilirler.
(There is a webmethod called HelloWorld. It takes a String as a parameter named a and returns with adding an expression which is "türkçe karakterlerle doğru çalışıyor" (which means it works correctly with turkish characters) to the return value. If you don't know how to create a webservice on .NET you can see the example by clicking here.)

[WebMethod]
public string HelloWorld(string a){
       return "türkçe karakterlerle doğru çalışıyor:  " + a;
}



Artık android tarafına geçebiliriz.
(Now we can go to the android side)

private void soapDeneme() {
  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>"+
    "<HelloWorld xmlns=\"http://tempuri.org/\">"+
    "<a>abcçdefgğhıijklmnoöprsştuüvyz</a>"+
    "</HelloWorld>"+
    "</soap:Body>"+
    "</soap:Envelope>";
  String url = "http://www.mustafaguven.com.tr/androidDenemeWebservice/Service1.asmx";
  String soapAction = "http://tempuri.org/HelloWorld";
  new CallWebService().execute(url,soapAction,envelope); 
 }

Yukarıda en çok dikkat edilmesi gereken şey request'imizi bir AsyncTask içerisinde call etmemiz. Aslında bu 3.0'ın altındaki herhangi bir versiyonda belki problem yaratmayabilir ancak honeycomb ile birlikte android bu tür işlemleri UI thread üzerinde yapmanıza izin vermeyecektir. (NetworkOnMainThreadException, Designing For Responsiveness) Bunun için ya bir handler içerisinde call işlemini gerçekleştirmelisiniz ya da bizim bu örnekte yaptığımız gibi asynctask içerisinde.
(The most noticeable think above is that you must use another thread to call a webservice if the application which you are developing runs on the honeycomb (3.0) because UI thread does not support for this action otherwise it will be throwed out an exception called NetworkOnMainThreadException. for further information: Designing For Responsiveness)

 class CallWebService extends AsyncTask<String, String, String>{

  @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(parameters[0]);
   httppost.setHeader("soapaction", parameters[1]);
   httppost.setHeader("Content-Type", "text/xml; charset=iso-8859-9");

   String responseString="";
   try {
    HttpEntity entity = new StringEntity(parameters[2], "iso-8859-9");
    httppost.setEntity(entity);
    ResponseHandler rh=new ResponseHandlerTr();
    responseString=httpClient.execute(httppost, rh);
   }
   catch (Exception e) {
    Log.e("hata", e.getMessage());
   }
   httpClient.getConnectionManager().shutdown();
   return responseString;
  }
 }
(Yukarıda HttpEntity entity = new StringEntity(parameters[2], "iso-8859-9"); bölümünde charset'inizi "iso-8859-9" şeklinde belirtmezseniz webservice tarafında türkçe karakterlerin hepsi düzgün çalışmayacaktır.)
(You need to describe the encoding type as above to display turkish characters correctly)

Gelen response'da encoding yapmak için bu response'u handle etmemiz gerekli bunun için BasicResponseHandler class'ından extend ettiğimiz ResponseHandlerTr class'ını yazıyoruz.
(We are creating ResponseHandlerTr class which extends BasicResponseHandler to handle the response and encode it)

package com.quadro.main.Util;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.util.EntityUtils;

public class ResponseHandlerTr extends BasicResponseHandler {
 @Override
 public String handleResponse(HttpResponse response) throws HttpResponseException, IOException      {
    StatusLine statusLine = response.getStatusLine();
         if (statusLine.getStatusCode() >= 300) {
             throw new HttpResponseException(statusLine.getStatusCode(),
                     statusLine.getReasonPhrase());
         }

         HttpEntity entity = response.getEntity();
         return entity == null ? null : EntityUtils.toString(entity, "iso-8859-9");
 }
 
}
Webservice'ten gelen cevap:
(The response comes from the webservice)



Anlaşılmayan bir nokta olursa çekinmeden danışabilirsiniz.
(Please feel free to contact me if you have any questions)

17 Ağustos 2011 Çarşamba

Eclipse intelisense'in çıldırtan yavaşlığına çözüm - Solution for maddening slow performance of eclipse intelisense

Merhaba,

Eclipse ile android uygulaması geliştirirken zaman zaman intelisense'in uzun süreli donmalarına maruz kalmışsınızdır. Bunun nedeni, bug raporunda belirtildiği üzere dizi hesaplamalarındaki algoritma değişikliğiymiş. Bu can sıkıcı problemden kurtulmak için (eclipse 3.7 versiyonu) linkteki dosyayı indirip eclipse içindeki plugins klasörüne atmanız yeterli.

ENG: You may have noticed that eclipse just freezes when you are typing the letters followed by the (.) period while using auto completion feature with Android SDK plugin. This is caused by the change of algorithm which calculates the ranges as described in this bug report. This has been fixed for eclipse 3.7 version, but since it is still in milestone releases, it does not have support for Android SDK. Applying the fix is easy, you just replace org.eclipse.jdt.core_3.6.1.v_A68_R36x.jar inside your Eclipse plugins folder with the file provided in the above message. Here is the link:

http://adt-addons.googlecode.com/svn/patches/org.eclipse.jdt.core_3.6.1.v_A68_R36x.zip

İyi çalışmalar

15 Ağustos 2011 Pazartesi

StateListDrawable kullanarak kendi checkbox'ınızı yaratın. Create your own checkboxes with using StateListDrawable.

Merhaba,

Üzerinde çalıştığım bir projede dinamik oluşturduğum checkboxların theme üzerinde gözükmemesi ancak checked edildikten sonra fark edilmesi problemiyle karşılaştım. Bu sorunu gidermek için theme'i değiştirebilirdim ancak bu pek işime gelmedi, bende statelere göre checkboxların render edildiği imageleri değiştirme yoluna gittim. Umarım işinize yarar.
(Last day I came across a problem while using checkbox object. After that i decided to create my own custom checkbox then this morning i did it and wanted to share with you guys. With the code as can be seen below you can create your own custom checkbox too. What you need is paying some attention to StateListDrawable concept. I hope it will be remarkable detail for you.)

CheckBox myCheckbox = new CheckBox(this);
StateListDrawable stateList = new StateListDrawable();
int statePressed = android.R.attr.state_pressed;
int stateChecked = android.R.attr.state_checked;
stateList.addState(new int[] {-stateChecked}, new BitmapDrawable(BitmapFactory.decodeResource(getResources(), R.drawable.unchecked)));
stateList.addState(new int[] {stateChecked}, new BitmapDrawable(BitmapFactory.decodeResource(getResources(), R.drawable.checked)));
stateList.addState(new int[] {statePressed}, new BitmapDrawable(BitmapFactory.decodeResource(getResources(), R.drawable.pressed)));
myCheckbox.setButtonDrawable(stateList);

İyi çalışmalar.

12 Ağustos 2011 Cuma

java.lang.StackOverflowError hatasını neden alırsınız?

Merhaba,

Bu hatayı deadlock durumlarında alırsınız ve bunun 2 nedeni olabilir.

1. Eğer bir method kendi kendini sonsuz kere çağırıyorsa (Recursive methodlar)
private int getX(int b){
    b += 1;
    ...
    ...
    return getX(b);
}

2. Ya da birinci method ikinci methodu çağırırken ikinci method içindeki herhangi bir satır birinci methodu çağırıyorsa.

İyi çalışmalar


11 Ağustos 2011 Perşembe

Caused by: java.lang.IllegalStateException: (conn# 0) already closed - close() was never explicitly called on database

Merhaba,

Başlıkta 2 farklı hata görmektesiniz. SqliteOpenHelper'dan extend ettiğiniz db class'ındaki tüm sql işlemlerinde cursor'ı kapatmış olsanız dahi -ki bu bizzat benim yaşadığım problemdi- close() was never explicitly called on database hatasını alabilirsiniz. İlginç olan şey debug ederken bu hatayla karşılaşmamanız ve hatta hata geliyor olsa bile (logcat'ten gözlemleyebilirsiniz) uygulamanın çalışmaya devam ediyor olması. Bu problemi çözmek için cursor.close() işlemi sonrası db'yi de close ettiğimde ise doğal olarak tekrar tekrar db'yi açmamız gerekliliğiyle karşılaştım. Sonuç olarak bunların ikisini de bertaraf edecek bir çözümü sizinle paylaşacağım. Genellikle onDestroy eventinde cursor'ın ve/veya db'nin close edilmesi tavsiye edilmiş. Ancak gitmekte olduğunuz class her zaman bir activity olmayabilir. Ben hem daha farklı hem de hepimizin neredeyse her uygulamada kullandığı çakma bir factory pattern ile bu soruna çözüm getirebileceğimizi göstereceğim.

Anlaşılmayan nokta olursa yorum yazabilirsiniz.

Aşağıda ürünler ile ilgili db işlemlerini gerçekleştiren bir class'ımız var. Herhangi bir aktivitide yapılan bir işlemde ürün bilgisi getirilecek varsayıyoruz.

public class UrunMgr {
    private static UrunMgr instance;
    Context context;
    private SQLiteDatabase db;

    // constructor
    private UrunMgr(Context context) {
        this.context = context;
        DBHelper helper = new DBHelper(context);
        this.db = helper.getWritableDatabase();
    }

    public static UrunMgr getInstance(Context context) {
        if(instance == null) {
            instance = new UrunMgr(context);
        }
        return instance;
    }

    public Urun getUrunByTip(String urunTipi)
        Urun oUrun;
        Cursor cursor;
        try {
        cursor = db.query(MyConstants.TBL_URUNLER, new String[] {
"_id", "Urun", "Ad", "Ord"}, "Urun = '" + urunTipi + "'", null, null, null,
null, null);

        cursor.moveToFirst();
        if (!cursor.isAfterLast()) {
            do {
                oUrun = populateList(cursor);
            } while (cursor.moveToNext());
        } else {
            oUrun = null;
        }
        cursor.close();
        } catch (SQLException e) {
            Log.e("UrunMgr - getUrunByTip() db hatası alındı: ", e.toString()); e.printStackTrace();
        }

        return oUrun;
    }
}


cok basit anlamda başka bir activity'den UrunMgr class'ı içindeki herhangi bir methodu call etmek için;

Urun urun = UrunMgr.getInstance(this).getUrunByTip("1");

Yukarıdaki örnekte, singleton'a benzer bir yapı ile (çakma factory pattern dedik) constructor'ın private'a çekildiğini, bu sayede tüm çoklama işlemlerinin getInstance methodu üzerinden yapıldığını, dolayısıyla tekrar tekrar UrunMgr class'ının yaratılmadığını (multi-thread hariç) basitçe anlatmaya çalıştım. Dediğim gibi anlaşılmayan bir nokta olursa yorum bölümünden ulaşabilirsiniz, iyi çalışmalar.

9 Ağustos 2011 Salı

Eclipse Shortcuts (Kısayolları)

http://www.allapplabs.com/eclipse/eclipse_shortcuts.htm adresinden alıntılanmıştır. Sık kullanabileceğinizi düşündüğüm kısayolları türkçeye çevirip koyuyla belirttim. (Ctrl+Shift+L ile hızlıca kısayollar menüsüne ulaşmanız da mümkün.)

vigational Shortcuts
F10Main menu (Ana Menü)
Shift F10Context menu (Sağ Click)
Ctrl F10View menu (Breakpoint koyulan alanda sağ click)
Workspace navigation
F12Activate editor (Kod alanını aktifleştir)
Ctrl+Shift+WSwitch editor (Editörler arası geçiş)
Ctrl F6Next editor (Sonraki editör) 
Ctrl Shift F6Prev editor (Önceki editör)
Ctrl F7Next workspace (Sonraki workspace)
Ctrl Shift F7Prev workspace (Önceki workspace)
Ctrl F8Next perspective (Sonraki perspektif [Java-Debug hızlı geçiş için])
Ctrl Shift F8Prev perspective (Önceki perspektif)
Alt LeftBack (Editörler arası sola doğru dolaşma)
Alt RightForward (Editörler arası sağa doğru dolaşma)
Files
Alt Shift SSource (veya sağ click source)
Ctrl Shift RJump to file (Dosyaya atla)
Ctrl NNew file (Yeni dosya)
Ctrl SSave file (Dosyayı kaydet)
Ctrl Shift SSave all files (Hepsini göster)
Ctrl F4Close file (Dosyayı kapat)
Ctrl Shift F4Close all files (Tümünü kapat)
Find
Ctrl LGoto line (Satıra git)
Ctrl FFind (Bul)
Ctrl JIncremental find (İleriye doğru tek tek bul)
Ctrl Shift JIncremental find prev (Geriye doğru tek tek bul)
Ctrl KFind next (Sonrakini bul)
Ctrl Shift KFind prev (Öncekini bul)
Ctrl HSearch workspace (Ara)
Ctrl (dot)Navigate next 
Ctrl (comma)Navigate prev
Java navigation
F3Goto declaration
Ctrl Shift UFind references in file (Açık ekran içinde nesnenin referanslarını bul)
Ctrl Shift GFind references in workspace (Tüm proje içinde nesnenin referanslarını bul)
Ctrl GFind declarations in workspace
Ctrl Shift PNavigate to matching bracket/brace
Ctrl TPopup type hierarchy
Ctrl Shift TOpen Type
Ctrl OOutline of current source
Ctrl F3Outline of current cursor position
Ctrl Shift ArrowJump beetween methods up or down
F2Show Javadoc
F4Show hierarchy
Ctrl Alt HOpen call hierarchy
General editing
Alt ArrowMove line(s) up or down 
Alt Shift UpExpand selection to enclosing element (Kapsandığı elemana kadar seçimi genişlet)
Alt Shift RightExpand selection to next element
Alt Shift LeftExpand selection to previous element
Alt Shift DownRestore previous selection (Önceki seçime dön)
Ctrl Alt ArrowDuplicate line(s) up or down (Alt satıra kopyala)
Shift EnterInsert line below (Satır atla devam et)
Ctrl Shift EnterInsert line above (Alta satır ekle, atlamadan devam et)
Ctrl DDelete line (Satır sil)
Ctrl Shift QToggle Quick Diff
Ctrl Shift YConvert to lowercase 
Ctrl Shift XConvert to uppercase
Java editing
Alt Shift URemove occurrence annotations (Notasyonları kaldır)
Ctrl 1Quick fix (works even when there are no errors
Ctrl Shift MAdd import (Namespaceleri ekle)
Ctrl Shift FReformat (Yeniden formatla)
Ctrl Shift OOrganize Imports (Kullanılmayan namespaceleri kaldır, gerekli olanları ekle)
Ctrl /Comment
Ctrl \UnComment
Ctrl Shift SpaceParameter hints (Değişkenin alabileceği değerler)
CtrlHyperlink identifier
Ctrl ICorrect indentation
Shift SpaceIncremental content assist
Debugger
F5Step into (Methodun içine girerek devam et)
F6Step over (Satır satır devam et)
F7Run to return (Sonraki breakpoint'e git)
F8Resume (Devam et)
F9Relaunch last (Sonuncuyu yeniden aç)
F11Run/debug last 
Ctrl F11Run (Çalıştır)
Ctrl Shift BToggle breakpoint (Breakpoint koy/kaldır)
Ctrl DDisplay
Ctrl QInspect (İncele)
Ctrl RRun to line (Satıra git)
Ctrl URun snippet
Refactoring
Alt TRefactoring menu
Ctrl Shift ZUndo refactor
Ctrl Shift YRedo refactor
Alt Shift RRename
Alt Shift VMove
Alt Shift IInline
Alt Shift MExtract method (Method oluştur)
Alt Shift LExtract local (Field oluştur)
Alt Shift CChange method signature (Method dönüş tipini değiştir)
Misc
F5Refresh
F1Infopop
F2Show resizeable hover