58 ville l'évolution du système client iOS IM

[Note de l'éditeur] 58 ville version App 1.0 depuis le début, nous avons été engagés à la recherche depuis système de messagerie instantanée. Dans le processus, savoir comment réduire le couplage entre les systèmes de messagerie instantanée et le niveau de la page, ce qui réduit la complexité du système de messagerie instantanée est la clé pour réduire le coût de la technologie pour améliorer l'efficacité de la recherche et le développement. À cet égard, l'auteur du processus d'évolution de l'architecture du système client iOS IM et l'expérience est résumée, espérant donner conçus ou modifiés pour optimiser les développeurs du module de messagerie instantanée pour fournir une référence.

App pour 58 ville à ces informations que l'affichage principal et la plate-forme commerciale concernée, IM fonctionnalité de messagerie instantanée au sein de l'application, par rapport aux appels et messages texte, mais a également contribué à une position décisive sur la transaction des biens / services. Voilà pourquoi, depuis le début de la version 1.0, a été engagé dans la recherche depuis système de messagerie instantanée. Dans le processus d'auto-étude, nous avons découvert comment réduire le couplage entre les systèmes de messagerie instantanée et le niveau de la page, ce qui réduit la complexité du système de messagerie instantanée est la clé pour réduire le coût de la technologie pour améliorer l'efficacité de la recherche et le développement.

Par conséquent, cet article se concentrera sur deux aspects de la quatrième processus de transition ensemble dans 58 ville ios architecture système client de messagerie instantanée. Tout d'abord, comment soulever le système de messagerie instantanée repose sur la base de données et de l'interface Socket, le deuxième, IM chat page du modèle MVC traditionnel à l'accord pour la nouvelle architecture. Nous espérons fournir une référence aux développeurs des scénarios d'affaires similaires.

Les anciennes versions de problème du système IM rencontré

58 App dès le début du projet pour étudier le système de messagerie instantanée, mais seulement des outils de messagerie texte, la messagerie photo, audio, et d'autres types de base. Bien que l'entreprise a besoin scène simple, mais toujours rencontrer les problèmes suivants.

évolutivité peu de données

En utilisant le format de données est Google ProtocolBuffer (ci-après dénommé PB), parce que ce format XML de données et JSON par rapport au même format de données, plus petit, résolu plus rapidement. Mais PB est implémenté en C ++, relativement difficiles à utiliser. Vous devez écrire différents types de messages pour différentes structures de données de PB, PB nécessitant chacun une structure de données séparée méthode d'analyse plus poussée. Depuis le développement de 58 entreprises, du protocole de données augmente la complexité du système.

codes de différence Encapsulation, les coûts de développement de haute

Avant la transmission de données, la sécurité, les données doivent être transmises sont cryptées par l'algorithme cryptographique spécifique, la réutilisation des AsyncSocket de transmission de données. Correspondant à chaque type de message reçu, vous devrez décrypter, convertir le format dans un modèle d'objet PB. Ce schéma, chaque plus douloureux lorsque le nouveau type de message, algorithme de chiffrement à écrire, analyseur de modèle d'écriture de PB. Cela permettra non seulement l'expansion du code est faible, la difficulté de développement est relativement importante.

Strong Code de couplage

S'il y a des types de messages supplémentaires chacun, les données d'écriture à un nouveau message dans la couche d'interface DB et stockées analysable. De même, également dans la nouvelle couche de transport Socket Interface émetteur-récepteur correspondant à celle-ci. Cette conception, processus de développement de couplage est élevé.

la lisibilité du code mauvaise

Au sein de l'App un seul type de message, appelé WBMessagModel. Sur le jugement de type de message est effectuée en WBMessageModel se distingue par un champ spécifique, tel que selon mtype de détermination de champ, isOnlineTip, comme m_msgtype manière chaos relative.

Pour résoudre le problème ci-dessus, construire un système de messagerie instantanée, extensible à couplage lâche, nous avons décidé de reconstruire.

La nouvelle version du système IM

Evolution de l'Architecture

Puisque le code IM ancien système couplant un événement grave des problèmes sont difficiles à tracer. Et l'évolutivité pauvres, chaque version de la demande pour la recherche et le développement, sont modifiés à partir du bas à la couche d'affaires, ce qui affecte les progrès de la recherche et le développement. problèmes IM rencontrés dans le processus de développement avant le collage, le nouveau système de messagerie instantanée est un besoin urgent de résoudre le problème suivant.

  • Simplifier le processus d'appel

processus de développement des entreprises pour réaliser la séparation « chiffrement de données sous-jacente DB + + + chiffrement des données de transfert de données » en appelant l'interface sous-jacente émetteur-récepteur peut être fait, le message stocké.

  • Conception d'interface de couche intermédiaire à faible couplage

Une interface de couche de liaison intermédiaire de liaison à faire bout à bout, et les interfaces de la couche sous-jacente d'affaires sans couplage. Si vous faites cela, plus tard dans la mise à niveau IM sous-jacente ou même remplacer, il suffit de régler l'interface de service re-docking avec l'interface sous-jacente, de sorte que le haut de l'entreprise ne la perception, de sorte qu'aucune perception d'itérations.

  • Modèle et design interface unique fonction

Sur la couche de processus métier spécifique, pour obtenir le modèle de séparation, conception unifiée. Sur le modèle, le seul modèle précédent IM divisé selon leur type. Interface, à travers le fond, la structure de la couche intermédiaire est divisée en une couche de service, chaque interface pour mener à bien leurs fonctions.

  • évolutivité

de manière abstraite en utilisant des codes de protocole et d'organisation orientés, de sorte que le nouveau message selon le protocole. UITableView fait à l'aide existante et de nouvelles catégories de types de messages peuvent calculer automatiquement la cellule de hauteur. En approche de conception sur cette entreprise, être en mesure de localiser rapidement le problème. Si les nouveaux types de messages, se concentrer uniquement sur le nouveau modèle de message et l'interface de message correspondant peut être complètement rempli sans prêter attention à la date de vue, ainsi que la façon de calculer la hauteur de vue et ainsi de suite. Cerner ces principes de conception afin de veiller à ce que faire l'itération rapide, et répondent ainsi aux besoins croissants des utilisateurs dans le processus de développement des affaires.

Sur la base des objectifs ci-dessus, l'architecture globale de IM montre. La figure 1 reconstruit.

Figure 1 La nouvelle architecture de messagerie instantanée

La nouvelle architecture du système IM global se compose de la couche de service d'interface rez-de-chaussée, couche d'affaires trois parties. Le principal émetteur-récepteur de données sous-jacente, de stockage et d'autres du traitement est associée, ainsi que les interfaces communes abstraites sous-jacentes, les interfaces d'interagir avec la couche de service. couche de service Interface est responsable de la transmission des données sous-jacentes raisonnablement à la couche d'affaires, de même, le trafic de données peuvent être transmises à la couche sous-jacente à travers la couche de service d'interface. Clarté de la couche de service d'interface permet non seulement le traitement des données couche d'affaires plus facile, mais aussi réduit considérablement la couche de couplage et l'entreprise sous-jacente. couche d'affaires axée sur les besoins spécifiques de la scène, comment utiliser les données pour une vue d'affichage. Cette conception est basée, les informations suivantes sur la mise en uvre spécifique entre les différents niveaux.

conception flux d'appels interface simple sous-jacente

Nouveau fond IM avec de nouvelles idées de conception, comme le montre la figure. Au fond, les données pour l'évolutivité, abandonné avant protocoles de données PB, format JSON au lieu d'utiliser le protocole classique comme émetteur-récepteur de données côté Socket.

Figure architecture sous-jacente 2

Dans le modèle de message, abandonner la stratégie avant que le message est un seul modèle, mais le modèle taillé dans un message texte en fonction du type de message, le modèle de messagerie photo et un autre modèle de message de base.

58 App DB et le traitement interne est encapsulé dans Socket SDK, exposant seulement le IMClient externe interface sous-jacente. En plus de tous les messages d'événements liés sont des interfaces interactives et des processus internes IMClient sous-jacents ne se soucient pas complètement. Une telle couche d'affaires complètement imperceptible et émission-réception comment les données sont stockées, et simplifie considérablement le coût d'accès.

Mais les lecteurs peuvent être mis en doute, IM SDK a été construit tant de types de messages, après qu'il n'y a pas de nouveaux types de messages dans le SDK comment faire? Pour résoudre ce problème, 58 App utilise une stratégie d'objets personnalisés iOS et le profil de retour solution similaire - dans une définition d'un nouveau message, tant qu'il a hérité de la fondation du type de message et suivez l'accord de IMMessageCoding. Le protocole défini dans les méthodes de codage et de décodage, qui, méthode encode est utilisée pour stocker le message dans un nouveau type de données dans une base de données (bien sûr, ce processus ne nécessite pas les développeurs de se concentrer sur le dessus, ils reviennent tout simplement à stocker dans cette fonction les données peuvent); décoder un procédé de restauration des données dans la base de données pour le modèle de message correspondant. Maintenant, nous avons un moyen défini de type de message, et comment l'utiliser? Afin d'être en mesure de percevoir la couche d'interface personnalisée de type de message sous-jacent a besoin d'un unifiée IMClient après l'initialisation, immédiatement à l'enregistrement, l'enregistrement connaîtra le courant IM sous-jacent type de message, et comprendre comment stocker et récupérer des données. Sur la base de cette conception, le 58 courant App fond IM peut être étendu à tout autre type de message, et pas besoin de modifier le code sous-jacent.

Le code sous-jacent est non seulement une bonne évolutivité, et offre beaucoup d'accord comme base de certaines scènes a également été conçu. Ces protocoles sont dynamiquement personnalisés ou supprimés. Par exemple, lorsqu'une liste de contacts changements, la nécessité de modifier l'image de contact, vous pouvez personnaliser l'accord de IMClientConversationListUpdateDelegate sous-jacent. Lorsqu'il est utilisé, le addUpdateConversationListDelegate côté entreprise:, contacter mise à jour à l'écoute de revenir en arrière, la mise à jour de l'avatar d'exécution en vous inscrivant l'accord. Lorsqu'il ne est pas, par removeUpdateConversationListDelegate: mode, soulevant l'auditeur. Des scènes similaires ont également reçu un message de protocole, la ligne de changement d'état de protocole. De cette manière, vous pouvez être flexible configuré le code de service de messagerie instantanée pour les conditions de surveillance du changement.

À l'heure actuelle, par l'abstraction du code sous-jacent, pour fournir une interface avec la couche supérieure isolé traitement de données internes et de nombreux services de messagerie instantanée peuvent être personnalisés pour atteindre, et ainsi de réaliser l'activité spécifique sans couplage. Avec cette conception de bas niveau, il peut être utilisé comme base de la messagerie instantanée SDK, à d'autres usages App, l'intégration rapide des fonctionnalités IM.

La conception à faible couplage, à des fonctions d'interface unique de couche intermédiaire

Pour être en mesure de communiquer et de la couche sous-jacente d'affaires, et aucun couplage mutuel, nous avons créé une couche d'interface à la liaison intermédiaire de liaison. Le scénario d'activité réelle, les points intermédiaires des trois cas, la couche d'interface, à savoir log interface associée, interfaces de messagerie et de message associé interfaces concernées, respectivement, et l'interface de butée inférieure uniforme. En divisant le scénario d'affaires, le processus de développement peut localiser rapidement le module concerné correspondant au service. Pour les messages fournis par le modèle sous-jacent, et ne sont pas directement utilisés, la raison sous-jacente est pas du tout vue du modèle de message concernés montrant attributs, tels que la hauteur de la ligne, la réutilisation des attributs d'identification (décrits en détail dans la section suivante). MVVM dans la machine virtuelle a besoin de quelques attributs vues associées et donc le modèle de message sous-jacent dans des messages de chat serait Cell modèle directement utilisable. Grâce à cette division et le modèle de message interface de service de conversion sous-jacente unifiée l'interface de messagerie ou des changements modèle, même après, tant que faire re-conversion et le modèle de message re-accosté au milieu de l'interface, haut entreprise change complètement imperceptibles ci-dessous.

couche d'affaires de conception évolutive

Comme l'ancien projet de système de messagerie instantanée est un début de construction, des scénarios simples d'affaires de traitement, le manque d'évolutivité. Par exemple, tous les messages utilisent le même modèle de données, la taille du code entraînera l'expansion du scénario d'affaires, le modèle augmente, l'utilisation d'un grand nombre de biens redondants insupportable. Dans la conception, l'ancienne architecture utilise le modèle de conception MVC, en raison de plus de vue le chat dans la scène de chat, pour traiter le type VC, VC intérieur est très gonflé. En raison des limites de l'architecture précédente. Cette nouvelle demande de service d'architecture de messagerie instantanée, comment concevoir un couplage faible et une forte expansion de la couche d'affaires? Ensuite, nous dire la mise en uvre spécifique.

  • un message partagé IM Modèle: Effacer les problèmes ci-dessus, maintenant 58 App seul message au modèle avant, divisé en texte, images, voix, rappels, modèle d'information audio, vidéo et autres, ils héritent d'un modèle unifié du message de classe de base, le message de classe de base stocke modèle les données de messagerie instantanée nécessaires, telles que les informations utilisateur dans le chat, les messages d'état envoyés.

  • En utilisant MVVM Architecture: Afin de réduire le couplage entre le VC respectif et affichage de la discussion. modèle de gestion VC divers messages, les besoins de message aux données d'affichage de vue stockées dans le modèle. Entre le message et le modèle de vue des messages pour obtenir des données dans les deux sens de liaison. De façon que le message stocké dans le modèle correspondant à celui-ci en vue de la discussion, le point de vue de chat de sorte que lorsque le message de changement et la nécessité de données mettent à jour le modèle, le modèle attribué au message directement. Lorsque vous souhaitez afficher les Varie de chat en fonction du changement d'attribut modèle de message, cette fonction est réalisée par voie de KVO. scénario IM exemple, nous envoyons un message, un message d'état est envoyé dans le modèle, lorsqu'un changement d'état de transmission (par exemple, le succès ou l'échec de la transmission), vue dans le chat peut être mis à jour en fonction de la valeur modifiée;

  • Protocole utilisé pour le modèle et vue organisation IM: Par protocole orienté, modèle organisationnel et vue messagerie instantanée, messages instantanés peuvent être améliorés modèle d'extensibilité et la vue. Ci aura un rôle important dans le cadre des détails techniques spécifiques, le quatrième set de protocole dans 58 système de conception IM orienté.

détails techniques

La liste de chat page Détails techniques

Étant donné que les caractéristiques du module IM, ainsi que les besoins de développement des affaires, le type de messagerie instantanée sera de plus en plus. Afin d'éviter de dépenser beaucoup d'énergie à chaque fois dans le processus de développement pour calculer la UITableView de la hauteur dans la cellule, pour laquelle nous utilisons dans l'App XIB créer différentes cellules et utiliser la manière AutoLayout pour voir la disposition de la cellule. Bien sûr, vous pouvez également le code écrit à la main, puis utilisez la mise en page AutoLayout. App mise en page avec l'IM dans XIB Le but est de permettre l'affichage de vue mise en page plus intuitive et une meilleure vue de la partie ainsi séparée et VC. Lorsque la cellule tout aménagement raisonnable est terminée, vous pouvez appeler le système systemLayoutSizeFittingSize: méthode d'obtention très Cell. Sur la base de cette idée, 58 App intérieur pour augmenter la capacité de calculer automatiquement la hauteur de cellule UITabelView, comme suit:

#import < UIKit uikit.h = "" >

#import "WBAutoCalculateTableViewDelegate.h"

@interface NSObject (WBAutoCalculateTableView)

@property (nonatomic, assign) CGFloat kid_height;

@end

@interface UITableView (WBAutoCalculateTableView)

- (CGFloat) heightForRowWithReuseIdentifier: (NSString * indentifiercellEntity: (NSObject *) cellEntity;

@end < / UIKit >

Tout d'abord, nous avons augmenté la catégorie à NSObject, et a ajouté la catégorie de biens kid_height, le but après avoir calculé la hauteur de la cellule, mieux son cache. Alors, la prochaine reload UITableView, simplement retourne le cache été plus élevé.

En second lieu, nous devons ajouter une catégorie UITableView. Utilisez heightForRowWithReuseIndentifier: cellEntity: cette API, après que le courant messages entrants identité de réutilisation cellulaire et le modèle courant de nouvelles, retourne la hauteur actuelle de la cellule. L'appelant n'a absolument pas les détails de calcul de la hauteur, les calculs sont terminés, profiteront immédiatement de la hauteur du cache de propriétés de classe NSObject dans le modèle de message.

Afin de répondre à différents types de messages modalités de données de remplissage des cellules incompatibles, nous présentons le protocole suivant:

#import < Fondation Foundation.h = "" >

@protocol WBCellConfigDelegate < NSObject >

@Required

- (void) setModel: (id) cellEntity;

@end < / NSObject > < / Fondation >

Alors, que tous les Messages Services UITableView doivent suivre cet accord, cet accord standardise l'unité de données de remplissage entre les différents messages Cell. messages cellulaires différents utilisent différents types de modèles de messagerie, mais il peut être rempli avec les mêmes spécifications.

@protocol WBAutoCalculateCellViewModelProtocol < NSObject >

@Required

- (NSString *) cellReuseIndentifier;

- (void) registerCellForTableView: (UITableView *) tableView;

@optional

- (CGFloat) cellHeight;

@end < / NSObject >

Pour répondre à la vue des messages dans le spectacle à venir, mais aussi selon le type de message en cours, utilisez le modèle pour juger quel genre de vue, 58 application en utilisant le modèle de sorte que chaque message suivant le protocole ci-dessus, chaque message est stocké avec le modèle correspondant identité de réutilisation. Comme il y a plusieurs façons d'enregistrement de cellules, telles que l'enregistrement de classe ou par l'enregistrement Nib, conçu ici comme une interface flexible, de manière cellulaire enregistrée entièrement aux développeurs ont décidé.

Le protocole facultatif suivant, mais également l'accent sur cette introduction - (CGFloat) cellHeight. L'accord est que, bien que la plupart de la scène peut calculer automatiquement la hauteur d'une cellule, mais certains types de messages hauteur est fixe, il n'y a pas besoin de calculer. Pour résoudre ce problème, nous donnons le modèle de message ajoute un accord en option cellHeight, si le modèle de message pour parvenir à cet accord, la hauteur de la cellule n'est pas automatiquement par la décision de la valeur de retour de cette méthode.

Parfois, comme des blocs de construction pour faire le projet, par la description ci-dessus, nous avons beaucoup de petites solutions, comme ayant beaucoup de pièces de construction, la façon dont ces programmes sont organisés, à la suite de ces « blocs de construction » assemblés temps. Parce que nous sommes organisés et gérés par uitableview de pages vues de chat, tout en tableView: heightForRowAtIndexPath: est une méthode proxy importante, actuellement mis en uvre comme suit:

#pragma marque UITableViewDelegate

- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) {indexPath

CGFloat cellHeight = 0;

ça < wbautocalculatecellviewmodelprotocol > cellEntity = self.viewModel.dataSource ;

// Enregistrer la cellule à tableview dans par cellindentifier

si ( ) {

if (! self.tableViewRegisters de ) {

;

self.tableViewRegisters = @ (1);

}

}

si ( ) {

cellHeight = ;

} Else {

cellHeight = ;

}

retourner cellHeight;

} < / Wbautocalculatecellviewmodelprotocol >

Dans cette méthode, nous voyons chaque cellEntity (modèle de message), nous avons suivi le WBAutoCalculateCellViewModelProtocol décrit ci-dessus. Dans ce procédé, le modèle permet à chaque message à enregistrer leur type de cellule, puis calculer la hauteur de la cellule, si le modèle de message a procédé de cellHeight, calculer la hauteur par ce procédé, sinon par les considérés automatiquement de manière mentionnée ci-dessus des rendements élevés hauteur de cellule.

le traitement des cellules sur l'écran, la source de données la méthode UITableView tableview: cellForRowAtIndexPath: le noyau de la méthode est actuellement mis en oeuvre comme suit:

- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) {indexPath

ça < wbautocalculatecellviewmodelprotocol > MODÈLE = self.viewModel.dataSource ;

NSString * cellIndentifier = ;

UITableViewCell < wbcellconfigdelegate >  * = Cell ;

si (cellule && ) {

;

}

si (cellule) {

cell = (UITableViewCell < wbcellconfigdelegate >  *) ;

}

cellule de retour;

} < / Wbcellconfigdelegate > < / Wbcellconfigdelegate > < / Wbautocalculatecellviewmodelprotocol >

Réutilisation trouver le message identifié par le modèle. Parce qu'il a été tableView: heightForRowAtIndexPath: Lorsqu'une cellule enregistrée, Cell retour tout sera capable de taper dans la file d'attente des messages en réutilisant. Cellule WBCellConfigDelegate suivre le protocole de message, les données comportant une façon uniforme, de sorte que lors du remplissage.

En approche de conception orientée protocole, nous tableView dans VC dans les sources de données et méthodes proxy devient si simple. Et si dans l'expansion future du nouveau type de message, continuer à suivre le protocole approprié, VC est une ligne dans le code ne sont pas modifiés, tant que les développeurs au large et notez le nouveau modèle de nouvelles et des vues peut être.

Figure 3 Design du service de couche intermédiaire liaison de raccordement

Le traitement des détails techniques du message hors-ligne Voip

Le processus de développement réel, nous avons rencontré un problème lorsque B est pas en ligne, en bavardant avec B peut initier des messages audio et vidéo à B, le serveur de signalisation à l'intégralité du message, va créer une file d'attente, le message de tous les B la signalisation enregistrée. Après un certain temps, lorsque les journaux B, pendant le serveur B décommander tous la signalisation m'a envoyé. Depuis le début de la conception sans tenir compte du temps que, lorsqu'un problème survient lorsque B commence, A a envoyé un message vidéo à venir, B reçoit la première signalisation vidéo signale lors des messages vidéo hors ligne (le cas échéant) . Cela a donné lieu à B essayer de se connecter un canal vidéo qui n'existe pas déjà, et laisser le chat vidéo A-B ne se connecter. Pour ce support client séquence de signalisation, en utilisant la technologie condition de verrouillage de signalisation ordonnée de connexion vidéo traité, comme représenté sur la Fig.

Figure 4 Conception séquence de signalisation d'appel

Des solutions spécifiques sont les suivantes:

  • Tout d'abord, nous créons une file d'attente concurrente, lorsque le signal de signalisation au client, et mis en exécution concurrente file d'attente;

  • Afin de garantir un ordre d'exécution de signalisation Voip, nous introduisons la condition de verrouillage NSCondition, la file d'attente de traitement parallèle signal de Voip, pour acquérir une condition de verrouillage, l'acquisition est terminée, nous balise de variable isAvLockActive Bool est OUI, le signal de traitement préliminaire, après le traitement initial est terminé Unlock la condition de verrouillage;

  • En raison de Déverrouillez la condition de verrouillage, la file d'attente d'autres Voip ont une chance de gérer la signalisation. Traitement, détection état isAvLockActive, si OUI, le Voip, les conditions de processus de verrouillage d'attente est n'a pas encore la signalisation décrit précédemment été traitée effectuée;

Lorsqu'un événement de signal Voip entièrement traité, les conditions déclencheront de verrouillage du signal, cette fois, la file d'attente pour d'autres conditions de verrouillage du signal peut être traité. Dans ce cas, nous revenir à l'étape 2, jusqu'à ce que la file d'attente n'a pas de traitement du signal Voip en attente.

résumé

La reconfiguration du système de messagerie instantanée, par la séparation de l'interface sous-jacente couplée de telle sorte que le SDK IM réduit de telle sorte que le protocole conçu pour la page de chat d'amélioration de l'évolutivité, court interne App élargi texte riche, images, emplacement, résumé, carte et d'autres types de messages. Espoirs pour reconstituer le cours de 58 App IM, donner conçus ou modifiés pour optimiser les développeurs du module de messagerie instantanée pour fournir une référence. À l'avenir, nous serons sur la façon d'améliorer les performances de la page et réduire encore l'accord de tous les détails sur le trafic des utilisateurs, continuer à améliorer GI.

Auteur: jouer Jiang, ingénieur de recherche iOS 58 ville, spécialisée dans le développement des infrastructures et l'optimisation des performances du système App IM, a dominé l'architecture du système App IM 58 ville et le développement.

Editeur: chef Tangmen (tangxy@csdn.net)

déclaration: Cet article RPSC « programmeur » de l'article original sans autorisation, s'il vous plaît ne réimprime pas pour la réimpression, s'il vous plaît laisser un message.

Guangqu BRT mis en service en 2020, la ligne 26 kilomètres situé à 5 étage de stationnement de bus
Précédent
« Right Tour » corbeau volant dans les sites de médias, P & G a déclaré que l'épargne d'un milliard de budget média | Parti quotidien
Prochain
Les développeurs à la fin ce qui doit développer l'acquisition du langage
Pourquoi Go sera ma langue préférée?
499 yuans! Un plus explorateur sac à bandoulière Publié: tissus de qualité militaire
« Un moyen facile de manger quelque chose et aller « sentiment de plats cuisinés maison à Chengdu Shuangliu Airport à partir du sol
Les programmeurs ce qui ne va pas? Pourquoi me tuer le ciel
Jingdong a commencé à croître la sagesse de légumes de l'usine d'usine auto-construit pour la première fois dans les médias publics
2019 manière dont l'argent est encore Aung un « géant » Il a également côté délicat!
Pékin 52 normes techniques d'assainissement mises en uvre en 2019 Jour de l'An
Quête Pékin « de l'axe sud souterraine », ligne 8 à Zhushikou extrémité sud de segment Yonhap d'opération d'essai
Un plus State Bank 6T annoncé le prix: la version initiale du 3999 yuans empereur 3399 yuans
Les consommateurs étrangers, « l'économie réseau Red » acheter? Voici un petit sondage
monde des arts martiaux, mais pas la rupture du jeûne un plus 6T première évaluation: l'explosif phare tous azimuts