android工作经历

邮轮:

 

国际散客票:

 

自助打包:

 

目的地:

 

团聊首页:

 

常旅:

 

机票:  首页,列表页。难点,业务复杂逻辑多,需要自由组合、套票选择互补影响,所有操作都是各自的,这个地方开始引用fragment来实现。

 

火车票: 首页、列表页、详情页、订单页、占座页。

      难点:订单中的旅游券(2种样式,后台控制),优惠券之间的关联逻辑,有互斥、旅游券使用规则传给前端在前端判断,但是优惠券的逻辑在后台,不同坐席人数不一样。方案,在更改坐席人数的时候刷新优惠数据和总价,重新选择券,重新计算

           占座页面的倒计时,一直困扰我,用的是系统的countDownTimer,但是在5.0系统以前是有bug的,虽然调用了cancel,从messageQueue中删掉了所有的message,但是可能存在handler正好在处理,结果又继续调用sendMessageDelayed延迟发送,这样就停不下来

         在5.0中,源码中加入了标记,标记已经cancel,在handleMessage中就会return。

 

ta打点: 记录数据发送至途牛服务器。难点:所有的页面打点要按照一种规则,进入页面只打一次点,回退不打点,杀掉恢复不打点!!!!!,还有记录from页面。有时候一个页面不是一个activity或者fragment,可能是一个view。发送机制,什么时候发送?发送多少?

      有插件,插件的context不一样。最后还要要方便新增页面打点,很简单的就能增加一个新页面的打点。

      activity有baseActivity,在生命周期中做判断是第一次进入,再更具Onsave里是否有数据判断是不是恢复出来的。记录前、现、后三个数据。view的显示隐藏出发点来模仿生命周期。

      发送机制,考虑到数据量问题,一次发送5条,在满5条的时候、退到后台的时候发送数据。用的队列存取数据,一边只管存,一边利用满5个发起数据传输、发送失败重试3次,结束后再判断是否满5个,满就继续发送。靠2个触发点维持持续发送,最后再靠退至后台全部发送。

      后续:后来接触到aop,可以考虑。还有发送触发机制,当后退到退出会漏数据。

      现在有个新的技术,google的protocol buffer,将数据转换成二进制,从而减少发送数据量,1 00000000 前面那个1表示后面是否还有,0表示没有了,1表示还有后续。

      用例一个jar包通过编译定义model规则的文件生成java model,再通过这些model存放数据,最终转成二进制,发送的时候利用GZIPoutputstream发送压缩数据,可以实现减少很多数据量。

 

动态加载dex:学习到了dex的动态加载,使用classLoad直接读取dex文件,直接使用里面的class。这个技术需要注意主工程中与动态dex中不要有相同的类,不然会出问题。该技术最先被使用在动态加载第三方jar包上(途牛app),用以减少方法数。

          方法把需要用到的jar中的方法都提取到一个interface中,然后写一个实现类,直接调用jar包中的类完成操作。然后把这个类单独打个jar包出来,再与第三方jar一起打成apk(现在可以利用gradle直接使用provide,interface不参与打包)。然后把这个apk放入主工程的assert目录中,在应用启动的时候尝试读取  data     目录下是否有该dex文件,没有的话从assert中copy过去。用一个manager类管理已经加载进来的classload和实现interfase的类,方便直接使用。基于classLoad的插件化的基础~

    

 

酒店: UI复杂(详情页),动画效果复杂(悬浮+自动置顶,实现方案:悬浮使用多加一层布局,根据滑动位置显示隐藏。自动置顶,expandListView的smoothScrollToPositionFromTop方法,需要注意偏移量是head+group+已展开的child)

          业务逻辑复杂(筛选项,经纬度,poi之间的关联,detail的展示模式互相切换,价格、房型计划等),价格计划排序复杂,有的房型没有价格,放在最后,然后有价格却满房的放在前面,最上面再放按价格排序可以购买的,当切换模式的时候价格减去了返现,全部重新排序。。。

          用的是Comparator,不过这里面有个坑。。。1.6的源码中,超过32个元素的时候,使用的是归并方法,先分成一个个有序的队列,然后再合并起来,然而在低于32个元素的之后只是使用二分查找法(折半插入排序)。这里还要明确一点重写的compare必须保证

      the sign of {@code compare(a,b)} must be the opposite of the sign of {@code
      * compare(b,a)} for all pairs of (a,b)</li>
      * <li>From {@code compare(a,b) > 0} and {@code compare(b,c) > 0} it must
      * follow {@code compare(a,c) > 0} for all possible combinations of {@code
      * (a,b,c)}

        不满足的时候、一旦超过32个元素,就会crash。  

 

超级自由行:机票,难点:业务复杂,优惠组合(套票,优惠组合),单程(单程,自由往返),切换互不影响;

            时间控制,当天往返,时间失效,反正不能大于去程,价格日历;

      购物车,难点:本地存储机制,SharedPreferences和文件虽然都可以存,首先文件可以存储在内存卡中,不占用空间,但是文件操作麻烦,容易各种异常

            但是sharedPreferences和文件都不方便操作,每次都要取出string,然后再转成model再去修改某个字段属性,再存成string,异常蛋疼。

            最终考虑是用数据库sqlite,好处,增删改方便,不用做大量无用操作,能够很快定位数据、取出数据。

            那么现在又有个问题了,数据库操作应该放在线程中操作,防止arn。但是操作数据库只能一个一个的操作,会锁表,还要按照顺序执行,这样就考虑到使用HandlerThread:再非UI线程中依次执行。

            有点郁闷的就是数据库表变更的时候比较麻烦,这个要需要注意,测试好。

            现在有个新的问题,在设计的时候并没有考虑做成等待数据库返回是否操作成功。所以在插入数据库还没有完成的时候,立马读取会得到错误数据。现在考虑给购物车增加操作成功与否的回调,在回调

            里做需要等待数据库完成之后才能做的事情,但是这里有个复杂的地方,极端情况下,有多个页面都在调用handlerThread处理数据库任务,那么返回必须要分别通知调用者,这样每个调用者都要实现

            一个回调然后发送给handler去处理,handler处理完通过这个listener类返回结果,这个class要实现序列化(通过message通信)。这样还有一个问题,因为handlerThread是工作在非ui线程,所以每次

            执行回调函数的时候都是在非ui线程中,我们需要让每次执行回调都是执行在ui线程中,解决方法:再使用一个handler(使用Looper的mainLooper),然后每次数据库完成之后发送message给它,在它

            的handlemessage执行回调。

                当时写的get方法都加了synchronized,发现实际上并不需要因为方法内没有成员变量,只有局部变量,使用的db是单例中的,然而数据库操作其实是对文件的操作,sdk已经使用lock了,不需要我们自己再次加锁.

            关于我们到底要不要在调用db的时候使用synchronized,参考另外一篇随笔.

            另外之前的设计是uiThread->发送message到handlerThread->处理完再次发送message给uiThreadHandler->回调,之间都是通过message的bundle来传递数据,这样有缺陷,需要组装bundle数据,再

            解析bundle数据,有2次这种过程,并且对象还需要序列化,增加开销。然而我们可以直接post(new Runnable()),传递执行体(内在其实是会obtain()一个message然后赋给callback属性,当handler处理message

            的时候,发现有callback就会直接执行,否则才会按照处理message其他属性的方法),一来避免了组装bundle和解析bundle,二来不需要序列化!!。

      预定页,难点:下单逻辑较复杂,重点页面额外注意保证流程通畅。

      通用页面-费用明细:要事先就确认好UI样式,写好框架,要便于扩展。

 

Boss3品类: 资源选择1/2,难点:移植代码,结构不同移植起来麻烦,等于重写。先理清逻辑,再找出可以重用的代码布局。

 

目的地详情:新的页面需求,展示更多更丰富的内容,图片多,布局复杂,横向纵向类listview,考虑以后的扩展性,没有直接使用scrollView,怕以后遇到需要展示很多重复布局的内容,效率低。最终使用expandListView,易扩展,即使以后扩展也可以不影响效率。

      但是在开发过程中,遇到了问题,困扰了很久,关于view的回收重用,很容易混乱。总结如下:

      1、getGroupCount,是group的个数,这里尽可能写明确数值,不要动态变化。原因等会再说。

      2、getGroupType,一定要写对了,这个是position对应的,要把所有的可能种类都算进去,如果groupPosition顺序变了,就会有问题了,因为缓存的时候是根据position来的,顺序变化了就混乱了。这就是第一点的原因,解决方案是个数全算,如果本次木有,那么在getView的时候返回一个new View(Context),这个View也要算作一个groupType!!!!!!!!!!!!!!!!!!所以可以每个group的new View都可以用这个type来指定。那么就是groupCount +1;

      3、getChildTypeCount,这里是所有的child种类个数,所有的!!!

      4、getChildType,每个postition下的child种类,并且是依次升序的,0开始,这个和group不冲突,不能跳跃,不然会内部数组越界!!!!!!!!!!!!!!!!!!!!!!!!!!!坑爹的,当时耗了我好长时间

       如此就可以完全利用listView的回收机制,每个种group和child都可以有缓存,当需要的时候,不用再次做重复的创建,查找,赋值。

      这里还有个注意的地方可以提升性能:因为根据需求我们很多的group都是有个性的,不会有重复的,那么就是说当group创建出来之后,个数不会变多,样子也不会变(在重新赋值之前)。所以我们要重复利用这点,当从屏幕之外再次回到屏幕内的时候,我们直接retrun convertView。这里我使用的方式是给需要这种机制的地方设置一个flag,表示是否有数据变化,在getView中返回convertView之前把标志变回去,表示刷新过数据了。否则就在有缓存的时候&数据没有变化的时候直接retrun convertView。又因为这里有好几个种groupView,每一个写个flag太麻烦,要很多个全局变量,所以就跟源码学习,使用了二进制的与或来完成,一个int型变量就可以记录很多个标记,相互不冲突。

 

 

hot fix:

 

 

 

 

 

 

插件化:

 

 

 

 

fresco:

 

 

 

 

性能优化:

 

posted @ 2015-11-26 14:40  saki_god  阅读(478)  评论(0编辑  收藏  举报