Android。从数据库中获取数据,然后进行网络查询。如何实施?
Android. Fetch data from database and then make network query. How to implement?
我对 Android 开发还很陌生,目前正在尝试编写一个应用程序来显示多个城市明天的天气。抱歉,我可能会在这个问题中使用任何不正确的术语。
我想达到的目标:
应用程序将从本地数据库获取数据,然后对从数据库获取的数据构建 HTTP 查询,获得 JSON 响应并形成列表元素。
我目前拥有的:
除 SQL 功能外的所有内容。
这是我的主要 activity 代码的快照。我使用 LoaderCallbacks<List<Weather>>
在 onCreateLoader(int i, Bundle bundle)
中使用所需参数构建 URI,发送 HTTP 查询并通过 WeatherLoader(this, uriList)
获取数据,并使用 [=16= 形成 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();
}
如您所见,城市 "hardcoded" 通过 List<String> cities = new ArrayList<>();
在 onCreateLoader(int i, Bundle bundle)
。这就是为什么我决定在我的应用程序中实现 SQL 城市存储。我知道如何使用 ContentProvider
和 CursorAdapter
.
在 android 应用程序中实现 SQL 功能
那么问题是什么?
如果我是正确的,如果我们想查询本地数据库,我们应该使用 LoaderManager.LoaderCallbacks<Cursor>
。
不幸的是,我无法想象如何将当前 LoaderCallbacks<List<Weather>>
和 LoaderCallbacks<Cursor>
合并为一个 activity 以使其按我的意愿工作。
其实我想改变
List<String> cities = new ArrayList<>();
在类似的事情上
Cursor cursor = new CursorLoader(this, WeatherEntry.CONTENT_URI, projection, null, null, null);
根据 CursorLoader
returns.
的结果构建 URI
但是,我们应该在单独的线程中进行 SQL 查询,并在单独的线程中进行 HTTP 查询(!)。我们应该嵌套threads/loaders(在sql获取数据和return一个List<T>
范围内的http查询)吗?甚至无法想象这怎么可能,如果可以...
请帮帮我,我卡住了!
好吧,我第一眼看的不是很明显,但我终于解决了这个问题。
在上面的问题中,我们有一个硬编码的城市列表:
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 class 并在构造函数中传递所需的参数(在我的例子中,它是构建 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 查询在单独的线程上,我们不会对应用程序性能有任何问题。
希望解决方案对某人有所帮助,祝你有愉快的一天!
我对 Android 开发还很陌生,目前正在尝试编写一个应用程序来显示多个城市明天的天气。抱歉,我可能会在这个问题中使用任何不正确的术语。
我想达到的目标:
应用程序将从本地数据库获取数据,然后对从数据库获取的数据构建 HTTP 查询,获得 JSON 响应并形成列表元素。
我目前拥有的:
除 SQL 功能外的所有内容。
这是我的主要 activity 代码的快照。我使用 LoaderCallbacks<List<Weather>>
在 onCreateLoader(int i, Bundle bundle)
中使用所需参数构建 URI,发送 HTTP 查询并通过 WeatherLoader(this, uriList)
获取数据,并使用 [=16= 形成 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();
}
如您所见,城市 "hardcoded" 通过 List<String> cities = new ArrayList<>();
在 onCreateLoader(int i, Bundle bundle)
。这就是为什么我决定在我的应用程序中实现 SQL 城市存储。我知道如何使用 ContentProvider
和 CursorAdapter
.
那么问题是什么?
如果我是正确的,如果我们想查询本地数据库,我们应该使用 LoaderManager.LoaderCallbacks<Cursor>
。
不幸的是,我无法想象如何将当前 LoaderCallbacks<List<Weather>>
和 LoaderCallbacks<Cursor>
合并为一个 activity 以使其按我的意愿工作。
其实我想改变
List<String> cities = new ArrayList<>();
在类似的事情上
Cursor cursor = new CursorLoader(this, WeatherEntry.CONTENT_URI, projection, null, null, null);
根据 CursorLoader
returns.
但是,我们应该在单独的线程中进行 SQL 查询,并在单独的线程中进行 HTTP 查询(!)。我们应该嵌套threads/loaders(在sql获取数据和return一个List<T>
范围内的http查询)吗?甚至无法想象这怎么可能,如果可以...
请帮帮我,我卡住了!
好吧,我第一眼看的不是很明显,但我终于解决了这个问题。
在上面的问题中,我们有一个硬编码的城市列表:
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 class 并在构造函数中传递所需的参数(在我的例子中,它是构建 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 查询在单独的线程上,我们不会对应用程序性能有任何问题。
希望解决方案对某人有所帮助,祝你有愉快的一天!