Hilfsbereit, pfeilschnell und immer informiert! (Alles rund um T-Mobile G1 und Android)

Einfaches GoogleMaps mit Threads - Tutorial

Dieses Tutorial basiert auf meinem GoogleMaps mit der Geocoder Klasse Tutorial, wo ich beschrieben habe, wie man Adressen in Longitude und Latitude konvertiert. Liest man allerdings aufmerksam die GeoCoder-API, dann stolpert man über den Hinweis, dass es hilfreich sei, die GeoCoder-Instanz in einem eigenständigen Thread und unabhängig von der GUI auszuführen. Dieses Tutorial zeigt, wie man in Android Threads einsetzt.

Wie die Anwendung aussieht:

Wie die GoogleMaps mit der Geocoder Klasse Anwendung, nur dass zusätzlich ein Ladebalken eingeblendet wird.

Was ist ein Thread?

Lies folgendes Kapitel in der Java Dokumentation um zu verstehen was Threads sind und wie sie funktionieren. Außerdem ist folgendes Kapitel in der Android API interessant.
Alternativ kannst du auch einfach folgende Definition hinnehmen:
Ein Thread erlaubt einen paralleler Prozess - Während wir die Adresse suchen, können wir gleichzeitig noch einen Ladebalken anzeigen.

Wie können wir Threads in Android verwenden?

Wir können ein Runnable Interface einbinden und die run() Funktion aufrufen.

public class simpleGoogleMaps extends MapActivity implements runnable{
  public void onCreate(Bundle savedInstanceState) {
  Thread thread = new Thread(this);
  thread.start();
  }
 
  public void run() {
    //do something
  }
} 

Lies dieses Tutorial von helloandroid.com um zu sehen, wie diese Methode funktioniert.
Aber es gibt noch einen alternativen Weg, wie man Threads verwenden kann und der meiner Meinung nach wesentlich sauberer ist.

GeoCoder mit Threads:

1. Wenn man sich den Quellcode des vorherigen Tutorials anschaut, dann sieht man, dass alles Wichtige, wie das Suchen der Adresse, in der OnClickListener Funktion stattfindet. Wenn wir mit Threads arbeiten, ist das Einzige, was wir in der OnClickListener Funktion tun, das Anzeigen des Ladebalkens und der Start eines neuen Threads in dem parallel alle weiteren wichtigen Funktionen ausgeführt werden.

btnSearch.setOnClickListener(new OnClickListener() {
  public void onClick(View v) {
    //Show a progress dialog
    pd = ProgressDialog.show(simpleGoogleMaps.this, "Working..", "Searching your address", true, false);
 
    //create a new thread
    searchAdress = new Thread() {
      public void run(){
        //action of the thread
      }
    }
  }
}); 

2. Während der Ladebalken angezeigt wird, müssen wir nach einer Adresse suchen. Also fügen wir unserem Thread die Suchfunktion hinzu und starten sie.

btnSearch.setOnClickListener(new OnClickListener() {
  public void onClick(View v) {
    //Show a progress dialog
    pd = ProgressDialog.show(simpleGoogleMaps.this, "Working..", "Searching your address", true, false);
 
    //create a new thread
    searchAdress = new Thread() {
      public void run(){
        String addressInput = adress.getText().toString(); // Get input text
        try {
          foundAdresses = gc.getFromLocationName(addressInput, 5); // Search addresses
        } catch (Exception e) {
          // @todo: Show error message
        }
        showAdressResults.sendEmptyMessage(0);   
      }
    }
    searchAdress.start();
  }
}); 

3. Nun zeigt unsere Anwendung einen hübschen Ladebalken an und sucht im Hintergrund nach einer Adresse. Sehr schön!
Nachdem der Suchvorgang beendet ist (Ende des try-catch-Statement), senden wir eine Benachrichtigung an die neue Funktion showAdressResults, die wir noch erstellen müssen.

private Handler showAdressResults = new Handler() {
  public void handleMessage(Message msg) {
  }
} 

4. showAdressResults ist ein Handler, der die ganze Zeit nichts weiter tut, als darauf zu warten, dass er eine Benachrichtigung bekommt. Wenn er eine bekommt weiß er, dass eine Adresse gefunden wurde. Dann müssen wir den Ladebalken ausblenden und die Karte anzeigen.

private Handler showAdressResults = new Handler() {
  public void handleMessage(Message msg) {
    if (foundAdresses.size() == 0) { // if no address found,
      // display an error
      Dialog locationError = new AlertDialog.Builder(simpleGoogleMaps.this)
      .setIcon(0)
      .setTitle("Error")
      .setPositiveButton(R.string.ok, null)
      .setMessage("Sorry, your address doesn't exist.")
    .create();
    locationError.show();
 
    } else { // else display address on map
      for (int i = 0; i < foundAdresses.size(); ++i) {
      // Save results as Longitude and Latitude
      Address x = foundAdresses.get(i);
      lat = x.getLatitude();
      lon = x.getLongitude();
    }
    navigateToLocation((lat * 1000000), (lon * 1000000),myMap); // display the found address
  }
} 

Fertig ist unsere erste Android-Anwendung mit Threads :-)

Kompletter Quellcode:

//lots of imports.. see attached source
public class simpleGoogleMaps extends MapActivity {
     protected boolean isRouteDisplayed() {
          return false;
     }
 
     //lots of variables here
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
 
          myMap = (MapView) findViewById(R.id.simpleGM_map); // Get map from XML
          btnSearch = (Button) findViewById(R.id.simpleGM_btn_search); // Get button from xml
          adress = (EditText) findViewById(R.id.simpleGM_adress); // Get address from XML
 
          gc = new Geocoder(this); // create new geocoder instance
 
          btnSearch.setOnClickListener(new OnClickListener() {
               public void onClick(View v) {
 
                    pd = ProgressDialog.show(simpleGoogleMaps.this, "Working..", "Searching your address", true, false); //Show a progress dialog
 
 
                    searchAdress = new Thread() {
                         public void run(){
                              String addressInput = adress.getText().toString(); // Get input text
                              try {
                                   foundAdresses = gc.getFromLocationName(addressInput, 5); // Search addresses
                                   Thread.sleep(1500); //just to show you that it works
                              } catch (Exception e) {
                                   // @todo: Show error message
                              }
                              showAdressResults.sendEmptyMessage(0);                           
                         }
                    };
                    searchAdress.start();
               }
          });
     }
 
     private Handler showAdressResults = new Handler() {
             @Override
             public void handleMessage(Message msg) {
               pd.dismiss();
 
                    if (foundAdresses.size() == 0) { // if no address found,
                         // display an error
                         Dialog locationError = new AlertDialog.Builder(
                                   simpleGoogleMaps.this).setIcon(0).setTitle(
                                   "Error").setPositiveButton(R.string.ok, null)
                                   .setMessage("Sorry, your address doesn't exist.")
                                   .create();
                         locationError.show();
 
                    } else { // else display address on map
                         for (int i = 0; i < foundAdresses.size(); ++i) {
                              // Save results as Longitude and Latitude
                              // @todo: if more than one result, then show a
                              // select-list
                              Address x = foundAdresses.get(i);
                              lat = x.getLatitude();
                              lon = x.getLongitude();
                         }
                         navigateToLocation((lat * 1000000), (lon * 1000000),myMap); // display the found address
                    }
 
             }
         };   
 
     /**
      * Navigates a given MapView to the specified Longitude and Latitude
      *
      * @param latitude
      * @param longitude
      * @param mv
      */
     public static void navigateToLocation(double latitude, double longitude, MapView mv) {
          GeoPoint p = new GeoPoint((int) latitude, (int) longitude); // new
          // GeoPoint
          mv.displayZoomControls(true); // display Zoom (seems that it doesn't
          // work yet)
          MapController mc = mv.getController();
          mc.animateTo(p); // move map to the given point
          int zoomlevel = mv.getMaxZoomLevel(); // detect maximum zoom level
          mc.setZoom(zoomlevel - 1); // zoom
          mv.setSatellite(false); // display only "normal" mapview
     }
} 

Ich hoffe das Tutorial veranschaulicht etwas die Funktionsweise von Threads.
Dieses Tutorial wurde von mir bereits hier auf englisch im Anddev.org Forum veröffentlicht.

AnhangGröße
simpleGoogleMaps-withThread.tar74.5 KB