【问题标题】:Re-Using already fetched data from server in android在android中重新使用已经从服务器获取的数据
【发布时间】:2014-01-26 11:31:03
【问题描述】:

我正在使用从服务器获取的数据(文本、图像 URL 等)填充列表视图。 该数据可能每周/每月在服务器端更新。 我想要的是,*当我的应用程序第一次加载列表视图时,从服务器获取的数据存储在应用程序/客户端,以便以后在重绘列表视图时重新使用。*如何我能做到吗?

一个可能的解决方案是:

  1. 第一次使用 Application 类下载此数据,将其存储在 SQLlite db 中,并在绘制列表视图时始终从 DB 获取结果。稍后我可以使用 GCM Send-To-Sync tickles 更新 sqldb 数据。 -每次绘制列表视图时都查询 db,这不是一个好习惯吗?? 实现此目的的任何常见做法。

关于使用 Loaders/Loading Manager 的任何看法。

编辑

问题中的数据是一个json包。具有类似结构(考虑数组大小为最大 100)

 [ // JSON Array
         { // JSON Object
         "rank":1,"country":"China",
         "population":"1,354,040,000",

         "flag":"http://www.androidbegin.com/tutorial/flag/china.png"
         }, (
     ]

【问题讨论】:

  • 我不完全知道你存储的是什么类型的数据,所以不能具体评论。您还可以在内部/外部存储中创建文件结构。可用于从 db 查询数据的加载程序。不要查询数据库是否可以缓存数据,无论是在主内存中还是在内部存储中某处的转储中(如数据库快照,直到更新)。
  • @hbansal 添加了数据样本
  • 在下面发布带有示例代码的答案,正如我之前所说,所有答案都只是可能的实现,您必须自己决定并明智地选择。此外,请记住数据集的内存使用情况,这将影响您设计此系统的方式。

标签: android sqlite listview android-listview


【解决方案1】:

是的,您需要将从服务器获取的数据存储到数据库本身,然后将数据从SQLiteDatabase绑定到ListView

不能说每次绘制listview都查询db,不是一个好习惯吗..,因为你只需要在activity启动或者数据库中的数据发生了变化。

重新使用已经从服务器获取的数据...意味着您需要一个持久存储,并且应该定期或根据用户请求更新该存储。

【讨论】:

  • 您说“您只需要在活动启动或数据库中的数据更改时获取一次数据。”,我该怎么做。如果我第一次绘制 listview 的结果来自查询数据库,我以后如何使用相同的获取结果..?谢谢
【解决方案2】:

这取决于您拥有什么样的数据。通常,如果它位于来自服务器的 json 包中,那么您可以保留该原始 json 并在每次应用启动时解析和填充数据。但这只有在数据量太大时才有用。如果您在任何给定时刻拥有的数据量很大,那么您将不得不解析格式化数据并将其存储到 SQL DB,在每次应用启动时查询和获取数据。

此外,在数据量可控的情况下,您可以使用静态数据模型实例在应用生命周期期间将数据保留在内存中。

您可以通过多种方式解决此问题,但这完全取决于您拥有多少数据以及采用何种格式,因此您需要决定哪种方法可以获得最佳性能。

【讨论】:

  • 是的,它是一个Json包。什么可以归类为大数据?如果我说一个最多有 100 个 Json 对象的 json 数组(每个对象有 4-5 个字符串变量),你建议采用什么方式?谢谢
  • 10,000+ 通常被认为是一大块数据。低于此值的任何内容,系统都可以轻松处理。再说一次,这取决于您所针对的设备范围。
  • 100 个 json 对象是非常小的数据集,您可以将其保存在 sdcard 中的文本文件中并在启动时读取它
  • 请在下面查看我的答案以了解我的实现。您认为这种方法有什么问题吗?
【解决方案3】:

使用此静态方法在 sdcard/internal storage 中创建一个目录。

/**
* Cache class to create dir, save, retrieve and delete files
* File names are hashCode of the url json is downloaded from.
**/
public class FileCache {

    static File cacheDir;
    static final String DIRECTORY_ADDRESS = "/Android/data/<app_package>/.<directory name>";

    public static createDirectory(Context context){
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir=new File(Environment.getExternalStorageDirectory(),DIRECTORY_ADDRESS);
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }    

    public static File getFile(String url){
        String filename=String.valueOf(url.hashCode());
        File f = new File(cacheDir, filename);
        return f;
    }

    public static void saveFile(InputStream is, File file){
        try {
            OutputStream os = new FileOutputStream(file);
            copyStream(is, os);
            os.close();
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
    }

    /**
    * Clear all files at app uninstall or application or on 
    * low memory or some other event, using a boradcastreceiver/alarmmanager
    **/
    public static void clear(){
        File[] files=cacheDir.listFiles();
        if(files==null)
            return;
        for(File f:files)
            f.delete();
    }
}

现在在应用程序启动时创建目录:

FileCache.createDirectory(context);

设置完成后,您必须使用 AsyncTask 获取 JSONArray,并将其存储在文件中

/**
* Add this in doinBackground()
**/
private File getJson(Stirng url) {
    File f = FileCache.getFile(url);
    if(f != null && f.isFile()) {
        //file exists in directory, no need to download.
        return f;
    }

    try {
        URL fileUrl = new URL(url);

        HttpURLConnection conn = (HttpURLConnection) fileUrl.openConnection();
        conn.setConnectTimeout(120000);
        conn.setReadTimeout(120000);
        conn.setInstanceFollowRedirects(true);

        InputStream is = conn.getInputStream();
        FileCache.saveFile(is, f);

        return f;                      
    } catch (Exception ex) {
        return null;
    }
}

现在您必须在每次需要加载数据并将数据绑定到列表视图时运行此线程。我排除了如何从文件中解析 json 并将其转换为适配器的 List 的详细信息。

如果文件中不可用,此 AsyncTask 将自动获取数据,并在 onPostExecute() 中加载到列表视图中。因此,从第二次开始,它不会花费太多时间,并且数据将被持久化存储。您也可以在ListView中添加分页,即如果您有1000个json哈希,您可以从这个文件中批量读取数据,这也是一个很好的优化。

现在,每当数据更新(每月/每周)时,使用 GCM 发送推送通知,运行服务,使用 FileCache.clear() 清除旧文件,然后从服务启动此 AsyncTask,所有这些都在后台,因此如果之后打开应用程序,您将从文件存储中获取更新的数据。

把所有东西都放在 sqlite 中是一个类似的选择,然后你必须使用 DbAdapters、CursorLoaders 和 CursorAdapters,然后使用服务来更新数据库。

但是由于您没有大型数据集,我认为文件缓存就足够了。

希望对您有所帮助。 :)

【讨论】:

  • 请看下面我的回答。你觉得这种方法有什么问题吗?
  • 不要在 SharePref 中使用大块,它仅用于存储原始键=>值对。
  • thx.. 查看您上面的解决方案,您提到 “现在在应用程序启动时创建目录:” 不会总是在应用程序启动时创建一个新目录或覆盖上一个目录?.
  • 不,看这部分,if(!cacheDir.exists()) cacheDir.mkdirs();
  • 愚蠢的错过......对不起......thx......顺便说一句,我可以只使用缓存目录而不使用外部存储,我想当外部存储不可用时会避免数据不可用。
【解决方案4】:

我实现如下:

(如果您在实施、性能方面发现任何问题,请发表评论)

  1. 第一次从服务器获取数据(长度为 50 的 Json 数组),将其作为字符串(JsonArray.toString()) 存储在 sharedpreferences 中并填充列表视图。
  2. 当需要再次显示列表视图时,从 sharedpreferences 中获取存储的 jsonarraystring,将其转换回 jsonarray 并填充列表视图。
  3. 要从服务器获取更新的数据,请发送 GCM tickle 以在后台使用新值更新存储的 sharedpreferences 数组。

到目前为止,这种方法运行良好。你们看到我在任何阶段都会面临的任何问题吗? 谢谢你

【讨论】:

  • 1. Sharedpreferences 不适合存储大块数据。在这种情况下,您的 json 数组对于基于键值对的存储系统来说很大。更简单的选择是将其存储在 SD 卡 2 上。在应用程序运行期间将解析的数据保存在内存中。没有必要一次又一次地解析相同的数据,因为总数据不够大 3. 在您的应用程序流真正需要之前不要完成您的活动。据我所知,列表视图位于应用程序的主屏幕上,需要一次又一次地显示。
  • +1 FunkyIdol,我们不应该在 SharedPreferences 中添加大块数据,最好使用 sdcard 代替。
猜你喜欢
  • 1970-01-01
  • 2019-01-26
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
  • 2011-05-28
相关资源
最近更新 更多