65.9K
CodeProject 正在变化。 阅读更多。
Home

端到端真实世界 BlackBerry 应用,第三部分

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.91/5 (8投票s)

2008年7月13日

CPOL

4分钟阅读

viewsIcon

43394

downloadIcon

185

端到端真实世界 BlackBerry 应用演练,第 3 部分。

引言

这是我正在介绍的 一系列文章 的第三部分,我将引导你完成一个端到端的 BlackBerry 应用的创建,该应用将作为我 知识库 示例 Web 应用的移动前端。

Home-Screen1.gif

在本系列文章的上一篇中,我完成了应用程序用户界面的屏幕构建。我还添加了一些测试代码,以便根据一些基本的使用场景来测试界面。

在本文中,我将介绍如何从设备的闪存中存储和检索数据。这是一个非常重要的主题,因为大多数应用程序如果没有保存某些状态在设备重置后保持不变的能力,就没有价值。

我们需要保存什么数据?

  1. 我们的应用程序服务器的 URL。
  2. 最近查看的文章列表。
  3. 上述列表应包含的最近查看文章的最大数量。

第 1 项和第 3 项是应用程序设置,它们的存储和检索将在“选项屏幕”的上下文中进行。

最近查看文章的列表将在用户打开文章时构建。它将在应用程序关闭时保存到内存中。

使用持久存储存储数据

BlackBerry API 通过持久存储(PersistenStore 类)提供在设备重置后持久化信息的功能。持久对象(PersistentObject 类)是其内容可以在设备重置后持久存在的对象。这些对象(以键值对的形式)可以提交到持久存储,然后通过键检索。以下是持久存储的一个示例用法,摘自 BlackBerry API 文档。

static Vector addresses;
static PersistentObject persist;
 
static {
         
   long KEY =  0xa3b3159378f59a29L;
   persist = PersistentStore.getPersistentObject( KEY );
   addresses = (Vector) persist.getContents();
   if( addresses == null ) {
       addresses = new Vector();
       persist.setContents( addresses );
       persist.commit()
   }
}
     
void add( Address a ) {
   addresses.addElement( a );
   persist.commit();
}

Options 类

Options 类封装了与存储和检索我们需要在设备重置后持久化的数据相关的所有代码。类的构造函数将负责从持久存储中检索我们的应用程序的持久对象,如下所示。

private Options() {
    store = PersistentStore.getPersistentObject(0xf6354ee14c6cc857L);
}

构造函数是私有的,以加强单例行为,这将防止我们的应用程序存储的多个实例同时被使用。

数字 0xf6354ee14c6cc857 是“KnowledgeBase”的哈希值。您可以通过选择文件顶部的包名(在本例中为“KnowledgeBase”),然后右键单击并选择“Convert “KnowledgeBase” To Long”菜单项来获得此哈希值。

我选择用作需要持久化的应用程序状态容器的对象,而该对象又将被我们提交到持久存储的持久对象实例所包含,是一个 LongHashtableCollection,它是一个使用长整数作为键的 Hashtable 集合。

这是一张图表,可以帮助您直观地了解我们的数据将如何在内存中存储。

Persistent-Store-Diagram.gif

我创建了几个私有方法,set(long key, Object value)get(long key),它们将直接处理设置和获取持久存储的内容。

private void set(long key, Object value) {
    synchronized(store) {
        settings = (LongHashtableCollection)store.getContents();
        if (null == settings) {
            settings = new LongHashtableCollection();
        }
        settings.put(key,value);   
        store.setContents(settings);
        store.commit();
    }
}    

private Object get(long key) {
    synchronized(store) {
        settings = (LongHashtableCollection)store.getContents();
        if (null != settings && settings.size() != 0) {
             return settings.get(key);
        } else {
             return null;
        }
    }
}

要提交到存储的每个独立数据项(应用程序设置等)将由一个唯一的键标识。

private static final long KEY_APP_SRV_URL = 0;
private static final long KEY_MAX_CACHED_ARTICLES = 1;
private static final long KEY_CACHED_ARTICLES = 3;

基于这些数据项,消费者可以使用 Options 类的这些方法。

public static String getAppServerUrl() {
   String url = (String)getInstance().get(KEY_APP_SRV_URL);
   if (null == url || url.length() == 0) {
     url =  DEFAULT_SERVICE_URL; 
     setAppServerUrl(url);
   }
   return url;
}

public static void setAppServerUrl(String url) {
   getInstance().set(KEY_APP_SRV_URL,url);
} 

public static int getMaxCachedArticles() {
   int maxCachedArticles;
   String maxCachedArticlesString = 
            (String)getInstance().get(KEY_MAX_CACHED_ARTICLES);
    if (null == maxCachedArticlesString || 
        maxCachedArticlesString.length() == 0) {
        maxCachedArticles =  DEFAULT_MAX_CACHED_ARTICLES; 
        setMaxCachedArticles(maxCachedArticles);
    } else {
        maxCachedArticles = Integer.parseInt(maxCachedArticlesString);
    }
   return maxCachedArticles;
}

public static void setMaxCachedArticles(int maxCachedArticles) {
   getInstance().set(KEY_MAX_CACHED_ARTICLES,String.valueOf(maxCachedArticles));
} 

public static Article[] getCachedArticles() {
    Article[] articles = (Article[])getInstance().get(KEY_CACHED_ARTICLES);
   if (null == articles ) {
     articles =  new Article[0]; 
     setCachedArticles(articles);
   }
   return articles;
}

public static void setCachedArticles(Article[] articles) {
   getInstance().set(KEY_CACHED_ARTICLES,articles);
}

选项屏幕

在“选项屏幕”中,显示和保存应用程序设置非常简单。这些操作由 displayOptions()saveOptions() 处理。

public void displayOptions() {
        
    String serverUrl = Options.getAppServerUrl();
    int maxCachedArticles = Options.getMaxCachedArticles();
    
    appServerUrlField.setText(serverUrl);
    maxCachedArticlesField.setText(String.valueOf(maxCachedArticles));
    
}

private void saveOptions() {
    
    String serverUrl = appServerUrlField.getText().trim();
    String maxCachedArticlesString = maxCachedArticlesField.getText().trim();
    
    if (null == serverUrl || serverUrl.length() == 0 ||
        null == maxCachedArticlesString || 
            maxCachedArticlesString.length() == 0) {
          
          Dialog.alert("Please correct the invalid options");
          return;  
    }
    
    Options.setAppServerUrl(serverUrl);
    Options.setMaxCachedArticles(Integer.parseInt(maxCachedArticlesString));
    this.setDirty(false);
    this.close();
    
}

请注意,saveOptions() 在通过我们的 Options 类提交到内存之前,会进行简单的验证。

文章屏幕

让我们修改 ActiclesScreen 类构造函数中的测试代码,以利用我们的 Options 类保存和检索最近查看文章列表的能力。以前,我们的测试代码只是创建了一个包含虚拟文章的列表,以便在屏幕上显示它们。

ArticlesScreen() {
        
    this.setTitle("Articles");
    
    // Create a few dummy articles in order to test the screen;
    articles = new Article[15];
    Article article;
    for (int i = 0; i < 15; i++) {
        article = new Article();
        article.title = "Dummy article " + Integer.toString(i);
        article.author = "ramonj";
        article.contents = "This is a test article";
        article.tags = new String[] {"tag 1", "tag 2", "tag 3"};
        article.dateCreated = "01/01/2008";
        articles[i] = article;
     }

    articlesList = new ArticlesListField();
    articlesList.set(articles);
    this.add(articlesList);

}

现在,让我们先检查是否缓存了任何文章,如果没有,则创建我们的虚拟文章并将其提交到设备的内存中。

ArticlesScreen() {
        
    this.setTitle("Articles");
    
    articles = Options.getCachedArticles();
    
    if (null == articles || articles.length == 0) {
    
        // Create a few dummy articles in order to test the screen;
        articles = new Article[15];
        Article article;
        for (int i = 0; i < 15; i++) {
            article = new Article();
            article.title = "Dummy article " + Integer.toString(i);
            article.author = "ramonj";
            article.contents = "This is a test article";
            article.tags = new String[] {"tag 1", "tag 2", "tag 3"};
            article.dateCreated = "01/01/2008";
            articles[i] = article;
        }
        
        Options.setCachedArticles(articles);

    }
    
    articlesList = new ArticlesListField();
    articlesList.set(articles);
    this.add(articlesList);

}

本文的最后一项非常重要,它涉及到任何我们希望提交到持久存储的对象都必须实现 net.rim.device.api.util.Persistable 接口的事实。由于我们存储 Article 类的实例以持久化最近查看文章列表跨设备重置,因此 Article 类必须显式实现此接口。这是代码。

import net.rim.device.api.util.Persistable;

class Article implements Persistable {
    public String title;
    public String dateCreated;  
    public String author;
    public String[] tags;
    public String contents;
}

下一步

好了,就到这里。在下一篇文章中,我将添加网络代码。这将基本完成设备端代码,并为转向服务器端做好准备。在服务器端,我将把处理与手持设备通信的部分整合起来。

之前的文章

© . All rights reserved.