Apa sebenarnya yang dilakukan metode posting?

106

Saya menemukan fitur yang sangat aneh.

Saat saya mencoba menjalankan animasi di utas utama, animasi tidak dimulai. Ketika saya menjalankan animasi tersebut menggunakan

getView().post(new Runnable() {
            @Override
            public void run() {
                getView().startAnimation(a);
            }
        });

Itu dimulai.

Saya telah mencetak CurrentThreadsebelum memulai animasi dan keduanya mencetak main.

Jelas, saya melewatkan sesuatu di sini, karena keduanya harus memulai animasi di utas utama ... Dugaan saya adalah bahwa saat pos menambahkan tugas ke antrean, ini dimulai pada "waktu yang tepat", tetapi saya ingin tahu apa yang terjadi di sini secara lebih mendalam.

EDIT: Biarkan saya menjelaskan semuanya - pertanyaan saya adalah, mengapa memulai animasi pada posting menyebabkannya dimulai, ketika memulai animasi di utas utama tidak.

Gal
sumber
Apakah perilaku ini khusus untuk versi Android? Saya tidak dapat mereproduksinya di Android 4.1.2!
Akdeniz
Saya mereproduksi perilaku ini di Android 2.3.3. Tapi untuk AnimationDrawable! AnimationInstance biasa mulai berhasil dianimasikan di setiap penyiapan. Dalam AnimationDrawablekasus; ketika Anda mencoba untuk memulainya onCreate, itu tidak dimulai karena tidak melekat pada tampilan pada saat itu. Jadi ini bukan masalah threading AnimationDrawable. Mungkin hal yang sama berlaku untuk Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Jawaban:

161

post : post menyebabkan Runnable ditambahkan ke antrian pesan,

Runnable: Merupakan perintah yang dapat dijalankan. Sering digunakan untuk menjalankan kode di Thread yang berbeda.

run () : Mulai menjalankan bagian aktif dari kode kelas. Metode ini dipanggil saat utas dimulai yang telah dibuat dengan kelas yang mengimplementasikan Runnable.

getView().post(new Runnable() {

         @Override
         public void run() {
             getView().startAnimation(a);
         }
     });

kode :getView().startAnimation(a);

dalam kode Anda,

posting menyebabkan Runnable ( kode akan dijalankan di utas yang berbeda) untuk menambahkan antrian pesan.

Jadi startAnimation akan diaktifkan di utas baru saat diambil dari messageQueue

[EDIT 1]

Mengapa kami menggunakan utas baru, bukan utas UI (utas utama)?

Benang UI:

  • Saat aplikasi dimulai, Ui Thread dibuat secara otomatis

  • itu bertanggung jawab untuk mengirimkan acara ke widget yang sesuai dan ini termasuk acara menggambar.

  • Ini juga merupakan utas tempat Anda berinteraksi dengan widget Android

Misalnya, jika Anda menyentuh tombol a di layar, untaian UI mengirimkan peristiwa sentuh ke widget yang pada gilirannya menyetel status tertekannya dan memposting permintaan yang tidak valid ke antrean peristiwa. Rangkaian UI menghentikan permintaan dan memberi tahu widget untuk menggambar ulang sendiri.

Apa yang terjadi jika pengguna menekan tombol yang akan melakukan longOperation?

((Button)findViewById(R.id.Button1)).setOnClickListener(           
             new OnClickListener() {        
        @Override
    public void onClick(View v) {
            final Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
}
});

UI membeku. Program bahkan mungkin macet.

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap b = loadImageFromNetwork();
        mImageView.setImageBitmap(b);
    }
  }).start();
}

Ini melanggar aturan android yang tidak pernah mengupdate UI langsung dari thread pekerja

Android menawarkan beberapa cara untuk mengakses thread UI dari thread lain.

  • Activity.runOnUiThread (Runnable)
  • View.post (Dapat Dijalankan)
  • View.postDelayed (Dapat dijalankan, panjang)
  • Penangan

Seperti di bawah ini,

View.post (Dapat Dijalankan)

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}

Penangan

final Handler myHandler = new Handler(Looper.getMainLooper());

(new Thread(new Runnable() {

    @Override
    public void run() {
       final Bitmap b = loadImageFromNetwork();
      myHandler.post(new Runnable() {                           

        @Override
        public void run() {
           mImageView.setImageBitmap(b);
          }
        });
      }
    })).start();                
}

masukkan deskripsi gambar di sini

Untuk info lebih lanjut

http://android-developers.blogspot.com/2009/05/painless-threading.html

http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/

Talha
sumber
15
Jadi, mengapa memulai animasi di postingan berbeda dengan menjalankannya di thread utama, padahal keduanya pada akhirnya berjalan di thread yang sama?
Gal
Karena model single thread ini bisa menghasilkan performa yang buruk di aplikasi Android.
Talha
1
Apa hubungan kinerja buruk dengan tidak menampilkan animasi?
Gal
17
Saya tidak berpikir ini menjawab pertanyaan, ini lebih seperti jawaban umum untuk pemula yang tidak tahu apa-apa tentang ui-thread dan multi threading. Ini tidak menjelaskan mengapa melempar animasi ke depan dalam antrian membuat animasi bekerja; animasi seharusnya menjadi sesuatu yang dijalankan secara langsung di ui-thread tanpa menggunakan trik post () atau runOnUiThread ().
carrizo
3
Semua pekerjaan UI harus di utas utama (utas UI). Trik yang membuat animasi bekerja dengan menggunakan post () daripada langsung memanggil di utas utama adalah Waktu: Jika Anda langsung memanggil di utas utama itu berarti Anda mengatakan "mulai animasi sekarang", Tapi saat ini mungkin tampilan belum siap untuk animasi (mengukur, menggambar ...). Tetapi jika Anda meletakkannya di post (), itu akan menunggu startAnimation dalam antrian kadang-kadang untuk menyiapkan tampilan yang siap untuk anim.
NguyenDat
35

Apakah ini dilakukan di onCreate atau onCreateView? Jika demikian, aplikasi mungkin tidak dalam keadaan di mana View dilampirkan ke jendela. Banyak algoritme yang didasarkan pada metrik Tampilan mungkin tidak berfungsi karena hal-hal seperti pengukuran dan posisi Tampilan mungkin belum dihitung. Animasi Android biasanya membutuhkannya untuk dijalankan melalui matematika UI

View.post sebenarnya mengantrekan animasi pada loop pesan View, jadi setelah tampilan dilampirkan ke jendela, ia mengeksekusi animasi alih-alih menjalankannya secara manual.

Anda sebenarnya menjalankan sesuatu di thread UI, tetapi pada waktu yang berbeda

Joe Plante
sumber
Jawaban yang diterima menyesatkan di mana poster menyatakan "posting menyebabkan Runnable (kode akan dijalankan di utas berbeda)". Ini adalah jawaban yang benar "Anda sebenarnya menjalankan sesuatu di thread UI, tetapi pada waktu yang berbeda" - plus 1
smitty1
18

Lihat di sini untuk jawaban yang bagus. view.post () hampir sama dengan handler.post (). Ini masuk ke antrean utas utama dan dijalankan setelah tugas tertunda lainnya selesai. Jika Anda memanggil activity.runOnUiThread (), itu akan segera dipanggil di UI thread.

Tas Morf
sumber
31
Satu perbedaan besar (dan sangat membantu) yang saya temukan adalah runnable di view.post () akan dipanggil saat View pertama kali ditampilkan. IE, Anda dapat mengaturnya untuk memulai animasi setelah penggelembungan View lalu di beberapa titik di masa mendatang, akhirnya menambahkannya ke hierarki tampilan. Pada titik mana, kemudian animasi akan dijalankan dan Anda tidak perlu khawatir tentang itu.
DeeV
Sebenarnya, handler.post () tidak selalu memposting pesan / runnable di thread utama. Itu tergantung pada bagaimana handler dibuat (itu dapat dikaitkan dengan Looper di utas yang berbeda). Di sisi lain, view.post () akan selalu berjalan di utas utama
Yair Kukielka
4

Masalah yang saya pikir bisa menjadi metode siklus hidup di mana Anda memanggil metode post (). Apakah Anda melakukannya di onCreate ()? jika demikian, lihat apa yang saya temukan di dokumentasi onResume () aktivitas:

onResume ()

Ditambahkan dalam API level 1 void onResume () Dipanggil setelah onRestoreInstanceState (Bundle), onRestart (), atau onPause (), agar aktivitas Anda mulai berinteraksi dengan pengguna. Ini adalah tempat yang baik untuk memulai animasi , membuka perangkat dengan akses eksklusif (seperti kamera), dll.

https://developer.android.com/reference/android/app/Activity.html#onResume ()

Jadi, seperti yang dikatakan Joe Plante, mungkin tampilan belum siap untuk memulai animasi saat Anda memanggil post (), jadi coba pindahkan ke onResume ().

PD: Sebenarnya jika Anda memindahkan kode ke onResume () maka saya pikir Anda dapat menghapus panggilan post () karena Anda sudah berada di ui-thread dan tampilan harus siap untuk memulai animasi.

carrizo
sumber
3
onResumedapat dipanggil beberapa kali (layar tidur, aktivitas didorong ke backstack, dll ...) setelah awalnya saat "tampilan sudah siap". Jika dipanggil dari onResume, maka bendera mungkin diperlukan untuk melacak cuaca animasi telah dimulai, untuk menghindari (ulang) mulai beberapa kali.
samis