最近一家叫Robinhood的创业公司推出了零佣金的股票交易服务公测,用户可以通过iOS上的专用App交易。
这家公司的两个创始人斯坦福大学毕业后去了纽约给华尔街的大型股票经济公司做交易系统,做了几年后突然有一天发现券商公司对每一笔交易付出的成本微乎其微,但要收取每个客户多达10美金的佣金。。。于是他们毅然决定辞职,回到硅谷创业去了。
公司打出的口号是就是零佣金,如果你交易的资金来源于自己,那么买入和卖出交易,以及银行转入和转出(不包括电汇)都是免费的。
零佣金对资金不那么充裕的年轻人来说非常有吸引力。那么问题来了,这家公司靠什么赚钱呢?
交易过股票的人大概都知道,券商会提供融资服务给流动资金不充足的投资者,融资交易(Securities Margin Trading)就是投资者以资金或证券作为质押,向券商借入资金用于证券买卖,并在约定的期限内偿还借款本金和利息。相应的还有融券服务,融券交易是投资者以资金或证券作为质押,向券商借入证券卖出,在约定的期限内,买入相同数量和品种的证券归还券商并支付相应的融券费用。所以Robinhood首先可以通过融资融券服务盈利;
对于投资者在Robinhood账户里的未购买股票的闲余资金,Robinhood可以通过金融市场获得一部分利息收入;
提供收费接口给小型的量化和对冲基金;
目前Robinhood已经完成了千万美元的风险投资,现阶段提供颠覆性的交易体验,吸引更多年轻的新投资者,赢得用户的信任显然更为重要。用户多了,盈利就不是问题了。。。

App界面
Android Library项目中如果使用Android Gradle plugin打aar包,通过maven依赖的库,或者是local依赖的aar都不会包含在生成的aar包里,如果项目是发布一个SDK,为了方便开发者使用,我们倾向于生成一个包含所有依赖库以及.so等文件的aar包。
通过反复研究和测试,以下Gradle脚本能满足需求,如果需要对代码运行ProGuard混淆,则需要使用Gradle 2.1
方法是为项目增加一个sub project(如pkg_project
)专门用于打包,该项目中build.gradle
内容如下:
apply plugin: 'java'
version = 1.0
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:19.1.0'
}
}
repositories {
mavenCentral()
}
dependencies {
compile project(':<your_library_project>') //此处填写需要打包的Android Library Project name
}
task sync_jars() << {
//把所有依赖的.jar库都拷贝到build/aar/libs下
copy {
into buildDir.getPath() +"/aar/libs"
from configurations.compile.findAll {
it.getName().endsWith(".jar")
}
}
}
task sync_aars(dependsOn:':<your_library_project>:assembleRelease') << {
//把所有依赖的.aar库里包含的classes.jar都拷贝到build/aar/libs下,并重命名以不被覆盖
def jar_name
def aar_path
def dest_dir = buildDir.getPath()+"/aar"
configurations.compile.findAll {
it.getName().endsWith(".aar")
}.collect {
aar_path = it.getPath()
jar_name = "libs/"+it.getName().replace(".aar",".jar")
copy {
from zipTree(aar_path)
into dest_dir
include "**/*"
rename 'classes.jar', jar_name
}
}
}
task fataar(dependsOn:[sync_aars, sync_jars]) << {
task (obfuse_classes_jar, type: proguard.gradle.ProGuardTask) {
//把build/aar/libs/*.jar混淆后生成build/aar/classes.jar
configuration "proguard.cfg"
injars buildDir.getPath()+"/aar/libs"
outjars buildDir.getPath()+"/aar/classes.jar"
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
libraryjars "${System.getProperty('java.home')}/Contents/Classes/classes.jar"
libraryjars System.getenv("ANDROID_HOME")+"/platforms/android-19/android.jar"
}.execute()
task (gen_aar, type: Zip) {
//把生成最终的aar包,注意libs目录需要被排除
def dest_dir = buildDir.getPath()+"/aar/"
baseName = "mysdk-all"
extension = "aar"
destinationDir = file(buildDir.getPath())
from dest_dir
exclude "libs"
}.execute()
}
拷贝文件
如果想事实看到拷贝的速度可以用rsync -aP
版本:Ver 0.3
在手机上我们需要持久化应用的一些数据(典型的如本地的设置信息),同时又希望能重装应用或换一台手机登录后能把这些数据再同步回来。业界有SyncMl标准,覆盖的功能很完善,正因为要保证兼容性,开源的实现都较重。如何借鉴这个标准自己来实现一个多端双向同步可扩展的功能呢?
App使用同步协议可以将原本必须在线操作的功能(如:删除一个联系人,修改一个联系人的备注信息)也可以在断网情况下完成。
我们假设一些前提:
- 同一时刻只有一端(iPhone,iPad或其他移动设备)能和服务器同步;
客户端和服务端的时间一致或误差较小;可在长连建立时通过协议记录时间差
- 客户端保存全量数据(对于客户端只保存部分数据的情况后面再做讨论);
应用场景
- 通讯录同步
- 最近联系人
- App客户端设置
- 最近会话列表
- 黑名单
- 群设置
- 群成员
- 用户的一些设置和开关
背景
开发一个App一般会生成内测版和正式版,甚至还会有不同渠道的版本,不同版本的配置可能会不一样,比如内测版会需要记录完整的日志。
Android手机对于同样的Application Id的App只能安装一个版本,如果我们需要同时安装内测版和正式版,就必须修改其中一个版本的Application Id。
解决方案
Gradle支持buildTypes和productFlavors两种定制方法,这里只介绍通过buildType的解决方案。通过productFlavors则可有效解决渠道包,arm,x86等分平台以及付费版和广告版的打包问题。
修改debug版的包名
配置如下:
android {
buildTypes {
release {
...
}
debug {
applicationIdSuffix '.debug'
...
}
}
}
安装第三方App方法1
- 准备一个U盘,注意用FAT格式(Windows能读写就OK)
- 将需要安装的App下载到U盘,文件后缀名必须是.apk
- 将U盘插入电视盒子,然后通过盒子自身带的文件管理App安装:如Magic Box:应用--> 本地播放,进入后选中U盘上的.apk文件即可安装
安装第三方App方法2
- 如果盒子不带USB盘,还可以用adb远程安装
- adb connect <your_magic_box_ip>
- adb install <you_app_to_install>.apk
直播和回放App
- 在电脑上用浏览器下载:http://app.shafa.com/shafa.apk 到U盘
- 按方法1安装好后,在“应用“中找到“沙发管家”
- 启动“沙发管家”,安装直播App:如“龙龙直播”,也有支持回放的App,如“电视猫视频”等。
问题背景
团队一起在开发一个Android项目,工程师有的使用Eclipse,有个使用Intellij IDEA,有的使用Android Studio。每个人安装的Android SDK build-tools可能都不一样,有的是19.0.3,有的是19.1.0,不同版本的build-tools对Gradle Plugin也有相应的要求,如19.0.3对应的是com.android.tools.build:gradle:0.10.+,19.1.0对应的是com.android.tools.build:gradle:0.12.+,下面是一个典型的build.gradle配置文件。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.10.+'
}
}
apply plugin: 'android-library'
android {
compileSdkVersion 19
buildToolsVersion 19.0.3
defaultConfig {
minSdkVersion 8
targetSdkVersion 19
}
}
Android的线程和内存模型
Android操作系统在boot后,会启动一个Zygote(受精卵)进程,Zygote进程负责创建大部分应用程序进程。Zygote进程启动加载核心程序库和数据结构到内存后会创建一个Dalvik虚拟机(DVM)进程--SystemServer,此进程会包含大部分的系统服务(包括管理Activity的服务ActivityManagerService),SystemServer初始化后,Zygote进程会侦听本地的socket端口, 等待进一步的指令。当新的app被启动时,Zygote会为这个app创建一个DVM----直接fork出一个子进程,这种架构的好处是同时启动多个App时,多个App进程可以访问共享内存。

Android App的进程也是一个DVM,内部有许多线程在执行,比如,主UI线程(Main Thread),垃圾回收线程等。其中主UI线程负责执行我们写的应用代码。对于只做很少的I/O操作或耗时操作的App,单一线程开发模式问题不大,但是如果有大量IO或者CPU计算的任务,我们就必须在其他线程内完成了。
因为主UI线程需要根据硬件刷新率[^3]同步用户界面的重绘。手机应用体验流畅要求界面帧率[^3]达到每秒60,也就是说每16.67毫秒就需要重绘一帧,这意味着如果我们在主线程上执行的任务超过16毫秒,就会出现丢帧现象,也就是界面会开始变卡。。。
Android异步执行任务的方法有以下几种:
AsyncTask
AsyncTask是最常用的异步方法,功能结构设计的也很丰富,给使用者足够的控制,使用上主要是将异步执行的任务放在下面方法里。
查看日志 adb logcat
下面命令将只显示错误日志,和所有Tag=mytag的调试日志,-C 会用不同颜色区分不同级别的日志,但只有Android 4.3以后才支持。
adb logcat [-C] *:E <mytag>:D
远程调试 adb over TCP
首先在手机或Pad上执行以下命令(要求root)
su
setprop service.adb.tcp.port 5555
stop adbd
start adbd
再执行下面命令则可以看到手机的网络地址
有root权限
adb shell su -c cat /data/data/app.package.name/databases/application.sqlite | sed 's/\r$//' > application.sqlite
应用可调试的话
adb shell
run-as app.package.name \
cp /data/data/package.name/databases/application.sqlite /sdcard/
exit
adb pull /sdcard/application.sqlite ~/
使用备份方法
adb backup -f ~/data.ab -noapk app.package.name
dd if=data.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" | tar -xvf -