13 Ağustos 2018 Pazartesi

Android Fragment Caching

Uygulamanızda birden çok fragment olduğunu ve aynı fragmentları tekrar tekrar kullandığınızı varsayın. Yapmak istediğimiz ise kullanılan fragmentları cachelemek ve tekrar oluşturma maaliyetinden kurtulmak, bellek yönetimiyle uğraşmamak. Bunun Fragment API da bulunan için hali hazırda bir sınıf var. FragmentManager.

NEDEN

Örnek olarak instagram uygulamasında 5 tane tab var ve her sayfa kendi içinde scroll edildiğinde aynı data ve aynı pozisyonda kaldıkları yerden devam edebiliyor. Bizim yapmak istediğimizde bu şekilde hem data hem de UI hiç bozulmadan kaldığı yerden devam edebilecek bir yapı inşa etmek. 

NASIL

1.Adım

add methodunu kullanarak sürekli yeni fragment oluşturmuş oluruz. Yani ne datayı ne de UI saklamış oluruz. Yani yapmamız gereken replace i kullanmak. 

FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment frStack = manager.findFragmentByTag("Tag");


if (frStack == null) {
    transaction.replace(R.id.main_fl, frNew, "Tag");
} else {
    transaction.replace(R.id.main_fl, frStack);
}
transaction.addToBackStack(null);
transaction.commit();


Yukarıda yapmak istediğimiz yeni oluşturulacak olan fragment stackte var mı(findFragmentByTag). Eğer yoksa yeni fragmentı ekle varsa eski mevcutta oluşturulmuş olan fragmentı.

2.Adım

Ama replace ile çağırdığımızda da göreceksiniz ki oncreateview onresume … methodlarına tekrar girecek (bakınız fragment lyfecycle ). Yapmamız gereken ne oncreateview() veya onresume methodunda çağırdığınız fonksiyonları tekrardan çağırmadan sadece rootview i return etmek.

private View view;
private boolean isLoaded;

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup cntr,Bundle savedInstanceState) {

    if (isLoaded == false) {
        view = inflater.inflate(R.layout.fragment_scroll, cntr);
...
        service call, findbyViewIds ...
...
        isLoaded = true;
    }

    return view;
}

2.adımı farklı yöntemlerle de yapabilirsiniz! Önemli olan sayfa ilk açıldığında yapılması gereken işlemlerin -servis çağrısı, setonclick işlemleri gibi- replace de yapılmaması. 

SONUÇ

Aşağıdaki ekran görüntüsünden tüm fragmentların durumlarını koruduğunu görebilirisniz. Örnek proje linkte mevcut.
DİPNOT1

Burada show()-hide() yolunu da izleyebilir, caching neden gerekli diyebilirsiniz. Ama bu yöntem tüm fragmentlarınızı ayakta tutar yani hepsi aslında onresume da kalır. Replace ettiğiniz fragmentler ise sırasıyla pause stop… methodlarından geçer yani container dan tamamen o fragmentı sökmüş olursunuz. Replace etmek “hide ardından show etme” işlemi göre daha maaliyetli olmasına karşın memory den kazandırma gibi önemli bir kozu var. Hide ettikten sonra ayakta kalan tüm fragmentlarınızın heap te kaldığını ve outofmemory hatası alabileceğinizi göz önünde bulundurmalısınız. 

DİPNOT2

transaction.addToBackStack(null);

addtobackstack methodu yaptığımız replace işleminin stackte bir karşılığı olduğunu belirtiyor. Aksi halde tablar arası geçişte cachte ten hiçbirşey gelmediğini ve geri butonunda tüm fragmentlarınızın tek seferde kaybolduğunu göreceksiniz. 

29 Haziran 2018 Cuma

Android Kod Optimizasyonu 

Optimizasyon adından da anlaşıldığı üzere mevcutta var olan, çalışan bir şeyi daha iyi hale getirmektir. Kod optimizasyonuna ise kullanılmayan kaynakların (unused strings,image, layout,code) silinmesiyle başlayabilirsiniz. ve bunu yapan android studio’ya entegre bir tool zaten mevcut.
bkz: LİNT

YARARI NEDİR

“Ne optimizasyonu şimdi, zaten süre sıkışık, yetişmesi gereken onca işin arasında kim uğraşacak ”diyebilirsiniz. Normal. Dedik biz de yalan yok. Ama şöyle düşünün ki ;
  • Kodunuzu potansiyel oluşabilecek buglardan arındıracaksınız.
  • Apk boyutunuz azalacak.
  • Kodunuz daha temiz olacak.

NASIL KULLANACAĞIZ

YÖNTEM 1
Tool u kullanmak için yapmanız gereken menü bardaki Analyze > Inspect Code a tıklamak. Açılan pencere aşağıdaki gibi olacaktır. Burada proje geneli veya özellikle bir modül vs. tarama yapılsın diye seçenekler mevcut.
Tamam a tıkladığınızda tarama yapılır ve Inspection Results alanında uygulamanıdaki kod iyileştirmeleri gereken veya bug çıkarabilecek kısımlar size gösterir.
Projeniz biraz genişlediyse ekranınızda yukarıdaki resime oranla 2–3 kat fazla sonuç çıkmış olabilir. Bazen çok enteresan hatalar da. Ama en temel uygulamadaki gereksiz kaynakların ve kodların silinmesini istiyorsanız aşağıdaki yolu izleyebilirsiniz.
Android Lint:Performance > Unused resource = Kullanılmayan kaynaklar. image, strings, layout, layout içindeki başıboş id ler, style.xml ler vs..
Declaration redundancy > Unused declaration = Kullanılmayan kodlar. parametre, sınıf, method…
Özellikle unused resource alanı tamamen gereksiz şeyleri barındırıyor burada bulunanları silebilirsiniz. Ama unused decleration kısmındakileri silerken tek tek incelemenizde fayda var.”Ben annotation ların tetiklediği methodları silmeye çalıştığına bizzat şahit oldum. “ Örnek: Otto ”
YÖNTEM 2
Uygulamayı bu şekilde sonradan taramak yerine her build ettiğinizde bu hataları görme gibi de bir seçeneğiniz var. Bunun için res klasörünün altına bir xml klasörü açıp onun içine de aşağıdaki örnekte olduğu gibi bir xml dosyası tanımlamasınız.
Dosya hata fırlatmasını istediğiniz durumları içermelidir. Kullanılmayan çeviriler, layout performans problemleri, manifest hataları,
<?xml version="1.0" encoding="UTF-8"?><lint>
    <!-- kullanılmayan kaynaklarda hata fırlat -->
    <issue id="UnusedResources" severity="error"/>
    <!-- hardcodedtext lerde hata fırlat -->
    <issue id="HardcodedText" severity="error" />
    
</lint>
Ardından build.gradle da bu yazdığınız xml dosyasının yolunu belirtmelisiniz.
android {
    ...
    lintOptions {
        lintConfig file("src/main/res/xml/lint.xml")
    }
}
Kodu derlediğinizde aşağıda bulunan örnekteki gibi bir sonuç ekranı çıkacaktır. İlk olarak size apk yı bu şekilde hatalı da olsa atabilmeniz için bir öneri sunuyor. “abortOnError false yapmazsanız hataları çözmeden apk oluşturulmaz”. Onun altında ise sıra sıra hatalar listelenir.
Dipnot: Tüm bunlara ek olarak FindBugs , PMD gibi toolları da inceleyebilirsiniz.

20 Mart 2018 Salı

Temel Rxjava (4)

zip()
Bir sayfada iki tane servise çıktığınız düşünün. Örnek profil güncelleme. Bunun içinde aynı anda iki tane servise çıkıyorsunuz. Profil fotoğrafı güncellemek için ayrı, ad soyad vs için ayrı. Senaryo şu şekilde.
  • Güncelle butonuna basıldığı anda ekranda progress başlatılacak
  • İki servis çağrılacak aynı anda çağrılacak
  • İkisinden de cevap geldiği anda progress kaldırılacak
Şimdi burda şöyle bir sıkıntı var. Hangi servisten önce cevap dönecek. Progressi kaldırmamız için ikisinden de cevap dönmüş olması lazım. Bunun için rxjava daki reduce() methodunu kullanabilirsiniz.
Observable.zip(

Observable.just("foto güncelleme 3 saniye")
.delay(3, TimeUnit.SECONDS).subscribeOn(Schedulers.newThread()),

Observable.just("diger servis 6 saniyelik gecikme")
.delay(6, TimeUnit.SECONDS).subscribeOn(Schedulers.newThread()),

(o1, o2) -> new Object())
        
.doOnSubscribe(disposable -> 
startTime = System.currentTimeMillis())
        
.doOnComplete(() -> {
            
long totalDuration = System.currentTimeMillis() - startTime;
Log.d("Result","Total : " + totalDuration + "ms");
        })
.subscribe();
D/Result: Total : 6003ms
Aynı anda çağırılan iki servis için de yeni thread açıldığı için aynı anda başlamıştır. doOnComplete ise ikinci servisten cevap geldiğinde çağrılmıştır. Yeni thread açmazsanız senkron olarak işletilir.
debounce()
Bir şeyler search edeceğiniz zaman genelde siz yazdıktan sonra biraz zaman geçer ”200ms–300ms” sonra tetiklenme olur.- Her harf için işlem yapılmasın da, tam olarak ne istediğini yazsın ondan sonra bir arama gerçekleşsin.-
Klasik yöntem textchangelistener içinde bir timer başlatılır. Her girilen yeni karakter için timer sıfırlanır , yeniden başlatılır. Bende bu şekilde yazıyorum hala . Ama debounce ile bu nasıl yapılır bir de ona bakalım.
RxTextView.textChanges(editTextVariableName)
        .debounce(500, TimeUnit.MILLISECONDS)
        .subscribe(new Action1<String>() {
            @Override
            public void call(String value) {
               // 500 ms sonra tetiklenecek değer
            }
        });

13 Ekim 2017 Cuma

Temel RxJava (3)

Operatörler

Rxjava yı geleneksel java koduna göre bir adım öne geçiren olayın , yayınlanan datayı işlemek için çok geniş metod seti olduğundan ilk yazdımda bahsetmiştim . Bu yazımda bunlardan birkaçını açıklamaya çalışacağım .

flatMap()

Bir observable ın sonucunda oluşan değer tekrar başka bir observable a bağlanıyorsa . Yani asenkron bir işlem sonucu aslında başka bir asenkron işlem sonucuna bağlıysa içiçe iki tane observable yazmaktansa flatmap kullanılır .


Single.fromCallable(new Callable() {
            @Override
            public Integer call() throws Exception {
                return 1;
                //return 2;
            }
        }).flatMap(new Function>() {
            @Override
            public SingleSource apply(Integer integer) throws Exception {

                if (integer == 1)
                    return Single.just("fisrt");
                else
                    return Single.just("second");
            }
        }).subscribe(new Consumer() {
            @Override
            public void accept(String s) throws Exception {
                Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
            }
        });
Ekran Çıktısı : "first"
Örnekte ilk olarak Integer dönen bir observable oluşturulmuştur . Başka bir observable bunu dinlemektedir ve dönen sonuca göre String bir değer dönmektedir .

reduce()

Bir Observable birden çok değer üretiyor ve sonuc olarak bu değerlerin toplamı lazımsa reduce kullanılabilir .


  Observable.create(new ObservableOnSubscribe>() {
            @Override
            public void subscribe(ObservableEmitter> e) throws Exception {


                ArrayList strings = new ArrayList<>();
                strings.add("Hello");
                strings.add("Bro");
                e.onNext(strings);

                strings = new ArrayList<>();
                strings.add("Whats");
                strings.add("Up");
                e.onNext(strings);
                e.onComplete();

            }
        }).reduce(new BiFunction, List, List>() {
            @Override
            public List apply(List strings, List strings2) throws Exception {
                strings.addAll(strings2);
                return strings;
            }
        }).subscribe(new Consumer>() {
            @Override
            public void accept(List strings) throws Exception {
                Toast.makeText(MainActivity.this, strings.toString(), Toast.LENGTH_SHORT).show();
            }
        });

Ekran Çıktısı : [Hello,Bro,Whatsup,Kro]

onNext ile yayınlanan her "strings" listesi reduce methoduna girer ve orada parent strings e eklenir . Subsribe ise onComplete den sonra çalışır ve sonuç olarak iki onNext metoduyla yayınlanmış olan listelerin artarda eklenmiş halini alır .

Temel RxJava (2)

Observable - Single - Completable - Maybe

Bir önceki yazımda observable nedir nasıl kullanılırdan bahsetmiştim . Şimdi ise Observable yerine kullanılabilecek diğer dinleyici türlerine bir göz atalım .

Observable -> Birden fazla kez dinleyici tetiklenecek ise kullanılır . Bu yüzden onNext , onComplete adında iki methodu vardır . Her yayınlamada onNext tetiklenirken tüm akış bittiğinde ise onComplete tetiklenir .


Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {

                e.onNext("Hello");
                e.onNext("Ardahan");
                e.onComplete();

            }
        }).subscribe(new Observer() {
            @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onNext(Object o) {
                Log.d(Tag, o);
            }

            @Override
            public void onError(Throwable e) {}

            @Override
            public void onComplete() {}
        });

Single -> Dinleyici sadece bir kez tetiklecek ise kullanılır . Bu yüzden onSuccess methodu mevcuttur .


Single.create(new SingleOnSubscribe() {
            @Override
            public void subscribe(SingleEmitter e) throws Exception {
                e.onSuccess("Hello");

            }
        }).subscribe(new SingleObserver() {
            @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onSuccess(Object o) {
                Log.d(Tag, o);
            }

            @Override
            public void onError(Throwable e) {}
        });

Completable - > Single ile aynı gibi görünsede burada dinleyici tetiklenirken observable dan herhangi bir değer beklenmez . Arkaplanda bir işlem gerçekleştirilir ama sadece bitip bitmediğiyle ilgilenilir . 


Completable.create(new CompletableOnSubscribe() {
            @Override
            public void subscribe(CompletableEmitter e) throws Exception {
                e.onComplete();
            }
        }).subscribe(new CompletableObserver() {
            @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onComplete() {
                Log.d(Tag, "done");
            }

            @Override
            public void onError(Throwable e) {}
        });

Maybe -> Single ile Completable birleşimi gibidir . Hem onSuccess hem de onComplete methodları mevcut . Yani hiçbir sonuç dönmeyedebilir , tek bir sonuç dönedebilir .


Maybe.create(new MaybeOnSubscribe() {
            @Override
            public void subscribe(MaybeEmitter e) throws Exception {
                e.onSuccess("Hello");
               // e.onComplete();
            }
        }).subscribe(new MaybeObserver() {
            @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onSuccess(String s) {
                Log.d(Tag, s);
            }

            @Override
            public void onError(Throwable e) {}

            @Override
            public void onComplete() {
                Log.d(Tag, "done");
            }
        });

Temel RxJava (1)

RxJava Nedir ?

RxJava (Reactive Extension) en temelde observable pattern üzerine kurulu asenkron işlemler için kullanılan güçlü bir kütüphanedir . Kendi içinde 3 parçaya ayrılır .

Observable (Gözlenen) - Observer (Gözlemci) - Datayı işleyen methodlar 

Observable = Datanın yayınlandığı yer
Observer = Datanın dinlendiği yer
Datayı İşleme = Data üzerinde filtreleme , ekleme , çıkarma vs.. işlemlerin yapıldığı yer

Observable.just(" Hello World ").map(new Function() {
            @Override
            public String apply(String s) throws Exception {
                return s.trim();
            }
        }).subscribe(new Consumer() {
            @Override
            public void accept(String s) throws Exception {
                Log.d("", s);
            }
        });


Ekran Çıktısı : HelloWorld

Yukarıdaki örnekte en basit haliyle bir örnek görebilirisiniz . "Hello World" yaylınlanıyor -> trim() ile filtreleniyor -> subscribe edilen yerde sonuç görüntüleniyor .

Neden RxJava Kullanmalıyım ?

Yukarıdaki örnekte bir string yayınlanmış onu da bir yerden dinlemiş basmışsın , bunun için mi yani bu rxjava diyebilirsiniz :) haklısınız . Peki ama neden neden kullanmalıyım diye sorarsanız !

(Java)


 new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                final List strings = new ArrayList<>();
                strings.add("Hello");

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, strings.toString(), Toast.LENGTH_SHORT).show();
                    }
                });


            }
        }).start();

Yapmak istediğim çok basit , bana bir liste dönecek olan bir işlem ama 500 milisaniyelik bir gecikme ile cevap veriyor . Cihazda UI kitlemeden arkaplanda bir işlem yapacağınızı düşünün ki bunu yapmak için yeni bir thread açmanız gerekiyor -Örnek amaçlı ben sonucu liste dönen bir işleme Thread.sleep(500) ile bir gecikme ekledim -. Ardından sonucu ekrana basacığınızı düşünün ki bunun içinde tekrar UI thread e geçmeniz yani bir runOnUiThread başlatmanız gerekiyor -Aksi takdirde exception alırsınız -.

Bunun yerine

(RxJava)

Observable.fromCallable(new Callable>() {
            @Override
            public List call() throws Exception {

                Thread.sleep(500);

                List strings = new ArrayList<>();
                strings.add("Hello");

                return strings;
            }
        }).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer>() {
            @Override
            public void accept(List strings) throws Exception {
                Toast.makeText(MainActivity.this, strings.toString(), Toast.LENGTH_SHORT).show();
            }
        });


ObserveOn = Subsriber hangi threadde çalışması dinlemesi gerektiğini belirtiyoruz . -AndroidSchedulers.mainThread() yani main threadde dinle . -
SubscribeOn = Observable sonucu yayınlanacak olacak işlemin hangi threadde çalışması gerektiğini belirtiyoruz . -Ben newThread() kullandım ama Schedulers.io da aynı işi yapıyor sadece var olan ama boşa çıkmış threadlerden birini kullanıyor .-

Bunlara ek olarak RxJava ile data üzerinde işlem yapabileceğiniz çok güçlü bir method set (Operators) olduğunu hesaba katarsak . RxJava geleneksel java koduna göre daha baskın geliyor .


Makalenin devamını linkten bulabilirisniz

23 Nisan 2017 Pazar

Data Binding

Android uygulama geliştirirken UI ile java sınıflarını birbirine bağlamanın en sıkıcı işlerden biri olduğu bilinir . Bu sebeptendir ki bu amaçla kullanılan Butterknife şu android geliştiricileri arasında en popüler kütüphanlerden biri haline geldi . Son zamanlarda sıkıntıyı farketmiş olucaklar ki Google/IO 2015 te yeni data binding support library i tanıttı . Bu kütüphane sayesinde itemlerinizi tek tek  arayüzden findByViewId ile yakalayıp ardından onları cast edip -hataya gayet açık yöntemler- ardından onlara tek tekmodelinizdeki ilgili parametreleri doldurmak gibi zahmetli bir işten kurtulabilirsiniz . Kullanımı gayet basit tutulan kütüphanenin örneğini sayfanın en altındaki linkten bulabilirsiniz .

STEP 1 (Temel)
İlk olarak kütüphaneyi kullanabilmeniz için build.gradle dosyanızdan dataBinding i aktif hale getirmeniz yeterli . "Gradle version minimum 1.5.0 olmalı !"

...android {
compileSdkVersion ...
buildToolsVersion ...
defaultConfig {...}
dataBinding { enabled = true }
 ...
} 

Kullancağınız layout dosyalarında ise dikkat etmeniz gereken ilk şey "<layout>" tag' ile başlayıp derleyiciye bu dosyayı bind etmesi gerektiğini belirtmektir.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout 
  android:layout_width="match_parent" 
  android:layout_height="match_parent">
 
     <TextView android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

</RelativeLayout>
</layout>

Tebrikler artık findByViewId methodundan kurtuldunuz . Bu dosyadaki itemlerinizi direk activity sınıfınızdan kullanabilirsiniz .

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
final ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main);
activityMainBinding.text.setText("Hello World Test"); }
STEP 2(Arayüze Müdahale)
Android binding library yukarıda da görüldüğü gibi ilk etapta findByViewId ile araryüzü activity e bağlama zorunluluğunu ortadan kaldırmıştır . Tabi sadece bununla sınırlı olmayan kütüphane ile modellerinizi de bu arayüz itemlerine zahmetsiz init edebilirsiniz .

Bunun için ilk yapmanız gereken layout dosyanızda kullancağınız model i "<data>" tag i içerinde belirtmek .

<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android">
  <data>
      <variable 
           name="testModel"
           type="com.example.databinding.Test" />
  </data>

<RelativeLayout 
   android:layout_width="match_parent" 
   android:layout_height="match_parent"> 
<TextView 
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="@{testModel.textStr}" />

 </RelativeLayout> 
</layout>
Örnekte kullanılacak olan TestModel i sadece textStr adında bir string içeren basit bir pojo modeli olarak düşünebilirsiniz . TextView altında bulunan android:text="@{testModel.textStr}" tagi bu textview a modeldeki textStr nin doldurulacağı anlamına gelir . Artık tek yapmanız gereken activity sınıfınızdan arayüze modelinizi setlemek .

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        final ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(
                this, R.layout.activity_main);
        Test test = new Test("Hello World Test 2");
        activityMainBinding.setTestModel(test);
    }
Kullanımı gayet basit tutulan kütüphanenin örneğini linkten bulabilirsiniz . -Picasso ile image nasıl eklenir , layout a logic nasıl yazılır linkte mevcut -