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

时间:2017-02-08 07:40:05

标签: android android-sqlite android-cursorloader asynctaskloader

我对Android开发很新,目前正在尝试编写一个可以显示明天多个城市天气的应用。对于我在此问题中可能使用的任何终端,我们深表歉意。

我想要达到的目标:

App将从本地数据库获取数据,然后对从数据库获取的数据构建HTTP查询,获取JSON响应并形成列表元素。

我目前拥有的内容:

除SQL功能之外的所有内容。

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

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(); } 中的List<String> cities = new ArrayList<>();进行了“硬编码”。这就是为什么我决定在我的应用程序中实现城市的SQL存储。我知道如何使用onCreateLoader(int i, Bundle bundle)ContentProvider在Android应用中实现SQL功能。

那么问题是什么?

如果我是正确的,如果我们想要查询本地数据库,我们应该使用CursorAdapter

不幸的是,我无法想象如何在一个活动中合并当前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);返回的结果上构建URI。

但是,我们应该在单独的线程中使用单独的线程和HTTP查询进行SQL查询(!)。我们应该做嵌套线程/加载器(sql范围内的http查询获取数据并返回CursorLoader)?甚至无法想象它是如何做到的,如果是这样的话......

请帮助我,我已经卡住了!

1 个答案:

答案 0 :(得分:2)

好的,第一眼看到我并不明显,但我终于解决了这个问题。

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

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 类中并在构造函数中传递所需的参数(在我的例子中,它是构建的SharedPreferences参数)一个HTTP查询)。

这是我的代码:

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查询位于单独的线程上,我们对应用程序性能没有任何问题。

希望解决方案能帮到别人,祝你有愉快的一天!