Я создаю свое первое приложение для Android и столкнулся с проблемой.Надеемся, что вы можете помочь.
Приложение извлекает данные из внешних API REST, используя лямбда-функции AWS.Он использует FragmentPagerAdapter для отображения данных на нескольких фрагментах с помощью горизонтального пролистывания.Каждый запрос API увеличивает стоимость сервиса, поэтому необходим дисковый кеш.Подкласс AsyncRequest используется для выполнения лямбда-функций AWS, которые выполняют запросы к соответствующему API REST и записывают возвращенные данные JSON в файл кэша в getCacheDir ().
Метод onStart () выполняет вызов AsyncTask.OnPostExecute () AsyncTask записывает данные в соответствующий файл кэша после завершения doInBackground ().Метод onResume () инициализирует FragmentPagerAdapter, который открывает файлы кэша для чтения данных и заполнения представления.Метод onStop () удаляет все кэшированные файлы.
Похоже, FragmentPagerAdapter пытается прочитать файл кэша до того, как doInBackground () завершит работу, и вызовет onPostExecute (), поэтому файл еще не существует;однако журналы указывают, что это происходит только с одной - первой - лямбда-функцией.Всего имеется 4 лямбда-запроса AWS, в результате которых на диск записывается 4 файла кэша.Первоначально у меня были вызовы AsyncTask в onCreate () и логика для создания экземпляра FragmentPagerAdapter в onCreate (), но после вызовов AsyncTask.Я надеялся, что, переместив вызовы в AsyncTask на более раннюю точку жизненного цикла активности, это устранит проблему, но это не так, и я подозреваю, что это связано с тем, что FragmentPagerAdapter пытается загрузить файл кэша в пользовательском интерфейсе.поток, в то время как вызовы AsyncTask все еще выполняются в фоновом потоке ...
Я хотел бы сделать HTTP-запросы непосредственно к лямбда-функциям AWS, заставить пользователя ждать, пока они все не будут завершены и записаны вкеш (покажите «загрузка ...»), затем загрузите представления, но я не могу понять, как реализовать лямбду AWS со стандартной библиотекой HTTP, такой как Volley.
Независимо от того, вынужден ли я использовать AsyncTask или нет, мне нужен способ гарантировать, что я не читаю из файлов кэша, пока они не будут успешно записаны.
Я хочу приложениеиспользовать файлы кэша исключительно для загрузки данных в представления.Первоначально он должен обращаться к внешним API только при открытии приложения, а затем периодически через некоторый интервал (30 минут?), Чтобы гарантировать актуальность данных, если пользователь сохраняет их открытыми достаточно долго.
Activity и FragmentPagerAdapter
@Override
protected void onCreate(Bundle savedInstanceState)
{
if(savedInstanceState == null)
verifyPermissions();
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
}
@Override
protected void onStart()
{
super.onStart();
try
{
CognitoCachingCredentialsProvider credentials = Weather.this.getCredentialsProvider();
LambdaInvokerFactory factory = Weather.this.getLambdaInvokerFactory(credentials);
Location location = this.getLocation();
Log.d("Successfully created Location object.");
String lat = Double.toString(location.getLatitude());
Log.i("Latitude: " + lat + ".");
String lon = Double.toString(location.getLongitude());
Log.i("Longitude: " + lon + ".");
RequestParams params = new RequestParams(Double.toString(location.getLatitude()), Double.toString(location.getLongitude()));
LambdaFunctions fxns = new LambdaFunctions();
for(Field field : fxns.getClass().getDeclaredFields())
{
String name = field.getName();
CacheController cache = new CacheController(this.getCacheDir());
cache.setCacheFile(new File(cache.getCacheDir(), name));
if(cache.exists())
{
File cacheFile = cache.getCacheFile();
Log.i("Cache file exists: " + cacheFile.getAbsolutePath());
long lastModified = cacheFile.lastModified();
/**
* Cache file expired. Query API and rewrite.
*/
if((System.currentTimeMillis() - lastModified) >= CACHE_LIFE_MILLIS)
{
Log.i("Cache file expired: " + cacheFile.getAbsolutePath());
RequestTemplate request = new RequestTemplate(factory);
Log.d("Successfully created RequestTemplate object for AWS Lambda function '" + name + ".'");
new AsyncRequest(request, field.getName(), getCacheDir()).execute(params);
Log.d("Successfully submitted AsyncRequest for AWS Lambda function '" + name + ".'");
}
}
else
{
Log.i("Cache file does not exist for '" + name + "' data.");
RequestTemplate request = new RequestTemplate(factory);
Log.d("Successfully created RequestTemplate object for AWS Lambda function '" + name + ".'");
new AsyncRequest(request, field.getName(), getCacheDir()).execute(params);
Log.d("Successfully submitted AsyncRequest for AWS Lambda function '" + name + ".'");
}
}
}
catch (RuntimeException e)
{
Log.e("Failed to get lcoation from device.");
Log.e(e.getMessage());
Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT);
}
}
@Override
protected void onResume()
{
ViewPager pager = findViewById(R.id.viewPager);
pager.setAdapter(new WeatherPagerAdapter(getSupportFragmentManager(), this));
super.onResume();
}
private void deleteFiles()
{
LambdaFunctions fxns = new LambdaFunctions();
for (Field field : fxns.getClass().getDeclaredFields())
{
String name = field.getName();
Log.i("Current lambda name: " + name);
CacheController cache = new CacheController(this.getCacheDir(), name);
try
{
cache.delete(name);
}
catch (NullPointerException e)
{
Log.e("Unable to locate cache file for AWS Lambda function '" + name + ".'");
}
}
}
@Override
protected void onStop()
{
deleteFiles();
super.onStop();
Log.i("WeatherOutdoors paused.");
}
private class WeatherPagerAdapter extends FragmentPagerAdapter
{
private Activity activity;
public WeatherPagerAdapter(FragmentManager fm, Activity activity)
{
super(fm);
this.activity = activity;
}
@Override
public Fragment getItem(int pos)
{
switch(pos)
{
case 0:
Log.d("Returning Data Fragment...");
return DataFragment.newInstance();
case 1:
LambdaFunctions fxns = new LambdaFunctions();
for(Field field : fxns.getClass().getDeclaredFields())
{
String name = field.getName();
CacheController cache = new CacheController(this.activity.getCacheDir(), name);
if (cache.exists())
{
SummaryData data = new SummaryData();
String data = data.getSummaryData(getCacheDir());
Log.d("Loaded Summary data successfully.");
Log.d("Returning Summary Fragment...");
return SummaryFragment.newInstance(data);
}
else
{
Log.i("Cache file does not exist for '" + name + "' data.");
}
}
default: return DataFragment.newInstance();
}
}
@Override
public int getCount()
{
return 2;
}
}
AsyncTask
@Override
protected String doInBackground(RequestParams... params)
{
WeatherInterface weatherInterface =
AsyncRequest.this.getRequestTemplate().getLambdaFactory().build(WeatherInterface.class);
try
{
String functionName = AsyncRequest.this.functionName;
Log.v("Submitting request for " + functionName + ".");
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String data = gson.toJson(AsyncRequest.this.getRequestTemplate().getLambdaResponse(weatherInterface, functionName, params));
Log.v("Successfully obtained data from AWS Lambda '" + functionName + ".'");
return data;
}
catch (LambdaFunctionException lfe)
{
Log.v("Failed to invoke AWS Lambda function '" + this.functionName + ".'");
String exception = lfe.getMessage();
Log.d(exception);
Log.v(lfe.getStackTrace().toString());
return exception;
}
catch (Exception e)
{
Log.e(e.getMessage());
return e.getMessage();
}
}
@Override
protected void onPostExecute(String result)
{
/**
* Save data to disk
*/
CacheController cache = new CacheController(this.cacheDir);
cache.write(this.functionName, result);
Log.i("Wrote weather data for service '" + this.functionName + "' to cache.");
}
CacheController (File IO)
public boolean exists()
{
return new File(getCacheFile().getAbsolutePath()).exists();
}
public void write(String label, String data)
{
try
{
File cache = new File(this.cacheDir, label);
File temp = new File("/sdcard/" + label);
FileWriter writer = new FileWriter(cache);
FileWriter tempWriter = new FileWriter(temp);
writer.write(data);
tempWriter.write(data);
writer.close();
tempWriter.close();
Log.i("Cache file written: " + cache.getAbsolutePath());
Log.i("Temp file written: " + cache.getAbsolutePath());
}
catch (IOException e)
{
Log.e(e.toString());
}
}
public String read(String label)
{
File cache = new File(this.cacheDir, label);
StringBuilder builder = new StringBuilder();
try
{
BufferedReader buffer = new BufferedReader(new FileReader(cache));
String line = buffer.readLine();
while (line != null)
{
builder.append(line).append("\n");
line = buffer.readLine();
}
Log.i("Cache file read: " + cache.getAbsolutePath());
}
catch (FileNotFoundException e)
{
Log.e(e.toString());
}
catch (IOException e)
{
Log.e(e.toString());
}
return builder.toString();
}
public void delete(String label)
{
File cache = new File(this.cacheDir, label);
String path = cache.getAbsolutePath();
if(cache.exists())
{
if (cache.delete())
{
Log.i("Cache file deleted successfully: " + path);
}
else
{
Log.e("Failed to delete cache file: " + path);
}
}
else
{
Log.i("Unable to delete - file does not exist: " + path);
}
}
Подробный вывод Logcat
Приложение, в конечном счете, аварийно завершает работу, так как оно возвращает JsonNull вместо JsonElement при попытке прочитать файл кэша и проанализировать JSON из него.Я намеренно не поймаю исключение, пока не выясню это.
2019-01-22 17:04:10.234 12407-12407/com.somesite.someapp D/CognitoCachingCredentialsProvider: Loading credentials from SharedPreferences
2019-01-22 17:04:10.245 12407-12407/com.somesite.someapp V/GPSCoordinates::getLocation(): Successfully instansiated LocationManager from Context.LOCATION_SERVICE.
2019-01-22 17:04:10.246 12407-12407/com.somesite.someapp V/GPSCoordinates::getLocation(): Set Accuraccy: ACCURACY_FINE
2019-01-22 17:04:10.246 12407-12407/com.somesite.someapp V/GPSCoordinates::getLocation(): Set Horizontal Accuraccy: ACCURACY_HIGH
2019-01-22 17:04:10.246 12407-12407/com.somesite.someapp V/GPSCoordinates::getLocation(): Set Vertical Accuraccy: ACCURACY_HIGH
2019-01-22 17:04:10.250 12407-12407/com.somesite.someapp V/GPSCoordinates::getLocation(): Successfully instansiated Location from device.
2019-01-22 17:04:10.250 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully created Location object.
2019-01-22 17:04:10.250 12407-12407/com.somesite.someapp I/Activity::onStart(): Latitude: 39.********.
2019-01-22 17:04:10.250 12407-12407/com.somesite.someapp I/Activity::onStart(): Longitude: -75.********.
2019-01-22 17:04:10.251 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file does not exist for 'lambda1' data.
2019-01-22 17:04:10.251 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully created RequestTemplate object for AWS Lambda function 'lambda1.'
2019-01-22 17:04:10.252 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully submitted AsyncRequest for AWS Lambda function 'lambda1.'
2019-01-22 17:04:10.253 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file exists: /data/user/0/com.somesite.someapp/cache/lambda2
2019-01-22 17:04:10.253 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file expired: /data/user/0/com.somesite.someapp/cache/lambda2
2019-01-22 17:04:10.253 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully created RequestTemplate object for AWS Lambda function 'lambda2.'
2019-01-22 17:04:10.253 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully submitted AsyncRequest for AWS Lambda function 'lambda2.'
2019-01-22 17:04:10.253 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file exists: /data/user/0/com.somesite.someapp/cache/lambda3
2019-01-22 17:04:10.254 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file expired: /data/user/0/com.somesite.someapp/cache/lambda3
2019-01-22 17:04:10.254 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully created RequestTemplate object for AWS Lambda function 'lambda3.'
2019-01-22 17:04:10.254 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully submitted AsyncRequest for AWS Lambda function 'lambda3.'
2019-01-22 17:04:10.254 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file exists: /data/user/0/com.somesite.someapp/cache/lambda4
2019-01-22 17:04:10.254 12407-12407/com.somesite.someapp I/Activity::onStart(): Cache file expired: /data/user/0/com.somesite.someapp/cache/lambda4
2019-01-22 17:04:10.255 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully created RequestTemplate object for AWS Lambda function 'lambda4.'
2019-01-22 17:04:10.255 12407-12407/com.somesite.someapp D/Activity::onStart(): Successfully submitted AsyncRequest for AWS Lambda function 'lambda4.'
2019-01-22 17:04:10.256 12407-12455/com.somesite.someapp V/AsyncRequest::doInBackground(): Submitting request for lambda1.
2019-01-22 17:04:10.257 12407-12455/com.somesite.someapp V/RequestTemplate::getLambdaResponse(): Returning lambda response for lambda1.
2019-01-22 17:04:10.284 12407-12407/com.somesite.someapp D/ViewRootImpl@ed43136[Activity]: ThreadedRenderer.create() translucent=false
2019-01-22 17:04:10.288 3698-4331/? D/WindowManager: openInputChannel mInputChannel: 2a6c060 com.somesite.someapp/com.somesite.someapp.Activity (server)
2019-01-22 17:04:10.291 12407-12455/com.somesite.someapp D/NetworkSecurityConfig: No Network Security Config specified, using platform default
2019-01-22 17:04:10.292 12407-12407/com.somesite.someapp D/InputTransport: Input channel constructed: fd=59
2019-01-22 17:04:10.292 12407-12407/com.somesite.someapp D/ViewRootImpl@ed43136[Activity]: setView = DecorView@d2ed16c[Activity] touchMode=true
2019-01-22 17:04:10.295 12407-12455/com.somesite.someapp I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
2019-01-22 17:04:10.295 12407-12455/com.somesite.someapp I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
2019-01-22 17:04:10.304 12407-12407/com.somesite.someapp D/ViewRootImpl@ed43136[Activity]: dispatchAttachedToWindow
2019-01-22 17:04:10.314 12407-12407/com.somesite.someapp D/Activity$ActivityPagerAdapter::getItem(): Returning Data Fragment...
2019-01-22 17:04:10.315 12407-12407/com.somesite.someapp V/CacheController::<init>(): /data/user/0/com.somesite.someapp/cache/lambda1
2019-01-22 17:04:10.316 12407-12407/com.somesite.someapp I/Activity$ActivityPagerAdapter::getItem(): Cache file does not exist for 'lambda1' data.
2019-01-22 17:04:10.316 12407-12407/com.somesite.someapp V/CacheController::<init>(): /data/user/0/com.somesite.someapp/cache/lambda2
2019-01-22 17:04:10.319 12407-12407/com.somesite.someapp E/CacheController::read(): java.io.FileNotFoundException: /data/user/0/com.somesite.someapp/cache/lambda1 (No such file or directory)
2019-01-22 17:04:10.319 12407-12407/com.somesite.someapp I/CacheController::read(): Cache file read: /data/user/0/com.somesite.someapp/cache/lambda3
2019-01-22 17:04:10.320 12407-12407/com.somesite.someapp I/CacheController::read(): Cache file read: /data/user/0/com.somesite.someapp/cache/lambda4
2019-01-22 17:04:10.321 12407-12407/com.somesite.someapp D/AndroidRuntime: Shutting down VM
--------- beginning of crash
2019-01-22 17:04:10.322 12407-12407/com.somesite.someapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.somesite.someapp, PID: 12407
java.lang.ClassCastException: com.google.gson.JsonNull cannot be cast to com.google.gson.JsonObject
at com.somesite.someapp.lambda1.getSummaryData(lambda1.java:46)
Буду признателен за любую помощь, которую вы можете оказать!Спасибо.