我对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
)?甚至无法想象它是如何做到的,如果是这样的话......
请帮助我,我已经卡住了!
答案 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查询)。
这是我的代码:
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);
}
}
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查询位于单独的线程上,我们对应用程序性能没有任何问题。
希望解决方案能帮到别人,祝你有愉快的一天!