IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Android Discussion :

SQLite et multithread


Sujet :

Android

  1. #1
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2011
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 144
    Points : 118
    Points
    118
    Par défaut SQLite et multithread
    Bonjour,

    J'ai récemment créé un sujet pour une problématique similaire, mais étant donné que j'ai effectué plusieurs changements entre temps et que certains points n'étaient pas clair, j'ai préféré partir sur un nouveau sujet avec une base solide d'explications et de détails. Du coup, le message est un peu long =D




    Côté application/fonctionnement

    Pour décrire le fonctionnement (simplifié et avec seulement la partie qui nous intéresse ici) de l'application, disons que j'ai :
    - Une activité de "démarrage" qui vérifie la connexion internet, certains params, etc.
    Si tout est ok, l'activité se termine et lance l'activité Menu.
    Si la base de donnée est vierge (premier lancement/données applications supprimées), une AsyncTask est lancée pour récupérer la liste des informations à insérer dans la base de données SQLite (pendant ce temps, l'utilisateur est "bloqué" avec un progressDialog). L'activité Menu est ensuite lancé.
    C'est aussi cette activité qui lancera le services de vérification et de mise à jour.

    - Une activité qui sert de Menu qui permet à l'utilisateur de choisir la liste à afficher.
    Il y a parfois quelques insertion sql à faire ici, l'utilisateur est bloqué par un progressDialog en attendant.
    Chaque bouton lancera l'activité "Liste", avec un contenu qui change un peu selon le bouton.

    - Une activité Liste pour afficher, comme son nom l'indique, une liste d'infos issue de la base SQLite.
    Juste au dessus de la liste, il y a un EditText : il permet de filtrer la liste (ce qui signifie donc l'exécution de requêtes SQL à chaque caractères saisis).


    Pour afficher les informations dans la liste, j'ai une classe qui étends CursorAdapter pour extraire les informations et filtrer selon l'EditText. A chaque fois qu'un caractère est saisi, une méthode "filtrer" de l'adapter est appelé et lancera une AsyncTask "FilterTask" (en prenant soin d'annuler le FilterTask précédent s'il y en avait) pour effectuer les requêtes.
    Selon les listes, il peut y avoir une mini-requête (une seule ligne) d'ajout ou de suppression, effectuée dans une AsyncTask spéciale lorsque l'utilisateur aura "touché" un élément de liste.

    Je pense que je vais me faire taper dessus car je n'utilise pas les Loaders / ContentProvider... mais j'ai assez de mal à comprendre comment ils fonctionnent. Donc j'ai essayé de bricoler un système moi même pour que les traitements SQL et les filtres ne tournent pas dans l'UI. Si vous avez des conseils ou des remarques, je suis preneur.





    Côté base de données

    J'ai donc ma classe qui étend SQLiteOpenHelper pour construire la table et pour garder une référence statique sur son instance (singleton). J'ai pu lire que c'était essentiel pour faire du multithread.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class DatabaseHelper extends SQLiteOpenHelper 
    {
     
    	private static DatabaseHelper mHelperInstance = null;
     
    	public static synchronized DatabaseHelper getHelperInstance(Context context) 
    	{
    	    if (mHelperInstance == null) 
    	    {
    	    	mHelperInstance = new DatabaseHelper(context.getApplicationContext());
    	    }
     
    	    return mHelperInstance;	    
    	}
     
     
     
    	public DatabaseHelper(Context context) 
    	{
    		super(context, DATABASE_NAME, null, DATABASE_VERSION);
    	}
     
     
    }
    En ce qui concerne la classe qui contient les méthodes pour effectuer les requêtes SQL, je l'ai modifié pour que l'ouverture et la fermeture de la base fonctionne avec du multithread (je ne sais pas si c'est le meilleur moyen, mais ça a l'air de fonctionner).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public class ClientsSQLite
    { 
     
    	private SQLiteDatabase sqliteDatabase; 
    	private DatabaseHelper databaseHelper;
     
    	private static int nb_instance = 0;
     
     
    	public ClientsSQLite(Context context)
    	{
    		databaseHelper = DatabaseHelper.getHelperInstance(context);
    		this.sqliteDatabase = databaseHelper.getWritableDatabase();
    	}
     
    	public void open()
    	{
    		if (nb_instance == 0)
    		{
    			this.sqliteDatabase = this.databaseHelper.getWritableDatabase();
    		}
     
    		nb_instance++;
    	}
     
     
    	public void close()
    	{
    		nb_instance--;
     
    		if (nb_instance == 0)
    		{
    			this.sqliteDatabase.close();
    		}
    	}
     
    	public void doMachinTruc(String machin, string truc)
    	{
    		this.sqliteDatabase.execSQL("........."); 
    	}
     
    ...
     
    }

    J'ai parfois vu certains bouts de codes qui ne gardaient pas de référence sur SQLiteDatabase, mais seulement sur le DatabaseHelper. Du coup, il n'y avait pas de méthode open/close et à chaque requête, il faut utiliser la méthode getWritableDatabase() pour récupérer une référence.
    Q1 : Qu'en pensez-vous ? Ça n'affecte pas trop les performances vu qu'il faut à chaque fois récupérer une référence + allouer un objet ?







    Côté mise à jour asynchrone des données

    Jusqu'ici, je ne sais pas si c'est "bien fait" ou si on peut mieux faire, mais tout marche bien.
    Ce que je souhaite faire maintenant, c'est qu'un Service se connecte de temps en temps à un serveur web pour savoir si l'application doit mettre à jour sa base SQLite; et si c'est le cas, il faut mettre à jour la table.
    La vérification est totalement transparente, pas besoin d’embêter l'utilisateur pour simplement demander à un serveur de dire "oui ou non" ^^
    Si le serveur répond non, le service se termine, la prochaine vérification se fera quelques jours plus tard.

    Si le serveur répond oui, c'est que les données ont changé : je suis obligé de vider la table, et de faire une insertion de masse.
    La raison principale de cette "vidange" est que le serveur est incapable de me dire "par rapport à ta version, il faut que tu insert ça ça et ça, que tu update ce truc, et que tu delete ces machins". Il ne peut que me dire "insert tout çaaaaaaaaaaaaaaaaaaaaa".
    Donc si une donnée est supprimée du serveur, ou modifiée, je n'ai pas trouvée d'autre solutions que de vider la table SQLite sur Android pour prendre en compte la modification.

    Q2 : Est-ce que vous voyez un autre moyen que de vider et de tout ré-insérer ?

    Q2 bis : A tout hasard, est-ce qu'il existe un petit framework/outils/truc permettant de rendre plus intelligent le serveur ?



    Le service étant asynchrone, et la durée des traitements pouvant varier, il est impossible de prévoir quand les insertions dans la base vont être effectué.
    Cela peut donc être problématique si l'application modifie déjà des données, ou si l'utilisateur filtres la liste (select).

    Pour commencer à contrer ce problème, j'ai d'abord mis en place un singleton (voir code au dessus) pour l'accès à la base de données.
    Vu que ce sont des insertions de masses, j'utilise une seule transaction + requête préparée pour améliorer les performances.
    Les transactions SQLite sont en mode "exclusive" sur Android donc, lorsqu'une transaction démarre, toutes les autres requêtes sont bloquées; que ça soit une modification (update, insert, delete) ou un simple select. Il ne devrait donc pas y avoir de données "corrompues".
    L'insertion de la mise à jour durant quelques secondes, l'utilisateur peut par contre être bloqué lorsqu'il utilise la base de données (s'il filtre la liste par exemple) et il devra attendre la fin de la transaction.

    Alors du coup, pour contrer ce problème qui contrait déjà un problème (faut suivre hein ), j'ai créé une seconde table identique qui n'est utilisée que par le Service. De plus, j'appelle la méthode yieldIfContendedSafely pour éviter que cette transaction soit bloquante. L'insertion dans cette table ne pose ainsi aucun problème et ne verouille donc jamais la base de données (utilisateur non bloqué).
    Ceci étant fait, il faut que je transfère les données de cette table dans l'originale pour que ça puisse être utilisable par l'application. Pour cela, je créé manuellement une transaction qui engloble la "vidange" de la table principale puis la requête "INSERT INTO table_principale SELECT * FROM table_secondaire". Cette transaction est volontairement bloquante car il faut protéger cette section critique, mais c'est extrèmement rapide (moins de 0,5 secondes sur émulateur, à la place de 6 secondes pour l'insertion depuis le JSON).
    Avec tout ce système, la mise à jour se fait totalement en arrière plan, et l'utilisateur n'est pas bloqué (peut-être une fraction de seconde lors de l'INSERT SELECT, mais ce sera surement rare/négligeable). Je pense utiliser le système de notification pour prévenir l'utilisateur lorsque la mise à jour commande (insert dans table secondaire), et lorsque la mise à jour est terminée.

    Q3 : En espérant que vous ayez réussi à comprendre mon explication, qu'en pensez-vous ? Avez-vous des suggestions, des améliorations... ou même un changement radical à y apporter ?

    Q4 : Si l'utilisateur est sur la listeview lorsque la mise à jour est terminée (et que la table secondaire a bien été insérée dans la table principale), la listview n'est pas rafraîchie : il faut quitter l'activité (retour Menu) puis revenir sur la listview. Je suppose que c'est dû au fait que le Cursor utilisé par l'adapter reste inchangé. Comment dire à l'adapter/au Cursor que le contenu de la base a changé, et qu'il faut se "réactualiser" ?





    Côté Service

    Pour l'instant, j'utilise une AsyncTask que je lance manuellement à différent endroits pour effectuer des tests sur la mise à jour. De plus, je n'ai jamais utilisé de Service et il y a encore trop de zones floues pour moi. J'ai donc quelques questions spécifiques aux Services Android :

    Q5 : Tout d'abord, par rapport à mes besoins, je n'arrive pas à déterminer s'il me faut un LocalService ou un RemoteService.
    Il ne faut pas qu'il tourne dans le thread UI, mais il n'a pas non plus besoin d'être utilisé par d'autres applications...

    Q6 : Pour ce qui est du "bind", je pense que je n'en ai pas besoin car le Service ne dépend pas des activités, et l'inverse est vrai aussi. De plus, le service s'arrête lui-même. Est-ce que j'ai bon, ou est-ce qu'il y a une raison que je ne vois pas qui m'obligerait à "binder" le service ?

    Q7 : Est-ce qu'on peut continuer d'éxécuter un Service lorsque l'application est terminée ? Est-ce valable autant pour un RemoteService qu'un LocalService ?

    Q8 : Si oui, lorsque l'application est relancée, est-ce qu'il existe un moyen de savoir si mon service est exécuté (ou non) afin d'éviter de le relancer deux fois ?



    Un grand merci d'avance pour votre aide (j'espère que je n'ai pas trop abusé avec la longueur et les questions).
    Si un point semble flou, n'hésitez pas à me demander plus de détails.

  2. #2
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Bonjour,

    Q1: Personnellement je préfère gérer mon instance de database au niveau de l'application... On a grosso-modo un SQLiteDatabase statique, initialisé au démarrage de l'application, fermé quand l'application quitte. Tout le monde peut y avoir accès...

    Q2: Si le serveur peut être modifié... alors le mieux est oui d'avoir un truc genre "quelles sont les modification depuis telle date", et recevoir une liste d'objet "detruits", "mis à jour", ou "ajoutés" depuis cette date... Pour une grande quantité de données, cela peut faire une sacrée différence. C'est ce qu'on appelle des mises à jour incrémentales. L'astuce, est donc pour les données coté serveurs: de ne JAMAIS faire de delete (de toute manière, en data management c'est toujours une mauvaise idée), mais de "flaguer" l'objet comme étant détruit, et de conserver deux date: date de création, date de modification... "Détruire" un objet coté serveur, est mettre le flag "deleted" à vrai, et la date de modification à maintenant... "Insérer" coté serveur, est mettre le flag "deleted" à faux, et les dates de création/modification à maintenant.... "Mettre à jour" coté serveur, juste modifier la date de modification.
    Bien sur toutes les requêtes métier coté serveur devront vérifier ce flag deleted...
    Coté client du coup c'est facile, on recoit un objet flaggé "deleted" ? on le détruit... on recoit un objet dont la date de création > date demandée ? on le crée... les autres on les met juste à jour.

    Q2 bis. voir ci dessus.

    Q3 Effectivement, comme la quantité de données peut être importante, il vaut mieux dans ce cas créer un table temporaire (staging) la remplir indépendamment de la table primaire, et à la toute fin... faire un simple switch de tables.

    Q4 Cursor.reload(), adapter.notifyDataSetChanged().... mais il faut que le service signale qu'il a effectué les modifications (broadcast / listeners).

    Q5 LocalService

    Q6 Utiliser même un "IntentService" dans ce cas...

    Q7 Non et non... Un service est un élément à part entière de l'application, sauf qu'au contraire des activités il n'y a pas d'interface... Application ==> (Service(s), Activité(s))

    Q8 De toute manière un service n'existe qu'une fois... startService() ne lancera le service que s'il n'est pas déjà lancé.
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  3. #3
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2011
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 144
    Points : 118
    Points
    118
    Par défaut
    Pour commencer, je te remercie pour ta réponse rapide.


    Citation Envoyé par nicroman Voir le message
    Personnellement je préfère gérer mon instance de database au niveau de l'application... On a grosso-modo un SQLiteDatabase statique, initialisé au démarrage de l'application, fermé quand l'application quitte. Tout le monde peut y avoir accès...
    J'ai fait la même chose du coup, car ça me semble effectivement mieux (ça évite les ouvertures/fermetures, ça simplifie le multithread...).
    En plus, avant de faire ça, j'ai eu une exception Database locked lors d'un test (le service qui insert dans le secondaire, et une Asynctask qui insère dans une autre table).




    Citation Envoyé par nicroman Voir le message
    Effectivement, comme la quantité de données peut être importante, il vaut mieux dans ce cas créer un table temporaire (staging) la remplir indépendamment de la table primaire, et à la toute fin... faire un simple switch de tables.
    Q1 : Est-ce qu'il vaut mieux créer une "vraie" table temporaire (CREATE TEMP TABLE my_temp_table...), ou utiliser une simple table ?

    Q2 : Si oui, comment fonctionne une table temporaire ? Je veux dire par là, quand est-ce qu'elle est supprimée ?
    Est-ce que c'est automatique, est-ce qu'il faut la supprimer manuellement, la vider... ? Est-ce qu'il faut que je me "méfie" avant de faire le transfert vers la table principale (au cas où elle ait disparu) ... ?
    J'ai essayé, mais à la création, j'ai une erreur "no such table: Temp_Clients".




    Citation Envoyé par nicroman Voir le message
    Cursor.reload(), adapter.notifyDataSetChanged().... mais il faut que le service signale qu'il a effectué les modifications (broadcast / listeners).
    La méthode Cursor.requery (reload n'existe pas) ré-actualise bien la liste, mais elle est marquée comme obsolète (avec comme information : "Don't use this. Just request a new cursor, so you can do this asynchronously and update your list view once the new cursor comes back").

    A chaque fois qu'un caractère est saisie, ma méthode filtrer effectue un select dans la base de données et récupère un nouveau Cursor de manière asynchrone. Elle utilise ensuite la méthode changeCursor (de CursorAdapter) mais ça ne ré-actualise pas ma liste. Je ne vois pas ce qu'entends Google par "just request a new cursor" du coup...

    Q3 : Comment ré-actualiser le Cursor, vu que cette méthode est devenu obsolète ?

    Q4 : Pour ce qui est des signaux, est-ce qu'il est possible que le Service envoie un seul Broadcast qui sera traité différemment selon l'activité qui le reçoit ?
    Car actuellement, je ne vois pas comment le faire (vu qu'on doit créer une classe qui étend BroadcastReceiver et redéfinir onReceive...).

    [EDIT] Je viens de tomber sur ce sujet, je pense que je m'y prends mal... Je vais tester autrement.

    [EDIT 2] Oui c'est bien moi qui m'y suis mal pris... Je peux le recevoir sur deux activités différentes. Bon, reste que une de mes activités reçoit plusieurs fois le broadcast x)



    Citation Envoyé par nicroman Voir le message
    Non et non... Un service est un élément à part entière de l'application, sauf qu'au contraire des activités il n'y a pas d'interface... Application ==> (Service(s), Activité(s))
    J'ai essayé de fermer l'application juste après le lancement de l'IntentService, et celui-ci continu tout de même de s'éxecuter.

    Q5 : Est-ce que c'est normal ? Est-ce que ça peut être "dangereux" ?

    Q6 : Pour le coup, est-ce qu'il faut détecter la fin de l'application pour forcer l'arrêt de l'IntentService, ou on peut le laisser finir son travail ?




    Citation Envoyé par nicroman Voir le message
    De toute manière un service n'existe qu'une fois... startService() ne lancera le service que s'il n'est pas déjà lancé.
    J'ai effectué un test en appelant plusieurs fois à la suite startService (ou en fermant/ré-ouvrant l'application, ça donne la même chose) et l'IntentService est lancé plusieurs fois. Pas en simultané, mais il y a un système de queue qui relancera ce même Service autant de fois que startService a été appelé.

    Q7 : Est-ce qu'on peut éviter ça ?




    Q8 : Dans un IntentService, je dois aussi appeler stopSelf (ou cette "extension" gère l'arrêt soi-même) ?
    A quoi servent les autres méthode stop*** ?

  4. #4
    Expert éminent

    Avatar de Feanorin
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    4 589
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 4 589
    Points : 9 149
    Points
    9 149
    Par défaut
    Je pense que pour les question sql le mieux serait de poser la question sur le forum concerné .

    Q3 : Comment ré-actualiser le Cursor, vu que cette méthode est devenu obsolète ?
    requery() .

    Q4 : Pour ce qui est des signaux, est-ce qu'il est possible que le Service envoie un seul Broadcast qui sera traité différemment selon l'activité qui le reçoit ?
    Car actuellement, je ne vois pas comment le faire (vu qu'on doit créer une classe qui étend BroadcastReceiver et redéfinir onReceive...).
    oui cela tu peux le faire .

    Il te suffit d'appeler une fonction de l'activity en cours (c'est pas super stable mais ça marche). Ou que chaque activity est capable d'implémenter le BroadCast, le Bind du Service fonctionne comme tel .

    ai essayé de fermer l'application juste après le lancement de l'IntentService, et celui-ci continu tout de même de s'éxecuter.
    Q5 : Est-ce que c'est normal ? Est-ce que ça peut être "dangereux" ?
    Voir question 8, dangereux pas forcément , dérangeant ca c'est sûr.

    'ai effectué un test en appelant plusieurs fois à la suite startService (ou en fermant/ré-ouvrant l'application, ça donne la même chose) et l'IntentService est lancé plusieurs fois. Pas en simultané, mais il y a un système de queue qui relancera ce même Service autant de fois que startService a été appelé.
    Il se lancera plusieurs fois mais normalement tu n'auras qu'une seule instance, c'est cela que disais nicroman.

    Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
    Par contre tu a bien un système de queue sur les opérations demandés.


    Q8 : Dans un IntentService, je dois aussi appeler stopSelf (ou cette "extension" gère l'arrêt soi-même) ?
    A quoi servent les autres méthode stop*** ?
    D'après la doc :
    Stops the service after all start requests have been handled, so you never have to call stopSelf().
    onStartCommand()
    The system calls this method when another component, such as an activity, requests that the service be started, by calling startService(). Once this method executes, the service is started and can run in the background indefinitely. If you implement this, it is your responsibility to stop the service when its work is done, by calling stopSelf() or stopService(). (If you only want to provide binding, you don't need to implement this method.)
    Responsable Android de Developpez.com (Twitter et Facebook)
    Besoin d"un article/tutoriel/cours sur Android, consulter la page cours
    N'hésitez pas à consulter la FAQ Android et à poser vos questions sur les forums d'entraide mobile d'Android.

  5. #5
    Membre extrêmement actif
    Profil pro
    Développeur
    Inscrit en
    Mars 2012
    Messages
    1 969
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Mars 2012
    Messages : 1 969
    Points : 3 375
    Points
    3 375
    Par défaut
    Pour ta db je ne vois pas trop l'intérêt de passer par un service, tu peux passer par une instance de classe de type singleton.

    Pour tes questions de service, l'intentService est très maniable, tu peux même lui passer des callbacks pour rafraichir tes view par exemple.

    Un localService se limite au start/stop à moins de le binder mais tout dépend si tu penses liés plusieurs applications à ton service ou juste une seule

    Tu as aussi le MessangerService qui ressemble fortement à l'intentService et est moins complexe que l'aidlService.
    Si la réponse vous a aidé, pensez à cliquer sur +1

  6. #6
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2011
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 144
    Points : 118
    Points
    118
    Par défaut
    Merci pour vos réponses, j'y vois plus clair sur certains points mais il reste quelques zones floues :



    Q1 : Ce n'est donc pas génant d'utiliser requery() alors qu'elle est marquée obsolète ?
    Q2 : Tant que l'API utilisée pour compiler (sur eclipse) l'application est inférieur à l'API où la méthode est devenue obsolète, c'est donc bon ? Même si l'utilisateur aura par exemple la dernière API ?




    Vu qu'il y a un système de queue sur le Service, et que je teste la date de dernière mise à jour avant de lancer tous le traitement, ce n'est finalement pas dérangeant (le premier s'exécutera complètement, et les autres ne passeront pas la première condition).

    Q3 : En supposant que je n'ai pas ce test, il faudrait donc re-définir les méthodes onStartCommand (et un autre pour l'arrêt que je n'ai pas trouvé) pour, par exemple, incrémenter et décrémenter le nombre de fois où le Service a été lancé par une activité ?




    Citation Envoyé par hotcryx Voir le message
    Pour ta db je ne vois pas trop l'intérêt de passer par un service, tu peux passer par une instance de classe de type singleton.
    Je passe par un Service pour que ce soit asynchrone, et pour que ça ne pose pas de problèmes vu que l'activité qui lancera la tâche sera très certainement différente de celle qui sera en vie lors de la fin du service.


    Citation Envoyé par hotcryx Voir le message
    Pour tes questions de service, l'intentService est très maniable, tu peux même lui passer des callbacks pour rafraichir tes view par exemple.
    Etant donné que le Service peut se terminer sur n'importe quelle activité, je ne pense pas qu'il soit possible d'utiliser un système de callbacks (c'est pourquoi j'utilise du coup les Broadcast). Ou alors, je me trompe et je serais dans ce cas curieux de voir ta solution.



    D'ailleurs, en parlant de Broadcast, j'ai une petite question.
    J'ai deux activités qui peuvent potentiellement recevoir le broadcast et effectuer une action spéciale (l'activité A et l'activité B).
    Petite info, c'est l'activité A qui lance l'activité B. Lorsqu'on est sur l'activité B, l'activité A est toujours "en vie", mais le contraire n'est pas vrai.

    Il faut que l'activité A puisse recevoir le broadcast même si on est sur l'activité B. De même, si l'utilisateur reçoit un appel/fait un retour aevc le bouton HOME/etc, il faut que les activités A et B recoivent quand même le broadcast.

    Au lieu de gérer le broadcast dans le onResume et le onPause, j'ai plutôt utilisé le onCreate et onDestroy :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
        protected void onCreate(Bundle savedInstanceState)
        {
    	    super.onCreate(savedInstanceState);
    	    setContentView(R.layout.activity_opac_list);
     
    	    ...
     
    		this.onUpdateService = new UpdateReceiver();  
     
    		IntentFilter intentFilter = new IntentFilter(Keys.BROADCAST_UPDATE_SERVICE);
            LocalBroadcastManager.getInstance(this).registerReceiver(this.onUpdateService, intentFilter);
        }
     
     
    	protected void onDestroy()
    	{
    		super.onDestroy();
    		LocalBroadcastManager.getInstance(this).unregisterReceiver(this.onUpdateService);
    	}
    Q4 : Est-ce que c'est bon pour ce fonctionnement, ou il faut éviter ?



    Petite question par rapport au cycle de vie. Lorsque l'activité arrive au stade onPause ou onStop et que le système a besoin de mémoire, le processus est tué et l'activité devra retourner dans le onCreate.

    Q5 : Lorsque c'est le cas, est-ce que les attributs sont aussi "détruits" (ils n'auront donc plus de valeur) ?
    Q6 : Est-ce qu'il y a un risque que l'activité écoute deux fois le Broadcast si le onCreate est appelé plusieurs fois ?



    Q7 : Dernière question, ma notification ne s'affiche pas sur téléphone avec la version 2.3.6, alors que ça fonctionne sur émulateur avec la 4.2.
    Est-ce que vous voyez une erreur dans le code ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    					final Notification notification = new Notification(R.drawable.ic_launcher, 
    							getResources().getString(R.string.notification_update_done_tick), System.currentTimeMillis()); 
     
    					notification.setLatestEventInfo(this, getResources().getString(R.string.notification_update_done_title), 
    						getResources().getString(R.string.notification_update_done_message), null); 
     
    					final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 
    					notificationManager.notify(Keys.NOTIFICATION_UPDATE_DONE, notification);

  7. #7
    Expert éminent

    Avatar de Feanorin
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    4 589
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 4 589
    Points : 9 149
    Points
    9 149
    Par défaut
    Q1 : Ce n'est donc pas génant d'utiliser requery() alors qu'elle est marquée obsolète ?
    Avais pas fait attention , je vois que la desactivate aussi :/.

    Soit à chaque fois réouvrir un Cusor et le fermer à la fin de son traitement.

    Soit passer par la classe Loader et LoaderManager
    http://developer.android.com/guide/c...s/loaders.html

    Ca change toutes les 5 APIs cette gestion.

    Q2 : Tant que l'API utilisée pour compiler (sur eclipse) l'application est inférieur à l'API où la méthode est devenue obsolète, c'est donc bon ? Même si l'utilisateur aura par exemple la dernière API ?
    Yep pas de souci après faut faire attention.

    Q3 : En supposant que je n'ai pas ce test, il faudrait donc re-définir les méthodes onStartCommand (et un autre pour l'arrêt que je n'ai pas trouvé) pour, par exemple, incrémenter et décrémenter le nombre de fois où le Service a été lancé par une activité ?
    Au lieu de lancer le service a chaque fois pourquoi ne pas plutôt te binder dessus ?

    Quitte à le lancer, il faudrait mieux si tu sais que tu vas l'appeler à l'infi que ce soit l'appelant qui l'arrête.

    Q5 : Lorsque c'est le cas, est-ce que les attributs sont aussi "détruits" (ils n'auront donc plus de valeur) ?
    Les attributs seront réinitialisé avec ton fichier xml. Si tu les a changé dynamique ton setContentView écrasera tout depuis le onCreate.

    Q6 : Est-ce qu'il y a un risque que l'activité écoute deux fois le Broadcast si le onCreate est appelé plusieurs fois ?
    Si tu t'es désabonné du broadcast avant que l'activity ce soit fermer non.
    Si tu as oublié tu vas avoir un listener qui va tourner en fond sans que tu puisse le gérer.

    Q7 : Dernière question, ma notification ne s'affiche pas sur téléphone avec la version 2.3.6, alors que ça fonctionne sur émulateur avec la 4.2.
    Est-ce que vous voyez une erreur dans le code ?
    je n'ai pas regardé le code mais attention les notifications ont subit un important changement depuis la JellyBean. regarde donc bien si tu es conforme avec l'API10
    Responsable Android de Developpez.com (Twitter et Facebook)
    Besoin d"un article/tutoriel/cours sur Android, consulter la page cours
    N'hésitez pas à consulter la FAQ Android et à poser vos questions sur les forums d'entraide mobile d'Android.

  8. #8
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2011
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 144
    Points : 118
    Points
    118
    Par défaut
    Citation Envoyé par Feanorin Voir le message
    Avais pas fait attention , je vois que la desactivate aussi :/.

    Soit à chaque fois réouvrir un Cusor et le fermer à la fin de son traitement.

    Soit passer par la classe Loader et LoaderManager
    http://developer.android.com/guide/c...s/loaders.html

    Ca change toutes les 5 APIs cette gestion.
    Ahhh... les loaders... j'avais peur de revenir dessus
    Je sais que c'est ce qu'il faut utiliser maintenant mais je n'arrive malheureusement pas à comprendre totalement comment ils fonctionnent avec SQLite (vu qu'il faut utiliser les ContentProvider pour le coup aussi, concept trop flou pour moi avec SQLite... on dirait, aux premiers abords, que ce n'est pas fait pour). Et aussi, j'ai peur que je ne puisse plus "choisir" les opérations bloquantes ou totalement asynchrones... à moins que le Loader gèrent bien le fait de vider/ré-insérer (ou l'insert from select d'une table secondaire) dans un table qui est utilisée ? Pareil pour les requêtes avec jointures ?

    Vu que je ne connais pas bien ce système, j'ai en fait du mal à voir si ça peut répondre à mes besoins d'utiliser une listview avec un CursorAdapter, de filtrer cette liste (select en jointures), de gérer le multithread, la partie du Service qui insert dans une table secondaire doit être totalement asynchrone et ne pas bloquer l'utilisateur qui utilise l'application et peut éventuellement manipuler la base (select, insert, delete)...
    Q1 : Si tu as déjà utilisé tout le système de Loader/ContentProvider/etc avec la base SQLite, est-ce que tu pense que ça peut répodre à mes besoins (énnoncés juste en dessus, et dans mon premier message où je décrit l'application) ?





    Citation Envoyé par Feanorin Voir le message
    Les attributs seront réinitialisé avec ton fichier xml. Si tu les a changé dynamique ton setContentView écrasera tout depuis le onCreate.
    Q2 : Tous les attributs de la classe java sont donc bien "passés à null" ?





    Citation Envoyé par Feanorin Voir le message
    Si tu t'es désabonné du broadcast avant que l'activity ce soit fermer non.
    Si tu as oublié tu vas avoir un listener qui va tourner en fond sans que tu puisse le gérer.
    Le problème, c'est que j'ai besoin de recevoir le broadcast même si l'activité n'est plus au premier plan, je ne peux donc pas utiliser le duo onPause/onResume habituel. La seule solution que j'ai trouvé, c'est d'écouter dans le onCreate et d'arrêter dans le onDestroy.
    J'ai testé en changeant l'orientation du téléphone (appelant onCreate à chaque fois), et lorsque le broadcast est envoyé, il est reçu qu'une seule fois.
    Par contre, pour tester lorsque le système kill l'activité... c'est pas vraiment possible
    Q3 : Du coup, le code ci-dessous est-il correct ? Ou est-ce un "coup de chance" que le listener ne soit présent qu'une fois ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_setup);		
     
    		...
     
    		this.onUpdateService = new UpdateReceiver();	    
     
    	    IntentFilter intentFilter = new IntentFilter(Keys.BROADCAST_UPDATE_SERVICE);
            LocalBroadcastManager.getInstance(this).registerReceiver(this.onUpdateService, intentFilter);
    	}
     
    	protected void onDestroy()
    	{
    		super.onDestroy();
    		LocalBroadcastManager.getInstance(this).unregisterReceiver(this.onUpdateService);
    	}




    Citation Envoyé par Feanorin Voir le message
    je n'ai pas regardé le code mais attention les notifications ont subit un important changement depuis la JellyBean. regarde donc bien si tu es conforme avec l'API10
    En fait, j'ai le bon code pour l'API 10 et le problème ne venait pas de là : je passais la valeur null à la place d'un PendingIntent (méthode setLatestEventInfo) car je ne voulais pas lancer d'activité en cliquant sur la notification. Du coup, j'ai trouvé une solution avec ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);

Discussions similaires

  1. SQLite et MultiThreading (via Wrapper C++)
    Par Aymerik dans le forum SQLite
    Réponses: 8
    Dernier message: 17/05/2010, 09h55
  2. qui connait sqlite ?
    Par Emmanuel Lecoester dans le forum SQLite
    Réponses: 23
    Dernier message: 19/02/2010, 13h44
  3. [WinAPI C++] MultiThreading?
    Par Gruik dans le forum Windows
    Réponses: 2
    Dernier message: 25/03/2004, 00h08
  4. [Win32]App multithread
    Par billyboy dans le forum Windows
    Réponses: 5
    Dernier message: 25/09/2003, 09h57
  5. Multithreading sous HP Ux 11
    Par pykoon dans le forum Autres éditeurs
    Réponses: 1
    Dernier message: 18/10/2002, 23h36

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo