1

Ich bin ziemlich neu in einer Android-Entwicklung und versuche derzeit, eine App zu schreiben, die das Wetter von morgen von mehreren Städten zeigen wird. Entschuldigung für alle fehlerhaften Begriffe, die ich in dieser Frage verwenden könnte.Android. Daten aus der Datenbank abrufen und dann eine Netzwerkabfrage durchführen. Wie zu implementieren?

Was ich erreichen möchte:

App werden die Daten von der lokalen Datenbank holen, dann eine HTTP-Abfrage auf die von einer DB abgerufenen Daten bauen, JSON-Antwort zu erhalten und eine Liste Elemente bilden.

Was ich derzeit haben:

Alles außer SQL-Funktionalität.

Hier ist der Schnappschuss meines Hauptaktivitätscodes. Ich benutze LoaderCallbacks<List<Weather>>, um URI mit den erforderlichen Parametern in zu bauen, senden HTTP-Abfrage und erhalten die Daten über WeatherLoader(this, uriList), und Formularelemente Ergebnisse in List mit WeatherAdapter.

public class WeatherActivity extends AppCompatActivity 
implements LoaderCallbacks<List<Weather>>, 
SharedPreferences.OnSharedPreferenceChangeListener { 

private static final int WEATHER_LOADER_ID = 1; 
private WeatherAdapter mAdapter; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.weather_activity); 

    ListView weatherListView = (ListView) findViewById(R.id.list); 

    mEmptyStateTextView = (TextView) findViewById(R.id.empty_view); 
    weatherListView.setEmptyView(mEmptyStateTextView); 
    mAdapter = new WeatherAdapter(this, new ArrayList<Weather>()); 
    weatherListView.setAdapter(mAdapter); 

    ... 

    weatherListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
     @Override 
     public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { 
      Weather currentWeather = mAdapter.getItem(position); 
      Uri forecastUri = Uri.parse(currentWeather.getUrl()); 
      Intent websiteIntent = new Intent(Intent.ACTION_VIEW, forecastUri); 
      startActivity(websiteIntent); 
     } 
    }); 

    ConnectivityManager connMgr = (ConnectivityManager) 
      getSystemService(Context.CONNECTIVITY_SERVICE); 
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); 
    if (networkInfo != null && networkInfo.isConnected()) { 
     LoaderManager loaderManager = getLoaderManager(); 
     loaderManager.initLoader(WEATHER_LOADER_ID, null, this); 
    } else { 
     View loadingIndicator = findViewById(R.id.loading_indicator); 
     loadingIndicator.setVisibility(View.GONE); 
     mEmptyStateTextView.setText(R.string.no_internet_connection); 
    } 
} 

@Override 
public Loader<List<Weather>> onCreateLoader(int i, Bundle bundle) { 

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 
    String tempUnit = sharedPrefs.getString(
      getString(R.string.settings_temp_unit_key), 
      getString(R.string.settings_temp_unit_default)); 

    List<String> uriList = new ArrayList<>(); 

    /*** 
    * 
    * Here we input cities for which we want to see the forecast 
    * 
    * ***/ 

    List<String> cities = new ArrayList<>(); 
    cities.add("London,uk"); 
    cities.add("Kiev,ua"); 
    cities.add("Berlin,de"); 
    cities.add("Dubai,ae"); 

    //For each city in the list generate URI and put it in the URIs list 
    for (String city : cities){ 
     Uri baseUri = Uri.parse(OWM_REQUEST_URL); 
     Uri.Builder uriBuilder = baseUri.buildUpon(); 

     uriBuilder.appendQueryParameter("q", city); 
     uriBuilder.appendQueryParameter("cnt", "16"); 
     uriBuilder.appendQueryParameter("units", tempUnit); 
     uriBuilder.appendQueryParameter("appid", "some_key"); 

     uriList.add(uriBuilder.toString()); 
    } 

    return new WeatherLoader(this, uriList); 
} 

@Override 
public void onLoadFinished(Loader<List<Weather>> loader, List<Weather> weatherList) { 

    mAdapter.clear(); 

    // If there is a valid list of forecasts, then add them to the adapter's 
    // data set. This will trigger the ListView to update. 
    if (weatherList != null && !weatherList.isEmpty()) { 
     mAdapter.addAll(weatherList); 
    } 
} 

@Override 
public void onLoaderReset(Loader<List<Weather>> loader) { 
    mAdapter.clear(); 
} 

Wie Sie sehen, Städte sind "Hardcoded" über List<String> cities = new ArrayList<>(); in onCreateLoader(int i, Bundle bundle). Aus diesem Grund habe ich beschlossen, den SQL-Speicher von Städten in meiner App zu implementieren. Ich weiß, wie man SQL-Funktionalität in Android-App mit ContentProvider und CursorAdapter implementieren.

Also, was ist das Problem?

Wenn ich richtig bin, sollten wir LoaderManager.LoaderCallbacks<Cursor> verwenden, wenn wir eine Abfrage an eine lokale DB vornehmen möchten.

Leider kann ich mir nicht vorstellen, wie LoaderCallbacks<List<Weather>> und LoaderCallbacks<Cursor> in einer Aktivität zusammengeführt werden, damit es funktioniert, wie ich will.

Eigentlich möchte ich List<String> cities = new ArrayList<>(); auf etwas ändern, wie Cursor cursor = new CursorLoader(this, WeatherEntry.CONTENT_URI, projection, null, null, null); die URI auf die Ergebnisse, dass CursorLoader kehrt zu bauen.

Aber wir sollten SQL-Abfrage in separaten Thread und HTTP-Abfrage auch (!) In separaten Thread. Sollten wir verschachtelte Threads/Loader (http-Abfrage in einem Bereich von sql Abruf von Daten und Rückgabe eines List<T>)? Kann mir gar nicht vorstellen, wie es geht, wenn ja ...

Hilf mir bitte, ich habe fest!

Antwort

1

Ok, das war mir auf den ersten Blick nicht klar, aber ich habe das Problem gelöst.

In der Frage oben hatten wir eine Liste der Städte, die fest einprogrammiert wurden:

List<String> cities = new ArrayList<>(); 
cities.add("London,uk"); 
cities.add("Kiev,ua"); 
cities.add("Berlin,de"); 
cities.add("Dubai,ae"); 

Selbst wenn wir annehmen, dass wir es mit einer DB-Abfrage, wie sich dies ändern wird:

// Connect to a DB 
... 
Cursor forecastCitiesDataCursor = mDb.query(true, WeatherContract.WeatherEntry.TABLE_NAME, projection, 
        null, null, null, 
        null, null, null); 
... 
// Fetch data from cursor 

. ..wir werden diese SQL-Abfrage auf dem Hauptthread haben. Also brauchen wir eine Lösung.

Das Beste, was ich für mich gefunden habe, es , dass SQL-Abfrage in CustomLoader Klasse gesetzt wird und benötigte Parameter in einem Konstruktor übergeben (in meinem Fall ist es SharedPreferences Parameter einer HTTP-Abfrage eingebaut).

Hier ist mein Code:

WeatherActivity.java

public class WeatherActivity extends AppCompatActivity implements LoaderCallbacks<List<Weather>>, 
     SharedPreferences.OnSharedPreferenceChangeListener { 
    ... 
    @Override 
    public Loader<List<Weather>> onCreateLoader(int i, Bundle bundle) { 

     SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 
     String tempUnit = sharedPrefs.getString(
       getString(R.string.settings_temp_unit_key), 
       getString(R.string.settings_temp_unit_default)); 

     return new WeatherLoader(this, tempUnit); 
    } 

    @Override 
    public void onLoadFinished(Loader<List<Weather>> loader, List<Weather> weatherList) { 
     // Hide loading indicator because the data has been loaded 
     View loadingIndicator = findViewById(R.id.loading_indicator); 
     loadingIndicator.setVisibility(View.GONE); 

     // Set empty state text to display "No forecasts found." 
     mEmptyStateTextView.setText(R.string.no_forecasts); 

     // Clear the adapter of previous forecasts data 
     mAdapter.clear(); 

     // If there is a valid list of forecasts, then add them to the adapter's 
     // data set. This will trigger the ListView to update. 
     if (weatherList != null && !weatherList.isEmpty()) { 
      mAdapter.addAll(weatherList); 
     } 
    } 

WeatherLoader.java

public class WeatherLoader extends AsyncTaskLoader<List<Weather>> { 
... 
    // Pass parameters here from WeatherActivity 
    public WeatherLoader(Context context, String tmpUnit) { 
     super(context); 
     mTempUnit = tmpUnit; 
    } 

    @Override 
    protected void onStartLoading() { 
     forceLoad(); 
    } 

    /** 
    * This is on a background thread. 
    */ 
    @Override 
    public List<Weather> loadInBackground() { 

     // List for storing built URIs 
     List<String> uriList = new ArrayList<>(); 
     // List for storing forecast cities 
     List<String> cities = new ArrayList<>(); 

     // Define a projection that specifies the columns from the table we care about. 
     ... 

     Cursor forecastCitiesDataCursor = mDb.query(true, WeatherContract.WeatherEntry.TABLE_NAME, projection, 
       null, null, null, 
       null, null, null); 

     // Get list of cities from cursor 
     ... 

     //For each city in the list generate URI and put it in the URIs list 
     for (String city : cities){ 
      Uri baseUri = Uri.parse(OWM_REQUEST_URL); 
      Uri.Builder uriBuilder = baseUri.buildUpon(); 

      uriBuilder.appendQueryParameter("q", city); 
      uriBuilder.appendQueryParameter("cnt", "16"); 
      uriBuilder.appendQueryParameter("units", mTempUnit); 
      uriBuilder.appendQueryParameter("appid", /*some id*/); 

      uriList.add(uriBuilder.toString()); 
     } 

     if (uriList == null) { 
      return null; 
     } 

     // Perform the network request, parse the response, and extract a list of forecasts. 
     List<Weather> forecasts = QueryUtils.fetchForecastData(uriList); 
     return forecasts; 
    } 

Also, was wir haben?

Wir haben persistenten Datenspeicher innerhalb der Arbeit mit ArrayAdapter implementiert, die dann verwendet werden, um eine HTTP-Abfrage durchzuführen. SQL-Abfrage befindet sich im separaten Thread und wir haben kein Problem mit der App-Leistung.

Hoffe, dass die Lösung jemandem helfen wird, einen schönen Tag!

Verwandte Themen