Instance unique d'une application

Soyez le premier à donner votre avis sur cette source.

Snippet vu 11 831 fois - Téléchargée 26 fois

Contenu du snippet

La demande revenant souvent sur les forums (et ayant cherché moi-même par le passé), j'ai développé une classe permettant de gérer le fait qu'une application ne doit être lancée qu'une seule fois. Son utilisation est très simple, il suffit de construire un objet UniqueInstance, en passant en paramètre le port de communication à utiliser, le message à communiquer si une application est déjà lancée, et les actions à effectuer lorsqu'une nouvelle instance a essayé d'être lancée (typiquement, passer l'application courante en premier-plan).

Source / Exemple :


import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.logging.Logger;

/**

  • Cette classe permet d'assurer l'unicité de l'instance de l'application. Deux applications ne peuvent pas être lancées
  • simultanément. Voici un exemple typique d'utilisation :
  • <pre>
  • // Port à utiliser pour communiquer avec l'instance de l'application lancée.
  • final int PORT = 32145;
  • // Message à envoyer à l'application lancée lorsqu'une autre instance essaye de démarrer.
  • final String MESSAGE = "nomDeMonApplication";
  • // Actions à effectuer lorsqu'une autre instance essaye de démarrer.
  • final Runnable RUN_ON_RECEIVE = new Runnable() {
  • public void run() {
  • if(mainFrame != null) {
  • // Si la fenêtre n'est pas visible (uniquement dans le systray par exemple), on la rend visible.
  • if(!mainFrame.isVisible())
  • mainFrame.setVisible(true);
  • // On demande à la mettre au premier plan.
  • mainFrame.toFront();
  • }
  • }
  • });
  • UniqueInstance uniqueInstance = new UniqueInstance(PORT, MESSAGE, RUN_ON_RECEIVE);
  • // Si aucune autre instance n'est lancée...
  • if(uniqueInstance.launch()) {
  • // On démarre l'application.
  • new MonApplication();
  • }
  • </pre>
  • @author rom1v
  • /
public class UniqueInstance { /** Port d'écoute utilisé pour l'unique instance de l'application. */ private int port; /** Message à envoyer à l'éventuelle application déjà lancée. */ private String message; /** Actions à effectuer lorsqu'une autre instance de l'application a indiqué qu'elle avait essayé de démarrer. */ private Runnable runOnReceive; /**
  • Créer un gestionnaire d'instance unique de l'application.
  • @param port
  • Port d'écoute utilisé pour l'unique instance de l'application.
  • @param message
  • Message à envoyer à l'éventuelle application déjà lancée, {@code null} si aucune action.
  • @param runOnReceive
  • Actions à effectuer lorsqu'une autre instance de l'application a indiqué qu'elle avait essayé de
  • démarrer, {@code null} pour aucune action.
  • /
public UniqueInstance(int port, String message, Runnable runOnReceive) { assert port > 0 && port < 1 << 16 : "Le port doit être entre 1 et 65535"; assert message != null || runOnReceive == null : "Il y a des actions à effectuer => le message ne doit pas être null."; this.port = port; this.message = message; this.runOnReceive = runOnReceive; } /**
  • Créer un gestionnaire d'instance unique de l'application. Ce constructeur désactive la communication entre
  • l'instance déjà lancée et l'instance qui essaye de démarrer.
  • @param port
  • Port d'écoute utilisé pour l'unique instance de l'application.
  • /
public UniqueInstance(int port) { this(port, null, null); } /**
  • Essaye de démarrer le gestionnaire d'instance unique. Si l'initialisation a réussi, c'est que l'instance est
  • unique. Sinon, c'est qu'une autre instance de l'application est déjà lancée. L'appel de cette méthode prévient
  • l'application déjà lancée qu'une autre vient d'essayer de se connecter.
  • @return {@code true} si l'instance de l'application est unique.
  • /
public boolean launch() { /* Indique si l'instance du programme est unique. */ boolean unique; try { /* On crée une socket sur le port défini. */ final ServerSocket server = new ServerSocket(port); /* Si la création de la socket réussit, c'est que l'instance du programme est unique, aucune autre n'existe. */ unique = true; /* Si il y a des actions à faire lorsqu'une autre instance essaye de démarrer... */ if(runOnReceive != null) { /* On lance un Thread d'écoute sur ce port. */ Thread portListenerThread = new Thread() { @Override public void run() { /* Tant que l'application est lancée... */ while(true) { try { /* On attend qu'une socket se connecte sur le serveur. */ final Socket socket = server.accept(); /* Si une socket est connectée, on écoute le message envoyé dans un nouveau Thread. */ new Thread() { @Override public void run() { receive(socket); } }.start(); } catch(IOException e) { Logger.getLogger("UniqueInstance").warning("Attente de connexion de socket échouée."); } } } }; /* Le Thread d'écoute de port est démon. */ portListenerThread.setDaemon(true); /* On démarre le Thread. */ portListenerThread.start(); } } catch(IOException e) { /* Si la création de la socket échoue, c'est que l'instance de n'est pas unique, une autre n'existe. */ unique = false; /* Si des actions sont prévues par l'instance déjà lancée... */ if(runOnReceive != null) { /*
  • Dans ce cas, on envoie un message à l'autre instance de l'application pour lui demander d'avoir le
  • focus (par exemple).
  • /
send(); } } return unique; } /**
  • Envoie un message à l'instance de l'application déjà ouverte.
  • /
private void send() { PrintWriter pw = null; try { /* On se connecte sur la machine locale. */ Socket socket = new Socket("localhost", port); /* On définit un PrintWriter pour écrire sur la sortie de la socket. */ pw = new PrintWriter(socket.getOutputStream()); /* On écrit le message sur la socket. */ pw.write(message); } catch(IOException e) { Logger.getLogger("UniqueInstance").warning("Écriture sur flux de sortie de la socket échoué."); } finally { if(pw != null) pw.close(); } } /**
  • Reçoit un message d'une socket s'étant connectée au serveur d'écoute. Si ce message est le message de l'instance
  • unique, l'application demande le focus.
  • @param socket
  • Socket connecté au serveur d'écoute.
  • /
private void receive(Socket socket) { Scanner sc = null; try { /* On n'écoute que 5 secondes, si aucun message n'est reçu, tant pis... */ socket.setSoTimeout(5000); /* On définit un Scanner pour lire sur l'entrée de la socket. */ sc = new Scanner(socket.getInputStream()); /* On ne lit qu'une ligne. */ String s = sc.nextLine(); /* Si cette ligne est le message de l'instance unique... */ if(message.equals(s)) { /* On exécute le code demandé. */ runOnReceive.run(); } } catch(IOException e) { Logger.getLogger("UniqueInstance").warning("Lecture du flux d'entrée de la socket échoué."); } finally { if(sc != null) sc.close(); } } }

Conclusion :


Exemple d'utilisation...

// Port à utiliser pour communiquer avec l'instance de l'application lancée.
final int PORT = 32145;
// Message à envoyer à l'application lancée lorsqu'une autre instance essaye de démarrer.
final String MESSAGE = "nomDeMonApplication";
// Actions à effectuer lorsqu'une autre instance essaye de démarrer.
final Runnable RUN_ON_RECEIVE = new Runnable() {
public void run() {
if(mainFrame != null) {
// Si la fenêtre n'est pas visible (uniquement dans le systray par exemple), on la rend visible.
if(!mainFrame.isVisible())
mainFrame.setVisible(true);
// On demande à la mettre au premier plan.
mainFrame.toFront();
}
}
});

UniqueInstance uniqueInstance = new UniqueInstance(PORT, MESSAGE, RUN_ON_RECEIVE);
// Si aucune autre instance n'est lancée...
if(uniqueInstance.launch()) {
// On démarre l'application.
new MonApplication();
}

A voir également

Ajouter un commentaire

Commentaires

blg81
Messages postés
2
Date d'inscription
mercredi 8 juin 2005
Statut
Membre
Dernière intervention
1 décembre 2006
-
"Car je ne vois pas pour quel type d'appli on aurait besoin de ça."

Pourtant la plupart des applications en ont besoin, par exemple quand on clique sur des fichiers .xls depuis l'explorateur Windows ils sont ouverts dans la même instance d'Excel.
rom1v
Messages postés
32
Date d'inscription
lundi 20 décembre 2004
Statut
Membre
Dernière intervention
21 septembre 2007
-
Tu ne passes pas d'arguments :(

Dans quel cas as-tu besoin de passer des arguments? Car je ne vois pas pour quel type d'appli on aurait besoin de ça.

Sinon il faut améliorer un peu, et rajouter cette possibilité avec la sérialisation par exemple?
blg81
Messages postés
2
Date d'inscription
mercredi 8 juin 2005
Statut
Membre
Dernière intervention
1 décembre 2006
-
Heu ... comment passe-t-on des arguments au concurrent pour une nouvelle instance?

Par exemple on appelle:
> uneApplication arg1, arg2 ...

Puis:
> uneApplication arg3, arg4 ...

comment l'instance existante reçoit-elle les arguments du second appel? par exemple, comment dans:
final Runnable RUN_ON_RECEIVE = new Runnable() {
public void run() {
if(mainFrame != null) {
// Si la fenêtre n'est pas visible (uniquement dans le systray par exemple), on la rend visible.
if(!mainFrame.isVisible())
mainFrame.setVisible(true);
// On demande à la mettre au premier plan.
mainFrame.toFront();
}

passer les nouveaux arguments?
rom1v
Messages postés
32
Date d'inscription
lundi 20 décembre 2004
Statut
Membre
Dernière intervention
21 septembre 2007
-
Merci bien Ijinn j'ai complété mon article: http://rom.developpez.com/java-instance-unique/
rom1v
Messages postés
32
Date d'inscription
lundi 20 décembre 2004
Statut
Membre
Dernière intervention
21 septembre 2007
-
Je n'avais pas pensé à cette solution, elle est en effet pas mal et sûr. Elle ne permet cependant pas de demander le passage au premier plan de l'instance déjà lancée, mais bon, on ne peut pas tout avoir...

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.