Android-开发-错误姿式100手

一、Android2.2 2.3 系统可以在UI线程中访问网络

  • 错误姿式:

    • Android初期,自学的时候,也搞搞美女图片类应用,跑在我的Htc G11手机上,在onCreate中的网络线程也知道会启个异步任务去处理.
    • 但onResume中的,直接去访问了网络,在我HTC G11上跑得很正常,但别人4.0手机上,竟然Crash了。抛出了:NetworkOnMainThreadExceptionn异常。
  • 原因:

    • 解释一下,从Honeycomb SDK(3.0)开始,google不再允许网络请求(HTTP、Socket)等相关操作直接在Main Thread类中,其实本来就不应该这样做,直接在UI线程进行网络操作,会阻塞UI、用户体验相当bad!即便google不禁止,一般情况下我们也不会这么做吧~
    • 所以,也就是说,在Honeycomb SDK(3.0)以下的版本,你还可以继续在Main Thread里这样做,在3.0以上,就不行了. 详见Android之NetworkOnMainThreadException异常
  • 解决方案:

    • 耗时操作一定要放到异步线程中。说到UI线程,就不得不说一下下面的代码,记住,这里好像是新开了一个线程出去,但这个只是把这个线程对象挂到了UI线程队列中去执行。所以也会阻塞,也会ANR。所以耗时操作也不能放到 handler中。
handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1000);

二、群起而攻之后的恶果

  • 错误姿式:

    • 客户端初期,主要是一个兴趣小组,临时起意,赛马级产品,分配好工作量,大家就各自干各自的事情了。这样的一个后果是,等产品发布上线,要维护的人发现,操,八种编码风格,百花齐放的局面!太他妈的难维护了。哥走了,你们玩!
  • 原因:

    • 因为群起而攻之,必然导致风格迥异的代码风格,命名方式,导致项目的难维护性。
  • 解决方案:

    • 那对Android项目App来说,统一编码规范就变得很有必要。比如下面的命名方式,大家特别注意颜色的命名方式,基本的颜色red blue green yellow 会写也能识别这个颜色,但色值却多样,所以颜色加色情来命名,可以避免一堆重复的color噢。
类型 备注
ProductActivity Activity的命名
ProductFragment Fragment的命名
ac_product layout命名
frag_product layout fragment命名
item_product layout命名
gray_cccccc 颜色命名
tv_product_title 元素命名

当然,如果分了模块后,为了避免资源文件重名,都要再加上前缀:product_frag_detail.xml这样子。


三、Android 2.2 2.3 不支持ActionBar,只好重新写一个新APP

  • 错误姿式:

    • 基于上面的项目一看,发现太难维护了,新的3.0出来以后,推出了ActionBar和Fragment,我要采用全新的方案来写一个新的App。而没有在原有App上进行改造。
  • 原因:

    • 我的ActionBar在无法在 2.2 2.3 机型上兼容。这个是Google的原因,我也没办法。而其实当时Google还没有出手support包。但业内已经有 actionbarsherlock,只能说眼界不够。到后来Google才支持了support V4,让2.2 用户也可以用上了ActionBar。
  • 解决方案:

    • 开源github还是要关注,要不然无知真的很可怕。

四、用户反馈一直转菊花

  • 错误资式:

    • 用户机器上一直转菊花,然后我们这边启动正常,百思不得其解。为什么用户的手机会一直加载中呢?
  • 原因:

    • 用户是通过推送进入App,但网络引擎竟然写在了MainActivity中。用户其他入口进来时,就直接跪了。因为网络引擎没有初始化。
  • 解决方案:

    • 这个时候有两个问题要去解决:1.我们的初始化应该放在App的onCreate中。2.Android App究竟有哪些可以启动App的入口呢?App 启动入口如下:

      1. 通过图标启动
      2. widget,好的桌面小控件流量也惊人噢!
      3. 推送,推送拉起App。
      4. Receiver拦截通话、或短信启动App、或开机启动
      5. Intent拦截,如:market, http, 以及share intent,Google叫:deep link 和 app link.
      6. 其他App直接拉起等。
      7. adb am start, 如:
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://www.baidu.com/s?wd=android\&spm=aaa"

众多入口,初始化做好了吗?这些入口你都有用到吗?不要放过任何一个用户可能用到你机会噢!

五、大量用户卸载App之灾

  • 错误姿式:

    • 新版本中上线了一个推送功能,还没有开始用推送呢,就大断用户卸载App,活跃用户持续下降,但Crash率稳定。
  • 错误原因:

    • 因为要兼容机型,GSM不通,所以推送SDK要一直保持一个长连接去获取状态,上线的推送SDK中竟然用了 handler.postDelayed来保持长联。服务器在国内,所以国内测试未出现超时的情况,但到了国外,千里迢迢的来你国内获取一个心跳,ANR现象非常严重,大量用户卸载App。
  • 解决方案:

    • 再次声明:handler.postDelayed 是在UI线程,不要再执行耗时操作。
    • ANR Application Not Responding Activity中5秒未响应,BroadcastReceiver 10秒未响应,就挂了。

六、老用户升级App使用某功能后Crash,新用户正常。

  • 错误姿式:

    • 发布新版本过程中,新安装用户一切正常,但老用户升级上来后,使用某功能就Crash。
  • 错误原因:

    • 新版本在sqllite数据库中新增加了一个字段,但未考虑到老版本升级用户,导致升级上来的用户,功能不可用。
  • 解决方案:

    • 至此以后,测试流程的大图上,就又增加了一个覆盖安装测试。覆盖安装测试主要就是用来保证原来遗留在App里的值,比如SP,DB,计算文件等是否和新版本兼容。
    • 建议大家SP的Key值统一放到一个地方,万一别人用了和你一样的Key,那你们两个的应用程序就等着挂吧。同时,可以有一个潜在技术需求:就是上传用户当前的SP值。会很方便排查问题。

七、秒杀活动全被手机给秒杀了

  • 错误姿式:

    • 搞大促,一般都会玩秒杀,然后整点开始一分钱抢个商品,再常见不过了,但产品刚刚上架,还没有到整点的时间,所有的库存都被无线端的用户给秒了。
  • 错误原因:

    • 最初的Android网络请求,用的还是Http,除了很容易被抓取到数据包以外,请求数据还很容易模拟,并且还能回放。用户就拼接出请求URL,然后循环调用下单接口;服务端也没有加上严格时间校验,认为前端页面可以拦得住。然后非据就产生了。
  • 解决方案:

    • 后期,采取了如下方案
      • 所有的网络请求走https
      • 参数中增加系统时间字段,防止用户重放
      • 增加请求参数签名,防参数篡改

八、客户端推送消息开关不生效

  • 错误姿式:

    • App中,一般都会设计一个可以关毕App推送的开关,以表示我们还是很民主的,你不想收推送消息,可以关掉。很友好吧?但遇到的现象就是:我明明在App中关掉了推送,但还是可以收到推送消息。
  • 错误原因:

    • 因为推送服务在另外一个进程中,和界面不是同一进程,但开关保存数据用 SP的 private_mode来存取的,导致在子进程中读取不到。导致关掉了推送消息,照样会收到推送消息。
  • 解决方案:

    • 修改为:getSharedPreferences("sp_file", Context.MODE_MULTI_PROCESS)来读写,可以跨进程读取,因为数据不多,所以暂时性的用SP来搞了。
    • 如果数据共享数据较多,就要用:ContentProvider来共享数据了。

九、推送消息打开后,按返回键直接退出了App

  • 错误姿式:

    • 一个推送消息过来后,点击通知栏,打开了产品详情页面,用户点击了返回键。竟然整个App都退出了。好不容易拉来一个流量,竟然这么轻易的就放过了?说好的揉虐一万遍呢?
  • 错误原因:

    • 直接通过Service中拦起了相应的Activity。然后就什么也没有做了。怎么也要再回到首页啊。
  • 错误引申:

    • 大家会想,那不简单,推送以后,先启动首页,然后再从首页去启动具体的页面。如果这样,一次性推送100W数据,那首页就要面临100W用户同时访问的压力,这是自己玩死自己的节奏,万万不可;

    • 这个时候,我们要怎么搞呢?新建一个通知处理Activity A,去开启业务页面,B页面点了返回时,会再回到Activity A。剩下的就是Activity A的事情了,即:在特定场景下就去唤起首页,并把自己关掉。考验Activity生命周期的时候就到了。大家知道,从一个Activity A到另一个Activity B,Activity A 会经历:onPause -> onStop过程,当再从Activity B回来的时候,会经历onResume,所以可以在onStop置个状态位,然后在onResume时读取这个状态位,发现经历过onstop了,就去打开首页,然后finish自己。


十、客户端每隔半小时Crash一次的问题

  • 错误姿式:

    • 客户端每隔半小时Crash一次,如此往复,Crash率一直飙升,愈演愈烈!
  • 错误原因:

    • 为了提升客户端日活,客户端启动时,从服务端获取一个规则,并保存到SP中。然后写了一个闹钟定时器,每隔半小时启动一次,解析规则并执行提醒操作。但服务端配置出了差错,导致解析字符串出错,然后抛出空指针。App在后台一直挂,但用户并没有感知。
  • 解决方案:

    • 这个问题的关键点在于:把用户下载的那个错误配置给修改正确。所以服务端先对规则进行修正。然后就是解决客户端存在SP里面规则的问题了。有两种方式可以解决。
      • 发送推送消息,让用户启动一次App,以更新错误的配置。
      • 客户端还有另外一个闹钟会定时发送一个网络请求,通过修改这个网络请求返回的数据,触发更新客户端配置的网络请求。
      • 对客户端下发指令,一键清除所有缓存:DB,SP都清理掉。
  • 总结:客户端的定时器程序,一定要保证健壮,不管服务端给你什么样的数据,都不能挂。所以程序的健壮性也很重要。

    • 无网络,App会不会挂?
    • 有网络,但服务端返回空数据,会不会挂?
    • 边界值,会不会挂?
    • 服务端多返回字段或少返回字段,会不会挂?

发表评论

您的电子邮箱地址不会被公开。