Запуск кода в основном потоке из другого потока
в службе android я создал поток (ы) для выполнения некоторых фоновых задач.
у меня есть ситуация, когда поток должен опубликовать определенную задачу в очереди сообщений основного потока, например Runnable
.
есть ли способ сделать Handler
основного потока и post Message
/Runnable
Это из моей другой теме ?
спасибо,
11 ответов
Примечание: этот ответ получил так много внимания, что мне нужно обновить его. Поскольку оригинальный ответ был опубликован, комментарий от @dzeikei получил почти столько же внимания, сколько и оригинальный ответ. Итак, вот 2 возможных решения:
1. Если ваш фоновый поток имеет ссылку на
Как правильно указал комментатор ниже, это не общее решение для служб, только для потоков, запущенных из вашей деятельности (служба может быть такой поток, но не все из них). По сложной теме сервисно-деятельностного общения ознакомьтесь, пожалуйста, со всем разделом услуг официального документа-он сложный, поэтому стоит разобраться в основах: http://developer.android.com/guide/components/services.html#Notifications
метод ниже может работать в самых простых случаях.
Если я правильно вас понимаю, вам нужен код для выполнения в GUI-потоке приложения (не могу думать ни о чем другом, называемом "основным" потоком).
Для этого существует метод on Activity
:
someActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//Your code to run in GUI thread here
}//public void run() {
});
надеюсь, это то, что вы ищете.
есть еще один простой способ, если у вас нет доступа к контексту.
1). Создайте обработчик из главного петлителя:
Handler uiHandler = new Handler(Looper.getMainLooper());
2). Реализовать интерфейс Runnable:
Runnable runnable = new Runnable() { // your code here }
3). Опубликуйте свой Runnable в uiHandler:
uiHandler.post(runnable);
Это все ; -) получайте удовольствие от потоков, но не забудьте синхронизировать их.
если вы запускаете код в потоке, например, задерживаете какое-то действие, вам нужно вызвать runOnUiThread
из контекста. Например, если ваш код находится внутри MainActivity
класс тогда используйте это:
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
myAction();
}
});
если ваш метод может быть вызван либо из main (UI thread), либо из других потоков, вам нужна проверка, например:
public void myMethod() {
if( Looper.myLooper() == Looper.getMainLooper() ) {
myAction();
}
else {
}
сжатый блок кода выглядит следующим образом:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// things to do on the main thread
}
});
это не включает передачу ссылки на действие или ссылки на приложение.
Котлин Эквивалент:
Handler(Looper.getMainLooper()).post(Runnable {
// things to do on the main thread
})
один метод, который я могу придумать, это:
1) позволить UI привязки к сервису.
2) выставьте метод, подобный приведенному ниже, с помощью Binder
, которая регистрирует ваш Handler
:
public void registerHandler(Handler handler) {
mHandler = handler;
}
3) в потоке пользовательского интерфейса, вызов метода после привязки к сервису:
mBinder.registerHandler(new Handler());
4) Используйте обработчик в потоке службы, чтобы опубликовать свою задачу:
mHandler.post(runnable);
HandlerThread
лучший вариант для обычных потоков java в Android .
- создать HandlerThread и запустить его
- создать проводник С петлителя из HandlerThread:
requestHandler
-
post
aRunnable
задачиrequestHandler
связь с потоком пользовательского интерфейса от HandlerThread
- создать
Handler
СLooper
для основного потока :responseHandler
и переопределитьhandleMessage
метод - внутри
Runnable
задача другого потока (HandlerThread
в этом случае), callsendMessage
onresponseHandler
- этой
sendMessage
результат вызоваhandleMessage
наresponseHandler
. - получить атрибуты из
Message
и обработать его, обновить UI
пример: Update TextView
С данными, полученными от веб-службы. Поскольку веб-служба должна вызываться в потоке, отличном от UI, created HandlerThread
для работы в Сети. Как только вы получите содержимое из веб-службы, отправьте сообщение в обработчик основного потока (UI Thread) и это Handler
обработает сообщение и обновит пользовательский интерфейс.
пример кода:
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);
полезные статьи:
handlerthreads-and-why-you-should-be-using-them-in-your-android-apps
следуйте этому методу. Используя этот способ вы можете просто обновить UI из фонового потока. runOnUiThread работает над основным потоком (UI). Я думаю, что этот фрагмент кода менее сложен и прост, особенно для начинающих.
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();
}
});
}
});
в случае услуги
создайте обработчик в oncreate
handler = new Handler();
тогда используйте его так
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
Я знаю, что это старый вопрос, но я наткнулся на основной поток, который я использую как в Kotlin, так и в Java. Это может быть не лучшее решение для службы, но для вызова чего-то, что изменит пользовательский интерфейс внутри фрагмента, это чрезвычайно просто и очевидно.
Java (8):
getActivity().runOnUiThread(()->{
//your main thread code
});
Котлин:
this.runOnUiThread(()->{
//your main thread code
});
public void mainWork() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//Add Your Code Here
}
});
}
Это также может работать в классе обслуживания не вопрос.
для Котлина вы можете использовать Anko corountines:
async(UI) {
// Code run on UI thread
// Use ref() instead of this@MyActivity
}