2013年1月30日星期三

Android开发笔记(十)

Android开发笔记(十)

  • sqlite数据的字符串类型问题
     问题描述:android程序使用sqlite数据存储,数据表中的字符串类型定义为STRING。如userName定义为STRING,在程序运行过程中发现这样奇怪的现象:userName字段的值是从外界计算得来的,在存入sqlite数据库前打印显示正常如152,658,956,364,812,存入数据库之后读取出来userName字段的值打印显示为1.52658956364812e+14,两者均以字符串的形式在日志打印。
     注意:该问题在固定设备上为必现,由于userName的值是根据设备ID计算得来,刚好其计算结果为均由数字组成的字符串。

     解决办法:修改userName字符串类型为VARCHAR(length),重新编译生成程序,之后运行正常。
     
     问题总结:在android平台开发涉及sqlite数据时,若涉及到字符串类型的字段(大部分情况都会涉及),需要注意字符串类型定义为VARCHAR比较可靠(但需在后面指定最大长度)。而STRING类型的值在保存之后可能被sqlite自动修改,如上面描述情况。
     另外,关于(android)sqlite 具体有哪些数据类型参见“sqlite 数据类型”。
  • double数值格式化
     Java编程在double数值字符串输出时,经常需要针对double数值格式化,如输出时保留小数点后两位。
     使用java.text.DecimalFormat(不止局限于double数值)可以很方便完成数值格式化操作。如:
            double shareFileRate = 100.0 / 3;
            final DecimalFormat df = new DecimalFormat( "0.00");
            System. out.println( "分享进度(%):" + df.format(shareFileRate));
     
     而在C/C++编程可以使用printfsprintf函数实现数值格式化(字符串)输出。

       另外,更详尽参考内容Double四舍五入保留n位小数
 。


  • View对象的长按菜单的响应函数
     “Menu在Android开发中很常用,一般情况下,创建一个菜单有两种方法。一种是在机子本身带的Menu按键上创建一个菜单,另外一种是利用OnCreateContextMenu创建一个菜单。”
    不然这样吧,下面内容以伪代码描述如何利用OnCreateContextMenu实现View对象的长按菜单功能。

     // 给列表Item添加长按点击事件。
     mLvList.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
                    @Override
                    public void onCreateContextMenu(ContextMenu menu, View v,
                              ContextMenuInfo menuInfo) {
                              ...
                             // 添加长按菜单项
                             // public abstract MenuItem add (int groupId, int itemId, int order, CharSequence title)
                              menu.add(MY_GROUP_ID, 0, 0, "菜单1");
                              menu.add(MY_GROUP_ID, 1, 0, "菜单2");
                              menu.add(MY_GROUP_ID, 2, 0, "菜单3");
                    }
               });
     ... 
     // 针对以上长按菜单的响应函数
     @Override
     public boolean onContextItemSelected(MenuItem item) {
          if (item.getGroupId() ==MY_GROUP_ID) {
               if (item.getItemId() == 0) {
                    // 菜单1事件处理                  
               } else if (item.getItemId() == 1) {
                    // 菜单1事件处理   
               } else if (item.getItemId() == 2) {
                    // 菜单2事件处理   
               }
          } else {
               // 特别注意:若收到的响应不是你想要的一定记得必须继续传递下去,
               // 否则后续其他地方的Menu对象收不到通知!
               return super.onContextItemSelected(item);
          }
     }

       另外,关于android开发的Menu介绍参考“Menu学习总结
”。
  • 同步或异步init/uninit问题
     在做系统开发时一般都包含有多个模块,而每个模块最好应该都有对应的init/uninit操作,初始化与反初始化模块。简单讨论这么一个问题,同步或异步完成init/uninit操作呢?以下简单罗列不同种方式的优缺点。
     1. 均同步
优点:调用逻辑简单,操作结果同步返回。
缺点:调用者需等待导致阻塞,可能影响程序顺畅运行。
     2. 均异步
优点:调用者无需等待,可继续执行其他操作。
缺点:需额外的异步处理,稍微增加程序复杂性。
     3. init异步uninit同步
优点:保证init操作不影响其他操作操作,而uninit同步为了保证模块资源及时得到释放。
缺点:uninit同步还是可能影程序是否顺畅,取决于具体情况。还有一种比较特殊情况可能存在问题,当多次频繁init/uninit切换,如下:
init 异步
-> uninit同步
-> init 异步返回OK  ------- 注:此时模块应该已被uninit
当然出现这样的问题是可以得到解决的。具体解决办法也很简单,即增加一变量标识模块init/uninit状态包括{pending,inited,uninited } 解法描述如下:
int _module_status = MODULE_STATUS_UNDEFINE;
Listen _listen = null;
void init(Listen listen) {
_module_status = MODULE_STATUS_PENGDING;
_listen = listen;
do_init(); // 异步反初始化
}
void on_inited() {
if (_module_status == MODULE_STATUS_PENGDING) {
_module_status = MODULE_STATUS_INITED;
_listen.notify_init_success();
} else {
// 模块已被反初始化了
}
}
void uninit() {
do_uninit(); // 同步反初始化
_module_status = MODULE_STATUS_UNINITED;
}
关键在于,init通知完成时只要判断状态变量是否为pending状态,若是则正常通知初始化成功,否则认为模块已被“抢先”反初始化了。
     4. init同步uninit异步(罕见)

 很明显1,2两者的优缺点相互对立的,而其中2,3的用法是比较常见,哪种更好需具体场景具体分析。

(完)

没有评论:

发表评论