显示标签为“笔记”的博文。显示所有博文
显示标签为“笔记”的博文。显示所有博文

2016年6月22日星期三

端午佳节倍思亲

端午佳节倍思亲
  2016第一帖

差不多三年没在Blogger上更新博文,自从有了微博、微信(朋友圈)、新闻App、头条App,生活的碎片化时间很容易被占据,业余时间想静下心来思考一些事情并且能写下来的机会真的很难得,少之又少。

其实,一直以来我比较喜欢写博文的,尤其篇幅不短而能娓娓道来那样的谈论下去的博文,当然有做到有深度的博文是我至高的追求。

借着端午假期,轻快地开始2016第一帖。端午节假三天时间,匆忙回家(福建.漳州)一趟看望父母、奶奶、外公、还有我的小侄女,以及走访家附近的亲朋好友。引用一首苏轼的词,帮助感受端午节的气息。

《浣溪沙》
【宋】苏轼
轻汗微微透碧纨。明朝端午浴芳兰。
流香涨腻满晴川。彩线轻缠红玉臂,
小符斜挂绿云鬟。佳人相见一千年。






图1. 排骨凉瓜汤,奶奶煲的汤甘甜可口,酷暑降火,八十多岁的奶奶还是心灵手巧,善解人意,祝奶奶身体健康。



图2. 外公在家门口送别,九十多岁的外公今年身体没有往年矫健了,祝外公身体健康。


图3.老宅家门口的树花开了,从老宅搬出已有五年光阴了,树儿仍然亭亭玉立。


2013年2月27日星期三

Fwd: Android(程序)性能分析 Traceview



Android(程序)性能分析 Traceview



  • 介绍Traceview
     “Traceview是android平台配备的一个很好的性能分析工具。它可以通过图形界面的方式让我们了解我们要跟踪的程序的性能,并且能具体到method。”
     Traceview程序是(Eclipse)android开发环境自带工具存在...\android-sdks\tools\;
     Traceview图形界面如下: 
     
图1. Traceview界面
   

    •      a. 时间轴面板
          显示不同线程的执行情况,线上有不同颜色部分是代表线程执行不同函数。右上角显示时间轴的时间总长,左上角显示当前选中时间点的性能数据。
          注:在图形上点击鼠标拖拽就可以有放大效果,恢复原图只要双击最上面的时间轴横线即可。


    •      b. Profile面板
          纵轴显示程序执行到的不同函数。横轴显示对应函数的性能数据,包括这些字段:Incl Cpu Time %,Incl Cpu Time,Excl Cpu Time %,Excl Cpu Time,Incl Real Time %,Incl Real Time,Excl Real Time %,Excl Real Time,Calls+RecurCalls/Total,Cpu Time/Call,Real Time/Call。
          大体上介绍一下以上字段含义:
          Incl是Inclusive(包含的),表示该函数的调用包括所有子函数的调用。
          Excl是Exclusive(独有的),表示该函数的调用仅包括基本操作,不包括子函数的调用。
          Cpu Time,就是真正在占用CPU的时间,Elapsed Real Time = CPU Time + Wait Time。详细参考http://en.wikipedia.org/wiki/CPU_time
          Real Time,就是时钟计时的时间(时长)。
          "Elapsed real time is always same or more than CPU time for computer program which use only one CPU for processing. If no wait is involved for I/O or other resources, elapsed real time and CPU time are very similar."
          %,占有的百分比。
          所以,字段“Incl Cpu Time %”就表示对应函数的Cpu Time在整个时间轴的函数调用中占的百分比。


    •      c. 搜索框

     搜索框根据函数名称搜索显示对应的性能数据。

还有,可以参考Traceview 官网说明 http://developer.android.com/tools/debugging/debugging-tracing.html


  • 抓取分析数据
     上面内容介绍了Traceview工具,光有工具而没有分析数据,哪来的性能分析。所以这部分内容讲述抓取分析数据。
    该部分说明如何抓取分析数据,即创建分析数据文件(Trace File)。其方法有三种:

    •      1. 使用Eclipse的DDMS功能
         该方法是最简便也最实用,没有之一。而且不需要去修改代码也不管程序是否有读写SDCard权限(如方法2)。具体操作如下:
          a. Eclipse切换至DDMS页面,选中需要分析的程序(如xlwireless.wirelessadhocnetwork),点击右上角按钮“Start Method Profiling”,如下图:

          
图2. DDMS启动Traceview

          b. 停止操作时点击同一按钮,则Eclipse会自动弹出中Traceview图形界面,直接显示了Trace(文件)的分析结果。
          c. 若想保持分析数据(trace文件),只要操作Eclipse菜单File -> Save As ... 即可。
          的确是实在太简洁的操作吧!

          注意:该方法不支持Android 1.5设备,Android 2.1或更早版本需SDCard机器权限,而Android 2.2之后即使没有SDCard也行。(引用自官方说明)
    • If you are using DDMS, Android 1.5 devices are not supported.
    • If you are using DDMS, Android 2.1 and earlier devices must have an SD card present and your application must have permission to write to the SD card.
    • If you are using DDMS, Android 2.2 and later devices do not need an SD card. The trace log files are streamed directly to your development machine.


    •      2. 使用Debug Class代码添加功能
         MainActivity的OnCreate方法添加:
  Debug.startMethodTracing("xl-adhoc-trace");
         MainActivity的OnDestory方法添加:
  Debug.stopMethodTracing();
         或者,你也可以换成在MainActivity的OnStart与OnStop方法。有人出现这样情况:“但是在实际的测试时发现这种方式其实并不好用,因为通常情况下我们的activity的onDestroy()是由系统决定何时调用的,因此可能等了很长时间都不会得到这个trace文件。因此决定在onStop()中来调用Debug.stopMethodTracing()。这样当我们切换到其它activity或者点击home键的时候onStop()就会被调用,我们也就可以得到完整的trace file。”

        使用该方式获取trace数据需注意:程序须有SD卡读写权限,当然还有代码权限。
        程序运行结束后,会在SD卡下生成对应trace文件(如xl-adhoc-trace.tracing),使用adb pull命令拉取trace文件或其他可视化拷贝都可以。


    •      3. adb 命令行启动
        命令如下:
         adb shell am profile start
         adb shell am profile stop
        但该方法没有去做过尝试。



  • 分析性能
     工具 + 数据 = 分析结果

     通过已保存有的trace文件,用命令行启动Traceview分析程序性能。
     1. 把Traceview目录添加到系统的环境变量Path,如c:\users\<user-name>\android-sdks\tools\;
     2. CMD命令行:traceview  ***.trace (注意:全路径的trace 文件,否则可能不行)。
     3. 自动弹出traceview图形界面,通过这个图形界面就开始分析程序的性能。

     结合图形,做简单的示例分析:
     a. 日志版的自组网程序刚启动时间(2s左右)的性能结果:
         
         从上图分析结果:打印日志的逻辑占了程序性能很大比例。

     b. 非日志版分享文件时的性能结果:
          
          从上图分析结果:可以优化的地方包括Wifi列表更新操作的refreshWifiScanResult,文件分享(发送方)的onRequestFileRange。


  •      具体函数的分析
          针对分享文件数据(上图的)详细分析一下,查找refreshWifiScanResult与onRequestFileRange函数在具体地方占用其性能比例在哪里?
          见下图显示,用红圈标注出了性能的关键地方。
          
          
          
          

     
(完)

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的用法是比较常见,哪种更好需具体场景具体分析。

(完)

2012年12月27日星期四

Android开发笔记(九)


Android开发笔记(九)

  • 单线程处理多模块逻辑
     最近工作其中的一个重要任务,在组网层(?)之上实现任务层逻辑,目前功能包括文件传输与历史记录。为了模块具有良好的独立性与复用性,除了设计一套良好的对外接口外(在此不讨论接口问题,参见~~),还需考虑合适的线程模型--采用单线程。其实单线程模型设计很简单,关键在于:
     1. 管理单线程的生命周期,并维护其线程队列。
     2. 调用接口,内部逻辑第一件事做的是转至内部处理线程即投递消息到线程队列。
          在线程循环处理消息的前提下,线程队列将消息继续投递至对应的子模块消息处理。
     3. 内部子模块必须实现独立的消息处理函数,子模块间的消息互相不影响。

     Java(android)SDK就提供了如下说明的线程解决方案而且很完善,正所谓得天独厚啊。相关的类型及说明如下:
     HandleThread或Thread,即Java提供的线程,主要就是创建线程对象,start线程,退出线程。
     Looper,即线程的消息队列,每一个线程对应一个消息队列,一对一关系。退出线程是由Looper.quit退出线程消息循环替代完成的。
     Handler,即关联至对应线程队列的消息处理类型。一个消息队列允许对应有多个消息处理对象,一对多的关系。

  • 随机读写文件
     刚开始做Android开发时,遇到读写文件情况都是顺序的读写,所以使用InputStream或OutputStream可以简单顺利搞定。但后来遇到其他复杂的应用场景,可能不是顺序的读写即随机读写。
     1. 首先尝试使用java.io.RandomAccessFile,简单使用示例如下:
     文件输入输出流:mFileOutput = new RandomAccessFile(new File(uri.getPath()), "rwd");//允许读写,追加写入。
     文件输入流:mFileInput = new RandomAccessFile(new File(uri.getPath()), "r");
     设置偏移位置:mFileOutput.seek(mFileOutput.length());
     读取文件(流)大小:mFileInput.length();
     读取数据:readLen += mFileInput.read(buffer, 0, bufferLen);
     写入数据:mFileOutput.write(data); mFileOutput.flush();
     关闭文件流:mFileInput.close();
     注意:如上创建随机读写文件流,均以android.net.Uri对象打开文件,下同
     关于RandomAccessFile类型的具体介绍参见:android-sdk-RandomAccessFilejdk-RandomAccessFile
     
     很遗憾的是,我在Android设备上尝试使用RandomAccessFile随机读写文件未成功,在创建对象时候失败抛异常:

FileNotFoundExceptionif the file cannot be opened or created according to mode.
     网上搜索很多办法犹未解决问题,由于项目时间逼近所以暂留下问题在此笔记,容后续再考虑。你有什么办法吗?

     2. 接着,找了另一种办法实现了随机读写文件这样的功能,使用java.nio.channels.FileChannel 对象操作文件读写。使用FileChannel的简单示例如下:
     输入流FileChannel:
          ContentResolver cr = context.getContentResolver();
          mInputStream = (FileInputStream) cr.openInputStream(uri);
          mFileInput = mInputStream.getChannel();
     输出流FileChannel:
          ContentResolver cr = context.getContentResolver();
          FileOutputStream fileOutputStream = (FileOutputStream) cr.openOutputStream(uri);
          mFileOutput = fileOutputStream.getChannel();
     设置偏移位置:mFileInput.position(beginPos); 或mFileOutput.position(mFileOutput.size());
     读取数据:mFileInput.read(buffer);// buffer is ByteBuffer object.
     写入数据:writtenLen += mFileOutput.write(ByteBuffer.wrap(data, 0, data.length));
     关闭Channel:mFileInput.close(); 或关闭对应的文件流:mInputStream.close();
     
     这次就成功地以Uri对象打开了(输入输出)文件流,得到对应的FileChannel对象,并顺利地实现了随机读写文件流功能。
     相比RandomAccessFile,虽然FileChannel这种办法看起来粗略一些,更多的是需自己设计,但其效率未必较差:D 。
     关于FileChannel类型的具体介绍参见:android-sdk-FileChanneljdk-FileChannel

btw. android文件路径如:(/mnt/sdcard/...),Uri内容如:(content://media/external/...)。



  • 对象序列化类型
     可以先尝试回答一个问题,在Java编程什么时候需要用到对象序列化?
     1. 通过序列化保存对象,如保存至磁盘。
     2. 通过序列化传输对象。如网络传输,进程间传输等。
     在Java编程做Android开发过程中,对象序列化可以使用到的主要类型包括: ParcelParcelable、Serializable。
1.在使用内存的时候,Parcelable 类比Serializable性能高,所以推荐使用Parcelable类。
2.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3.Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点, 也不提倡用,但在这种情况下,还是建议你用Serializable 。


在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。在这样的环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列化机制。Parcel的实现过程是这样的:
1. 整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比JAVA序列化中使用外部存储器会高很多;
2. 读写时是4字节对齐的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)这句宏定义就是在做这件事情;
3. 如果预分配的空间不够时newSize = ((mDataSize+len)*3)/2;会一次多分配50%;
4.  对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。

  ParcelParcelable简单的使用,如下:
  Parcelable praceable = intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
  NetworkInfo otherNetworkInfo = null;
  if (praceable != null) {
     final Parcel pracel = Parcel. obtain();
     praceable.writeToParcel(pracel, 0);
     otherNetworkInfo = (NetworkInfo) pracel.readValue(NetworkInfo. class.getClassLoader());
  }

   注意:Serializable的实现,只需要继承(implements) Serializable即可。这只是给对象打了一个标记,系统会自动将其序列化。
  关于ParcelParcelable、Serializable三者的详细说明参见:android-sdk-Parcelandroid-sdk-Parcelableandroid-sdk-Serializable

(完)


2012年11月27日星期二

iOS开发学习笔记(二)

iOS开发学习笔记(二)
  -- 继续入门篇


  • > 开发框架
     iOS开发框架由上而下可以这样概括包括:Cooca Touch,Media,Core Services,Core OS。如下图所示:
          
     越下层内容,越偏向于系统底层服务,不同层次的内容举例如下:
Core OS
     CFNetwork、Security
Core Services
     Core Foundation、 Address Book、 System Configuration
Media
     Core Graphics、Media Player、OpenGL ES、Quartz Core
Cocoa Touch
     UIKit、AddressBook UI

     更详细内容,参见官网SDK Reference说明。
     注意:我们在开发过程中,尽量选用上层提供的类型进行封装实现所要的逻辑,完成不了才考虑更底层的框架,这也是iOS开发的普遍规则。


  • > 开发原则
   技术选择原则
     只有上层的技术框架提供不了我们所需的效果才去考虑下层框架的技术
   项目框架的构建
     遵守MVC的设计原则,尽量把数据、界面、控制这三层分清楚
   可移植性考虑
     鉴于移动平台的多样性,核心业务功能和模块尽量使用c或者c++进行开发


  • Objective-C语言
   Objective-C必须紧记的特点
所有类都以NSObject为基类
对象都以指针的形式来表示
#import代替#include
self代替this
id代替void*
nil代替NULL
BOOL类型用YES/NO代替TRUE/FALSE
不能多继承,只能单继承(只有一个父类)
不支持命名空间
不支持运算符重载
支持多态、动态识别和反射机制


  • Objective-C的类、类别、协议和委托
     * 类(Class)
     类和C++的概念是一样的,是对象的抽象,在Objective-C中明确分为接口和实现这两部分
     ** 类的声明:
     @interface 类名:父类<协议1,协议2,…>
     {
          内部变量
     }
     @property (修饰1,修饰2,…) 类名* 属性名;
     …
     +(返回类型)静态方法:参数;
     -(返回类型)动态方法:参数;
     …
     @end
   
     ** 类的实现:
     @implementation 类名
     @synthesize 属性1,属性2…
     类方法的具体实现 ...
     @end

     * 类的属性(Property)
     在类的interface内部定义:
     @property (修饰1,修饰2,…) 类名* 属性1;
     @property (修饰1,修饰2,…) 类名* 属性2;

     在类的implementation同步:
     @synthesize 属性1,属性2…

     类的属性实际上是暴露给外部使用的成员变量, 定义了一个属性实际上就定义了一个成员变量的存取方法(有getter和setter两个函数)
     例如 @property (assign, nonatomic)  NSInteger val;
     在@synthesize之后就等于实现了如下两个方法:
     - (void)setVal:(NSInteger)input;
     - (NSInteger)getVal;

     类的属性修饰符说明
          strong     强拥有属性(新编译器常用)
          weak     弱拥有属性(OS?X?v10.6和iOS?4不支持)
          copy     深拷贝
          assign     基础数值类型赋值、浅拷贝
          retain     对象引用,对赋值对象引用计数+1
          nonatomic非原子,即多线程访问时不加锁
          readwrite     可读写属性(默认的修饰, 有getter和setter)
          readonly     只读属性(没有setter函数)

          注意:
          NSString类型的类属性一般用copy修饰进行深拷贝

     
* 类别(Category)
          类别是对现有类的功能扩展和分类,类别也分为声明和实现两部分
          类别的声明:
          @interface 现有类 (类别名称)
          +(返回类型)静态方法:参数;
          -(返回类型)动态方法:参数;
          @end
          类别的实现:
          @implementation 现有类(类别名称)
          方法的具体实现
          @end
     同一个类的类别的名字必须是唯一的
     类别的方法若和现有类的方法相同则会覆盖原方法(请慎用这个特性)
     类别只能为现有类增加新的方法而无法增加新的数据成员

     *协议(protocol)
     类似C++的纯虚类,和Java的接口概念相似。协议的定义只有声明而无需实现。
     声明:
     @protocol <父协议1,父协议2,…>
     @optional  --该修饰符下面的方法可以实现也可以不实现
     方法声明
     @required     --默认修饰符,要求下面的方法必须实现
     方法声明
     @end

     *委托(delegate)
     委托即代理的意思,是指把方法的实现转交给其他遵守协议的类
     使用委托的步骤:
     1.定义协议(如@protocol MyProto…)
     2.定义委托对象,常见的定义方法有
          id<协议> 委托对象;
          NSObject<协议>* 委托对象;
     3.调用委托对象遵守的协议中的方法


  • Objective-C的动态识别和调用
     * 选择器selector的概念
     selector的类型是SEL
     定义一个选择器并赋值:
     SEL mySelector = @selector(方法名)
     调用选择器指定的方法:
     [类对象 performSelector:mySelector];
     判断某对象或类是否响应指定的选择器:
     [类对象 respondsToSelector:mySelector];
     [类名称 instanceRespondToSelector:mySelector];

     * Objective-C的动态识别和调用方法总结
          -(Class)NSClassFromString(NSString*)   
                              让指定的字符串变成已定义的类名        
          -(BOOL)isKindOf:class-object             
                              对象是否为class-object或其父类
          -(BOOL)isMemberOf:class-object             
                              判断对象是否为class-object
          -(BOOL)respondsToSelector:selector        
                              判断对象是否能响应selector指定的方法
          +(BOOL)instanceRespondToSelector:selector   
                              判断指定的类实例是否能响应selector
          +(BOOL)isSubclassOfClass:class-object        
                              判断对象是否是指定类的子类
          -(id)performSelector:selector             
                              调用用selector指定的方法
          +(BOOL)comformsToProtocol:protocol        
                              判断一个类是否遵守指定的协议

  • > 开发中常见的前缀
     NS-     NextSTEP
     CF-     Core Foundation
     CG-     Core Graphics
     CA-     Core Animation
     UI-     User Interface
     IB-     Interface Builder



  • > 网络资料
     在学习iOS开发过程中,寻找问题过程中,或多或少收藏了一些博文。在此同样分享一下:

(完)

2012年11月25日星期日

Android开发笔记(八)


Android开发笔记(八)
  -- 组播与SQLite存储


  • Android程序利用组播收发数据
   > 实现功能:
     多台Android设备之间,或与PC、笔记本等设备,存在相同的WLAN环境的前提下,不同设备之间彼此收发数据,相互发现。

   > 网络环境:
     在相同的WLAN环境下。包括:相同BSSID的WIFI,或同SSID不同BSSID但可以直连。
     且所处的WLAN允许转发UDP数据包,因为组播是建立在UDP协议基础上的。

   > 组播流程:
     1. 创建一个组播Socket(MulticastSocket in Java)绑定固定的端口。这里可以设置Socket属性,如setLoopbackMode是否禁用收到本机的组播(环回)消息。
     2. 将该Socket加入到指定的组播IP地址,关于组播地址的说明可参考百度百科
     224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
     224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
     224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
     239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

     3. 可以正常发送或接收组播消息。另外在发送接收之前设置Socket超时时间setSoTimeout。

     PS. 可以说Java SDK把组播功能封装在MulticastSocket,使用起来相当的简便的,完全谈不上难度。
     注意:
     a,收发组播数据包时候,建议最好先发组播数据,再接收组播数据。这样比较好,因为发出去数据包了,尽快保证其他设备可以收到数据包。
     b,不同设备间设置的组播地址及端口都应该保持一致,才可能彼此收发对方的组播数据。

   > 示例程序
简单的测试示例代码参见Github

  • Android程序Sqlite存储数据应用


  > 模块设计
     a 封装android.database.sqlite API;
          class SQLiteOpenHelperImpl,继承基类SQLiteOpenHelper。提供的功能主要包括:
          1,创建所需的SQLite数据库(*.db),同时负责创建数据表。
          2,对外提供只读或读写的SQLiteDatabase对象。
          3,关闭数据库。
          在什么时候创建表呢?当然了若表已存在则不必创建, SQL语句如“CREATE TABLE table_name IF NOT EXIST ……”,几个地方可能需要考虑创建数据表的,包括 onCreate或onOpen或getReadableDatabase或getWritableDatabase。
               
          class SQLiteManager.
          主要管理SQLiteOpenHelperImpl或SQLiteDatabase对象,为配合业务逻辑做一些功能实现。

     b 接口实现层
          class LogicModuleImplement
          即实现如下接口,更加面对业务需求的功能实现,要放在该类型完成。
     c 接口层
          class LogicModuleInterface
          提供对外使用的接口,具体定义内容根据不同项目的业务需求而定。

PS:以上简单描述模块设计源自个人项目开发的总结,可能完全不适合你,或全是错的。若可帮到当然最乐意了!

  Android中SQLite应用详解该链接的内容关于Android的SQLite应用讲解的很详细,有需要的同学不妨认真参考一下。

(完)