Chạy mã trong luồng chính từ luồng khác


326

Trong một dịch vụ Android, tôi đã tạo (các) luồng để thực hiện một số tác vụ nền.

Tôi có một tình huống trong đó một luồng cần đăng một số tác vụ nhất định trên hàng đợi tin nhắn của luồng chính, ví dụ như một Runnable .

Có cách nào để lấy Handlerchủ đề chính và bài Message/Runnable lên nó từ chủ đề khác của tôi không?

Cảm ơn,


2
Bạn cũng có thể sử dụng Trình thu phát tùy chỉnh .... hãy thử câu trả lời của tôi tại đây, [Trình thu phát nội bộ] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

Có rất nhiều cách. Ngoài câu trả lời của David & nhận xét của David trong câu trả lời của anh ấy, (3) bạn có thể sử dụng Bộ thu phát hoặc (4) chuyển bộ xử lý trong phần bổ sung của Intent được sử dụng để bắt đầu dịch vụ, sau đó truy xuất bộ xử lý của luồng chính trong dịch vụ bằng getIntent ( ) .getExtras ().
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, Tại sao lại gắn thẻ câu hỏi này từ năm 2012 với hầu hết các câu trả lời trong Java bằng thẻ kotlin?
Tenfour04

Câu trả lời:


617

LƯU Ý: Câu trả lời này đã nhận được rất nhiều sự chú ý, mà tôi cần cập nhật nó. Kể từ khi câu trả lời ban đầu được đăng, bình luận từ @dzeikei đã nhận được gần như nhiều sự chú ý như câu trả lời ban đầu. Vì vậy, đây là 2 giải pháp có thể:

1. Nếu chủ đề nền của bạn có một tham chiếu đến một Context đối tượng:

Đảm bảo rằng các luồng nhân viên nền của bạn có quyền truy cập vào một đối tượng Ngữ cảnh (có thể là bối cảnh Ứng dụng hoặc bối cảnh Dịch vụ). Sau đó, chỉ cần làm điều này trong chủ đề công nhân nền:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. Nếu chủ đề nền của bạn không có (hoặc cần) a Context đối tượng

(được đề xuất bởi @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

1
Cảm ơn David, nó đã giúp ích cho tôi, một điều nữa nếu bạn có thể giúp tôi, nếu tôi mở rộng Handler và xử lýMessage () nó sẽ ngăn luồng chính xử lý các thông điệp của nó chứ? đó chỉ là một câu hỏi vì tò mò ..
Ahmed

2
Không. Nếu bạn phân lớp Handler(hoặc sử dụng Handler.Callbackgiao diện), handleMessage()phương thức của bạn sẽ CHỈ được gọi cho các tin nhắn đã được đăng bằng trình xử lý của bạn. Chủ đề chính là sử dụng một trình xử lý khác để gửi / xử lý tin nhắn để không có xung đột.
David Wasser

205
Tôi tin rằng bạn thậm chí sẽ không cần ngữ cảnh nếu bạn sử dụngHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
Điểm nhỏ; mã của bạn không đi đến nơi ... hiện tại. Nó nên lànew Runnable(){@Override public void run() {....}};
Richard Tingle

1
@SagarDevanga Đây không phải là nơi thích hợp để hỏi một câu hỏi khác. Xin vui lòng gửi một câu hỏi mới, không bình luận cho một câu trả lời không liên quan. Bạn sẽ nhận được phản ứng tốt hơn và nhanh hơn theo cách đó.
David Wasser

138

Như một nhà bình luận dưới đây đã chỉ ra một cách chính xác, đây không phải là một giải pháp chung cho các dịch vụ, chỉ dành cho các luồng được khởi chạy từ hoạt động của bạn (một dịch vụ có thể là một luồng như vậy, nhưng không phải tất cả các luồng đó). Về chủ đề phức tạp của giao tiếp hoạt động dịch vụ, vui lòng đọc toàn bộ phần Dịch vụ của tài liệu chính thức - nó rất phức tạp, vì vậy sẽ phải trả tiền để hiểu những điều cơ bản: http://developer.android.com/guide/components/service.html # Thông báo

Phương pháp dưới đây có thể hoạt động trong trường hợp đơn giản nhất.

Nếu tôi hiểu bạn một cách chính xác, bạn cần một số mã được thực thi trong luồng GUI của ứng dụng (không thể nghĩ về bất kỳ thứ gì khác gọi là luồng "chính"). Đối với điều này có một phương pháp trên Activity:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

Bác sĩ: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

Hy vọng đây là những gì bạn đang tìm kiếm.


20
OP nói rằng anh ta đang chạy các chủ đề trong một Service. Bạn không thể sử dụng runOnUiThread()trong a Service. Câu trả lời này là sai lệch và không giải quyết câu hỏi được hỏi.
David Wasser

31

Có một cách đơn giản khác, nếu bạn không có quyền truy cập vào Bối cảnh.

1). Tạo một trình xử lý từ bộ xử lý chính:

Handler uiHandler = new Handler(Looper.getMainLooper());

2). Triển khai giao diện Runnable:

Runnable runnable = new Runnable() { // your code here }

3). Đăng Runnable của bạn lên uiHandler:

uiHandler.post(runnable);

Đó là tất cả ;-) Hãy vui vẻ với các chủ đề, nhưng đừng quên đồng bộ hóa chúng.


29

Nếu bạn chạy mã trong một luồng, ví dụ như trì hoãn một số hành động, thì bạn cần phải gọi runOnUiThreadtừ ngữ cảnh. Ví dụ: nếu mã của bạn nằm trong MainActivitylớp thì hãy sử dụng:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

Nếu phương thức của bạn có thể được gọi từ chính (luồng UI) hoặc từ các luồng khác, bạn cần kiểm tra như:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}

7
OP nói rằng anh ta đang chạy các chủ đề trong một Service. Bạn không thể sử dụng runOnUiThread()trong a Service.
David Wasser

@DavidWasser Điều đó có được ghi nhận ở bất cứ đâu không? Các tài liệu phương pháp không đề cập bất cứ điều gì về nó. developer.android.com/reference/android/app/NH
Greg Brown

1
@GregBrown Như bạn đã chỉ ra bởi liên kết của bạn đến Activitytài liệu, runOnUiThread()là một phương pháp Activity. Nó không phải là một phương pháp Service, do đó bạn không thể sử dụng nó. Điều gì có thể rõ ràng hơn thế?
David Wasser

@DavidWasser Đủ công bằng. Tôi thậm chí không nhớ tại sao tôi lại hỏi câu hỏi đó (nó đã được đăng gần một năm trước).
Greg Brown

24

Một khối mã cô đọng như sau:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

Điều này không liên quan đến việc chuyển xuống tham chiếu Hoạt động hoặc tham chiếu Ứng dụng.

Tương đương với Kotlin:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })

20

Phiên bản Kotlin

Khi bạn đang hoạt động , sau đó sử dụng

runOnUiThread {
    //code that runs in main
}

Khi bạn có bối cảnh hoạt động , mContext sau đó sử dụng

mContext.runOnUiThread {
    //code that runs in main
}

Khi bạn đang ở một nơi không có ngữ cảnh , hãy sử dụng

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

Không chắc đây là phiên bản của Kotlin, nhưng tôi đã phải thêm niềng răng:runOnUiThread(){...}
Wex

5

Cách đơn giản nhất đặc biệt là nếu bạn không có ngữ cảnh, nếu bạn đang sử dụng RxAndroid, bạn có thể làm:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}

5

Mã Kotlin chính xác hơn bằng cách sử dụng trình xử lý:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }

4

Một phương pháp tôi có thể nghĩ ra là:

1) Để UI liên kết với dịch vụ.
2) Đưa ra một phương thức như phương pháp dưới đây bằng cách Binderđăng ký của bạn Handler:

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3) Trong luồng UI, gọi phương thức trên sau khi ràng buộc với dịch vụ:

mBinder.registerHandler(new Handler());

4) Sử dụng trình xử lý trong luồng của Dịch vụ để đăng tác vụ của bạn:

mHandler.post(runnable);

3

HandlerThread là tùy chọn tốt hơn cho các chủ đề java bình thường trong Android.

  1. Tạo HandlerThread và khởi động nó
  2. Tạo Trình xử với Looper từ HandlerThread:requestHandler
  3. postmột Runnablenhiệm vụ trênrequestHandler

Giao tiếp với giao diện người dùng từ HandlerThread

  1. Tạo một Handlervới Looperchủ đề chính: responseHandlerhandleMessagephương thức ghi đè
  2. Bên trong Runnablenhiệm vụ của Chủ đề khác ( HandlerThreadtrong trường hợp này), hãy gọi sendMessagevềresponseHandler
  3. sendMessageKết quả này gọi handleMessagetrong responseHandler.
  4. Nhận các thuộc tính từ Messagevà xử lý nó, cập nhật giao diện người dùng

Ví dụ : Cập nhật TextViewdữ liệu nhận được từ một dịch vụ web. Vì dịch vụ web nên được gọi trên luồng không phải UI, được tạo HandlerThreadcho Hoạt động mạng. Khi bạn nhận được nội dung từ dịch vụ web, hãy gửi tin nhắn đến trình xử lý chủ đề chính (UI Thread) của bạn và đóHandler sẽ xử lý tin nhắn và cập nhật UI.

Mã mẫu:

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);

Bài viết hữu ích:

handlerthreads-and-why-you-be-be-used-them-in-your-android-apps

android-looper-handler-handlerthread-i


2

Tôi biết đây là một câu hỏi cũ, nhưng tôi đã bắt gặp một chủ đề chính mà tôi sử dụng trong cả Kotlin và Java. Đây có thể không phải là giải pháp tốt nhất cho một dịch vụ, nhưng để gọi một thứ gì đó sẽ thay đổi UI bên trong một đoạn thì điều này cực kỳ đơn giản và rõ ràng.

Java (8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

Thực hiện theo phương pháp này. Sử dụng cách này, bạn có thể chỉ cần cập nhật giao diện người dùng từ một luồng nền. runOnUiThread hoạt động trên luồng chính (UI). Tôi nghĩ đoạn mã này ít phức tạp và dễ dàng hơn, đặc biệt là cho người mới bắt đầu.

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

trong trường hợp dịch vụ

tạo một xử lý trong oncreate

 handler = new Handler();

sau đó sử dụng nó như thế này

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp hạn chế, ngay lập tức. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
iBug

1

đối với Kotlin, bạn có thể sử dụng Anko corountines :

cập nhật

doAsync {
   ...
}

không dùng nữa

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}

1

Vì vậy, tiện dụng nhất là để làm:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

Và sau:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}

0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Điều này cũng có thể làm việc trong một lớp dịch vụ không có vấn đề.


Mặc dù mã này có thể trả lời câu hỏi, bạn vẫn có thể xem xét thêm một vài câu giải thích vì điều này làm tăng giá trị câu trả lời của bạn cho những người dùng khác!
MBT

0

Với Kotlin, nó giống như thế này bên trong bất kỳ chức năng nào:

runOnUiThread {
   // Do work..
}

2
Điều này chỉ khi bạn đang ở trên một Activity.
Farsheel
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.