活动生命周期和AsyncRequest:尝试缓存AWS Lambda响应数据

时间:2019-01-23 17:29:48

标签: android caching android-activity android-asynctask lifecycle

我正在构建我的第一个Android应用程序,但是遇到了问题。希望您能提供帮助。

应用程序使用AWS lambda函数从外部REST API中提取数据。它使用FragmentPagerAdapter通过水平滑动在多个片段上显示数据。每个API请求都会增加服务成本,因此需要磁盘缓存。 AsyncRequest的子类用于执行AWS lambda函数,该函数向相应的REST API发出请求,并将返回的JSON数据写入getCacheDir()中的缓存文件。

onStart()方法调用AsyncTask。 doInBackground()完成后,AsyncTask的onPostExecute()会将数据写入相应的缓存文件。 onResume()方法初始化FragmentPagerAdapter,它将打开缓存文件以读取数据并填充视图。 onStop()方法删除所有缓存的文件。

看起来FragmentPagerAdapter尝试在doInBackground()完成并触发onPostExecute()之前读取缓存文件,因此该文件尚不存在。但是,日志表明这仅发生在第一个lambda函数上。共有4个AWS lambda请求,这些请求导致将4个缓存文件写入磁盘。最初,我在onCreate()中有对AsyncTask的调用,也有在onCreate()中实例化FragmentPagerAdapter的逻辑,但是在AsyncTask调用之后。我希望通过将对AsyncTask的调用移到活动生命周期中的较早位置,可以解决此问题,但事实并非如此,并且我怀疑这是因为FragmentPagerAdapter试图在UI中加载缓存文件线程,而AsyncTask调用仍在后台线程中执行...

我想直接向AWS lambda函数发出HTTP请求,让用户等待它们全部完成并写入缓存(显示“正在加载...”),然后加载视图,但是我无法弄清楚如何使用Volley这样的标准HTTP库来实现AWS Lambda。

是否强制使用AsyncTask,我需要一种方法来确保在成功写入缓存文件之前,不要从中读取它们。

我希望应用程序专门使用缓存文件将数据加载到视图中。它仅应在打开应用程序时首先访问外部API,然后以一定间隔(30分钟?)定期运行,以确保如果用户保持打开时间足够长,则数据是最新的。

活动和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(文件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输出

该应用程序最终崩溃,因为在尝试读取缓存文件并从其中解析JSON时,它返回的是JsonNull而不是JsonElement。在我弄清楚这一点之前,我没有故意抓住异常。

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)

非常感谢您能提供的帮助!谢谢。

0 个答案:

没有答案