Chính xác thì phương pháp đăng bài làm gì?


106

Tôi đã gặp phải một tính năng rất kỳ lạ.

Khi tôi đang cố chạy hoạt ảnh trên luồng chính, nó không bắt đầu. Khi tôi chạy hoạt ảnh nói bằng cách sử dụng

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

Nó bắt đầu.

Tôi đã in CurrentThreadtrước khi bắt đầu hoạt ảnh và cả hai lần in main.

Rõ ràng, tôi đang thiếu một cái gì đó ở đây, vì cả hai sẽ bắt đầu hoạt ảnh trên chuỗi chính ... Tôi đoán là khi bài đăng thêm nhiệm vụ vào hàng đợi, nó bắt đầu ở "thời điểm chính xác" hơn, nhưng tôi rất muốn biết những gì xảy ra ở đây ở độ sâu hơn.

CHỈNH SỬA: Hãy để tôi làm rõ mọi thứ - câu hỏi của tôi là, tại sao bắt đầu hoạt ảnh trên bài đăng lại khiến nó khởi động, khi bắt đầu hoạt ảnh trên chuỗi chính thì không.


Hành vi này có dành riêng cho phiên bản Android không? Tôi không thể tái tạo nó trên Android 4.1.2!
Akdeniz 12/12/12

Tôi đã tái tạo hành vi này trên Android 2.3.3. Nhưng cho AnimationDrawable! Phiên bản thông thường Animationbắt đầu hoạt ảnh thành công trên mỗi thiết lập. Trong AnimationDrawabletrường hợp; khi bạn cố gắng khởi động nó onCreate, nó không bắt đầu vì không được gắn vào chế độ xem tại thời điểm đó. Vì vậy, nó không phải là một vấn đề phân luồng cho AnimationDrawable. Có thể điều tương tự áp dụng cho Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Câu trả lời:


161

post : post khiến Runnable được thêm vào hàng đợi tin nhắn,

Runnable: Đại diện cho một lệnh có thể được thực thi. Thường được sử dụng để chạy mã trong một Thread khác.

run () : Bắt đầu thực thi phần đang hoạt động của mã lớp. Phương thức này được gọi khi một luồng được bắt đầu đã được tạo với một lớp triển khai Runnable.

getView().post(new Runnable() {

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

:getView().startAnimation(a);

trong mã của bạn,

bài đăng khiến Runnable ( sẽ được chạy trong một chuỗi khác) để thêm hàng đợi tin nhắn.

Vì vậy startAnimation sẽ được kích hoạt trong một chuỗi mới khi nó được tìm nạp từ messageQueue

[CHỈNH SỬA 1]

Tại sao chúng ta sử dụng một luồng mới thay vì luồng giao diện người dùng (luồng chính)?

Chủ đề giao diện người dùng:

  • Khi ứng dụng được khởi động, Ui Thread được tạo tự động

  • nó chịu trách nhiệm gửi các sự kiện đến các vật dụng thích hợp và điều này bao gồm các sự kiện vẽ.

  • Nó cũng là chuỗi bạn tương tác với các tiện ích Android

Ví dụ: nếu bạn chạm vào nút trên màn hình, chuỗi giao diện người dùng sẽ gửi sự kiện cảm ứng đến tiện ích con, từ đó thiết lập trạng thái được nhấn của nó và đăng yêu cầu vô hiệu hóa hàng đợi sự kiện. Chuỗi giao diện người dùng sắp xếp lại yêu cầu và thông báo cho tiện ích con để tự vẽ lại.

Điều gì xảy ra nếu người dùng nhấn một nút sẽ thực hiện LongOperation?

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

Giao diện người dùng bị đóng băng. Chương trình thậm chí có thể bị sập.

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

Nó phá vỡ quy tắc Android không bao giờ cập nhật giao diện người dùng trực tiếp từ chuỗi công nhân

Android cung cấp một số cách để truy cập chuỗi giao diện người dùng từ các chuỗi khác.

  • Activity.runOnUiThread (Runnable)
  • View.post (Runnable)
  • View.postDelayed (Runnable, lâu)
  • Người xử lý

Như bên dưới,

View.post (Runnable)

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();
}

Người xử lý

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();                
}

nhập mô tả hình ảnh ở đây

Để biết thêm thông tin

http://android-developers.blogspot.com/2009/05/p Đau-threading.html

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


15
Vậy tại sao việc bắt đầu hoạt ảnh trên bài đăng lại khác với việc chạy nó trên chuỗi chính, khi cả hai cuối cùng đều chạy trên cùng một chuỗi?
Gal

Bởi vì mô hình luồng đơn này có thể mang lại hiệu suất kém trong các ứng dụng Android.
Talha 12/12/12

1
Hiệu suất kém liên quan gì đến việc không hiển thị hoạt ảnh?
Gal

17
Tôi không nghĩ điều này trả lời cho câu hỏi, đây giống như một câu trả lời chung chung cho những người mới bắt đầu chưa biết gì về ui-thread và multi threading. Điều này không giải thích tại sao việc ném hoạt ảnh phía trước trong hàng đợi làm cho hoạt ảnh hoạt động; một hoạt ảnh được cho là thứ để thực thi trực tiếp trong ui-thread mà không sử dụng bất kỳ thủ thuật post () hoặc runOnUiThread () nào.
carrizo vào

3
Tất cả công việc của giao diện người dùng phải nằm trên Chủ đề chính (luồng giao diện người dùng). Mẹo làm cho hoạt ảnh hoạt động bằng cách sử dụng post () thay vì gọi ngay trong chuỗi chính là Thời gian: Nếu bạn gọi ngay trong chuỗi chính nghĩa là bạn đã nói "bắt đầu hoạt ảnh ngay bây giờ", Nhưng lúc này có thể chế độ xem chưa sẵn sàng. cho hoạt ảnh (đo, vẽ ...). Nhưng nếu bạn đưa nó vào post (), một lúc nào đó nó sẽ chờ startAnimation trong hàng đợi để chuẩn bị xem sẵn sàng cho hoạt ảnh.
NguyenDat

35

Điều này đang được thực hiện trên onCreate hay onCreateView? Nếu vậy, ứng dụng có thể không ở trạng thái mà Chế độ xem được gắn vào cửa sổ. Nhiều thuật toán dựa trên số liệu Chế độ xem có thể không hoạt động vì những thứ như số đo và vị trí của Chế độ xem có thể chưa được tính toán. Hoạt ảnh Android thường yêu cầu chúng chạy qua toán học giao diện người dùng

View.post thực sự xếp hàng hoạt ảnh trên vòng lặp thông báo của View, vì vậy một khi chế độ xem được gắn vào cửa sổ, nó sẽ thực thi hoạt ảnh thay vì để nó thực thi theo cách thủ công.

Bạn thực sự đang chạy mọi thứ trên chuỗi giao diện người dùng, nhưng vào một thời điểm khác


Câu trả lời được chấp nhận là gây hiểu lầm khi người đăng nói rằng "bài đăng gây ra Runnable (mã sẽ được chạy trong một chuỗi khác)". Đây là câu trả lời chính xác "Bạn thực sự đang chạy mọi thứ trên chuỗi giao diện người dùng, nhưng vào một thời điểm khác" - cộng với 1
smitty1

18

Có một cái nhìn ở đây cho một câu trả lời tốt. view.post () giống với handler.post () khá nhiều. Nó đi vào hàng đợi luồng chính và được thực thi sau khi các tác vụ đang chờ xử lý khác kết thúc. Nếu bạn gọi activity.runOnUiThread (), nó sẽ được gọi ngay lập tức trên chuỗi giao diện người dùng.


31
Một sự khác biệt lớn (và cực kỳ hữu ích) mà tôi đã tìm thấy là runnable trong view.post () sẽ được gọi khi View được hiển thị lần đầu tiên. IE, bạn có thể đặt nó để bắt đầu hoạt ảnh khi có sự lạm phát của Chế độ xem, sau đó vào một thời điểm nào đó trong tương lai, cuối cùng hãy thêm nó vào hệ thống phân cấp chế độ xem. Tại thời điểm đó, hoạt ảnh sẽ thực thi và bạn sẽ không phải lo lắng về điều đó.
DeeV 12/12/12

Trên thực tế, handler.post () không phải lúc nào cũng đăng một thông báo / runnable trên chuỗi chính. Nó phụ thuộc vào cách tạo ra trình xử lý (nó có thể được liên kết với một Looper trên một luồng khác). Mặt khác, view.post () sẽ luôn luôn chạy trên các chủ đề chính
Yair Kukielka

4

Vấn đề mà tôi nghĩ có thể là phương thức vòng đời mà bạn đang gọi phương thức post (). Bạn có đang làm điều đó trong onCreate () không? nếu vậy, hãy xem những gì tôi tìm thấy trong tài liệu onResume () của hoạt động:

onResume ()

Đã thêm vào API cấp 1 void onResume () Được gọi sau onRestoreInstanceState (Bundle), onRestart () hoặc onPause (), để hoạt động của bạn bắt đầu tương tác với người dùng. Đây là một nơi tốt để bắt đầu hoạt ảnh , mở các thiết bị truy cập độc quyền (chẳng hạn như máy ảnh), v.v.

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

Vì vậy, như Joe Plante đã nói, có thể chế độ xem chưa sẵn sàng để bắt đầu hoạt ảnh tại thời điểm bạn gọi post (), vì vậy hãy thử chuyển nó sang onResume ().

PD: Thực ra nếu bạn chuyển mã sang onResume () thì tôi nghĩ bạn có thể xóa lệnh gọi post () vì bạn đã ở trong ui-thread và khung nhìn đã sẵn sàng để bắt đầu hoạt ảnh.


3
onResumecó thể được gọi nhiều lần (màn hình chuyển sang chế độ ngủ, hoạt động bị đẩy sang ngăn xếp, v.v.) sau khi nó ở chế độ ban đầu khi "chế độ xem đã sẵn sàng". Nếu được gọi từ onResume, thì có thể cần một cờ để theo dõi thời tiết mà hoạt ảnh đã được bắt đầu, để tránh (lại) bắt đầu nhiều lần.
samis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.