*Tutoriel et exemples sur les
Handlers
Android
Un Handler
est une classe threading
définie dans le paquet android.os
par laquelle nous pouvons envoyer et traiter des objets Message
et Runnable
associés à la MessageQueue
d'un thread
.
Vous commencez par créer une instance de Handler
. Puis cette instance est associée à un seul thread
ainsi qu'à la file d'attente de messages de ce thread
. Le Handler
que vous venez de créer sera alors lié au thread
ou à la file de messages du thread
qui le crée. Il pourra alors délivrer des messages et des runnables
à cette file d'attente de messages et les exécuter au fur et à mesure qu'ils en sortent.
Utilisations de `Handler'.
Handler
a deux utilisations principales
- Planification des messages et des
runnables
qui doivent être exécutés dans le futur. - Mettre en file d'attente les actions qui doivent être exécutées dans le
thread
d'arrière-plan.
Handler' et
Looper'.
Handler
est une classe fondamentale pour la façon dont nous faisons le threading
dans android, au niveau de l'infrastructure. Elle travaille main dans la main avec le Looper
. Ensemble, ils sous-tendent tout ce que fait le threading
principal, y compris l'invocation des méthodes du cycle de vie de l'Activité
.
Looper
s'occupera de distribuer le travail sur son thread
à boucle de messages. D'autre part, Handler
aura deux rôles :
- Premièrement, il fournira une interface pour soumettre des messages à sa file d'attente
Looper
.
dans sa file d'attente.
Deuxièmement, il implémentera le callback pour traiter ces messages quand ils seront distribués par leLooper
.
Chaque Handler
est lié à un seul Looper
et, par extension, à un thread
et à sa looper
MessageQueueue.
Nous avons dit que Handler
fournit une interface pour soumettre le travail aux threads de Looper
. En dehors de cela, Handler
définit également le code qui traite les messages soumis.
Par exemple, dans le code suivant, la classe MyHandler
va surcharger la méthode handleMessage()
de Handler
.
C'est là que nous écrivons ensuite notre code de gestion des messages :
public class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// your message handling code here
}
}
Disons que nous avons créé un thread
appelé MyThread
. Nous pouvons alors créer notre handler
à l'intérieur de notre MyThread
thread
par l'intermédiaire de la fonction par défaut
constructeur
Handler()
.
Ainsi, myHandler
sera attaché au Looper
du thread
courant au lieu du Looper
du thread
principal :
public class MyThread extends Thread{
private Handler myHandler;
@Override
public void run() {
Looper.prepare();
myHandler = new MyHandler();
Looper.loop();
}
public Handler getHandler(){
return myHandler;
}
}
Ensuite, une fois lancé, le Looper
thread
va attendre à l'intérieur de la méthode loop()
de la classe Looper
que les messages soient ajoutés à sa file d'attente.
Ensuite, un autre thread
peut ajouter un message à cette file d'attente. Il peut le faire en utilisant la méthode submit()
.
Lorsque cela se produit, le thread
en attente va envoyer le message à notre cible MyHandler
en invoquant la méthode handleMessage()
du `handler.
L'instance/objet Handler
nous permet d'envoyer des messages à la classe Handler
à partir de n'importe quel thread
et par conséquent, il est toujours envoyé au thread
de Looper et traité par le bon
Handler`,
Handler
Exemples rapides
Regardons des exemples rapides de Handler
, des snippets et des howTos.
1. Comment montrer une activité splash avec Handler
?
Notre but est d'afficher une activité splash via Handler
.
Supposons que vous ayez ce layout comme notre activity_splash
. Il a imageview et plusieurs textviews.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android_id="@+id/activity_splash"
android_layout_width="match_parent"
android_layout_height="match_parent"
tools_context="com.pchef.cc.personalchef.Splash">
<ImageView
android_layout_width="match_parent"
android_layout_height="match_parent"
android_id="@+id/s_img"
android_alpha="0.7"
android_src="@drawable/splash"
android_scaleType="centerCrop"/>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_orientation="vertical"
android_layout_centerInParent="true">
<TextView
android_layout_width="200dp"
android_layout_height="wrap_content"
android_text="Personal Chef"
android_layout_gravity="center"
android_gravity="center"
android_textColor="#fff"
android_fontFamily="cursive"
android_textStyle="bold|italic"
android_layout_marginBottom="50dp"
android_textSize="60dp"/>
<TextView
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_gravity="center"
android_textColor="#fff"
android_textSize="22dp"
android_fontFamily="sans-serif-condensed"
android_id="@+id/tv1"
android_text="Dont know what to cook ?"
/>
<TextView
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_id="@+id/tv2"
android_gravity="center"
android_layout_marginLeft="20dp"
android_layout_marginRight="20dp"
android_textSize="18dp"
android_textColor="#fff"
android_fontFamily="sans-serif-condensed"
android_text="Our Personal Chef Can help you"
android_layout_marginTop="14dp" />
</LinearLayout>
</RelativeLayout>
Ensuite, nous pouvons venir créer notre SpashActivity
. Elle dérive de l'AppCompatActivity. Nous faisons plusieurs importations dont le Handler
lui-même ainsi que Glide, une bibliothèque d'imageloader.
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Spinner;
import com.bumptech.glide.Glide;
import java.util.concurrent.TimeUnit;
public class Splash extends AppCompatActivity {...}
Nous aurons une ImageView comme arrière-plan :
ImageView background;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
background = (ImageView) findViewById(R.id.s_img);
Nous allons utiliser glide pour charger l'image dans une imageview :
Glide.with(this)
.load(R.drawable.splash)
.into(background);
Enfin, nous venons et instancions notre handler
:
final Handler handler = new Handler();
Puis on invoque la méthode postDelayed()
en passant dans un Runnable
où nous implémentons la méthode run()
.
C'est à l'intérieur de la méthode run()
que nous faisons notre travail de fond. Notez que nous passons aussi le délai en millisecondes.
handler.postDelayed(new Runnable() {
@Override
public void run() {
//Do something after delay
finish();
startActivity(new Intent(Splash.this, Home.class));
}
}, 3000);
}
}
3. Comment utiliser un Handler
pour rafraîchir le WebView
Dans cet échantillon rapide, nous voulons utiliser la méthode postDelayed()
de Handler
pour rafraîchir un webview. Vous rafraîchissez un webview en utilisant la méthode reload()
du webview. Nous passons le temps de retard en millisecondes.
void refreshWebPage(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
LightningView webview = getCurrentWebView();
if (webview != null) {
webview.reload();
}
}
}, 200);
}
4. Comment utiliser un Handler
pour charger une animation ?
Android est un framework riche en animations. La possibilité d'utiliser des animations a été facilitée par la présence d'un Hnadler. Regardons un exemple de snippet qui peut nous montrer comment charger une animation en utilisant un Handler
.
Nous allons passer la vue à animer, l'identifiant de la ressource d'animation, le temps de retard et l'objet Contexte. Nous allons utiliser la méthode postDelayed
de Handler
. Là, nous passons notre classe annonyme Runnable
ainsi que le temps de retard.
public static void animationIn(final View view, final int animation, int delayTime, final Context context) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Animation inAnimation = AnimationUtils.loadAnimation(
context.getApplicationContext(), animation);
view.setAnimation(inAnimation);
view.setVisibility(View.VISIBLE);
}
}, delayTime);
}
C'était l'animation in, eh bien nous avons aussi l'animation out :
public static void animationOut(final View view, final int animation, int delayTime, final boolean isViewGone, final Context context) {
view.setVisibility(View.VISIBLE);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
Animation outAnimation = AnimationUtils.loadAnimation(
context.getApplicationContext(), animation);
view.setAnimation(outAnimation);
if (isViewGone)
view.setVisibility(View.GONE);
else
view.setVisibility(View.INVISIBLE);
}
}, delayTime);
}
Handler
post()
Handler
a une méthode appelée post()
:
post(Runnable r)
Comme vous pouvez le voir, cette méthode prend un objet exécutable. La méthode post()
fait en sorte que l'objet Runnable
r soit ajouté à la file d'attente des messages.
Voyons plusieurs exemples concrets de cette méthode :
1. Utiliser la méthode post()
du Handler
pour ouvrir un clavier
Voici comment utiliser la méthode post()
du Handler
pour ouvrir un clavier.
public void openIME(final EditText v) {
final boolean focus = v.requestFocus();
if (v.hasFocus()) {
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
InputMethodManager mgr = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
boolean result = mgr.showSoftInput(v, InputMethodManager.SHOW_FORCED);
log.debug("openIME " + focus + " " + result);
}
});
}
}
2. La méthode post()
de Handler
avec un Executor
Un Executor
est un objet qui exécute les tâches Runnable
soumises.
public DownloadStatusDeliveryImpl(final Handler handler) {
this.mDownloadStatusPoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
}
};
}
3. Classe utilitaire réutilisable Handler
complète
Voici un exemple de classe utilitaire qui explore davantage l'utilisation de Handler
:
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
public final class HandlerUtils {
private HandlerUtils() {
}
public static void uiPost(Runnable action) {
if (action == null) {
return;
}
uiHandler().post(action);
}
public static void uiPostDelayed(Runnable action, long delayMillis) {
if (action == null) {
return;
}
uiHandler().postDelayed(action, delayMillis);
}
public static void uiRemoveCallbacks(Runnable action) {
uiHandler().removeCallbacks(action);
}
public static void threadPost(Runnable action) {
if (action == null) {
return;
}
threadHandler().post(action);
}
public static void threadPostDelayed(Runnable action, long delayMillis) {
if (action == null) {
return;
}
threadHandler().postDelayed(action, delayMillis);
}
public static void threadRemoveCallbacks(Runnable action) {
threadHandler().removeCallbacks(action);
}
private static Handler uiHandler() {
return Holder.handler;
}
private interface Holder {
Handler handler = new Handler(Looper.getMainLooper());
}
private static Handler sThreadHandler;
private static synchronized Handler threadHandler() {
if (sThreadHandler == null) {
HandlerThread thread = new HandlerThread("HandlerUtils.sThreadHandler");
thread.start();
sThreadHandler = new Handler(thread.getLooper());
}
return sThreadHandler;
}
}
4. Comment exécuter un Runnable
spécifié sur le thread
principal ?
Nous passons l'action à exécuter sur le thread
principal.
public final class HandlerUtils {
private static Handler handler = new Handler(Looper.getMainLooper());
public static void runOnUiThread(Runnable action) {
if (Looper.myLooper() == Looper.getMainLooper()) {
action.run();
}
else {
handler.post(action);
}
}
}
Regardons quelques exemples complets.
Exemple 1 : Différents exemples de Handler
.
Cet exemple va explorer divers scénarios d'utilisation pratique que vous êtes susceptibles de rencontrer en utilisant la classe android.os.Handler
.
Etape 1 : Créer un projet Java ou Kotlin
Créez un projet java dans android studio. Vous pouvez également créer un projet Kotlin et utiliser le convertisseur de code pour convertir de java à Kotlin.
Étape 2 : Dépendances
Aucune dépendance n'est nécessaire pour ce projet.
Étape 3 : Permissions
Aucune permission n'est nécessaire pour ce projet.
Etape 4 : Conception de la mise en page
Il s'agit d'ajouter un certain nombre de textviews à votre activité principale. Ajoutez également un bouton. Vous pouvez les organiser en utilisant une disposition linéaire verticale :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.lomza.examples.handlers.MainActivity">
<TextView
android:id="@+id/tv_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_02"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_03"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_04"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_05"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_06"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button_07"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Post with a view Handler"/>
<TextView
android:id="@+id/tv_07"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Étape 5 : Écrire le code
Nous avons ensuite écrit notre code. Vous commencez par étendre le AppCompatActivity
pour créer notre activité principale.
L'activité principale avec des méthodes pour démontrer l'utilisation de Handlers, Runnables, et Messages :
public class MainActivity extends AppCompatActivity {
private TextView tv01;
private TextView tv02;
private TextView tv03;
private TextView tv04;
private TextView tv05;
private TextView tv06;
private TextView tv07;
private Button button07;
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private Handler currentThreadHandler;
private Handler decorViewHandler;
private final ChangeTextHandler customHandler = new ChangeTextHandler(this);
private final Handler notLeakyHandler = new Handler();
private final Runnable notLeakyRunnable = new ChangeTextRunnable(this, "Hi from leak-safe Runnable!");
private static final String TAG = "[Handlers]";
private static final String BUNDLE_KEY = "greeting";
Tout d'abord, vous pouvez poster une tâche avec le thread
ordinaire en utilisant le code suivant :
private void postTaskWithOrdinaryThread() {
Runnable notForHandlerTask = new Runnable() {
@Override
public void run() {
// as written here - https://developer.android.com/guide/components/processes-and-threads.html#Threads,
// do NOT access the Android UI toolkit from outside the UI thread, as sth unexpected may happen
// for instance, you might get android.view.ViewRootImpl$CalledFromWrongThreadException
tv01.setText("Hi from Thread(runnable)!");
// if you call thread.run(), this would be TRUE, as no new Thread would be created
// read the explanation here - http://stackoverflow.com/a/35264580/655275
Log.d(TAG, "[postTaskWithOrdinaryThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
Thread thread = new Thread(notForHandlerTask);
thread.start();
}
Et voici comment vous pouvez poster une tâche avec handler
sur le thread
principal :
@UiThread
private void postTaskWithHandlerOnMainThread() {
Runnable mainThreadTask = new Runnable() {
@Override
public void run() {
// since we use Looper.getMainLooper(), we can safely update the UI from here
tv02.setText("Hi from Handler(Looper.getMainLooper()) post!");
Log.d(TAG, "[postTaskWithHandlerOnMainThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
mainThreadHandler.post(mainThreadTask);
}
Voici comment vous pouvez poster une tâche avec handler
sur le thread
concurrent :
private void postTaskWithHandlerOnCurrentThread() {
currentThreadHandler = new Handler();
Runnable currentThreadTask = new Runnable() {
@Override
public void run() {
// since we use current thread (and from onCreate(), it's the UI thread), we can safely update the UI from here
tv03.setText("Hi from Handler() post!");
Log.d(TAG, "[postTaskWithHandlerOnCurrentThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
currentThreadHandler.post(currentThreadTask);
}
Et voici comment vous pouvez poster une tâche dans le thread
d'arrière-plan :
private void postTaskInsideBackgroundTask() {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// pretend to do something "background-y"
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
tv04.setText("Hi from a Handler inside of a background Thread!");
}
});
}
});
backgroundThread.start();
}
Voici comment utiliser l'annotation code>@UiThread</code pour poster une tâche avec cette fenêtre et ce textview :
@UiThread
private void postTaskWithThisWindowAndTextViewHandlers() {
// this line will return null from onCreate() (and even if called from onResume()) and cause NPE when trying to post();
// this is because the handler isn't attached to the view if it's not fully visible
decorViewHandler = getWindow().getDecorView().getHandler();
Runnable decorViewTask = new Runnable() {
@Override
public void run() {
// View's post() uses UI handler internally
tv07.post(new Runnable() {
@Override
public void run() {
tv07.setText("Hi from getWindow().getDecorView().getHandler() > TextView.post()!");
Log.d(TAG, "[postTaskWithThisWindowAndTextViewHandlers] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
});
}
};
decorViewHandler.post(decorViewTask);
}
Et voici comment vous pouvez poster une tâche avec handler
en arrière-plan thread
:
private void postTaskWithHandlerOnBackgroundThread() {
final Runnable pretendsToBeSomeOtherTask = new Runnable() {
@Override
public void run() {
Log.d(TAG, "[postTaskWithHandlerOnBackgroundThread] Is there a looper? " + (Looper.myLooper() != null));
// you'll get java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// this is because Handlers need to have a Looper associated with them; when the task was run on the main thread,
// main thread looper was used, but if we call this from the background thread, there is NO looper to use
// read more - https://developer.android.com/reference/android/os/Looper.html
postTaskWithHandlerOnCurrentThread();
}
};
final Thread thread = new Thread(pretendsToBeSomeOtherTask);
thread.start();
}
Voici comment vous pouvez poster une tâche avec un Handler
et un Runnable
non fuyants :
private void postTaskWithNotLeakyHandlerAndRunnable() {
// in order to eliminate leaks, both Handler and Runnable should be static
// static inner classes do not hold an implicit reference to the outer class
// it seems like a lot of useless work, but it's the most accurate and bug-free way
// read more - http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
notLeakyHandler.postDelayed(notLeakyRunnable, 500);
}
Voici le code complet ;
**MainActivity.java
`
package com.lomza.examples.handlers;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
/**
* Main activity with methods to demonstrates the usage of Handlers, Runnables, and Messages :)
*
* @author Antonina Tkachuk
*/
public class MainActivity extends AppCompatActivity {
private TextView tv01;
private TextView tv02;
private TextView tv03;
private TextView tv04;
private TextView tv05;
private TextView tv06;
private TextView tv07;
private Button button07;
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private Handler currentThreadHandler;
private Handler decorViewHandler;
private final ChangeTextHandler customHandler = new ChangeTextHandler(this);
private final Handler notLeakyHandler = new Handler();
private final Runnable notLeakyRunnable = new ChangeTextRunnable(this, "Hi from leak-safe Runnable!");
private static final String TAG = "[Handlers]";
private static final String BUNDLE_KEY = "greeting";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
@Override
protected void onStart() {
super.onStart();
postTaskWithOrdinaryThread();
postTaskWithHandlerOnMainThread();
postTaskWithHandlerOnCurrentThread();
postTaskInsideBackgroundTask();
//postTaskWithHandlerOnBackgroundThread();
postTaskWithNotLeakyHandlerAndRunnable();
sendMessageToChangeTextHandler();
}
@Override
protected void onStop() {
super.onStop();
// when posting Runnables or Messages, always remember to call removeCallbacks() or removeMessages()
// or removeCallbacksAndMessages() for both.
// this ensures that all pending tasks don't execute in vain; for instance, the user has left our activity
// and he doesn't really care if some job is finished or not, so it's our responsibility to cancel it
// pass null to remove ALL callbacks and messages
mainThreadHandler.removeCallbacks(null);
currentThreadHandler.removeCallbacks(null);
if (decorViewHandler != null)
decorViewHandler.removeCallbacks(null);
customHandler.removeCallbacksAndMessages(null);
notLeakyHandler.removeCallbacks(notLeakyRunnable);
}
private void initView() {
tv01 = (TextView) findViewById(R.id.tv_01);
tv02 = (TextView) findViewById(R.id.tv_02);
tv03 = (TextView) findViewById(R.id.tv_03);
tv04 = (TextView) findViewById(R.id.tv_04);
tv05 = (TextView) findViewById(R.id.tv_05);
tv06 = (TextView) findViewById(R.id.tv_06);
tv07 = (TextView) findViewById(R.id.tv_07);
button07 = (Button) findViewById(R.id.button_07);
button07.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
postTaskWithThisWindowAndTextViewHandlers();
}
});
}
private void postTaskWithOrdinaryThread() {
Runnable notForHandlerTask = new Runnable() {
@Override
public void run() {
// as written here - https://developer.android.com/guide/components/processes-and-threads.html#Threads,
// do NOT access the Android UI toolkit from outside the UI thread, as sth unexpected may happen
// for instance, you might get android.view.ViewRootImpl$CalledFromWrongThreadException
tv01.setText("Hi from Thread(runnable)!");
// if you call thread.run(), this would be TRUE, as no new Thread would be created
// read the explanation here - http://stackoverflow.com/a/35264580/655275
Log.d(TAG, "[postTaskWithOrdinaryThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
Thread thread = new Thread(notForHandlerTask);
thread.start();
}
@UiThread
private void postTaskWithHandlerOnMainThread() {
Runnable mainThreadTask = new Runnable() {
@Override
public void run() {
// since we use Looper.getMainLooper(), we can safely update the UI from here
tv02.setText("Hi from Handler(Looper.getMainLooper()) post!");
Log.d(TAG, "[postTaskWithHandlerOnMainThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
mainThreadHandler.post(mainThreadTask);
}
private void postTaskWithHandlerOnCurrentThread() {
currentThreadHandler = new Handler();
Runnable currentThreadTask = new Runnable() {
@Override
public void run() {
// since we use current thread (and from onCreate(), it's the UI thread), we can safely update the UI from here
tv03.setText("Hi from Handler() post!");
Log.d(TAG, "[postTaskWithHandlerOnCurrentThread] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
};
currentThreadHandler.post(currentThreadTask);
}
private void postTaskInsideBackgroundTask() {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
// pretend to do something "background-y"
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
tv04.setText("Hi from a Handler inside of a background Thread!");
}
});
}
});
backgroundThread.start();
}
@UiThread
private void postTaskWithThisWindowAndTextViewHandlers() {
// this line will return null from onCreate() (and even if called from onResume()) and cause NPE when trying to post();
// this is because the handler isn't attached to the view if it's not fully visible
decorViewHandler = getWindow().getDecorView().getHandler();
Runnable decorViewTask = new Runnable() {
@Override
public void run() {
// View's post() uses UI handler internally
tv07.post(new Runnable() {
@Override
public void run() {
tv07.setText("Hi from getWindow().getDecorView().getHandler() > TextView.post()!");
Log.d(TAG, "[postTaskWithThisWindowAndTextViewHandlers] Current looper is a main thread (UI) looper: "
+ (Looper.myLooper() == Looper.getMainLooper()));
}
});
}
};
decorViewHandler.post(decorViewTask);
}
private void postTaskWithHandlerOnBackgroundThread() {
final Runnable pretendsToBeSomeOtherTask = new Runnable() {
@Override
public void run() {
Log.d(TAG, "[postTaskWithHandlerOnBackgroundThread] Is there a looper? " + (Looper.myLooper() != null));
// you'll get java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// this is because Handlers need to have a Looper associated with them; when the task was run on the main thread,
// main thread looper was used, but if we call this from the background thread, there is NO looper to use
// read more - https://developer.android.com/reference/android/os/Looper.html
postTaskWithHandlerOnCurrentThread();
}
};
final Thread thread = new Thread(pretendsToBeSomeOtherTask);
thread.start();
}
private void postTaskWithNotLeakyHandlerAndRunnable() {
// in order to eliminate leaks, both Handler and Runnable should be static
// static inner classes do not hold an implicit reference to the outer class
// it seems like a lot of useless work, but it's the most accurate and bug-free way
// read more - http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
notLeakyHandler.postDelayed(notLeakyRunnable, 500);
}
private static class ChangeTextRunnable implements Runnable {
private final WeakReference<MainActivity> activity;
private final String greetingMessage;
public ChangeTextRunnable(MainActivity activity, String greetingMessage) {
this.activity = new WeakReference<>(activity);
this.greetingMessage = greetingMessage;
}
public void run() {
if (greetingMessage == null) {
Log.e(TAG, "The message is null ChangeTextRunnable.run()!");
return;
}
MainActivity activity = this.activity.get();
if (activity == null) {
Log.e(TAG, "Activity is null ChangeTextRunnable.run()!");
return;
}
activity.tv05.setText(greetingMessage);
}
}
// === OBTAIN AND HANDLE A MESSAGE ===
private void sendMessageToChangeTextHandler() {
Message messageToSend = customHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_KEY, "Hi from custom inner Handler!");
messageToSend.setData(bundle);
messageToSend.what = 6;
customHandler.sendMessage(messageToSend);
}
private static class ChangeTextHandler extends Handler {
private final WeakReference<MainActivity> activity;
public ChangeTextHandler(MainActivity activity) {
this.activity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = this.activity.get();
if (activity == null) {
Log.e(TAG, "Activity is null ChangeTextHandler.handleMessage()!");
return;
}
final String text = (String) msg.getData().get(BUNDLE_KEY);
if (!TextUtils.isEmpty(text)) {
switch (msg.what) {
case 6:
activity.tv06.setText(text);
break;
default:
activity.tv01.setText(text);
break;
}
}
}
}
// === END - OBTAIN AND HANDLE A MESSAGE ===
}
Exécuter
Enfin, exécutez le projet.
Référence
Voici les liens de référence du code :
Numéro | Lien |
---|---|
1. | Télécharger le code |
2. | Suivre l'auteur du code |
Exemple 2 : Handler
avec ProgressBar Exemple
Dans ce tutoriel, nous voulons voir comment poster des mises à jour d'un thread
d'arrière-plan vers le thread
de l'interface utilisateur en utilisant un Handler
.
Nous cliquons sur un bouton et simulons un travail lourd en arrière-plan. Pendant ce temps, nous sommes en mesure de mettre à jour notre barre de progression au fur et à mesure que le travail se poursuit.
(a). MainActivity.java
C'est l'activité principale. Nous dérivons de la AppCompatActivity
. Un des imports que nous ajoutons est le Handler
du paquet android.os
.
...
import android.os.Handler;
...
Nous allons maintenir trois champs d'instance :
Handler
- une classe définie dans le paquetandroid.os
par laquelle nous pouvons envoyer et traiter les objetsMessage
etRunnable
associés à laMessageQueue
d'unthread
.ProgressBar
- un widget qui nous permet d'afficher la progression.Button
- un bouton d'action.
Voici le code complet :
package info.camposha.mrhandler;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
private ProgressBar mProgressBar;
private Button mStartButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
mProgressBar = findViewById(R.id.mProgressBar);
mStartButton = findViewById(R.id.startBtn);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
performStuff();
}
});
}
private void performStuff() {
//Simulate Heavy task in background thread
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 30; i++) {
final int currentProgressCount = i;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Post updates to the User Interface
mHandler.post(new Runnable() {
@Override
public void run() {
mProgressBar.setProgress(currentProgressCount);
}
});
}
}
}).start();
}
}
(b). activité_main.xml
C'est le layout principal de l'activité. A la racine, nous avons un [LinearLayout] (https://camposha.info/android/linearlayout). Cet élément nous permet de disposer ses enfants de façon linéaire, horizontalement ou verticalement. Nous avons également un TextView pour afficher le texte d'en-tête de notre application. Nous avons également une barre de progression pour afficher la progression. Nous avons également un bouton qui, lorsqu'il est cliqué, va démarrer le thread
pour effectuer notre travail en arrière-plan thread
.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="match_parent"
android_gravity="center"
android_orientation="vertical"
tools_context=".MainActivity">
<TextView
android_id="@+id/headerLabel"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_layout_alignParentTop="true"
android_layout_centerHorizontal="true"
android_fontFamily="casual"
android_text="Handler ProgressBar"
android_textAllCaps="true"
android_textSize="24sp"
android_textStyle="bold" />
<ProgressBar
android_id="@+id/mProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_layout_margin="10dp"
android_indeterminate="false"
android_max="10" />
<Button
android_id="@+id/startBtn"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Start" />
</LinearLayout>
Exemple 3 : Exemple simple de Handler
avec Timer
Cet exemple explore comment utiliser Handler
avec Timer
.
Step 1 : Dépendances
Aucune dépendance de tierce partie n'est nécessaire pour cet exemple.
Step 2 : Layouts
Nous n'avons pas besoin de layout pour cet exemple.
Étape 3 : Écrire le code
Voici le code complet :
HandlerActivity.java
package com.sdwfqin.sample.handler;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.sdwfqin.sample.R;
import java.lang.ref.WeakReference;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author zhangqin
*/
public class HandlerActivity extends AppCompatActivity {
private static final String TAG = "HandlerActivity";
private MyHandler mMyHandler;
private Timer mTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mMyHandler = new MyHandler(this);
mMyHandler.sendEmptyMessage(1);
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
mMyHandler.sendEmptyMessage(2);
}
}, 1000, 1000);
mMyHandler.sendEmptyMessage(3);
}
static class MyHandler extends Handler {
private WeakReference<HandlerActivity> mActivity;
public MyHandler(HandlerActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = mActivity.get();
switch (msg.what) {
case 1:
Log.e(TAG, "handlerA:case:1");
break;
case 2:
Log.e(TAG, "handlerA:case:2");
break;
case 3:
Log.e(TAG, "handlerA:case:3");
break;
default:
break;
}
}
}
@Override
protected void onDestroy() {
mTimer.cancel();
mMyHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
Référence
Téléchargez le code ci-dessous :
Numéro | Lien | |
---|---|---|
1. | Télécharger le code | |
2. | Suivre l'auteur du code | [Suivre l'auteur du code]. |
Handler
à mémoire sécurisée
L'implémentation originale de Handler
garde toujours une référence dure à handler
dans la file d'attente d'exécution. Tout objet dans Message ou Runnable
envoyé à android.os.Handler
sera référencé en dur pendant un certain temps. Si vous créez un Runnable
anonyme et que vous appelez postDelayed
avec un grand timeout, ce Runnable
sera gardé en mémoire jusqu'à ce que le timeout passe. Même si votre Runnable
semble petit, il fait indirectement référence à la classe propriétaire, qui est généralement quelque chose d'aussi grand que Activity
ou Fragment
.
Vous pouvez en savoir plus [ici] (https://medium.com/bumble-tech/android-handler-memory-leaks-7291c5be6101).
Solution - Utiliser un Handler
faible
Qu'est-ce qu'un handler
faible ?
C'est une implémentation Memory safer de
android.os.Handler
.
WeakHandler
est plus délicat que android.os.Handler
, il gardera les WeakReferences
vers les runnables
et les messages, et GC pourra les collecter une fois que l'instance de WeakHandler
ne sera plus référencée.
Comment l'utiliser ?
Etape 1 : L'installer
Enregistrez Jitpack
comme une url maven dans votre fichier build.gradle
au niveau du projet comme suit :
repositories {
maven { url 'https://jitpack.io' }
}
Ensuite, ajoutez la déclaration de mise en œuvre sous la fermeture des dépendances dans votre fichier build.gradle
de niveau application :
dependencies {
implementation 'com.github.badoo:android-weak-handler:1.2'
}
Sync pour l'installer.
Étape 2 : Écrire le code
Vous pouvez simplement utiliser WeakHandler
en remplacement de android.os.Handler
. Il suffit de l'utiliser comme vous le feriez avec le Handler
. Voici un exemple :
ExempleActivity.java
import com.badoo.mobile.util.WeakHandler;
public class ExampleActivity extends Activity {
private WeakHandler handler; // We still need at least one hard reference to WeakHandler
protected void onCreate(Bundle savedInstanceState) {
handler = new WeakHandler();
...
}
private void onClick(View view) {
handler.postDelayed(new Runnable() {
view.setVisibility(View.INVISIBLE);
}, 5000);
}
}
Référence
Trouvez les liens de référence ci-dessous :
Numéro | Lien |
---|---|
1. | Lire la suite |