Dans un service Android, j'ai créé un ou plusieurs threads pour effectuer certaines tâches en arrière-plan.
Dans certaines situations, un fil doit poster certaines tâches dans la file de messages du fil principal, par exemple un Runnable
.
Existe-t-il un moyen d'obtenir Handler
du thread principal et d'y poster Message
/Runnable
à partir de mon autre thread?
Merci,
NOTE: Cette réponse a tellement attiré l'attention que je dois la mettre à jour. Depuis que la réponse originale a été publiée, le commentaire de @dzeikei a attiré presque autant d'attention que la réponse d'origine. Alors voici 2 solutions possibles:
1. Si votre thread d'arrière-plan comporte une référence à un objet Context
:
Assurez-vous que vos threads de travail d’arrière-plan ont accès à un objet Context (il peut s’agir du contexte Application ou du contexte Service). Ensuite, faites ceci dans le thread de travail en arrière-plan:
// 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. Si votre thread d'arrière-plan n'a pas (ou n'a pas besoin) d'objet Context
(suggéré par @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);
Comme l'a souligné correctement un commentateur ci-dessous, il ne s'agit pas d'une solution générale pour les services, mais uniquement pour les threads lancés à partir de votre activité (un service peut être un tel thread, mais tous ne le sont pas) . activité communication s'il vous plaît lire toute la section Services de la doc officielle - c'est complexe, il serait donc utile de comprendre les bases: http://developer.Android.com/guide/components/services.html#Notifications
La méthode ci-dessous peut fonctionner dans les cas les plus simples.
Si je vous ai bien compris, vous avez besoin que du code soit exécuté dans le fil graphique de l’application (vous ne pouvez penser à rien d’autre appelé "fil" principal). Pour cela, il existe une méthode sur Activity
:
someActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//Your code to run in GUI thread here
}//public void run() {
});
J'espère que c'est ce que vous recherchez.
Il existe un autre moyen simple, si vous n’avez pas accès au contexte.
1). Créez un gestionnaire à partir du boucleur principal:
Handler uiHandler = new Handler(Looper.getMainLooper());
2) Implémenter une interface Runnable:
Runnable runnable = new Runnable() { // your code here }
3) Postez votre Runnable sur uiHandler:
uiHandler.post(runnable);
C'est tout ;-) Amusez-vous avec les threads, mais n'oubliez pas de les synchroniser.
Si vous exécutez du code dans un fil de discussion, par exemple retarder certaines actions, vous devez alors appeler runOnUiThread
à partir du contexte. Par exemple, si votre code est dans la classe MainActivity
, utilisez ceci:
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
myAction();
}
});
Si votre méthode peut être invoquée à partir de main (thread d'interface utilisateur) ou d'autres threads, vous devez effectuer une vérification comme suit:
public void myMethod() {
if( Looper.myLooper() == Looper.getMainLooper() ) {
myAction();
}
else {
}
Un bloc de code condensé est le suivant:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// things to do on the main thread
}
});
Cela n'implique pas la transmission de la référence d'activité ou de l'application.
Kotlin Equivalent:
Handler(Looper.getMainLooper()).post(Runnable {
// things to do on the main thread
})
Quand vous êtes sur une activité, utilisez
runOnUiThread {
//code that runs in main
}
Quand vous avez un contexte d'activité, mContext puis utilisez
mContext.runOnUiThread {
//code that runs in main
}
Lorsque vous êtes quelque part où pas de contexte disponible, utilisez
Handler(Looper.getMainLooper()).post {
//code that runs in main
}
Une méthode à laquelle je peux penser est la suivante:
1) Laissez l'interface utilisateur se lier au service.
2) Exposer une méthode comme celle ci-dessous par la Binder
qui enregistre votre Handler
:
public void registerHandler(Handler handler) {
mHandler = handler;
}
3) Dans le thread d'interface utilisateur, appelez la méthode ci-dessus après la liaison au service:
mBinder.registerHandler(new Handler());
4) Utilisez le gestionnaire dans le fil du service pour poster votre tâche:
mHandler.post(runnable);
Je sais que c’est une vieille question, mais j’ai trouvé un fil conducteur unique que j’utilise aussi bien dans Kotlin que dans Java. Ce n'est peut-être pas la meilleure solution pour un service, mais pour appeler quelque chose qui va changer l'interface utilisateur à l'intérieur d'un fragment, c'est extrêmement simple et évident.
Java (8):
getActivity().runOnUiThread(()->{
//your main thread code
});
Kotlin:
this.runOnUiThread {
//your main thread code
}
HandlerThread
est la meilleure option pour les threads Java normaux dans Android.
requestHandler
post
une Runnable
tâche sur requestHandler
Communication avec le fil d'interface utilisateur de HandlerThread
Handler
avec Looper
pour le thread principal: responseHandler
et remplacez la méthode handleMessage
Runnable
d'un autre thread (HandlerThread
dans ce cas), appelez sendMessage
à responseHandler
sendMessage
de handleMessage
dans responseHandler
.Message
et le traiter, mettre à jour l'interface utilisateurExemple: Mettez à jour TextView
avec les données reçues d'un service Web. Étant donné que le service Web doit être appelé sur un thread autre que l'interface utilisateur, créez HandlerThread
pour l'opération sur le réseau. Une fois que vous avez récupéré le contenu du service Web, envoyez un message à votre gestionnaire de thread principal (UI Thread) et Handler
gérera le message et mettra à jour l'interface utilisateur.
Exemple de code:
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);
Articles utiles:
handlerthreads-and-pourquoi-vous-devriez-être-en-utilisant-les-dans-vos-applications-Android
La manière la plus simple, surtout si vous n’avez pas de contexte, si vous utilisez RxAndroid, vous pouvez faire:
AndroidSchedulers.mainThread().scheduleDirect {
runCodeHere()
}
Code Kotlin plus précis en utilisant handler:
Handler(Looper.getMainLooper()).post {
// your codes here run on main Thread
}
Suivez cette méthode. De cette manière, vous pouvez simplement mettre à jour l'interface utilisateur à partir d'un thread d'arrière-plan. runOnUiThread fonctionne sur le thread principal (UI). Je pense que cet extrait de code est moins complexe et facile, surtout pour les débutants.
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();
}
});
}
});
dans le cas d'un service
créer un gestionnaire dans oncreate
handler = new Handler();
puis utilisez-le comme ça
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
public void mainWork() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//Add Your Code Here
}
});
}
Cela peut également fonctionner dans une classe de service sans problème.
pour Kotlin, vous pouvez utiliser Anko corountines :
mettre à jour
doAsync {
...
}
obsolète
async(UI) {
// Code run on UI thread
// Use ref() instead of this@MyActivity
}