繁体   English   中英

Android系统。 从数据库中获取数据,然后进行网络查询。 如何执行?

[英]Android. Fetch data from database and then make network query. How to implement?

我是Android开发的新手,目前正在尝试编写一个应用程序,以显示明天多个城市的天气。 对不起,我可能在这个问题中使用任何不恰当的术语。

我想达到的目标:

应用将从本地数据库中获取数据,然后对从数据库中获取的数据进行HTTP查询,获取JSON响应并形成一个列表元素。

我目前所拥有的:

除SQL功能外的所有内容。

这是我主要活动代码的快照。 我使用LoaderCallbacks<List<Weather>>onCreateLoader(int i, Bundle bundle)使用所需参数构建URI,发送HTTP查询并通过WeatherLoader(this, uriList)获取数据,并使用WeatherAdapter表单元素生成List

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();
}

如您所见,城市是通过List<String> cities = new ArrayList<>();进行“硬编码”的List<String> cities = new ArrayList<>(); onCreateLoader(int i, Bundle bundle) 这就是为什么我决定在我的应用程序中实现城市的SQL存储的原因。 我知道如何使用ContentProviderCursorAdapter在Android应用程序中实现SQL功能。

所以有什么问题?

如果我是正确的,如果我们要查询本地数据库,则应使用LoaderManager.LoaderCallbacks<Cursor>

不幸的是,我无法想象如何在一个活动中合并当前的LoaderCallbacks<List<Weather>>LoaderCallbacks<Cursor>以使其按我的意愿工作。

实际上,我想更改List<String> cities = new ArrayList<>(); 在类似Cursor cursor = new CursorLoader(this, WeatherEntry.CONTENT_URI, projection, null, null, null); CursorLoader返回的结果上构建URI。

但是,我们应该在单独的线程中进行SQL查询,而在单独的线程中也进行HTTP查询。 我们是否应该嵌套线程/加载器(在取数据的sql范围内进行http查询并返回List<T> )? 甚至无法想象怎么做,如果可以的话...

请帮帮我,我卡住了!

好的,乍一看对我来说并不明显,但是我终于解决了这个问题。

在上面的问题中,我们列出了经过硬编码的城市:

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

即使我们假设将其更改为数据库查询,如下所示:

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

...我们将在主线程上执行该SQL查询。 因此,我们需要一个解决方案。

我为我找到的最好的东西是, 将SQL查询放在CustomLoader类中,并在构造函数中传递所需的参数(在我的情况下,它是构建HTTP查询的SharedPreferences参数)。

这是我的代码:

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;
    }

那我们得到了什么?

我们已经在使用ArrayAdapter的工作中实现了持久性数据存储,然后用于执行HTTP查询。 SQL查询位于单独的线程上,应用程序性能不会有问题。

希望解决方案对您有所帮助,祝您有美好的一天!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM