Android'de bir gecikmeden sonra bir yöntem nasıl çağrılır


769

Belirli bir gecikmeden sonra aşağıdaki yöntemi çağırabilirim. Hedef c'de şöyle bir şey vardı:

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];

Java ile android bu yöntemin bir eşdeğeri var mı? Örneğin 5 saniye sonra bir yöntemi çağırabilmem gerekiyor.

public void DoSomething()
{
     //do something here
}

Yanıtlar:


1858

Kotlin

Handler().postDelayed({
  //Do something after 100ms
}, 100)


Java

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);



109
Bu çözüm yalnızca UI iş parçacığında yararlıdır. Aksi takdirde normal iş parçacığında, bence en iyi sürüm olmayan lüper uygulamanız gerekiyor
olivier_sdg

2
@olivier_sdg neden lüper kullanmanız gerekiyor?
djechlin

37
@djechlin Bir İşleyici her zaman sizin yayınladığınız Runnable'ı () işleyecek bir Lüper ile bağlantılı olmalıdır. UI iş parçacığı zaten bir Lüper ile birlikte gelir, böylece UI iş parçacığında yeni bir İşleyici () yapabilir ve doğrudan ona gönderilebilir () Çalıştırılabilirler. Bu Runnable'lar UI iş parçacığında yürütülür. Runnables'ın başka bir iş parçacığında yürütülmesini sağlamak için yeni bir iş parçacığı, ardından Looper.prepare (), yeni bir Handler () ve sonra Looper.loop () yapmanız gerekir. Bu yeni İşleyiciye gönderilen Runnable'lar bu yeni iş parçacığında yürütülür. Tüm bunları yapmazsanız, post () bir istisna atar.
Dororo

12
Eğer, siz de yapabilirsiniz gerekir iptal Runnable arayarak mesaj kuyruğunda hala sürece yürütme removeCallbacks(Runnable r)üzerinde Handler.
Dennis

9
gerekirimport android.os.handler
KaKa

322

Benim durumumdaki diğer cevaplardan hiçbirini kullanamadım. Bunun yerine yerli java Timer'ı kullandım.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);

43
bu, İşleyici kullananlardan daha iyidir, çünkü İşleyici UI iş parçacığında çalışmadığında Looper sorunları yoktur.
Ben H

32
Android dokümanı uyarınca artık gerekli olmadığında iptal etmek için zamanlayıcınıza bir referans tutmalısınız: "Zamanlayıcı artık gerekli olmadığında, kullanıcılar zamanlayıcının iş parçacığını ve diğer kaynakları serbest bırakan cancel () öğesini çağırmalıdır. Açıkça iptal edilmeyen zamanlayıcılar kaynakları süresiz olarak tutabilir. "
Pooks

14
Dikkat! Bu UI iş parçacığında çalışmaz. Bunu ui iş parçacığında çalıştırmak Önemli bir Hataya neden oldu: android.view.ViewRootImpl $ CalledFromWrongThreadException: Yalnızca bir görünüm hiyerarşisi oluşturan orijinal iş parçacığı görünümlerine dokunabilir.
vovahost

13
@vovahost, sadece zamanlayıcı bloğu içindeki UI bileşenlerini güncellediğiniz için
Tim

10
JDK 9'da java.util.Timer (ve TimerTask) kullanımdan kaldırılacağını unutmayın. TimerTask çok iyi olmayan görevler için yeni Konular oluşturur.
Varvara Kalinina

183

Not: Bu cevap, soru Android'i içerik olarak belirtmediğinde verildi. Android UI iş parçacığına özgü bir yanıt için buraya bakın.


Mac OS API'sı mevcut iş parçacığının devam etmesine izin veriyor ve görevi eşzamansız olarak çalışacak şekilde zamanlıyor. Java'da, eşdeğer işlev java.util.concurrentpaket tarafından sağlanır . Android'in hangi sınırlamaları getirebileceğinden emin değilim.

private static final ScheduledExecutorService worker = 
  Executors.newSingleThreadScheduledExecutor();

void someMethod() {
  
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  
}

3
Bu asla Runnable'ı benim için
çağırmaz

14
Yan not olarak: Bu , görevi daha sonra iptal etmenize de olanak tanır ; bu, bazı durumlarda yardımcı olabilir. Sadece ScheduledFuture<?>döndürülen tarafından bir referans depolayın worker.schedule()ve cancel(boolean)yöntemini çağırın .
Dennis

Bence bu cevap eski. .schedule artık Runnable yöntemi gibi görünmüyor ...? : /
beetree

5
@beetree bir yöntem ScheduledExecutorService.
erickson

3
Ui iş parçacığı nesneleri varsa, bu çalışmaz runOnUIThread (new runnable () {run () ....}); veya runler içinden runler nesnesini kullanarak runnable'ı yayınlayın () {}
Jayant Arora

107

5 saniye sonra UI İş Parçacığında bir şey yürütmek için:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);

8
Doğrulayın, bu, looper.prepare çağrısını önlemek ve her şeyi UI iş parçacığına yerleştirmek için en iyi çözümdür.
Mart'ta Tobliug

Bunun için teşekkürler, Looper sorunlarımda bana yardımcı oldular :)
Tia

1
Ana lüper üzerinde bir işleyici oluşturma konusunda dikkatli olurdum, o zaman bu iş parçacığında uzun süre görev yapılmamalıdır
Shayan_Aryan

40

UIThread içinde Handler'ı kullanabilirsiniz:

runOnUiThread(new Runnable() {

    @Override
    public void run() {
         final Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
           @Override
           public void run() {
               //add your code here
           }
         }, 1000);

    }
});

36

Tüm harika cevaplar için teşekkürler, ihtiyaçlarıma en uygun çözümü buldum.

Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);

class DoSomething extends Handler {
    @Override
    public void handleMessage(Message msg) {
      MyObject o = (MyObject) msg.obj;
      //do something here
    }
}

Bir öğenin tıklanmasıyla ilgili dokunma geri bildirimi almak için bu yaklaşımı kullanmam uygun olur mu?
eRaisedToX

24

KotlinVe JavaBirçok Yol

1. kullanma Handler

Handler().postDelayed({
    TODO("Do something")
    }, 2000)

2. TimerTask'ı kullanma

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

Veya daha da kısa

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)

Ya da en kısa

Timer().schedule(2000) {
    TODO("Do something")
}

3. kullanma Executors

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)

Java dilinde

1. kullanma Handler

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);

2. kullanma Timer

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);

3. kullanma ScheduledExecutorService

private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);

1
@ JanRabe Öneriniz için teşekkürler. Ben appriciate. Ancak soru şu How to call a method after a delay in Android. Ben de buna odaklandım. Diyeceğim şey şu ki. Aksi takdirde java sızıntıları, geliştiriciler için ayrı ayrı anlaşılması gereken büyük bir konudur.
Khemraj

20

Bu demoya bakın:

import java.util.Timer;
import java.util.TimerTask;

class Test {
     public static void main( String [] args ) {
          int delay = 5000;// in ms 

          Timer timer = new Timer();

          timer.schedule( new TimerTask(){
             public void run() { 
                 System.out.println("Wait, what..:");
              }
           }, delay);

           System.out.println("Would it run?");
     }
}

20

İşleyiciyi kullanmanız gerekiyor, ancak başka bir iş parçacığına runonuithreadgiriyorsanız, işleyiciyi UI iş parçacığında çalıştırmak için kullanabilirsiniz . Bu sizi aramak için atılan İstisnalardan kurtaracakLooper.Prepare()

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do something after 1 second
            }
        }, 1000);
    }
});

Oldukça dağınık görünüyor, ama bu yollardan biri.


4
Bu işe yarıyor, düzenlemek için en az 6 karakterli aptal SO kuralları nedeniyle yayınınızı düzenleyemiyorum, ancak 'yeni Handler'den sonra' () 'eksik' yeni Handler () 'olmalı
Jonathan Muller

2
Her şeyi UI iş parçacığına yerleştirmek yerine şunları yapabilirsiniz: yeni İşleyici (Looper.getMainLooper ())
Tobliug

17

View.postDelayed()Yöntem, aşağıdaki basit kod kullanmayı tercih ederim :

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);

1
Görünüm işleyicisinde zamanlanacağı için kullanıcı arabirimi öğesinin kendisini dondurmuyor mu?
JacksOnF1re

1
Hayır, yayınlanan görev 1 saniye içinde yürütülecek, ancak bu ikinci UI iş parçacığı sırasında başka yararlı işler yapıyor
demaksee

14

İşte benim en kısa çözümüm:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);

10
final Handler handler = new Handler(); 
Timer t = new Timer(); 
t.schedule(new TimerTask() { 
    public void run() { 
        handler.post(new Runnable() { 
            public void run() { 
                //DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
            }
        }); 
    } 
}, 5000); 

10

Eğer kullanıyorsanız Android Studio 3.0 ve üzeri Lambda ifadeleri kullanabilirsiniz. Yöntem callMyMethod()2 saniye sonra çağrılır:

new Handler().postDelayed(() -> callMyMethod(), 2000);

Gecikmeli çalıştırılabilir iptali iptal etmeniz gerekirse bunu kullanın:

Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);

Bunu nasıl iptal edebiliriz?
Damia Fuentes

Burada kaç kişinin mutlu bir şekilde Kotlin'e taşınacağına şaşırdım, ancak standart Java olan Lambda İfadelerini tamamen görmezden geldim.
TomDK

6

Zamanlayıcıyı öneririm çok belirli bir aralıkta çağrılacak bir yöntem zamanlamanızı sağlar. Bu, kullanıcı arayüzünüzü engellemez ve yöntem yürütülürken uygulamanızın yankılanmasını sağlar.

Diğer seçenek, wait (); yönteminde, geçerli iş parçacığı belirtilen süre boyunca engellenir. Bu, UI iş parçacığında bunu yaparsanız kullanıcı arayüzünüzün yanıt vermemesine neden olur.


2
Thread.sleep (), Object.wait () öğesinden daha iyidir. Bekle, bildirilmeyi beklediğinizi ve bazı etkinliklerin senkronize edildiğini gösterir. Uyku, belirli bir süre için hiçbir şey yapmak istemediğinizi gösterir. Zamanlayıcı, eylemin daha sonraki bir zamanda eşzamansız olarak gerçekleşmesini istiyorsanız giden yoldur.
Tim Bender

1
Bu doğru. Bu yüzden başka bir seçenek olarak listeledim ;-)
Nate

6

Basit bir hat Tutamağı Sonrası gecikmesi için aşağıdakileri yapabilirsiniz:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);

Umarım bu yardımcı olur


5

Bunu En Basit Çözüm için kullanabilirsiniz:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Write your code here
    }
}, 5000); //Timer is in ms here.

Else, Aşağıda başka bir temiz yararlı çözüm olabilir:

new Handler().postDelayed(() -> 
{/*Do something here*/}, 
5000); //time in ms

5

Yeni tanıtılan lambda ifadelerini kullanarak çok daha temiz hale getirebilirsiniz:

new Handler().postDelayed(() -> {/*your code here*/}, time);

5

Burada düşünülmesi gereken birkaç şey var, çünkü bu kedinin derisini almanın pek çok yolu var. Her ne kadar cevapların hepsi seçilmiş ve seçilmiş olsa da. Ben sadece "çoğunluk seçilen basit cevap" nedeniyle yanlış yönde gitmesini önlemek için bu uygun kodlama yönergeleri ile yeniden ele alınması önemlidir düşünüyorum.

İlk olarak, bu iş parçacığında genel olarak seçilen kazanan cevap olan basit Post Gecikmeli yanıtı tartışalım.

Dikkate alınması gereken birkaç şey. Post gecikmeden sonra bellek sızıntıları, ölü nesneler, gitmiş olan yaşam döngüleri ve daha fazlası ile karşılaşabilirsiniz. Bu yüzden düzgün bir şekilde kullanmak da önemlidir. Bunu birkaç şekilde yapabilirsiniz.

Modern gelişim uğruna KOTLIN'de tedarik edeceğim

Geri aramada UI iş parçacığını kullanmanın ve geri aramanıza bastığınızda etkinliğinizin hala canlı ve iyi olduğunu onaylamanın basit bir örneği.

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)

Ancak, etkinlik hala ortadan kalktığında geri aramanıza ulaşmak için hiçbir neden olmadığından bu hala mükemmel değildir. bu nedenle daha iyi bir yol, bir referans tutmak ve bunun geri aramalarını kaldırmak olabilir.

    private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
        A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
        if(activity != null && activity?.isFinishing == false){
            txtNewInfo.visibility = View.VISIBLE
            mHandler.postDelayed({
                if(activity != null && activity?.isFinishing == false){
                    txtNewInfo.visibility = View.GONE
                }
            }, NEW_INFO_SHOW_TIMEOUT_MS)
        }
    }

ve elbette onPause'da temizlemeyi halledin, böylece geri aramaya basmaz.

    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }

Şimdi bariz konuştuk, modern coroutines ve kotlin ile daha temiz bir seçenek hakkında konuşalım :). Bunları henüz kullanmıyorsanız, gerçekten kaçırıyorsunuz demektir.

   fun doActionAfterDelay() 
        launch(UI) {
            delay(MS_TO_DELAY)           
            actionToTake()
        }
    }

veya bu yöntemde her zaman bir UI lansmanı yapmak istiyorsanız şunları yapabilirsiniz:

  fun doActionAfterDelay() = launch(UI){ 
      delay(MS_TO_DELAY)           
      actionToTake()
  }

Tabii ki PostDelayed gibi, gecikme çağrısından sonra etkinlik kontrollerini yapabilmeniz veya diğer rota gibi onPause'da iptal edebilmeniz için iptal işlemini yaptığınızdan emin olmanız gerekir.

var mDelayedJob: Job? = null
fun doActionAfterDelay() 
   mDelayedJob = launch(UI) {
            try {
               delay(MS_TO_DELAY)           
               actionToTake()
            }catch(ex: JobCancellationException){
                showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
            }
        }
   }
}

// temizleme işlemlerini gerçekleştirme

override fun onPause() {
   super.onPause()
   if(mDelayedJob != null && mDelayedJob!!.isActive) {
      A35Log.v(mClassTag, "canceling delayed job")
      mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
   }
}

Lansmanı (UI) yöntem imzasına koyarsanız, iş çağrı kod satırına atanabilir.

öyleyse, hikayenin ahlaki gecikmiş eylemlerinizle güvende olmak, geri aramalarınızı kaldırdığınızdan veya işlerinizi iptal ettiğinizden emin olun ve elbette gecikme geri aramanızdaki öğelere dokunmak için doğru yaşam döngüsüne sahip olduğunuzu doğrulayın. Coroutines ayrıca iptal edilebilir eylemler sunar.

Ayrıca, genellikle coroutines ile birlikte gelebilecek çeşitli istisnaları ele almanız gerektiğini belirtmek gerekir. Örneğin, bir iptal, bir istisna, bir zaman aşımı, ne kullanmaya karar verirseniz verin. İşte coroutines kullanmaya gerçekten karar verirseniz daha gelişmiş bir örnek.

   mLoadJob = launch(UI){
            try {
                //Applies timeout
                withTimeout(4000) {
                    //Moves to background thread
                    withContext(DefaultDispatcher) {
                        mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
                    }
                }

                //Continues after async with context above
                showFancyToast("Loading complete", true, FancyToast.SUCCESS)
            }catch(ex: JobCancellationException){
                showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
            }catch (ex: TimeoutCancellationException) {
                showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
            }catch(ex: Exception){
                showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
            }
        }

1
Sorun değil Rajiv, bir adım daha ileri götürürdüm ve Canlı Veri'yi kullanarak coroutines'in temizleme çağrılarından kaçınmak için yaşam döngüsü farkında ve kendi kendine iptal olabileceğini söyleyebilirim, ancak bir cevaba çok fazla öğrenme eğrisi atmak istemiyorum;)
Sam

3

Bunu çağırmak için daha basit bir yöntem oluşturdum.

public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
    {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                try {
                    Method method =  activity.getClass().getMethod(methodName);
                    method.invoke(activity);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }, miliseconds);
    }

Kullanmak için şunu arayın: .CallWithDelay(5000, this, "DoSomething");


3
Böyle temel bir görevin yansıması mı?
Max Ch

İOS'tan benzer soru sormak için yöntem performSelector. bunu yapmanın en iyi yolu bu.
HelmiB

3

Biri aşağıda aldığınızda çalışır,

java.lang.RuntimeException: İş parçacığı içinde Looper.prepare () çağırmamış işleyici oluşturulamıyor

final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

3

Kotlin'i kullanarak, aşağıdakileri yaparak başarabiliriz

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)

2

Bunu kullanmak çok kolay CountDownTimer. Daha fazla ayrıntı için https://developer.android.com/reference/android/os/CountDownTimer.html

import android.os.CountDownTimer;

// calls onTick every second, finishes after 3 seconds
new CountDownTimer(3000, 1000) { 

   public void onTick(long millisUntilFinished) {
      Log.d("log", millisUntilFinished / 1000);
   }

   public void onFinish() {
      // called after count down is finished
   } 
}.start();

2

RxAndroid kullanıyorsanız, iş parçacığı ve hata işleme çok daha kolay hale gelir. Bir gecikmeden sonra aşağıdaki kod yürütülür

   Observable.timer(delay, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(aLong -> {
           // Execute code here
        }, Throwable::printStackTrace);

1

herkes yeni bir runnable veya mesaj göndermeden önce Handler'ı temizlemeyi unutuyor gibi görünüyor. Aksi takdirde birikebilir ve kötü davranışlara neden olabilirler.

handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.

handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.

1

İşte başka bir zor yol: çalıştırılabilir UI öğelerini değiştirdiğinde istisna atmaz.

public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {

    Runnable callBack;

    public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
        setDuration(delayTimeMilli);
        callBack = runnable;
        setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        callBack.run();
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }
}

Animasyonu şöyle çağırabilirsiniz:

view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));

Animasyon herhangi bir görünüme eklenebilir.



1

Ben şeyler temiz gibi: İşte benim uygulama, yöntem içinde kullanmak için satır içi kod

new Handler().postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

0

Android için uygun bir çözüm:

private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
            launcher.start();
.
.
private class MyLauncher extends Thread {
        @Override
        /**
         * Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any. 
         */
        public void run() {
            try {
                // Sleeping
                Thread.sleep(SLEEP_TIME * 1000);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
            //do something you want to do
           //And your code will be executed after 2 second
        }
    }

0

Benzer çözüm ama kullanımı çok daha temiz

Bu işlevi sınıfın dışına yazın

fun delay(duration: Long, `do`: () -> Unit) {

    Handler().postDelayed(`do`, duration)

}

Kullanımı:

delay(5000) {
    //Do your work here
}

Ne yapar?
Skizo-ozᴉʞS

sadece bir isim, orada her şeyi sakla. dodahili bir yöntemdir, bu yüzden `` değişken adı olarak kullanmak için kullanmalıyız
Manohar Reddy

Teşekkürler, ama neden bu değişken adı kullanılıyor? Demek istediğim bunun işlevi nedir.
Skizo-ozᴉʞS

1
do3 sn gecikmeden sonra böyle düşündüm
Manohar Reddy

0

Android'de, herhangi bir fonksiyonun geciktirilmesi için kotlin kodunun altına yazabiliriz

class MainActivity : AppCompatActivity() {

private lateinit var handler: Handler

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    handler= Handler()
    handler.postDelayed({
        doSomething()
    },2000)
}

private fun doSomething() {
    Toast.makeText(this,"Hi! I am Toast Message",Toast.LENGTH_SHORT).show()
}
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.