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

(完)


没有评论:

发表评论