南京邮电大学移动互联网俱乐部

移动端BaaS服务比较

作者:林翔宇

Backend-as-a-Service (BaaS) 服务可以有效减少移动端后台开发的工作量,大大减少数据存储、分析统计、推送服务开发的工作量。本文简单介绍几个BaaS服务,并且会持续更新。

Parse

Parse 是 YC走出的创业公司,之前被Facebook 5000万美元收购,应该是目前用户最多最流行的BaaS服务,提供的SDK也十分丰富,从Windows8到OS X, Android,iOS,Unity的SDK应有尽有。而且在Facebook下面稳定性也得到很大保证,不用担心过段时间突然停止服务。。

缺点是对国内服务存在速度与稳定性的问题,不过目前一直看着都还好。甚至推送服务比一些国内的都还及时。。。

刚出的价格方案,比原来降低了很多。

AVOSCloud

AVOS是陈士俊投资的公司,AVOSCloud是AVOS中国的研发中心的一个作品,不过目前貌似AVOS中国专心做AVOSCloud了。。

AVOSCloud刚出来要需要申请测试的时候我就在用了,当时还是对Parse的模仿,文档页面都模仿Parse的样式,类名前缀都用Parse的。。。理由是方便Parse用户切换。。确实,只要改下导入的包/头文件就可以了。。。文件存储用的是七牛云。。。

但是目前AVOSCloud不仅仅是对Parse的模仿了,产品迭代的很快,新功能持续上线,Dashboard体验也很好,值得尝试。

(未完待续)

部署Django Application(一)使用CentOS + Nginx + Gunicorn + Supervisord

作者: 林翔宇

本文是去年做掌上南邮第一版服务器程序的时候写的部署笔记,原来放在简书上,时隔一年后,还收到一两个喜欢,看来还有那么一点点价值,于是就搬过来好了。

初始化服务器环境

yum -y install vim git screen python-pip
curl -L https://github.com/robbyrussell/oh-my-    zsh/raw/master/tools/install.sh | sh
pip install virtualenvwrapper gunicorn django
mkdir /usr/local/virtualenv
mkvirtualenv python

配置gunicorn服务器

Gunicorn(gunicorn.org)是一个Python WSGI UNIX的HTTP服务器。,从Ruby的独角兽(Unicorn)项目移植。

在Django项目下建立shell脚本

#!/bin/bash
set -e 
LOGFILE=guni.log
LOGDIR=$(dirname $LOGFILE)
NUM_WORKERS=3  # cpu core nums * 2 + 1
USER=nobody
GROUP=nogroup
# WORKER=gevent # install python gevent
ADDRESS=127.0.0.1:8000
test -d $LOGDIR || mkdir -p $LOGDIR
exec gunicorn_django -w $NUM_WORKERS --bind=$ADDRESS \
 # -k $WORKER 
  --daemon \
  --user=$USER --group=$GROUP --log-level=error \
  --log-file=$LOGFILE 2>>$LOGFILE

安装Nginx用作反向代理与静态服务器

  • 使用http://lnmp.org/ 安装Nginx
  • 用Lnmp一键安装包带的脚本新建一个虚拟主机
  • 更改主机的Nginx配置文件

      server {
    
          listen       80;
          server_name  nuptapi.nupter.org;
          access_log   /home/wwwlogs/nuptapi.nupter.org.log;
          error_log    /home/wwwlogs/nuptapi.nupter.org.error.log;
          root /home/wwwroot/nuptapi.nupter.org;
    
          location  /static/ {
              alias /home/wwwroot/nuptapi.nupter.org/static/;
          }
    
          location  / {
              proxy_pass            http://127.0.0.1:8000;
              proxy_redirect        off;
              proxy_set_header      Host             $host;
              proxy_set_header      X-Real-IP        $remote_addr;
              proxy_set_header      X-Forwarded-For  $proxy_add_x_forwarded_for;
              client_max_body_size  10m;
          }
    
      }
    

用Supervisord守护进程

  • echo_supervisord_conf > /etc/supervisord.conf
  • vim /etc/supervisord.conf

更改配置文件以下的内容

  • chown=lxy:lxy ; socket file uid:gid owner
  • [program:nuptapi] command=/home/wwwroot/django_helloworld/gunicorn_start.sh

  • Shell运行supervisord

  • 同时supervisord 默认会在9001端口打开一个HTTP服务器,可以在Nginx再配置一个反向代理来远程登陆

其它

  • 配置DNS服务器,解析域名A记录到服务器IP
  • 重启Nginx

关于某App外包开发心得-Android篇(二)

  • 作者: 王涛

项目开发心得——合作篇

第一次做有规模的项目,跟之前的写些小东西相比,主要区别是:那人钱财,给人办事。但是既然是项目,我们就得以集体利益,也就是保质保量的完成东西为最终目标不是?

  1. 与客户交流

    1. 需求明确
      1. 95%以上的需求要定

      2. 改需求 = 做重复劳动 –> 程序员最难受的事情

    2. 阶段交付
      1. 明确交付时间

      2. 阶段交付,确保和客户的需求没有大的偏差

  2. 与UI交流

    1. 设计稿要程序参与
      1. 设计没有代码经验,难免会做出很多让程序心碎的设计

        这里最好熟悉AndroidUI设计推荐风格,因为只有越符合标准,越节约体力不是?

      2. 相同的功能设计的方式有很多种,有程序的参与会选出既好实现又符合需求的设计

      3. 各个部分的效果要讨论明确,不然程序会做画蛇添足的事

        例如我就把一个Item按钮写成了N个按钮,而且不只是多多了几个按钮那么简单

    2. 简洁设计,但要完整
      1. 能够表达清楚叫好了,没必要太花哨

      2. 面面俱到,只要是不用原生的地方都要给出设计资源,这里如果设计师没能考虑周全,我们程序就要做善意的提醒了

    3. Android多平台设计需求
      1. Android最蛋疼的分辨率适配,要明确需要适配的种类,

      2. 这时候就需要设计师要针对各个分辨率设计包括:

        1. 字号,这里需要说一下,建议不要设计太多种类的字号,想清楚要突出的部分,各个部分的之间的关系

        2. 色系,建议不要太花,定好基调,保持风格一致。

        3. 布局,Padding,Margin,border,corners

        4. 字段,要显示的内容,要明确,不然又要重复劳动,看到这四个字就浑身难受!

    4. 图片资源交接
      1. 发送方式

        1. trello.com是个很好的平台,可以用来上传下载图片,很方便,便于分类管理。

        2. 邮件,速度快,便于保存,但不好分类管理,文件多了容易找不到。

      2. 文件夹命名

        1. 建议根据所属页面命名,按层级分类

        2. 建议用英文例如:Home_Bottom_RadioButton

      3. 文件命名
        1. 必须用英文,如果设计用汉字,不仅有乱码风险,而且还得挨个换成英文,累死!

        2. 由于大家对英文单词理解不同,最好先讨论下

  3. 与Web后台交流

    1. API的字段要交流好。这就要建立在:需求确定 –> 设计显示内容确定 –> 程序实现方式确定 –> API的字段交流

    2. API说明文档要交流,不然容易忽略重要内容

      有个API是数据库要用户信息,我想当然以为是GET,但其实是POST,若是Web后台有特别考虑,要特别说明,不然会出现莫名其妙的错误

    3. 更新API要通知,不然会突然找不到数据。尤其是有一次被Web后台坑了,同样的字段,名字却打错了,害我调试好半天

  4. 与客户端合作者交流

    1. 项目开始前的计划:

      1. 项目结构:

        1. 代码:Model,Adapter,Activity,View,Utils,Constant

        2. 资源文件:

          1. 定好各个色值,字号,便于调用且统一

          2. 为了适配,建不同分辨率的资源文件,例如

            • values
            • values-mdpi
            • values-hdpi
            • values-xhdpi
          3. 为了适配,要用demin

          4. 若控件很多且相似,可以用style让代码量减少

      2. 模块划分:这次做的信息类应用,大致上以页面划分

      3. View:下拉菜单,Dialog,Toast,写好接口,方便使用

      4. Utils:应用中的某些部分可以做成工具类,文件操作,用户登录信息操作,网络连接检查等

      5. Constant:应用中的字符串常量

    2. 开发中遇到变化

      1. 几个人同时改动了一个类要注明,例如://Foci Add

      2. 了解别人的代码,为了学习,也为了整个项目更好开发

关于某App外包开发心得-iOS篇(一)

  • 作者 : 林翔宇

前不久和朋友一起合作了一个外包App项目,我负责iOS和后台Server,另外两名同学合作开发Android端。现在项目已经进入收尾上架工作,我将就开发过程中的一些感想与收获进行总结。

使用CocoaPods做依赖管理以及开发用到的第三方开源项目

CocoaPods真是依赖管理神器,比Andorid的Maven或者Gradle方便多了。具体试用可以看看这篇文章唐巧-用CocoaPods做iOS程序的依赖管理

而且最近CocoaPods的Spec有了第三方国内镜像,速度快多了,为什么之前开发的时候木有,加一个库要等半天(ノДT) 。 所以我之前修改Podfile后是这样更新的(不更新Spec库并且有提示):

1
pod install --verbose --no-repo-update   

我项目里面的第三方依赖有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

    #import <AVOSCloud/AVOSCloud.h> // AVOSCloud SDK
    #import <MAMapKit/MAMapKit.h>   // 高德地图
    #import <AFNetworking/AFNetworking.h> // AFNetworking 网络通信   

    #import <UIColor+ChineseColor.h> // 自己的项目复用模块
    #import <MMCommon/MMCommon.h>  

    #import <SVProgressHUD/SVProgressHUD.h> // 加载提示的HUD

    #import <Underscore.m/Underscore.h> // Objective-C的类似JavaScript的Underscore.js的辅助库
    
    #import <UMengAnalytics/MobClick.h>  // 友盟统计和SNS集成
    #import <TencentOpenAPI/TencentApiInterface.h>
    #import <TencentOpenAPI/QQApiInterface.h>
    #import <TencentOpenAPI/TencentOAuth.h>
    #import <UMengSocial/UMSocialData.h>
    #import <UMengSocial/UMSocialSnsService.h>
    
    #import <SDWebImage/UIButton+WebCache.h> // 异步图片加载
    #import <SDWebImage/UIImage+GIF.h>
    #import <SDWebImage/UIImageView+WebCache.h>
    #import <SDWebImage/UIImageView+WebCache.h>

不得不提的是使用中。。友盟以及SNS集成过程中,不够清楚的文档以及那些坑的地方,还有调试了几天才发现是SDK悄悄升级,之前的SDK不能用的情况。

在使用的时候,为了更优雅的进行依赖管理,我也为高德地图和友盟SNS创建了Podfile并且提交到了CocoaPods/Spec库里面,需要的童鞋可以看看https://github.com/oa414/Specs/commits/master

我们同时用了AVOSCloud与友盟,这两个第三方开发者平台功能有很多重复,但是当前各有长处。未来可能迁移到其中一个。

iOS MVC与界面布局的一些实践

最初的时候,为了快速开发,全部使用了Storyboard,这让我在一个小时就把几十个页面跳转关系全部做好了。。。而且可以作为原型演示给UI童鞋和Android童鞋看。到后来,发现Storyboard管理几十个页面完全是坑,很多东西代码写更方便,并且即使是23寸的显示器在Interface Building里面拖动各个View也好麻烦。。。于是把这个Tab布局的应用按照五个Tab拆成五个StoryBoard了。主要用代码做布局和跳转,同时取巧用了StoryBoard一些方便的地方。

把各个Tab对应内容分离到不同的StoryBoard的方法:(代码好丑)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

+(UIViewController *)initViewController :(NSString *)storyBoardName
                            identifier:(NSString *)identifier
                              iconName:(NSString *)iconName
                                 withTag:(int) tag{
    
    UIViewController *vc = [SXAppDelegate viewControllerWithStoryBoard:storyBoardName identifier:identifier];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
  
    [nav.tabBarItem setImage:[SXAppDelegate getIconImage:iconName]];
    
    nav.tabBarItem.image = [[SXAppDelegate getIconImage:iconName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

    
    UIImage *selectImage = [SXAppDelegate getIconPressedImage:iconName];
    selectImage = [selectImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    DebugLog(@"select Image %@", selectImage);
    [nav.tabBarItem setSelectedImage:selectImage];
    nav.tabBarItem.selectedImage = selectImage;
    [[UITabBar appearance] setTintColor:[SXAppDelegate getNavColor:iconName]];
    
    nav.tabBarItem.tag = tag;
    
    [nav.tabBarItem setTitleTextAttributes:@{
                                            NSForegroundColorAttributeName : [SXAppDelegate getNavColor:iconName] }     forState:UIControlStateSelected];

    
    
    NSDictionary *titles = @{
        @"home" : @"首页",
        @"product":@"产品",
        @"news": @"新闻",
        @"my": @"我的",
        @"more": @"更多",
    };
    [nav.tabBarItem setTitle:titles[iconName]];
    return nav;
}


    self.tabBarController = [[UITabBarController alloc] init];
    
    self.regVC = [SXAppDelegate initViewController:@"My" identifier:@"login" iconName:@"my" withTag:3];
    self.userVC = [SXAppDelegate initViewController:@"My" identifier:@"index" iconName:@"my" withTag:3];
    self.indexVC = [SXAppDelegate initViewController:@"Main" identifier:@"index" iconName:@"home" withTag:0];
    self.productVC = [SXAppDelegate initViewController:@"Product" identifier:@"index" iconName:@"product" withTag:1];
    self.newsVC = [SXAppDelegate initViewController:@"News" identifier:@"index" iconName:@"news" withTag:2];
    self.myVC = self.regVC;
    self.moreVC = [SXAppDelegate initViewController:@"More" identifier:@"index" iconName:@"more" withTag:4];
    
    DebugLog(@"Splash  %@", [SXSplashHelper splashImage]);
    
    if ([SXUser currentUser] == nil){
        [self setRegView];
    }else {
        [self setUserView];
    }
    
    self.window.rootViewController = self.tabBarController;

有几个常用的宏可以和大家分享下:

Debug环境下输出Log与行号

1
2
3
4
#ifdef DEBUG
#define DebugLog( s, ... ) NSLog( @"[FILE]%@ %*s [LINE]%-*d [METHOD]%@ %*s [MESSAGE]%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent],30 - ([[[NSString stringWithUTF8String:__FILE__] lastPathComponent] length]),"", 5,__LINE__, NSStringFromSelector(_cmd), 75 - ([NSStringFromSelector(_cmd) length]),"", [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )

用RGB生成UIColor

1
2
3
4
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

获取App版本号

1
#define APP_VERSION ([[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"])

判断是否是4寸Retina屏幕

1
IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )

让你事倍功半的开发工具

我觉得有一个外接液晶屏幕很重要。。。

此外是一把比较好的椅子。。

此外,推荐大家试试http://revealapp.com/, 一款帮你轻松调试界面的App,收费软件,有试用版本,可以免去改参数-编译-点击查看对应View-改参数-编译的麻烦流程,直接在Reveal里面修改参数就能看到对应效果

iOS Developer账号的那些坑 与 提交审核

iOS Developer 账号

如果希望认真学习开发iOS App,加入iOS开发者计划是必不可少的。当初为了真机调试,又是找破解Xcode的教程又是在淘宝上买开发者证书,最后还是靠亲爱的妈妈特地办了一张Visa信用卡乖乖付钱交了99美元年费。

但是并不是乖乖叫了钱就一番风顺了。创建一个应用,你需要在开发面板上分别配置开发者证书,应用ID,调试设备,推送证书。。。并且绑定对应调试设备和应用ID。。。推送证书还分开发设备的Sandbox环境和Production环境。。。

相关具体设置可以参见苹果开发者账号那些事儿

测试分发

据说TestFlight很不错。。。最近被Apple收购了,我在使用过程中也感到各种不便。不过一些在应用内反馈给测试人员的特性还不错。

最后我发测试包用的是Fir.im,感觉还不错。另外AVOS也提供了相应的功能,值得一试。

同时我也用一个晚上时间做了一个Android APK分发的Demo,放在 Github上,希望有空能完善。

关于某App外包开发心得-Android篇(一)

  • 作者: 戴志强

前段时间开发了人生的第一个外包App,相对平时做的一些App,有了一定的压力,而且有了更高的要求,不仅仅要有功能上的实现,而且有了更多其他的要求,比如手机的适配,改不完的要求,以及无良android商家对android的修改,比如小米,简直无力吐槽。最后统计java代码量达到了9991行,虽然java比较繁琐,而且我们技术有限,接口以及代码复用做的不太好,但是对于我们还是一个比较大的项目,然而关于这样一个项目是怎么样生成的,希望能给到大家一点启示。

项目功能的初步建模

一个比较大的项目,不管是外包,还是自己想做的,都会有一个大概的功能框架,虽然说做出模型图片,不是程序员的任务,但是通过这次项目,我深深的认识到,一个android的原型图,至少应该派一个程序员与美工(UI设计师~)进行交流,不然他们设计出来的菜单等级,以及酷炫的风格会让你蛋疼无比,然后还会丢下一句“说这么多干什么,不就是做不出来吗?”,为了你的代码难度在你的一定掌握范围内,所以一个靠谱的任必须去和美工一起定原型图,这个非常重要,因为android是没有做到绝对的界面与代码分离,而且基本上美工不懂android布局,所以他们完全不能认识到设计出来的布局到底有多大的难度,这是我遇到的第一个坑。。。

项目功能的考究

终于解决了基本模型的问题,接下来就是功能,无论你是做什么样的软件,功能是离不开的,举个例子,“掌上南邮”我虽然没有参与做,但是看到这个软件,大家都会知道功能有哪些,比如进入界面有个天气预报,用了webservice,接下来的图书馆,用了android中http传输信息然后jsoup爬取图书馆信息,等等。对这些功能做了基本分析,可以将这些技术点分个类,较简单,难,很难,这样你的侧重点,当然还有开发中可能遇到的坑,不要小瞧这一点,一个坑可以让你白忙活一天,还有太难的地方必须有一定的规划,不然就不只是浪费时间了。

项目页面分配

这个也是一个问题,现在做android应用,人多会有,项目结合以及理解性的偏差,所以我的建议是,“能一个人做的不要两个人”,这个真的是很重要,人多力量大主要体现在搬砖,在这里人越少你的代码的统一性更能体现,而且很多的地方都在你的控制范围内,比如数据方法封装。。。但是要两个人或者更多了,这个时候github,trello等作为团队合作的神器就来了,更重要的是分配任务时要注意尽量将粘合度比较低的界面分离出去。关于工具类以及接口的整合,我觉得可以先几个人用自己的类写,然后重构时再合到一起,或者还可以让一个比较靠谱的人去写工具类,注意要比较靠谱,因为他的速度要能跟上其他人的速度,为他们提供方法和接口。

项目工程的建立

大家终于要告别一个com.example.Hello写完全部类的时代了,一个好的工程目录可以清晰的看出这个项目的结构,一般不要以com.example中的example因为你提交你的android app到应用市场,他会读出你的包名,如果是example有的是不会接受的。

举个项目目录例子:

org.clownxiaoqiang.test
├── adapter
├── util    
├── widget
├── model     
└── ui
    ├── flash
    ├── home     
    ├── shop
    ├── me
    └── more

大家看到的这个例子,分了很多的目录其实还不算标准,在ui下面有了很多,这个就是我们看到的各种界面,一个大的集合为一个,这样不同人在开发时就不会干扰,而且很有序,其实一开始工作做好了,后面的代码重构会简单很多,这里有一个个人开发的rubychina4androidandroid app,大家可以看下他的工程目录,学习一下。

后记

一个好的写代码习惯也是非常重要的,比如命名,减少冗余代码等,不要小瞧任何一点,同时不要受身边的人干扰,不要认为写android就会低端,如果你能在这条路上做到极致,你也是牛X的,如果再有人对你炫耀他是做底层的,你就把手机丢他脸上,让他自己写app装上去。还有,一个靠谱的包工头,简直太TM重要了。

Android入门杂谈

作者: 林翔宇

入门

配置环境

  • 请在官方网站下载对应的JDK包安装。注意你的系统版本是32位还是64位的,分别下载对应的版本。如果是Windows用户,请尽量按默认选项安装,Mac / Linux用户请用对应的包管理工具安装。这一步出现问题,网上会有很多解决方案。
  • Android官方网站下载Android Studio或者ADT Bundle, 分别对应基于Intellij IDLE和Eclipese的Andorid开发IDE,解压就能使用。

学习语言

  • 你需要学习叫Java的一门编程语言,仅需入门,明白基础概念,并能写一些非常简单的小程序就可以了
  • 学到哪里足够了呢?知道if, for, while , 分别是用来干什么的, 知道类,对象,方法是什么东东, 最好能理解抽象类(abtract class)和接口(interface)这种奇怪的东西存在的原因~
  • 书籍推荐 《Head First Java》,适合有任意一门语言基础的,知道if, for, while 以及函数分别是用来干什么的孩子学习。
  • 你并不用十分深入的去学习Java,可以在边学Android的同时学习Java,两者的学习相辅相成。注意:如果你只是想写Android或者Java语言本身,你不必去接触J2EE那些庞大的知识体系,那些和Android基本没什么关系。
  • 不想用Java写Android?当然可以。
    • 你可以选择一些商业的第三方游戏引擎自带支持的语言写Android程序
    • 熟悉.Net? 试试 Xamarin.Android
    • 熟悉Scala或者Clojure? 这些基于JVM的语言也都有对应的写Android程序的方案
    • 是一个Web开发者? PhoneGap, Jquery Mobile等相关的开源框架在朝你招手。借助HTML5的新特性与强大的基于Webkit的浏览器内核,你也可以用Web前端技术写出不错的Andorid程序。
    • 但是话说回来,绝大多数Android程序还是用Java进行开发,并且用Java开发Android的资料很多很多。所以,尽量还是使用Java吧~

Android入门

  • 官方网站有一整套的Android Tranning 入门教程, 质量很高而且都是官方的最佳实践,强烈建议一看。
  • 当然官方教程门槛对于0基础的初学者还是有点高,此时建议买一本书学习Android的基本概念和基本入门。毕竟有本书的感觉是不一样的。。。推荐: Apress出版社,Wrox出版社, Orelly出版社的书,国内也引进了一些翻译版本。强烈建议看国外的书,至少能保证条理清晰。

入门到初步掌握

我建议按照以下步骤来

  • 学习最基本的Android概念,会写Hello World,用XML写最简单的布局。
  • 找一本cookbook类型的书,也就是每一小节都带你实现一个小小的功能,让你每次都能接触到新鲜的东西,比较有成就感,而且同时巩固基础。
  • 整理Android概念,比如重新回过头看看Android Tranning 入门教程,搞明白Activity, Service, Content Provider, Broadcast到底是什么东西,比较熟练的掌握一些常用的功能,比如按钮点击时间,对话框提醒,文件保存,网络资源获取等等。
  • 开始做一些小东西,阅读大量的优秀应用的源代码,学会使用开源库, 阅读Android Developer的 Training, API Guide以及 Tools。具体哪个类不明白的话,就去Reference查
  • 最后说一句,这只是个人实践得出来的感觉不算差的方法,我也打算尝试以另一种方式培训0基础的有兴趣的同学,具体的方法可以自己选择,但是我强烈建议2点
    1. 生命宝贵,有限的时间应该用来约漂亮姑娘(或泡帅哥),游玩,享受人生,而不是贪图方便看一些国内无厘头或者水分很多的资料
    2. 你投入的时间和你的收获正相关,只要付出努力终有回报

资源

善于利用现有的资源,善于使用第三方开源库

善于在网上寻找答案

  • 请务必使用Google而不是百度
  • 学会使用stackoverflow, 这里有几乎所有细节问题的解决方案

善于获取最新资讯

  • 关注Google/Android官方的资讯
  • 订阅AndroidDevWeekly,每周会发一封最新的资讯到邮箱

发布

用户追踪,反馈与推送通知

用户追踪和反馈可以自己实现客户端功能与服务器端,也可以使用第三方的服务。比如友盟, Parse 等。

因为谷歌很多服务,包括推送通知在大陆是被封掉的,建议使用极光推送,Parse的SDK。非常不建议用后台进程轮询的方法查询,费电费流量。

发布之前

Android市场现状

  • 国内发布Android应用很纠结,机锋,安卓,安智,木蚂蚁,应用汇等等。没有特别的方法,多注册几个账号吧。
  • 强烈建议注册Google Play开发人员,应用无需审核马上就能上架,面向全球市场。注册需要VISA/Mastercard的国际信用卡,以及一次性25美金的费用。

盈利

这里只谈个人开发者或业余小团队的盈利方式

Android国内收费应用市场很难发展,如果做游戏等应用可以考虑应用内购买的方式。普通应用的盈利方式有

  • 广告条。收入低,但是也是一种方式。国内有各种广告商,但是个人推荐Admob
  • 做外包项目。通过关系网接外包项目,或者上ElanceFreelancer, Odesk接国外外包项目,十分不推荐上猪八戒这些国内网站接项目,市场规范程度和收入远低于国外

参加搜狐比赛有感

作者:苏东生

去年的11月,搜狐公司举办了2014“比武招新”比赛,比赛主要分三个环节:“夺”宝典、“闯”奇关、“攻”擂台,最终优胜者将获得现金大奖和实习offer的奖励。上个学期我和两个老乡一起参加了比赛并且最后拿到了比赛的一等奖,我们自己都有些预想不到,本来只是抱着尝试的态度参加比赛而已。好吧,长话短说,现在跟大家分享一下我参加这次比赛的一些经验和收获吧。

首先是“夺”宝典环节,比较简单,其实也可以算做是一次海选,这个只要你用心按照它的程序去做就能过关,没什么好说的,主要就是让你了解搜狐公司的一些历史和这次比赛的详细情况。

接下来是“闯”奇关环节,也就是所谓的初赛了。初赛的内容有3种选择:一是针对搜狐新闻客户端(android和ios)的产品优化方案,二是搜狐视频客户端的产品优化方案,三是pc端搜狗浏览器的产品优化方案。我们选择的是做搜狐新闻客户端的优化方案,其实当时之前我都没用过android的搜狐新闻客户端,有点坑爹的赶脚,只好临时抱佛脚,把所有的新闻客户端都下了下来仔细的玩了个遍,从各个新闻客户端的特点,优缺点等方面分析,这样我们就开始了有了取长补短的一些观点。接着我们又从它的价值观定位等角度出发考虑提出建立以优质内容为主的新闻客户端生态圈,针对订阅的大板块的优化,提出了深度报道的观点深入挖掘新闻的价值。当然这只是一方面,既然是写优化方案,我们肯定是要考虑得更加长远,所以我们当时从它的发展趋势角度考虑创新性地提出加入SNS化因素,并做了一大堆细节上的考虑,包括流程,图片等。当然说了这么多杂七杂八的东西,也不能就只是一个凭空的想象,要考虑实现你自己观点的技术难度等问题,这就是做为技术人员的我该干的事了。最后总结一下比赛的心得,我认为主要是以下两方面:1.细节 2.创新

说了那么多理论上的东西,你要让别人如何信服你的观点呢,凭什么你的方案就比别人好。并不是你觉得可行它就可行的,没有点群众的基础怎么能行得通呢。所以接下来就是实际行动的时候了,我们做了一份调查问卷并且跑到教学楼大教室收集了200份样本,统计数据并且分析结果。

最后就是最为关键的“攻”擂台环节了,比赛地点也转移到了杭州的浙江大学,队伍到这里也只剩下了4支,除了我们之外的另外3支都是浙大本校的研究生。只有我们是大老远跑过来的本科生,听着这名头都有点惊呆了,压力山大啊。。现场决赛主要有这么几个环节,小牛试刀,即4组同学做各自方案的展示,为了更直观展示我们的观点,我们在最后还做了一个小品类的场景呈现。接着的搜狐论剑,4组队伍两两PK,找出对方方案中的漏洞,感觉有点像辩论赛啊(自信很重要)。。。最后就是评委拿奖的事了。

回想一下这次的比赛,之所以能拿到一等奖,最后经理也点评了,我们在提出任何一个观点的时候,都下足了功夫,做足了功课,并不会说就只是一个口头上的想象而已。正所谓知己知彼,百战百胜。我们有一个观点是增加一个本地新闻栏目,正好经理说在下一个版本里已经加入这个模块了,加上我们有他们其他三个队伍没有的东西,那就是调查问卷,所以就显得更有说服力了。(虽然这更多只是一种形式上的东西,毕竟我们的调查问卷基本都是针对学生,而且有挺多人都是随便写的,很有局限性)但对于比赛来说,这就是经理看中的地方,也是我们最终胜出的一个地方。

所以,做什么事情都不要只是想想或者是口头上说说而已,更多的是要你的实际行动,需要你做足准备,知己知彼,才能百战百胜。

How to Install Ruby on Rails on CentOS 6.5 Using Rbenv

  • Author: Lin Xiangyu

Introduction

Ruby on Rails

Ruby on Rails is one of the most popular web development framework, it’s build upon Ruby Programming Language, and it’s the hottest web development stack currently.

Rbenv

Rbenv is a shell script tools created by Sam Stephenson. It’s used for groom your app’s Ruby environment.Use rbenv can pick a Ruby version for your application and guarantee that your development environment matches production.

rbenv works by inserting a directory of shims at the front of your PATH:

 ~/.rbenv/shims:/usr/local/bin:/usr/bin:/bin

Through a process called rehashing, rbenv maintains shims in that directory to match every Ruby command across every installed version of Ruby—irb, gem, rake, rails, ruby, and so on.

CentOS

CentOS is derived from Red Hat Enterprise Linux. The target users of these distributions are usually businesses, which require their systems to be running the most stable way for a long time.So we are going to use CentOS 6.5 running our applications.

Step One – Install dependencies

Before, installing any package, it’s always recommended to update package repository cache use yum.

 sudo yum update

Now,in order to get necessary development tools and dependencies, run the following:

 sudo yum groupinstall -y 'development tools'
 sudo yum install -y gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel  sqlite-devel  

Step Two – Install Rbenv and ruby-build

Then we are ready to get Rbenv downloaded installed, run the following to check out rbenv into ~/.rbenv:

 git clone git://github.com/sstephenson/rbenv.git ~/.rbenv

Add ~/.rbenv/bin to your $PATH for access to the rbenv command-line utility:

 echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

Add rbenv init to your shell to enable rbenv shims and autocompletion.

 echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

Ruby-build is a Rbenv plugin which provides the rbenv install command that simplifies the process of installing new Ruby versions. Install rbenv-build:

 git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

reloaded your bash_profile to enable rbenv command:

 source ~/.bash_profile

Step Three – Install Ruby

Install Ruby 2.1.0 and make it the default

 rbenv install 2.1.0
 rbenv rehash
 rbenv global 2.1.0

Now you can run:

 ruby -v

to verify your ruby environment has been installed successful。 It will output something like this:

 ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]

Step Four – Install Nodejs

Ruby on Rails need a JavaScript runtime support. It use execjs gem which can automatically picks the best runtime available to evaluate your JavaScript program, then returns the result to you as a Ruby object.

ExecJS supports these runtimes:

  • therubyracer – Google V8 embedded within Ruby
  • therubyrhino – Mozilla Rhino embedded within JRuby
  • Node.js
  • Apple JavaScriptCore – Included with Mac OS X
  • Microsoft Windows Script Host (JScript)

So we can use therubyracer gem or Node.js in with our CentOS and MRI Ruby. In this guide we use Nodejs as JavaScript runtime.

Node.js is available from the Fedora Extra Packages for Enterprise Linux (EPEL) repository.

Extra Packages for Enterprise Linux (or EPEL) is a Fedora Special Interest Group that creates, maintains, and manages a high quality set of additional packages for Enterprise Linux, including, but not limited to, Red Hat Enterprise Linux (RHEL), CentOS and Scientific Linux (SL), Oracle Enterprise Linux(OEL).

To check if you have EPEL, run

 yum repolist

if you don’t see epel, install it via RPM

 rpm -Uvh http://download-i2.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

And then run the following command to install node:

 sudo yum install nodejs --enablerepo=epel

Step Four – Install Rails Gem and test it.

Rails 4.0 needs RubyGems 2.0.3, so you have to update your system by using following command

 gem update --system 2.0.3

Now, you can install the rails gem

 gem install rails
 rbenv rehash

Test your rails:

 rails new projectname
 cd projectname
 rails server

now open your browser and open http://your-server-ip:3000,you can find the rails project default page.

Puma: 一个为并发构建的Ruby服务器

本文翻译自Puma官方文档,同时发布于Github

译者:林翔宇

概述

Puma是一个简单,快速,基于线程,并且高并发的面向Ruby/Rack程序的 HTTP 1.1 服务器。Puma同时面向开发和产品环境。为了达到最高的效率,推荐使用一个实现了真正线程的Ruby实现,比如Rubinius或者JRuby。

为速度和并发构建。

Puma是一个简单,快速,基于线程,并且高并发的面向Ruby/Rack程序的 HTTP 1.1 服务器。它可以用于任何支持Rack的程序,可以取代Webrick或者Mongrel。它是为了Rubinius而设计的服务器,但是同样可以工作在JRuby和MRI下。Puma同时面向开发和产品环境。

Puam使用了一个继承自Mongrel的C优化的Ragel扩展,来快速并且精确地解析HTTP1.1请求(并且便于移植)。之后,Puma将这些请求放到一个你可以控制的内部线程池的线程之中,所以Puma为你的web应用提供了一个真实的并发环境。

在 Rubinius2.0 中,Puma会用真实的线程利用你所有的CPU核心,这意味着你不用使用多个进程来增加吞吐量。你可以看到在JRuby上类似的优点。

在 MRI中, 因为有全局解释器锁的存在(GIL),同时只能有一个真实的线程在运行。但是当你在做许多IO阻塞的事情的时候(比如HTTP调用像 Twitter的外部API的时候),Puma仍然会通过运行并发运行阻塞的IO调用增加MRI的吞吐量。( 基于 EventMachine的服务器,如Thin会关掉这个功能,要求你只能使用特别的库)。每个人的选择因实际的应用而异。为了达到最大的吞吐量,强烈推荐使用一个实现了真实线程的Ruby实现,比如Rubinius 或者 JRuby

快速入门

最简单的使用Puma的方式是使用 RubyGems:

$ gem install puma

现在puma命令已经加入到你的环境变量里了,在你的应用程序根目录下运行下面命令启动你的Rack应用:

$ puma app.ru

高级步骤

Sinatra

你可以用下面的命令用Puma运行你的Sinatra应用

$ ruby app.rb -s Puma

或者在你的应用中设置总是使用Puma

require 'sinatra'
configure { set :server, :puma }

如果你使用 Bundler,确保你把Puma加入了你的Gemfile中(步骤见下面)。

Rails

首先,确保你把Puma加入了你的Gemfile中。

gem 'puma'

然后用Rails命令启动Puma

$ rails s Puma

Rackup

你可以把Puma作为rackup的选项。

$ rackup -s Puma

你可以改变你的 config.ru 来默认选择Puma服务器,加入下面的这行:

#\ -s puma

配置

Puma提供了很多控制服务器器运行的选项,完整列表请查阅puma -h (或者 puma --help)

线程池

Puma利用了一个你可以改变的动态线程池,你可以通过-t (或者 --threads) 选项:设置最小和最大的线程池里的可以使用的线程数量

$ puma -t 8:32

Puma会动态的根据当前的负载改变线程数量,默认的数量是0:16, 大胆的尝试吧,但是不要把最大线程数量设置的太大,这会耗尽系统资源。

集群模式

Puma 2 提供了集群模式 , 允许你使用 fork出来的进程处理并发处理多个请求。 你可以用-w(or—workers`) 参数调整运行的work进程数量。

$ puma -t 8:32 -w 3

在提供真实线程的Ruby实现中,你应该把这个数字调整到和CPU核心数量相同。

注意集群模式下仍然是使用线程的, -t 选项设置每个 worker的进程数量,所以 -w 2 -t 16:16 可能开启了32个线程。

如果你在并发模式你可以选择在启动worker进程前预加载你的应用。如果想利用MRI Ruby 2.0推出的Copy on Write 的优点的话,这是必要的。只需要调用的时候指定一个 --preload 参数:

# CLI invocation
$ puma -t 8:32 -w 3 --preload

如果你在使用配置文件,使用preload_app!方法,并且使用-C参数指定配置文件:

$ puma -C config/puma.rb

# config/puma.rb
threads 8,32
workers 3
preload_app!

此外,你可以在配置文件里面定义一个代码块,来在每个工作线程启动的时候运行:

# config/puma.rb
on_worker_boot do
  # configuration here
end

这些代码可以用来在启动应用的时候初始化进程, 允许你做一些和Puma服务器有关,但是不和应用结合的事情。比如,你可以发送一个日志通知Puma启动了。

你可以多次增加钩子调用。

如果你使用ActiveRecord预加载你的应用, 推荐你在这里这样初始化你的连接池:

# config/puma.rb
on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

绑定 TCP / Sockets

相比较其他需要很多配置参数的服务器,Puma仅仅通过-b (或者 --bind)选项使用一个URI参数:

$ puma -b tcp://127.0.0.1:9292

想通过使用UNIX Sockets来取代TCP么(这能提高5-10%的性能),没问题!

$ puma -b unix:///var/run/puma.sock

如果你需要改变Unix socket的权限,只需要加入一个umask参数:

$ puma -b 'unix:///var/run/puma.sock?umask=0777'

希望更加安全?使用 SSL sockets!

$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'

控制/状态服务

Puma提供了一个内置的控制/状态服务app来提供对Puam自身的控制,这是一个打开Puma控制服务器的样例:

$ puma --control tcp://127.0.0.1:9293 --control-token foo

它会在localhost的9293端口打开Puma配置服务器。此外,所有的到控制服务器的请求需要包括一个token=foo 作为查询参数,作为简单的认证。更多app用法参见status.rb

配置文件

你可以同时使用一个-C (或 --config) 参数提供一个哦诶之文件:

$ puma -C /path/to/config

请参考 示例配置 或者查询 configuration.rb 查看所有的配置项目

重启

Puma有能力在更新版本的时候重启自己,当平台允许的时候(MRI, Rubinius, JRuby),Puma 进行热重启。这是和 unicornnginx 在重启的时候保持服务器sockets连接相同的。它可以保证重启的服务器替代旧的服务器的时候请求已经被处理。

Puma有2个内建的重启机制:

  • 发送SIGUSR2 信号到puma 进程
  • 使用 status server 和 issue /restart

当前和重启的进程不会共享代码,所以手动停止Puma并且关闭它也是安全的。

如果新的进程不能加载,它会简单的退出。你应该在生产环境下用一个监控程序运行Puma。

Cleanup Code

Puma不能理解所有你的App使用的资源,所以它在你用-C提供的配置文件里面提供了一个叫on_restart的钩子。传递给on_restart 会被Puma在重启自己的时候调用。

你应该在这个代码块里面关闭全局日志文件,redis连接等,这样它们的文件描述符不会在重启的进程里面泄漏。否则会导致文件描述符打开太多,当服务器重启次数很多的时候会导致应用崩溃 。

平台限制

不同的平台有差异,下列是Puma在不同平台上有差异的地方

  • JRuby, Windows: 服务器的Socket不能被无缝重启, 这些平台不能通过Ruby传递信息到新的进程
  • JRuby, Windows: 不支持集群模式 ,因为没有fork fork(2)
  • Windows: 不支持daemon mode ,因为没有fork(2)

pumactl

pumactl 是一个简单的CLI前端来控制或者查看上述的app状态。 请参考 pumactl --help

维护多个Puma / init.d / upstart 脚本

如果你想马上得到一个维护多个程序的脚本,请查看用于init.d 和 upstart脚本的tools/jungle

Capistrano 部署

Puma 已经被包括进 Capistrano deploy script, 你只要 require :

config/deploy.rb

1
require 'puma/capistrano'

然后

1
2
3
4
$ bundle exec cap puma:start
$ bundle exec cap puma:restart
$ bundle exec cap puma:stop
$ bundle exec cap puma:phased_restart

协议

Puma 的版权属于Evan Phoenix 和 贡献者. 它基于BSD 协议. 详见LICENSE文件。

Android反编译入门与反编译防范

作者: 林翔宇

反编译Java代码

参考http://www.oschina.net/question/54100_33457

文中给出下载链接版本较老,其中dex2jar可能会出现java.lang.OutOfMemoryError的异常。请去官网下载两个工具的最新版。

简单来说,用dex2jar把apk文件解压得到的classes.dex转化为jar文件,然后用JD-GUI打开这个Jar文件,查看源码。

反编译apk生成程序的源代码和图片、XML配置、语言资源等文件

同样参考http://www.oschina.net/question/54100_33457

使用apktool https://code.google.com/p/android-apktool/

混淆代码防范反编译

参考 http://blog.csdn.net/sunboy_2050/article/details/6727640

2014.3.4更新,感谢俱乐部苏东生同学在评论区提醒

新版的SDK在创建工程目录的时候已经提供了默认的模板,将project.properties的中“# proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt”的“#”去掉,再做一些定制就可以了。可以参考

http://blog.csdn.net/brokge/article/details/8989312

修改Android项目下default.properties文件,加上一句proguard.config=proguard.cfg。

当然同时目录下要有proguard.cfg文件,可以在android_sdk_path/tools/proguard/目录下找

其实似乎现在Android默认创建工程的时候就已经有了。。看一下default.properties注释就可以了。。。

注意

参考http://my.oschina.net/banxi/blog/55622

  • 当使用了除了android-support-v4这些API的时候,要添加相对应的声明
  • 可以让proguard帮我们忽略Log.d()这些语句

其他参考资料