Analyser XML à l'aide de l'analyseur d'extraction XML

 Bien que les instructions détaillées pour l’analyse XML et l’interaction avec des services Web spécifiques sortent du cadre de ce manuel, il est important de comprendre les technologies disponibles. Cette section fournit un bref aperçu de l’analyseur de traction XML et les sections suivantes illustrent l’utilisation de l’analyseur DOM et du lecteur JSON pour extraire les détails des tremblements de terre à partir du United States Geological Survey (USGS). L'API XML Pull Parser est disponible dans les bibliothèques suivantes:

  1. org.xmlpull.v1.XmlPullParser;
  2. org.xmlpull.v1.XmlPullParserException;
  3. org.xmlpull.v1.XmlPullParserFactory;

 Il vous permet d'analyser un document XML en un seul passage. Contrairement à l'analyseur DOM, Pull Parser présente les éléments de votre document dans une série séquentielle d'événements et de balises. Votre position dans le document est représentée par l'événement en cours. Vous pouvez déterminer l'événement en cours en appelant getEventType. Chaque document commence à l'événement START_DOCUMENT et se termine à END_DOCUMENT. Pour passer par les balises, appelez simplement next, ce qui vous obligera à parcourir une série d’événements START_TAG et END_TAG correspondants (et souvent imbriqués). Vous pouvez extraire le nom de chaque balise en appelant getName et extraire le texte entre chaque ensemble de balises à l'aide de getNextText. Le Listing 7-4 montre comment utiliser XML Pull Parser pour extraire des détails de la liste des points d'intérêt renvoyée par l'API Google Places. 

LISTING 7-4: Analyser XML à l'aide de l'analyseur de traction XML

 

  1. private void processStream(InputStream inputStream) {
  2. // Create a new XML Pull Parser.
  3. XmlPullParserFactory factory;
  4. try {
  5. factory = XmlPullParserFactory.newInstance();
  6. factory.setNamespaceAware(true);
  7. XmlPullParser xpp = factory.newPullParser();
  8. // Assign a new input stream.
  9. xpp.setInput(inputStream, null);
  10. int eventType = xpp.getEventType();
  11. // Allocate a variable for extracted name tags.
  12. String name;
  13. // Continue until the end of the document is reached.
  14. while (eventType!= XmlPullParser.END_DOCUMENT) {
  15. // Check for a start tag of the results tag.
  16. if (eventType == XmlPullParser.START_TAG &&xpp.getName().equals("result")) {
  17. eventType = xpp.next();
  18. // Process each result within the result tag.
  19. while (!(eventType == XmlPullParser.END_TAG &&xpp.getName().equals("result"))) {
  20. // Check for the name tag within the results tag.
  21. if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("name")) {
  22. // Extract the POI name.
  23. name = xpp.nextText();
  24. doSomethingWithName(name);
  25. }
  26. // Move on to the next tag.
  27. eventType = xpp.next();
  28. }
  29. // Do something with each POI name.
  30. }
  31. // Move on to the next result tag.
  32. eventType = xpp.next();
  33. }
  34. } catch (XmlPullParserException e) {
  35. Log.e("PULLPARSER","XML Pull Parser Exception",e);
  36. } catch (IOException e) {
  37. Log.e("PULLPARSER","IO Exception",e);
  38. }
  39. }

Connexion du visualiseur de tremblement de terre à Internet

Vous remplacerez la liste fictive de tremblements de terre par une liste réelle en la connectant à un flux de tremblement de terre, en le téléchargeant et en l'analysant de manière à ce qu'il soit possible. affiché dans votre fragment de liste. Le fichier XML du flux parasismique est analysé ici par l’analyseur DOM. Plusieurs alternatives existent, y compris XML Pull Parser décrit dans la section précédente. Vous pouvez également analyser le flux JSON à l'aide de la classe JsonReader, comme indiqué dans la section suivante. 

1. Pour cet exemple, l'aliment utilisé est l'aliment USGS Atom d'une journée pour les séismes d'une magnitude supérieure à 2,5 sur l'échelle de Richter. Ajoutez l'emplacement de votre flux en tant que ressource chaîne externe dans le fichier de ressources Strings.xml du dossier res / values. Cela vous permet éventuellement de spécifier un autre flux en fonction des paramètres régionaux d'un utilisateur: 

2.

 

  1. < resources >
  2. <string name="app_name">Earthquake</string>
  3. <string name="earthquake_feed"> https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom
  4. </string>
  5. < /resources >

3. Avant de pouvoir accéder à ce flux, votre application doit demander une autorisation pour accéder à Internet. Ajoutez l’utilisation Internet d’autorisations en haut de votre fichier manifeste:

 

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. < manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.professionalandroid.apps.earthquake">
  3. <uses-permission android:name="android.permission.INTERNET"/>
  4.  
  5.  
  6.  
  7. [... Application Node ...]
  8.  
  9. < /manifest >

 4. Notre accès Internet doit se faire sur un thread en arrière-plan et les résultats doivent persister lors des changements de configuration de l'appareil. Utilisez un modèle de vue et des données en direct pour cela. Commencez par ajouter une dépendance aux extensions de cycle de vie des composants d'architecture Android au fichier de génération Gradle de votre module d'application:

 

  1. dependencies {
  2.  
  3. [... Existing dependencies nodes ...]
  4.  
  5. impementation "android.arch.lifecycle:extensions:1.1.1"
  6. }
  1. Create a new EarthquakeViewModel that extends AndroidViewModel and includes a MutableLiveData variable that represents a List of Earthquakes. This View Model will be cached and maintained across configuration changes. Create a getEarthquakes method that will check if our Earthquake List Live Data has been populated already, and if not, will load the Earthquakes from the feed:

 

  1. public class EarthquakeViewModel extends AndroidViewModel {
  2. private static final String TAG = "EarthquakeUpdate";
  3. private MutableLiveData<List<Earthquake>> earthquakes;
  4. public EarthquakeViewModel(Application application) {
  5. super(application);
  6. }
  7. public LiveData<List<Earthquake>> getEarthquakes() {
  8. if (earthquakes == null) {
  9. earthquakes = new MutableLiveData<List<Earthquake>>();
  10. loadEarthquakes();
  11. }
  12. return earthquakes;
  13. }
  14. // Asynchronously load the Earthquakes from the feed.
  15. public void loadEarthquakes() {
  16.  
  17. }
  18. }

 5. Mettez à jour la méthode loadEarthquakes pour télécharger et analyser le flux parasismique. Cela doit être fait sur un thread en arrière-plan, alors implémentez une AyncTask pour simplifier ce processus. En arrière-plan, extrayez chaque séisme et analysez les détails pour obtenir l'identifiant, la date, la magnitude, le lien et la localisation. Une fois le flux analysé, mettez à jour le gestionnaire onPostExecute afin de définir la valeur des données en direct Mutable représentant notre liste de tremblements de terre. Cela alertera tous les observateurs inscrits en leur transmettant la liste mise à jour:

  1. public void loadEarthquakes() {
  2. new AsyncTask<Void,Void,List<Earthquake>>() {
  3. @Override
  4. protected List<Earthquake> doInBackground(Void... voids) {
  5. // Result ArrayList of parsed earthquakes.
  6. ArrayList<Earthquake> earthquakes = new ArrayList<>(0);
  7. // Get the XML
  8. URL url;
  9. try {
  10. String quakeFeed = getApplication().getString(R.string.earthquake_feed);
  11. url = new URL(quakeFeed);
  12. URLConnection connection;
  13. connection = url.openConnection();
  14. HttpURLConnection httpConnection = (HttpURLConnection)connection;
  15. int responseCode = httpConnection.getResponseCode();
  16. if (responseCode == HttpURLConnection.HTTP_OK) {
  17. InputStream in = httpConnection.getInputStream();
  18. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  19. DocumentBuilder db = dbf.newDocumentBuilder();
  20. // Parse the earthquake feed.
  21. Document dom = db.parse(in);
  22. Element docEle = dom.getDocumentElement();
  23. // Get a list of each earthquake entry.
  24. NodeList nl = docEle.getElementsByTagName("entry");
  25. if (nl != null &amp;&amp; nl.getLength() > 0) {
  26. for (int i = 0 ; i < nl.getLength(); i++) {
  27. // Check to see if our loading has been cancelled, in which
  28. // case return what we have so far.
  29. if (isCancelled()) {
  30. Log.d(TAG, "Loading Cancelled");
  31. return earthquakes;
  32. }
  33. Element entry = (Element)nl.item(i);
  34. Element id = (Element)entry.getElementsByTagName("id").item(0);
  35. Element title =(Element)entry.getElementsByTagName("title").item(0);
  36. Element g = (Element)entry.getElementsByTagName("georss:point").item(0);
  37. Element when = (Element)entry.getElementsByTagName("updated").item(0);
  38. Element link = (Element)entry.getElementsByTagName("link").item(0);
  39. String idString = id.getFirstChild().getNodeValue();
  40. String details = title.getFirstChild().getNodeValue();
  41. String hostname = "http://earthquake.usgs.gov";
  42. String linkString = hostname + link.getAttribute("href");
  43. String point = g.getFirstChild().getNodeValue();
  44. String dt = when.getFirstChild().getNodeValue();
  45. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
  46. Date qdate = new GregorianCalendar(0,0,0).getTime();
  47. try {
  48. qdate = sdf.parse(dt);
  49. } catch (ParseException e) {
  50. Log.e(TAG, "Date parsing exception.", e);
  51. }
  52. String[] location = point.split(" ");
  53. Location l = new Location("dummyGPS");
  54. l.setLatitude(Double.parseDouble(location[0]));
  55. l.setLongitude(Double.parseDouble(location[1]));
  56. String magnitudeString = details.split(" ")[1];
  57. int end = magnitudeString.length()-1;
  58. double magnitude = Double.parseDouble(magnitudeString.substring(0, end));
  59. if (details.contains("-"))
  60. details = details.split("-")[1].trim();
  61. else
  62. details = "";
  63. final Earthquake earthquake = new Earthquake(idString,qdate,details,l, magnitude, linkString);
  64. // Add the new earthquake to our result array.
  65. earthquakes.add(earthquake);
  66. }
  67. }
  68. }
  69. httpConnection.disconnect();
  70. } catch (MalformedURLException e) {
  71. Log.e(TAG, "MalformedURLException", e);
  72. } catch (IOException e) {
  73. Log.e(TAG, "IOException", e);
  74. } catch (ParserConfigurationException e) {
  75. Log.e(TAG, "Parser Configuration Exception", e);
  76. } catch (SAXException e) {
  77. Log.e(TAG, "SAX Exception", e);
  78. }
  79. // Return our result array.
  80. return earthquakes;
  81. }
  82. @Override
  83. protected void onPostExecute(List<Earthquake> data) {
  84. // Update the Live Data with the new list.
  85. earthquakes.setValue(data);
  86. }
  87. }.execute();
  88. }

 

  1. Update your Earthquake Main Activity to remove the dummy data and update the Earthquake List Fragment to use your new EarthquakeViewModel.

           6.1. Start by updating the Activity’s onCreate handler, removing the dummy data

 

  1. EarthquakeViewModel earthquakeViewModel;
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_earthquake_main);
  6. FragmentManager fm = getSupportFragmentManager();
  7. // Android will automatically re-add any Fragments that
  8. // have previously been added after a configuration change,
  9. // so only add it if this is an automatic restart.
  10. if (savedInstanceState == null) {
  11. FragmentTransaction ft = fm.beginTransaction();
  12. mEarthquakeListFragment = new EarthquakeListFragment();
  13. ft.add(R.id.main_activity_frame, mEarthquakeListFragment,TAG_LIST_FRAGMENT);
  14. ft.commitNow();
  15. } else {
  16. mEarthquakeListFragment=(EarthquakeListFragment)fm.findFragmentByTag(TAG_LIST_FRAGMENT);
  17. }
  18. // Retrieve the Earthquake View Model for this Activity.
  19. earthquakeViewModel = ViewModelProviders.of(this).get(EarthquakeViewModel.class);
  20. }

       6.2.  Dans le fragment de liste des tremblements de terre, mettez à jour le gestionnaire onActivityCreated. Utilisation de la méthode statique de méthode du fournisseur de modèle de vue pour récupérer l’instance actuelle de votre modèle de vue sismique. Ajoutez un observateur aux données en direct renvoyées par votre modèle de vue. Il définira la liste des tremblements de terre avec la liste des tremblements de terre lors de la création de votre activité, et à chaque fois que la liste des tremblements de terre analysés est mise à jour:

 

  1. protected EarthquakeViewModel earthquakeViewModel;
  2. @Override
  3. public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);
  4. // Retrieve the Earthquake View Model for the parent Activity.
  5. earthquakeViewModel = ViewModelProviders.of(getActivity()).get(EarthquakeViewModel.class);
  6. // Get the data from the View Model, and observe any changes.
  7. earthquakeViewModel.getEarthquakes().observe(this, new Observer<List<Earthquake>>() {
  8.  
  9. @Override
  10. public void onChanged(@Nullable List<Earthquake> earthquakes) {
  11. // When the View Model changes, update the List
  12. if (earthquakes != null)
  13. setEarthquakes(earthquakes);
  14. }
  15. });
  16. }

7. Lorsque vous exécutez votre projet, vous devriez voir une vue Recycler présentant les tremblements de terre des dernières 24 heures d'une magnitude supérieure à 2,5.

8- Les données sismiques sont mises en cache par le modèle de vue. Elles seront donc conservées lors des modifications de la configuration de l'appareil et ne s'actualiseront que lorsque l'application sera redémarrée. Mettons à jour l’application pour permettre aux utilisateurs d’actualiser la liste des tremblements de terre à l’aide du modèle de balayage à rafraîchir. Mettez à jour la ressource de présentation .xml fragment_earthquake_list pour inclure un SwipeRefreshLayout en tant que parent de RecyclerView:

 

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. < android.support.v4.widget.SwipeRefreshLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/swiperefresh" android:layout_width="match_parent"
  5. android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView
  6. xmlns:android="http://schemas.android.com/apk/res/android"
  7. xmlns:app="http://schemas.android.com/apk/res-auto"
  8. android:id="@+id/list"android:layout_width="match_parent"
  9. android:layout_height="match_parent"android:layout_marginLeft="16dp"
  10. android:layout_marginRight="16dp"app:layoutManager="LinearLayoutManager"
  11. />
  12. < /android.support.v4.widget.SwipeRefreshLayout >

9. Mettez à jour onCreateView dans le fragment de liste de tremblements de terre pour obtenir une référence à la structure d'actualisation de balayage ajoutée à l'étape 8, et mettez à jour onViewCreated pour affecter un écouteur de rafraîchissement à la présentation d'actualisation de balayage qui appelle une nouvelle méthode updateEarthquakes lorsque l'action de balayage est actualisée. 

  1. private SwipeRefreshLayout mSwipeToRefreshView;
  2. @Override
  3. public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
  4. View view = inflater.inflate(R.layout.fragment_earthquake_list, container, false);
  5. mRecyclerView = (RecyclerView) view.findViewById(R.id.list);
  6. mSwipeToRefreshView = view.findViewById(R.id.swiperefresh);
  7. return view; }
  8. @Override
  9. public void onViewCreated(View view, Bundle savedInstanceState) {
  10. super.onViewCreated(view, savedInstanceState);
  11. // Set the Recycler View adapter
  12. Context context = view.getContext();
  13. mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
  14. mRecyclerView.setAdapter(mEarthquakeAdapter);
  15. // Setup the Swipe to Refresh view
  16. mSwipeToRefreshView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
  17. @Override
  18. public void onRefresh() {
  19. updateEarthquakes();
  20. }
  21. });
  22. }
  23. protected void updateEarthquakes() { }

 

 10. Mettez à jour la méthode setEarthquakes pour désactiver l'indicateur visuel «rafraîchissement» lorsqu'une mise à jour a été reçue:

  1. public void setEarthquakes(List<Earthquake> earthquakes) {
  2. mEarthquakes.clear();
  3. mEarthquakeAdapter.notifyDataSetChanged();
  4. for (Earthquake earthquake: earthquakes) {
  5. if (!mEarthquakes.contains(earthquake)) {
  6. mEarthquakes.add(earthquake);
  7. mEarthquakeAdapter.notifyItemInserted(mEarthquakes.indexOf(earthquake));
  8. }
  9. }
  10. mSwipeToRefreshView.setRefreshing(false);
  11. }

 

11. La mise à jour elle-même sera effectuée par le modèle de vue sismique, avec lequel nous communiquons via l'activité parent. Définir un nouvel OnListFragmentInteractionListener dans le fragment de liste de tremblements de terre; elle doit inclure une méthode onListFragmentRefreshRequested qui est appelée lorsque nous demandons une actualisation via la méthode updateEarthquakes ajoutée à l’étape 9:

 

  1. public interface OnListFragmentInteractionListener {
  2. void onListFragmentRefreshRequested();
  3. }
  4. private OnListFragmentInteractionListener mListener;
  5. @Override
  6. public void onAttach(Context context) {
  7. super.onAttach(context);
  8. mListener = (OnListFragmentInteractionListener) context;
  9. }
  10. @Override
  11. public void onDetach() {
  12. super.onDetach();
  13. mListener = null; }
  14. protected void updateEarthquakes() {
  15. if (mListener != null)
  16. mListener.onListFragmentRefreshRequested();
  17. }

12. Revenez à l'activité principale de tremblement de terre et demandez-lui de mettre en œuvre l'interface définie à l'étape 11, et utilisez le modèle de vue de tremblement de terre pour forcer l'actualisation à la demande:Classe publique EarthquakeMainActivity étend AppCompatActivity implémente EarthquakeListFragment.OnListFragmentInteractionListener 

 

  1. {
  2. @Override
  3. public void onListFragmentRefreshRequested() {
  4. updateEarthquakes(); }
  5. private void updateEarthquakes() {
  6. // Request the View Model update the earthquakes from the USGS feed.
  7. earthquakeViewModel.loadEarthquakes();
  8. }
  9.  
  10.  
  11. [... Existing Class Definition ...]
  12. }

13. Vous devez également ajouter une action d'actualisation en tant qu'élément de menu ou dans la barre d'actions de votre application pour aider les utilisateurs incapables d'effectuer un mouvement de balayage (par exemple, les utilisateurs ayant des problèmes d'accessibilité peuvent déclencher des actions à l'aide de périphériques externes, tels que comme claviers et d-pads).