[金融知识]债券逆回购

一、债券回购的定义
债券质押式回购简单地说就是交易双方以债券为质押品的一种短期资金借贷行为。其中债券持有人(正回购方)将债券质押而获得资金使用权,到约定的时间还本并支付一定的利息,从而“赎回”债券。而资金持有人(逆回购方)就是正回购方的交易对手。在实际交易中债券是质押给了第三方即中国结算公司,这样交易双方否更加安全、便捷。
二、逆回购交易的品种
我们仅列出个人投资者经常参与的公司债(包括企业债等)回购品种。      上海证券交易所可交易的质押式回购(括弧中为交易代码)分为1日(204001)、2日(204002)、3日(204003)、4日(204004)、7日(204007)、14日(204014)、28日(204028)、91日(204091)、182日(204182)共9个品种。深圳交易所按回购期限分为分为1日(131910)、2日(131911)、3日(131900)、7日(131901)共4个品种。其中经常交易的只有沪深1日和7日四个品种,并且沪市的日均交易量又远远大于深市的交易量。

三、逆回购的申报流程
在交易界面,选择“卖出” (方向千万不能错),输入代码(比如131910),证券名称就是逆回购品种,数量最低是1000张,代表10万元,每10万元递增,点击融券下单就可以了(交易软件上不能显示余额)。如果是一天回购,则下一交易日(T+1日)资金可用但不可取,T+2日可取。可用指可以买入任何证券;可取指可以转入银行账户。
选择卖出价格,卖出价格指年化收益率,就这么简单了,点卖出就行了,资金晚上清算后自动又回到了账户上了,无需再操作。
注意事项:回购交易的规则是“一次交易,两次结算”。当你做了逆回购借出资金后,到期资金就自动回到你的账户上,期间不用做任何操作。比如1日回购,当日晚上资金就显示可用,但实际上是资金T+1日晚上返回到你的账户。        T+1日是指交易日期,故若周五做一日正回购,则下周一资金才可用,但实际上你仍然只获得了一天的年化利率。
7日回购是指7个自然日,比如周一做7日回购,则下周一资金就可用。其他品种类推。

四、手续费及收益率分析
通常证券公司收取的十万分之一手续费,以上图120万为例,手续费是12元,以当天1.3%年化利率一天回购参与清算收益是42.17元。
五、逆回购交易操作技巧
1. 用暂时闲置资金及尾盘可用资金参与。个人卖出股票后,资金当天是无法转出购买银行的理财产品的,但可以参加逆回购。交易所逆回购的参与者主要是除银行以外的机构投资者比如货币市场基金、财务公司等,尤其是现在新股申购上限的限制,迫使很多机构不得已而参与逆回购交易。个人投资者参与逆回购主要是在暂时闲置的资金没有更好的投资渠道时运用。

2. 有机会尽量参与。很多人不在乎这点回购的“小钱”,而往往把账户上暂时不用的现金就闲置在那里。以100万为例,我们看看差别。如果不做回购,则一天的活期利息为100万*0.36%/360=10元。如果做回购,一般利率为1.5%(周四可到4%),则回购利息为100万*1.5%/360=42元,减去最高的佣金10元,剩余32元,比活期高22元。我们觉得当那天恰好打开账户时就值得花10秒钟操作一下的。我曾做个了测算,100万股票资金通过逆回购和银行日日金理财可多增加1万元收入。

3.周一至周三尾盘可以将所有剩余的可用资金全额参加1天回购,完全不影响资金使用,周四可以视周五资金是否是需要转出购买理财产品决定,周五和节假日前谨慎参与。周五如果资金能转出做日日金之类的理财产品,就不要做逆回购,因为资金利率很低,而且周末两天也没有利息。特别是长假前一定要计算一下,转出和回购那个更有利。因为尽管长假前第二天的利率很高,但却失去了长假期间的利息。

4. 选择收益高的市场参与。沪深交易所都有回购品种,因为他们都没有什么风险,所以对于投资者来说肯定要选择收益率高的,而实时上很多投资者并不了解这一点。比如2010年3月25日深市回购利率最高达到9%,而沪市最高才4%!

5. 7日逆回购比连续1日逆回购更有利。如果限制资金超过1周,期间又无新股申购等更好的投资渠道,那么连续做1日和做一个7日逆回购那个更有利呢?简单计算如下。1日逆回购4次的利率平均为1.5%,周四为4%,则投资100万

总收益为100万*(1.5%*4+4%)/360=278元,减去佣金50元后净收益为228元。7日回购利率平均为1.5%,则收益为100万*1.5%*7/360=292元,减去佣金50元后净收益为242元,比连续做1日回购高34元!更重要的是做一次7日逆回购比较方便。

六、逆回购交易的风险
逆回购交易一般没有风险,因为逆回购方直接针对的结算公司这样的第三方。如果债券质押方到期不能按时还款,结算公司会先垫付资金,然后通过罚款和处置质押券等方式向融资方追诉。逆回购的风险就是机会成本,也就是失去了逆回购期间选择其他更高收益品种的机会。

总结:怎么说呢,国债一般适合于资金量比较大的,风险承受能力
比较低的客户,因为做国债逆回购基本上没什么风险。但是风险是和收益对等的,这就决定了做这类股票的收益不会太高,一般年化利率比较高的时候是在月末特别是季末的时候,能达到10%以上。

Android有用的代码集合

代码地址:https://github.com/Blankj/AndroidUtilCode/tree/master/utilcode/src/main/java/com/blankj/utilcode/utils

为方便查找,已进行大致归类,其目录如下所示:

  • Activity相关→ActivityUtils.java
    isActivityExists : 判断是否存在Activity
    launchActivity   : 打开Activity
  • App相关→AppUtils.java
    isInstallApp          : 判断App是否安装
    installApp            : 安装App(支持6.0)
    installAppSilent      : 静默安装App
    uninstallApp          : 卸载App
    uninstallAppSilent    : 静默卸载App
    launchApp             : 打开App
    getAppPackageName     : 获取App包名
    getAppDetailsSettings : 获取App具体设置
    getAppName            : 获取App名称
    getAppIcon            : 获取App图标
    getAppPath            : 获取App路径
    getAppVersionName     : 获取App版本号
    getAppVersionCode     : 获取App版本码
    getAppSignature       : 获取App签名
    getAppSignatureSHA1   : 获取应用签名的的SHA1值
    isSystemApp           : 判断App是否是系统应用
    isAppForeground       : 判断App是否处于前台
    getAppInfo            : 获取App信息
    getAppsInfo           : 获取所有已安装App信息
    cleanAppData          : 清除App所有数据
  • 栏相关→BarUtils.java
    setTransparentStatusBar : 设置透明状态栏(api大于19方可使用)
    hideStatusBar           : 隐藏状态栏
    getStatusBarHeight      : 获取状态栏高度
    isStatusBarExists       : 判断状态栏是否存在
    getActionBarHeight      : 获取ActionBar高度
    showNotificationBar     : 显示通知栏
    hideNotificationBar     : 隐藏通知栏
  • 清除相关→CleanUtils.java
    cleanInternalCache    : 清除内部缓存
    cleanInternalFiles    : 清除内部文件
    cleanInternalDbs      : 清除内部数据库
    cleanInternalDbByName : 根据名称清除数据库
    cleanInternalSP       : 清除内部SP
    cleanExternalCache    : 清除外部缓存
    cleanCustomCache      : 清除自定义目录下的文件
  • 剪贴板相关→ClipboardUtils.java
    copyText   : 复制文本到剪贴板
    getText    : 获取剪贴板的文本
    copyUri    : 复制uri到剪贴板
    getUri     : 获取剪贴板的uri
    copyIntent : 复制意图到剪贴板
    getIntent  : 获取剪贴板的意图
  • 关闭相关→CloseUtils.java
    closeIO        : 关闭IO
    closeIOQuietly : 安静关闭IO
  • 常量相关→ConstUtils.java
    MemoryConst : 存储相关常量
    TimeConst   : 时间相关常量
    RegexConst  : 正则相关常量
  • 转换相关→ConvertUtils.javaTest
    bytes2HexString, hexString2Bytes         : byteArr与hexString互转
    chars2Bytes, bytes2Chars                 : charArr与byteArr互转
    byte2Size, size2Byte                     : 字节数与unit为单位的size互转
    byte2FitSize                             : 字节数转合适大小
    bytes2Bits, bits2Bytes                   : bytes与bits互转
    input2OutputStream, output2InputStream   : inputStream与outputStream互转
    inputStream2Bytes, bytes2InputStream     : inputStream与byteArr互转
    outputStream2Bytes, bytes2OutputStream   : outputStream与byteArr互转
    inputStream2String, string2InputStream   : inputStream与string按编码互转
    outputStream2String, string2OutputStream : outputStream与string按编码互转
    bitmap2Bytes, bytes2Bitmap               : bitmap与byteArr互转
    drawable2Bitmap, bitmap2Drawable         : drawable与bitmap互转
    drawable2Bytes, bytes2Drawable           : drawable与byteArr互转
    view2Bitmap                              : view转Bitmap
    dp2px, px2dp                             : dp与px互转
    sp2px, px2sp                             : sp与px互转
  • 崩溃相关→CrashUtils.java
    getInstance : 获取单例
    init        : 初始化
  • 设备相关→DeviceUtils.java
    isRoot          : 判断设备是否root
    getSDKVersion   : 获取设备系统版本号
    getAndroidID    : 获取设备AndroidID
    getMacAddress   : 获取设备MAC地址
    getManufacturer : 获取设备厂商,如Xiaomi
    getModel        : 获取设备型号,如MI2SC
  • 判空相关→EmptyUtils.javaTest
    isEmpty    : 判断对象是否为空
    isNotEmpty : 判断对象是否非空
  • 编码解码相关→EncodeUtils.javaTest
    urlEncode                         : URL编码
    urlDecode                         : URL解码
    base64Encode, base64Encode2String : Base64编码
    base64Decode                      : Base64解码
    base64UrlSafeEncode               : Base64URL安全编码
    htmlEncode                        : Html编码
    htmlDecode                        : Html解码
  • 加密解密相关→EncryptUtils.javaTest
    encryptMD2, encryptMD2ToString                         : MD2加密
    encryptMD5, encryptMD5ToString                         : MD5加密
    encryptMD5File, encryptMD5File2String                  : MD5加密文件
    encryptSHA1, encryptSHA1ToString                       : SHA1加密
    encryptSHA224, encryptSHA224ToString                   : SHA224加密
    encryptSHA256, encryptSHA256ToString                   : SHA256加密
    encryptSHA384, encryptSHA384ToString                   : SHA384加密
    encryptSHA512, encryptSHA512ToString                   : SHA512加密
    encryptHmacMD5, encryptHmacMD5ToString                 : HmacMD5加密
    encryptHmacSHA1, encryptHmacSHA1ToString               : HmacSHA1加密
    encryptHmacSHA224, encryptHmacSHA224ToString           : HmacSHA224加密
    encryptHmacSHA256, encryptHmacSHA256ToString           : HmacSHA256加密
    encryptHmacSHA384, encryptHmacSHA384ToString           : HmacSHA384加密
    encryptHmacSHA512, encryptHmacSHA512ToString           : HmacSHA512加密
    encryptDES, encryptDES2HexString, encryptDES2Base64    : DES加密
    decryptDES, decryptHexStringDES, decryptBase64DES      : DES解密
    encrypt3DES, encrypt3DES2HexString, encrypt3DES2Base64 : 3DES加密
    decrypt3DES, decryptHexString3DES, decryptBase64_3DES  : 3DES解密
    encryptAES, encryptAES2HexString, encryptAES2Base64    : AES加密
    decryptAES, decryptHexStringAES, decryptBase64AES      : AES解密
  • 文件相关→FileUtils.javaTest
    getFileByPath             : 根据文件路径获取文件
    isFileExists              : 判断文件是否存在
    isDir                     : 判断是否是目录
    isFile                    : 判断是否是文件
    createOrExistsDir         : 判断目录是否存在,不存在则判断是否创建成功
    createOrExistsFile        : 判断文件是否存在,不存在则判断是否创建成功
    createFileByDeleteOldFile : 判断文件是否存在,存在则在创建之前删除
    copyDir                   : 复制目录
    copyFile                  : 复制文件
    moveDir                   : 移动目录
    moveFile                  : 移动文件
    deleteDir                 : 删除目录
    deleteFile                : 删除文件
    listFilesInDir            : 获取目录下所有文件
    listFilesInDir            : 获取目录下所有文件包括子目录
    listFilesInDirWithFilter  : 获取目录下所有后缀名为suffix的文件
    listFilesInDirWithFilter  : 获取目录下所有后缀名为suffix的文件包括子目录
    listFilesInDirWithFilter  : 获取目录下所有符合filter的文件
    listFilesInDirWithFilter  : 获取目录下所有符合filter的文件包括子目录
    searchFileInDir           : 获取目录下指定文件名的文件包括子目录
    writeFileFromIS           : 将输入流写入文件
    writeFileFromString       : 将字符串写入文件
    getFileCharsetSimple      : 简单获取文件编码格式
    getFileLines              : 获取文件行数
    readFile2List             : 指定编码按行读取文件到List
    readFile2SB               : 指定编码按行读取文件到StringBuilder中
    getFileSize               : 获取文件大小
    getFileMD5                : 获取文件的MD5校验码
    getDirName                : 根据全路径获取最长目录
    getFileName               : 根据全路径获取文件名
    getFileNameNoExtension    : 根据全路径获取文件名不带拓展名
    getFileExtension          : 根据全路径获取文件拓展名
  • 图片相关→ImageUtils.java
    bitmap2Bytes, bytes2Bitmap       : bitmap与byteArr互转
    drawable2Bitmap, bitmap2Drawable : drawable与bitmap互转
    drawable2Bytes, bytes2Drawable   : drawable与byteArr互转
    getBitmap                        : 获取bitmap
    scale                            : 缩放图片
    clip                             : 裁剪图片
    skew                             : 倾斜图片
    rotate                           : 旋转图片
    getRotateDegree                  : 获取图片旋转角度
    toRound                          : 转为圆形图片
    toRoundCorner                    : 转为圆角图片
    fastBlur                         : 快速模糊
    renderScriptBlur                 : renderScript模糊图片
    stackBlur                        : stack模糊图片
    addFrame                         : 添加颜色边框
    addReflection                    : 添加倒影
    addTextWatermark                 : 添加文字水印
    addImageWatermark                : 添加图片水印
    toAlpha                          : 转为alpha位图
    toGray                           : 转为灰度图片
    save                             : 保存图片
    isImage                          : 根据文件名判断文件是否为图片
    getImageType                     : 获取图片类型
    compressByScale                  : 按缩放压缩
    compressByQuality                : 按质量压缩
    compressBySampleSize             : 按采样大小压缩
  • 意图相关→IntentUtils.java
    getInstallAppIntent         : 获取安装App(支持6.0)的意图
    getUninstallAppIntent       : 获取卸载App的意图
    getLaunchAppIntent          : 获取打开App的意图
    getAppDetailsSettingsIntent : 获取App具体设置的意图
    getShareTextIntent          : 获取分享文本的意图
    getShareImageIntent         : 获取分享图片的意图
    getComponentIntent          : 获取其他应用组件的意图
    getShutdownIntent           : 获取关机的意图
    getCaptureIntent            : 获取拍照的意图
  • 键盘相关→KeyboardUtils.java
    hideSoftInput                 : 动态隐藏软键盘
    clickBlankArea2HideSoftInput0 : 点击屏幕空白区域隐藏软键盘(注释萌萌哒)
    showSoftInput                 : 动态显示软键盘
    toggleSoftInput               : 切换键盘显示与否状态
    isShowSoftInput               : 判断键盘是否显示
  • 日志相关→LogUtils.javaTest
    init       : 初始化函数
    getBuilder : 获取LogUtils建造者
    v          : Verbose日志
    d          : Debug日志
    i          : Info日志
    w          : Warn日志
    e          : Error日志
  • 网络相关→NetworkUtils.java
    openWirelessSettings               : 打开网络设置界面
    isAvailable                        : 判断网络是否可用
    isConnected                        : 判断网络是否连接
    is4G                               : 判断网络是否是4G
    isWifiConnected                    : 判断wifi是否连接状态
    getNetworkOperatorName             : 获取移动网络运营商名称
    getPhoneType                       : 获取移动终端类型
    getNetWorkType, getNetWorkTypeName : 获取当前的网络类型(WIFI, 2G, 3G, 4G)
  • 手机相关→PhoneUtils.java
    isPhone           : 判断设备是否是手机
    getIMEI           : 获取IMIE码
    getIMSI           : 获取IMSI码
    getPhoneStatus    : 获取手机状态信息
    dial              : 跳至填充好phoneNumber的拨号界面
    call              : 拨打phoneNumber
    sendSms           : 发送短信
    getAllContactInfo : 获取手机联系人
    getContactNum     : 打开手机联系人界面点击联系人后便获取该号码(注释萌萌哒)
    getAllSMS         : 获取手机短信并保存到xml
  • 正则相关→RegexUtils.javaTest
    isMobileSimple : 验证手机号(简单)
    isMobileExact  : 验证手机号(精确)
    isTel          : 验证电话号码
    isIDCard15     : 验证身份证号码15位
    isIDCard18     : 验证身份证号码18位
    isEmail        : 验证邮箱
    isURL          : 验证URL
    isChz          : 验证汉字
    isUsername     : 验证用户名
    isDate         : 验证yyyy-MM-dd格式的日期校验,已考虑平闰年
    isIP           : 验证IP地址
    isMatch        : string是否匹配regex
  • 屏幕相关→ScreenUtils.java
    getDeviceWidth, getDeviceHeight                 : 获取手机分辨率
    setTransparentStatusBar                         : 设置透明状态栏(api大于19方可使用)
    hideStatusBar                                   : 隐藏状态栏(注释萌萌哒)
    getStatusBarHeight                              : 获取状态栏高度
    isStatusBarExists                               : 判断状态栏是否存在
    getActionBarHeight                              : 获取ActionBar高度
    showNotificationBar                             : 显示通知栏
    hideNotificationBar                             : 隐藏通知栏
    setLandscape                                    : 设置屏幕为横屏(注释萌萌哒)
    snapShotWithStatusBar, snapShotWithoutStatusBar : 获取屏幕截图
    isScreenLock                                    : 判断是否锁屏
  • SD卡相关→SDCardUtils.java
    isSDCardEnable : 判断SD卡是否可用
    getDataPath    : 获取SD卡Data路径
    getSDCardPath  : 获取SD卡路径
    getFreeSpace   : 计算SD卡的剩余空间
    getSDCardInfo  : 获取SD卡信息
  • 服务相关→ServiceUtils.java
    isRunningService : 获取服务是否开启
  • Shell相关→ShellUtils.java
    isRoot  : 判断设备是否root
    execCmd : 是否是在root下执行命令
  • 尺寸相关→SizeUtils.java
    dp2px, px2dp     : dppx转换
    sp2px, px2sp     : sppx转换
    applyDimension   : 各种单位转换
    forceGetViewSize : 在onCreate()即可强行获取View的尺寸
    measureView      : ListView中提前测量View尺寸(注释萌萌哒)
  • SP相关→SPUtils.javaTest
    SPUtils    : SPUtils构造函数
    putString  : SP中写入String类型value
    getString  : SP中读取String
    putInt     : SP中写入int类型value
    getInt     : SP中读取int
    putLong    : SP中写入long类型value
    getLong    : SP中读取long
    putFloat   : SP中写入float类型value
    getFloat   : SP中读取float
    putBoolean : SP中写入boolean类型value
    getBoolean : SP中读取boolean
    getAll     : SP中获取所有键值对
    remove     : SP中移除该key
    contains   : SP中是否存在该key
    clear      : SP中清除所有数据
  • 字符串相关→StringUtils.javaTest
    isEmpty          : 判断字符串是否为null或长度为0
    isSpace          : 判断字符串是否为null或全为空格
    null2Length0     : null转为长度为0的字符串
    length           : 返回字符串长度
    upperFirstLetter : 首字母大写
    lowerFirstLetter : 首字母小写
    reverse          : 反转字符串
    toDBC            : 转化为半角字符
    toSBC            : 转化为全角字符
    getPYFirstLetter : 获得第一个汉字首字母
    cn2PY            : 中文转拼音
  • 线程池相关→ThreadPoolUtils.java
    ThreadPoolUtils                               : ThreadPoolUtils构造函数
    execute                                       : 在未来某个时间执行给定的命令
    execute                                       : 在未来某个时间执行给定的命令链表
    shutDown                                      : 待以前提交的任务执行完毕后关闭线程池
    shutDownNow                                   : 试图停止所有正在执行的活动任务
    isShutDown                                    : 判断线程池是否已关闭
    isTerminated                                  : 关闭线程池后判断所有任务是否都已完成
    awaitTermination                              : 请求关闭、发生超时或者当前线程中断
    submit                                        : 提交一个Callable任务用于执行
    submit                                        : 提交一个Runnable任务用于执行
    invokeAll, invokeAny                          : 执行给定的任务
    schedule                                      : 延迟执行Runnable命令
    schedule                                      : 延迟执行Callable命令
    scheduleWithFixedRate, scheduleWithFixedDelay : 延迟并循环执行命令
  • 时间相关→TimeUtils.javaTest
    milliseconds2String                               : 将时间戳转为时间字符串
    string2Milliseconds                               : 将时间字符串转为时间戳
    string2Date                                       : 将时间字符串转为Date类型
    date2String                                       : 将Date类型转为时间字符串
    date2Milliseconds                                 : 将Date类型转为时间戳
    milliseconds2Date                                 : 将时间戳转为Date类型
    milliseconds2Unit                                 : 毫秒时间戳单位转换(单位:unit)
    getIntervalTime                                   : 获取两个时间差(单位:unit)
    getCurTimeMills, getCurTimeString, getCurTimeDate : 获取当前时间
    getIntervalByNow                                  : 获取与当前时间的差(单位:unit)
    isLeapYear                                        : 判断闰年
    getWeek, getWeekIndex                             : 获取星期
    getWeek, getWeekIndex                             : 获取星期
    getWeekOfMonth                                    : 获取月份中的第几周
    getWeekOfYear                                     : 获取年份中的第几周
  • 吐司相关→ToastUtils.java
    init               : 吐司初始化
    showShortToastSafe : 安全地显示短时吐司
    showLongToastSafe  : 安全地显示长时吐司
    showShortToast     : 显示短时吐司
    showLongToast      : 显示长时吐司
    cancelToast        : 取消吐司显示
  • 压缩相关→ZipUtils.javaTest
    zipFiles           : 批量压缩文件
    zipFile            : 压缩文件
    unzipFiles         : 批量解压文件
    unzipFile          : 解压文件
    unzipFileByKeyword : 解压带有关键字的文件
    getFilesPath       : 获取压缩文件中的文件路径链表
    getComments        : 获取压缩文件中的注释链表
    getEntries         : 获取压缩文件中的文件对象
  • 更新Log→update_log.md

做这份整理是想把它作为Android开发的小字典,当遇到一些琐碎问题时,不用再面向百度或者谷歌查询API的使用,费时费力,这里有的话,大家尽管撸走;同时也希望它能逐日壮大起来,期待大家的Star和完善,当然我也会一直更新发布版本和日志,为了方便大家导入,现已上传jcenter;其中很多代码也是汇四方之精华,谢谢前辈们的提供,当然最终还是要通过单元测试的,如有错误,请及时告之;开设QQ群提供讨论,群号:74721490,至于验证问题对大家来说肯定都是小case;最近在玩微博,玩的话向大家求个关注

Download


Gradle:

compile 'com.blankj:utilcode:1.3.0'

Proguard


-keep class com.blankj.utilcode.** { *; }
-keep classmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**

 

文/Blankj(简书作者)
原文链接:http://www.jianshu.com/p/72494773aace

RESTful Web服务学习

作者:蒜瓣
链接:https://zhuanlan.zhihu.com/p/21644769
来源:知乎
 

随着 REST 成为大多数 Web 和 Mobile 应用的默认选择,势必要对它的基本原理有所了解。

在它提出十多年后的今天,REST 已经成为最重要的 Web 应用技术之一。随着所有技术朝着 API 方向发展,它的重要性有可能持续快速地增长。每门主要编程语言现在已经包含构建 RESTful Web 服务的框架。同样地,Web 开发者和架构师对 REST 和 RESTful 服务有一个清晰的理解是很重要的。这篇教程解释了 REST 架构,然后研究使用它构建通用地基于API的任务的细节。

什么是 REST

REST 代表表述性状态转移(representational state transfer),它是一种网络化超媒体应用的架构风格。它主要是用于构建轻量级的、可维护的、可伸缩的 Web 服务。基于 REST 的服务被称为 RESTful 服务。REST 不依赖于任何协议,但是几乎每个 RESTful 服务使用 HTTP 作为底层协议。

RESTful 使用HTTP post(创建、更新)数据、读取数据、删除数据。使用HTTP实现CRUD(创建、读取、更新、删除)操作。

RESTful 服务特点:

每个系统都使用资源。这些资源可以是图片,视频文件,网页,商业信息,或者在基于计算机的系统中可以被代表的任何事物。服务的目的是提供一个窗口给客户端以便客户端能访问这些资源。服务架构师和开发人员想要这些服务变得易于实现、维护、扩展、伸缩。RESTful 架构允许这些,甚至更多。一般来说,RESTful 服务应该有下面的属性和特征,也就是我要详细描述的内容:

  • 模型表示(Representations)
  • 消息(Messages)
  • URIs
  • 一致接口(Uniform interface)
  • (无状态)Stateless
  • 资源之间的链接(Links between resources)
  • 缓存(Caching)

模型表示(Representations)

RESTful 服务的焦点在资源上和怎么提供对资源的访问。资源很容易被认为和OOP中的对象一样。一个资源能由其他资源组成。当设计一个系统的时候,第一件要做的事情是定义资源和决定资源之间的关系。这有点像设计数据库的第一步。定义实体和关系。

一旦我们定义了资源,接下来我们需要找到一种用于在系统中表示这些资源的方法。你可以使用任何格式来表示资源。REST 对此没有限制。

例如,根据你的需求,你可以决定使用 JSON 或者 XML。如果你在构建 Web 服务,此服务用于 Web 页面中的 AJAX 调用,那 JSON 是很好地选择。 XML 可以用来表示比较复杂的资源。例如一个被称为“Person”的资源可以表示如下:

列表1:资源的JSON 表示。

{
    "ID": "1",
    "Name": "M Vaqqas",
    "Email": "m.vaqqas@gmail.com",
    "Country": "India"
}

列表2:资源的XML 表示。

<Person>
	<ID>1</ID>
	<Name>M Vaqqas</Name>
	<Email>m.vaqqas@gmail.com</Email>
	<Country>India</Country>
</Person>

实际上,你可以使用不止一种的格式并且决定使用其中哪一种用于依赖于客户端类型或一些请求参数的响应。无论使用哪个格式,好的模型表示(representation )应该具有以下明显的特征:

  • 客户端和服务端应该能够理解这种模型表示(representation )的格式。
  • 模型表示(representation )应该能够完整的表示资源。如果需要表示部分资源,然后你需要考虑将资源分解成子资源。分割大资源到更小的资源同样允许你传递更小的表现。较小的模型表示(representation)意味着更少的时间来创建和传输。这也意味着更快的服务。
  • 模型表示(representation)应该能够互相链接资源。可以通过替换 URI 或者是唯一 ID。

消息(Messages)

客户端和服务端经由消息相互沟通。客户端发送请求到服务器,服务器使用响应答复。除了实际的数据,这些信息也包含一些关于消息的元数据。对于设计 RESTful 服务了解 HTTP 1.1的请求格式和响应格式是很重要的。

HTTP 请求

图1中展示了HTTP请求格式。

图1:HTTP 请求格式

<VERB> GET, PUT, POST, DELETE, OPTIONS等等 HTTP 方法的一种。

<URI> 资源的URI。操作将在这个 URI 上执行。

<HTTP Version> HTTP 版本,通常是“HTTP v1.1”。

<Request Header> 包含 header 键值对集合的元数据。这些设置包含消息的信息和发送者像客户端的类型,客户端支持的格式,消息体的格式类型,响应的缓存设置,和许多信息。

<Request Body> 是实际的消息内容。在 RESTful 服务中,这就是消息中资源表示的位置。

在 HTML 消息中没有标签和标识标记区块的开始或结束。

列表三是简单的 POST 请求消息,这个请求想要插入一条新的 Person 资源。

列表3:简单 POST 请求

POST http://MyService/Person/
Host: MyService
Content-Type: text/xml; charset=utf-8
Content-Length: 123
<?xml version="1.0" encoding="utf-8"?>
<Person>
  <ID>1</ID>
  <Name>M Vaqqas</Name>
  <Email>m.vaqqas@gmail.com</Email>
  <Country>India</Country>
</Person>

列表4:GET 请求

GET http://www.w3.org/Protocols/rfc2616/rfc2616.html HTTP/1.1
Host: www.w3.org
Accept: text/html,application/xhtml+xml,application/xml; …
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 …
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,hi;q=0.6

HTTP Response

图2展示了 HTTP 响应的格式:

图2:HTTP 响应格式。

服务器返回 <response code>,<response code>包含请求的状态。<response code>通常是三位数字的HTTP状态码(3-digit HTTP status code)。

<Response Header> 包含关于响应消息的元数据和设置。

<Response Body> 包含如果请求成功的模型表示(representation)。

列表5是我从清单三的请求中得到的真实响应。

列表5:真实的 GET 请求的响应。

HTTP/1.1 200 OK
Date: Sat, 23 Aug 2014 18:31:04 GMT
Server: Apache/2
Last-Modified: Wed, 01 Sep 2004 13:24:52 GMT
Accept-Ranges: bytes
Content-Length: 32859
Cache-Control: max-age=21600, must-revalidate
Expires: Sun, 24 Aug 2014 00:31:04 GMT
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns='http://www.w3.org/1999/xhtml'>
<head><title>Hypertext Transfer Protocol -- HTTP/1.1</title></head>
<body>
...

响应码 200 OK 表示一切正常,并且消息体包含我请求的有效的模型表示(representation)。在这个例子中,模型表示(representation)是 HTML 文档,HTML 文档是通过在响应头中的 Content-Type 声明地。在这个消息中的 header 是不解自明地,但是我将会在接下来的文章中讨论他们中的一些。这有很多其他属性。你可以使用叫Fiddle的免费工具抓取调试这些HTTP请求和响应。

资源定位

REST 要求每个资源至少有一个 URI。 RESTful 服务使用人类可读的URIs层级目录来定位资源。URI 要做的工作是定义一个资源或资源集合。实际的操作由 HTTP 动作决定。URI 应该没有任何关于处理和动作的内容。这使我们能够调用相同的 URI 使用不同的 HTTP 动词来执行不同的操作。

假设我们有一个 person 的数据库并且我们希望通过服务器暴露给外部。Person 资源可以像下面这样被定位到:

MyService/Persons/1

此URL遵循格式:Protocol://ServiceName/ResourceType/ResourceID

对于构建良好的 URIs 这有些重要的推荐:

  • 使用复数名词命名你的资源。
  • 避免使用制造混乱的空格。使用_或者-代替。
  • URI 不区分大小写。为了更清晰我使用驼峰写法。你也可以使用全部小写的URIs。
  • 你也能够有你自己的约定,但是要在整个服务保持一致。确保你的客户端都知道这个约定。你的客户端 URIs 程序构建将更简单如果它们知道你遵循的资源层级和URI约定。
  • 好的 URI 是不会变更的。再决定服务的 URIs 之前要先思考思考。如果你需要改变资源的定位,不要放弃老的 URI。如果请求来自老的 URI,使用状态码300重定向客户端到新的location。
  • 避免使用动词命名你的资源直到你的资源是一个实际地操作或过程。动词更加适合操作的命名。例如,RESTful 服务不应该有 MyService/FetcthPerson/MyService/DeletePerson? 类似的 URI。

URI中的查询参数

前面的URI是用查询参数帮助构建的。

MyService/Persons?

查询参数方法运行良好而且 REST 不会阻止你使用查询参数。然而,这种方式有一些劣势:

  • 增加了复杂性,降低了可读性。如果你使用更多的参数问题会更加明显。
  • 像Google这样的搜索引擎爬网程序和索引器忽略uri查询参数。如果你正在进行Web开发,这是你的Web服务一部分很大的劣势,导致搜索引擎屏蔽。

查询参数的基本目的是提供参数给需要的数据项的操作。例如,如果你想要模型表示(presentation)格式由客户端决定,你可以通过参数实现像下面这样。

MyService/Persons/1?

MyService/Persons/1?

包含format和encoding参数的父子层级URI看上去逻辑上不正确因为它们没有这种关系。

MyService/Persons/1/jso

查询参数也允许可选参数。在URI中显然是不可能的。你仅仅应该在提供参数值给处理过程的时候使用。

统一接口

RESTful应该有统一接口。HTTP 1.1 提供了一系列方法,被称为动作。在这其中比较重要的动作是:

安全的操作是指对原始资源值不会产生影响的操作。列如,数学上的操作除以1就是安全的操作,因为无论多少次用1除一个数,原始数值都不会改变。幂等操作是指无论多少次执行都给出相同结果的操作。例如,数学上的乘以0就是幂等的,因为无论计算多少次结果都是零,结果都是一样的。类似的,一个安全的HTTP方法不会使服务器上的资源发生变化。一个幂等的HTTP方法无论执行多少次都会有相同的响应。把方法分类成安全和幂等的可以使客户端在不稳定的Web环境中再次触发相同的请求的结果变得更可预测。

在 Web 上 GET 可能是最流行的方法。它用来获取资源。

HEAD 仅返回响应头和空的响应体。这个方法可以用在你不需要全部的资源模型表示(representation)的时候。例如,HEAD 可以快速检测服务器上的资源是否存在。

OPTIONS 用于获取资源允许的操作。例如,思考下面的请求:

OPTIONS http://MyService/Persons/1 HTTP/1.1
HOST: MyService

在服务验证之后请求返回下面内容:

200 OK
Allow: HEAD, GET, PUT

第二行包含客户端可以使用的方法。

你应该仅仅出于它们的实际意义使用这些方法。例如:绝不要使用 GET 在服务器上创建或删除资源。如果你没这样做,将会扰乱你的客户端导致他们做出意外的操作。举例说明,然我们考虑下面的请求:

GET http://MyService/DeletePersons/1 HTTP/1.1
HOST: MyService

根据 HTTP 1.1 规范,GET 请求的目的是从服务器获取资源。但是它很容易实现一个删除 Person 的请求。这个请求也许运行的很棒,但是这不是RESTful 设计。换言之,使用 DELETE 方法来删除资源像下面这样:

DELETE http://MyService/Persons/1 HTTP/1.1
HOST: MyService

REST 建议统一接口,HTTP 提供了统一接口。然而,这由服务架构师和开发人员保持它的统一。

PUT 和 POST 的区别

对于这两个方法我提供了几乎相同的简短描述。这两个方法困扰着许多开发人员。所以让我们单独地讨论他们。

PUT 和 POST 的关键不同在于 PUT 是幂等的,而 POST 不是。

另一个不同,使用 PUT 你需要定义资源完整的 URI。这意味着客户端能构造资源的URI哪怕资源不存在于服务器上。客户端选择资源唯一的名字或 ID 是可能的。就像在服务器上创建一个用户需要客户端选择用户 ID。如果客户端不能猜测出资源完整的URI,你别无选择,只能使用 POST。

很明显,PUT 请求不会修改或创建超过一个资源,无论触发多少次(如果URI相同)。当资源存在时 PUT 和 POST 是没有区别的,都是更新已存在资源。第三个请求(POST MyService/Persons/)会在每次触发都创建资源。许多开发人员认为 REST 不允许 POST 被用于更新操作。然而,REST 并没有这样的限制。

无状态

RESTful 服务是无状态的并且不会为任何客户端保持状态。一个请求不应该依赖过去的请求,服务对待每个请求都是独立的。HTTP 是无状态协议的设计,你需要做一些额外的事情实现状态服务。使用当前的技术真的很容易实现状态服务。我们需要清楚的理解无状态和有状态设计以便我们避免误解。

无状态设计像这样:

Request1: GET MyService/Persons/1 HTTP/1.1

Request2: GET MyService/Persons/2 HTTP/1.1

每个请求都能被单独对待。

有状态设计,像这样:

Request1: GET MyService/Persons/1 HTTP/1.1

Request2: GET MyService/NextPerson HTTP/1.1

为了处理第二个请求,服务器需要记住客户端最后获取的 PersonID。换句话说,服务器需要记住当前状态————否则请求2无法处理。设计你的服务的方式是一个请求绝不要涉及前一个请求。无状态服务更容易集群,更容易维护,更容易伸缩。这样的服务提供了更好的响应时间,因为它们能容易的负载均衡。

资源之间的链接

资源模型表示(representation )可以包含其他资源的链接就像 HTML 页面包含到其他页面的链接一样。被服务返回的模型表示(representations )应该能驱动处理流就像网站的情况一样。当访问网站的时候,首先是索引页面,单击其中的一个链接跳转到另外一个页面等等。

让我们考虑下客户端请求一个包含许多其他资源的资源。替代输入所有资源,你可能会列出资源的链接。

例如,如果多个Person是Club的一部分,那么Club能像列表6中一样表示。

列表6:Club

<Club>        
	<Name>Authors Club</Name>
	<Persons>
		<Person>
			<Name>M. Vaqqas</Name>
			<URI>http://MyService/Persons/1</URI>
		</Person>
		<Person>
			<Name>S. Allamaraju</Name>
			<URI>http://MyService/Persons/12</URI>
		</Person>
	</Persons>
</Club> 

缓存

缓存是存贮生成结果的概念,使用存储结果替代在不久的将来重复的请求生成的结果。缓存可以在客户端、服务端或者他们之间的任何组件上完成,比如代理服务器。缓存是提升服务器性能的很棒的方法。但如果不妥善处理,会导致客户端使用失效的结果。

缓存可以由下面的HTTP头控制:

这些头部的值可以组合起来用在Cache-Control指令中来检查缓存结果是否有效。最通用的用于Cache-Control的指令如下:


服务决定这些头和指令的值是根据资源的特性。

文档化 RESTful 服务

RESTful 服务不必包含用于帮助客户端发现它的文档。因为URIs、链接、统一接口,在运行时极其容易发现 RESTful 服务。客户端仅需要简单地知道服务的基础地址,并且从这个地址客户端通过遍历资源正在使用的链接就能发现服务。OPTION 方法能被有效地用在发现服务的处理过程中。

这不意味着 RESTful 服务一点也不需要文档化。没有理由不文档化你的服务。你应该为开发人员和客户端文档化每个资源和URI。你可以用任何格式构建你的文档,但是它应该包含足够关于资源、URIs、可用方法的信息,和其他需要访问你的服务使用的信息。下面的 table 是我的 MyService 的简单文档。这个简单短小的文档包含了 MyService 的各方面并且足够开发一个客户端。

Service Name:MyService

Address: MyService/


你也可以文档化每个资源的模型表示(representations )并且提供一些简单的模型表示(representations )。

结论

开发轻量级的 Web 服务 REST 是极好的方法,容易实现,维护、暴露开放。HTTP 提供了卓越的接口来实现 RESTful 服务,比如统一接口和缓存。然而,那要开发人员正确地实现和利用这些特性。如果我们对基础有了正确地理解,那么使用现有的技术比如Python、.net、java实现REST服务是很容的。我希望这篇文章为你开始开发你自己的RESTful服务提供了足够的信息。

一个可以自动生成RESTful API的库,功能非常强大:github.com/Meituan-Dian

参考

rest.elkstein.org/2008/

http://www.drdobbs.com/web-development/restful-web-services-a-tutorial/240169069

github.com/wanbei/blog/

[转]首个微信小程序开发教程!

转自:http://gold.xitu.io/entry/57e34d6bd2030900691e9ad7

微信应用号(小程序,「应用号」的新称呼)终于来了!

目前还处于内测阶段,微信只邀请了部分企业参与封测。想必大家都关心应用号的最终形态到底是什么样子?怎样将一个「服务号」改造成为「小程序」?

我们暂时以一款简单的第三方工具的实例,来演示一下开发过程吧。(公司的项目保密还不能分享代码和截图。博卡君是边加班边偷偷给大家写教程。感谢「名片盒」团队提供他们的服务号来动这个手术,所以博卡君的教程就用「名片盒」的公众号滚动更新发布吧😄)

OK,为了让大家尽快看到这份教程,博卡君注定要熬夜了!今晚开始更新,希望明天一早就能发布第一篇教程!记录开始!看看几天能完成变身吧!

序言

开始开发应用号之前,先看看官方公布的「小程序」教程吧!(以下内容来自微信官方公布的「小程序」开发指南)

本文档将带你一步步创建完成一个微信小程序,并可以在手机上体验该小程序的实际效果。这个小程序的首页将会显示欢迎语以及当前用户的微信头像,点击头像,可以在新开的页面中查看当前小程序的启动日志。

1. 获取微信小程序的 AppID

首先,我们需要拥有一个帐号,如果你能看到该文档,我们应当已经邀请并为你创建好一个帐号。注意不可直接使用服务号或订阅号的 AppID。 利用提供的帐号,登录 https://mp.weixin.qq.com ,就可以在网站的「设置」-「开发者设置」中,查看到微信小程序的 AppID 了。

注意:如果我们不是用注册时绑定的管理员微信号,在手机上体验该小程序。那么我们还需要操作「绑定开发者」。即在「用户身份-开发者」模块,绑定上需要体验该小程序的微信号。本教程默认注册帐号、体验都是使用管理员微信号。

2. 创建项目

我们需要通过开发者工具,来完成小程序创建和代码编辑。

开发者工具安装完成后,打开并使用微信扫码登录。选择创建「项目」,填入上文获取到的 AppID,设置一个本地项目的名称(非小程序名称),比如「我的第一个项目」,并选择一个本地的文件夹作为代码存储的目录,点击「新建项目」就可以了。

为方便初学者了解微信小程序的基本代码结构,在创建过程中,如果选择的本地文件夹是个空文件夹,开发者工具会提示,是否需要创建一个 quick start 项目。选择「是」,开发者工具会帮助我们在开发目录里生成一个简单的 demo。

项目创建成功后,我们就可以点击该项目,进入并看到完整的开发者工具界面,点击左侧导航,在「编辑」里可以查看和编辑我们的代码,在「调试」里可以测试代码并模拟小程序在微信客户端效果,在「项目」里可以发送到手机里预览实际效果。

3. 编写代码

点击开发者工具左侧导航的「编辑」,我们可以看到这个项目,已经初始化并包含了一些简单的代码文件。最关键也是必不可少的,是 app.js、app.json、app.wxss 这三个。其中,.js 后缀的是脚本文件,.json 后缀的文件是配置文件,.wxss 后缀的是样式表文件。微信小程序会读取这些文件,并生成小程序实例。

下面我们简单了解这三个文件的功能,方便修改以及从头开发自己的微信小程序。

app.js 是小程序的脚本代码。我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量。调用 MINA 提供的丰富的 API,如本例的同步存储及同步读取本地数据。

//app.js

App({

  onLaunch: function () {

    //调用API从本地缓存中获取数据

    var logs = wx.getStorageSync('logs') || []

    logs.unshift(Date.now())

    wx.setStorageSync('logs', logs)

  },

  getUserInfo:function(cb){

    var that = this;

    if(this.globalData.userInfo){

      typeof cb == "function" && cb(this.globalData.userInfo)

    }else{

      //调用登录接口

      wx.login({

        success: function () {

          wx.getUserInfo({

            success: function (res) {

              that.globalData.userInfo = res.userInfo;

              typeof cb == "function" && cb(that.globalData.userInfo)

            }

          })

        }

      });

    }

  },

  globalData:{

    userInfo:null

  }

})

app.json 是对整个小程序的全局配置。我们可以在这个文件中配置小程序是由哪些页面组成,配置小程序的窗口  背景色,配置导航条样式,配置默认标题。注意该文件不可添加任何注释。

{

  "pages":[

    "pages/index/index",

    "pages/logs/logs"

  ],

  "window":{

    "backgroundTextStyle":"light",

    "navigationBarBackgroundColor": "#fff",

    "navigationBarTitleText": "WeChat",

    "navigationBarTextStyle":"black"

  }

}

app.wxss 是整个小程序的公共样式表。我们可以在页面组件的class属性上直接使用app.wxss中声明的样式规则。

/**app.wxss**/

.container {

  height: 100%;

  display: flex;

  flex-direction: column;

  align-items: center;

  justify-content: space-between;

  padding: 200rpx 0;

  box-sizing: border-box;

}

3. 创建页面

在这个教程里,我们有两个页面,index 页面和 logs 页面,即欢迎页和小程序启动日志的展示页,他们都在 pages 目录下。微信小程序中的每一个页面的【路径+页面名】都需要写在 app.json 的 pages 中,且 pages 中的第一个页面是小程序的首页。

每一个小程序页面是由同路径下同名的四个不同后缀文件的组成,如:index.js、index.wxml、index.wxss、index.json。.js 后缀的文件是脚本文件,.json 后缀的文件是配置文件,.wxss 后缀的是样式表文件,.wxml 后缀的文件是页面结构文件。

index.wxml是页面的结构文件:

<!--index.wxml-->

<view class="container">

  <view  bindtap="bindViewTap" class="userinfo">

    <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>

    <text class="userinfo-nickname">{{userInfo.nickName}}</text>

  </view>

  <view class="usermotto">

    <text class="user-motto">{{motto}}</text>

  </view>

</view>

本例中使用了 <view/>、<image/>、<text/>来搭建页面结构,绑定数据和交互处理函数。

index.js 是页面的脚本文件,在这个文件中我们可以监听并处理页面的生命周期函数、获取小程序实例,声明并处理数据,响应页面交互事件等。

//index.js

//获取应用实例

var app = getApp()

Page({

  data: {

    motto: 'Hello World',

    userInfo: {}

  },

  //事件处理函数

  bindViewTap: function() {

    wx.navigateTo({

      url: '../logs/logs'

    })

  },

  onLoad: function () {

    console.log('onLoad')

    var that = this

    //调用应用实例的方法获取全局数据

    app.getUserInfo(function(userInfo){

      //更新数据

      that.setData({

        userInfo:userInfo

      })

    })

  }

})


index.wxss是页面的样式表:

/**index.wxss**/

.userinfo {

  display: flex;

  flex-direction: column;

  align-items: center;

}

.userinfo-avatar {

  width: 128rpx;

  height: 128rpx;

  margin: 20rpx;

  border-radius: 50%;

}

.userinfo-nickname {

  color: #aaa;

}

.usermotto {

  margin-top: 200px;

}

页面的样式表是非必要的。当有页面样式表时,页面的样式表中的样式规则会层叠覆盖 app.wxss 中的样式规则。如果不指定页面的样式表,也可以在页面的结构文件中直接使用 app.wxss 中指定的样式规则。

index.json是页面的配置文件:

页面的配置文件是非必要的。当有页面的配置文件时,配置项在该页面会覆盖 app.json 的 window 中相同的配置项。如果没有指定的页面配置文件,则在该页面直接使用 app.json 中的默认配置。

logs的页面结构

<!--logs.wxml-->

<view class="container log-list">

  <block wx:for-items="{{logs}}" wx:for-item="log">

    <text class="log-item">{{index + 1}}. {{log}}</text>

  </block>

</view>

logs 页面使用 <block/> 控制标签来组织代码,在 <block/> 上使用 wx:for-items 绑定 logs 数据,并将 logs 数据循环展开节点

//logs.js

var util = require('../../utils/util.js')

Page({

  data: {

    logs: []

  },

  onLoad: function () {

    this.setData({

      logs: (wx.getStorageSync('logs') || []).map(function (log) {

        return util.formatTime(new Date(log))

      })

    })

  }

})

运行结果如下:

4. 手机预览

开发者工具左侧菜单栏选择「项目」,点击「预览」,扫码后即可在微信客户端中体验。

目前,预览和上传功能尚无法实现,需要等待微信官方的下一步更新。

如你所见,微信官方给出的开发指南还非常简单,很多细节、代码和功能都没有明确的展示,所以接下来就到博卡君展示实力的时候啦!开发教程正式开始!


第一章:准备工作

做好准备工作很重要。开发一个微信应用号,你需要提前到微信的官方网站(weixin.qq.com)下载开发者工具。

1. 下载最新微信开发者工具,打开后你会看到该界面:

2. 点击「新建 web+」项目,随后出现如下画面:

3. 该页面内的各项内容需要注意——

  • AppID:依照官方解释来填。
  • Appname: 项目最外层文件夹名称,如你将其命名为「ABC」,则之后的全部项目内容均将保存在「/ABC/…」目录下。
  • 本地开发目录:项目存放在本地的目录。

注:再次强调,如果你和团队成员共同开发该项目,则建议你们使用同样的目录名称及本地目录,以确保协同开发的统一性。如果你之前已有项目,则导入过程与以上内容近似,不再赘述。

4. 准备工作全部完成后,点击「新建项目」按钮,弹出框点「确定」。

5. 如上图所示,此刻,微信开发者工具已经为你自动构建了一个初始的 demo 项目,该项目内包含了一个微信应用项目所需具备的基本内容和框架结构。点击项目名称(图中即「cards」)进入该项目,就能看到整个项目的基本架构了:

第二章:项目构架

微信目前用户群体非常庞大,微信推出公众号以后,火爆程度大家都看得到,也同样推动着 Html 5 的高速发展,随着公众号业务的需求越来越复杂,应用号现在的到来也是恰到好处。

 

博卡君发现,微信提供给开发者的方式也在发生全面的改变:从操作 DOM 转为操作数据,基于微信提供的一个过桥工具实现很多 Html 5 在公众号很难实现的功能,有点类似于 hybrid 开发,不同于 hybrid 开发的方式是:微信开放的接口更为严谨,结构必须采用他提供给的组件,外部的框架和插件都不能在这里使用上,让开发者完全脱离操作 DOM,开发思想转变很大。

 

工欲善其事,必先利其器。理解它的核心功能非常重要,先了解它的整个运作流程。

生命周期:

在index.js里面:

开发者工具上 Console 可以看到:

在首页 console 可以看出顺序是 App Launch–>App Show–>onLoad–>onShow–>onReady。

首先是整个 app 的启动与显示,app 的启动在 app.js 里面可以配置,其次再进入到各个页面的加载显示等等。

可以想象到这里可以处理很多东西了,如加载框之类的都可以实现等等。

路由:

路由在项目开发中一直是个核心点,在这里其实微信对路由的介绍很少,可见微信在路由方面经过很好的封装,也提供三个跳转方法。

wx.navigateTo(OBJECT):保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面。

wx.redirectTo(OBJECT):关闭当前页面,跳转到应用内的某个页面。

wx.navigateBack():关闭当前页面,回退前一页面。

这三个基本上使用足够,在路由方面微信封装的很好,开发者根本不用去配置路由,往往很多框架在路由方面配置很繁琐。

组件:

此次微信在组件提供方面也是非常全面,基本上满足项目需求,故而开发速度非常快,开发前可以认真浏览几次,开发效率会很好。

其它:

任何外部框架以及插件基本上无法使用,就算原生的 js 插件也很难使用,因为以前的 js 插件也基本上全部是一操作 dom 的形式存在,而微信应用号此次的架构是不允许操作任何 dom,就连以前开发者们习惯使用的动态设置的rem.js也是不支持的。

此次微信还提供了 WebSocket,就可以直接利用它做聊天,可以开发的空间非常大。

跟公众号对比博卡君发现,开发应用号组件化,结构化,多样化。新大陆总是充满着惊喜,更多的彩蛋等着大家来发现。

接下来开始搞一些简单的代码了!

1. 找到项目文件夹,导入你的编辑器里面。在这里,博卡君使用了 Sublime Text 编辑器。你可以根据自己的开发习惯选择自己喜欢的编辑器。

2. 接下来,你需要根据自己的项目内容调整项目结构。在范例项目中,「card_course」目录下面主要包含了「tabBar」页面以及该应用的一些配置文件。

3. 示例项目的「tabBar」是五个菜单按钮:

4. 找到「app.json」文件,用来配置这个五个菜单。在代码行中找到「tabBar」:

你可以根据实际项目需求更改,其中:

  • 「Color」是底部字体颜色,「selectedColor」是切换到该页面高亮颜色,「borderStyle」是切换菜单上面的一条线的颜色,「backgroundColor」是底部菜单栏背景颜色。文字描述较为抽象,建议你一一调试并查看其效果,加深印象。
  • 「list」下的代码顺序必须依次放置,不能随便更改。
  • 「pagePath」之后的文件名内,「.wxml」后缀被隐藏起来了,这是微信开发代码中人性化的一点——帮你节约写代码的时间,无须频繁声明文件后缀。
  • 「iconPath」为未获得显示页面的图标路径,这两个路径可以直接是网络图标。
  • 「selectedIconPath」为当前显示页面高亮图标路径,可以去掉,去掉之后会默认显示为「iconPath」的图标。
  • 「Text」为页面标题,也可以去掉,去掉之后纯显示图标,如只去掉其中一个,该位置会被占用。

注意:微信的底部菜单最多支持五栏(五个 icons),所以在你设计微信应用的 UI 和基本架构时就要预先考虑好菜单栏的排布。

5. 根据以上代码规则,博卡君做好了示例项目的基本架构,供你参考:

6. 「Json」文件配置好后,「card_course」的基本结构入上图所示,不需要的子集都可以暂时删除,缺少的子集则需要你主动新建。删除子集时记得顺带检查一下「app.json」里的相关内容是否已经一并删除。

注意:博卡君个人建议你新建一个「wxml」文件的同时,把对应的「js」和「wxss」文件一起新建好,因为微信应用号的配置特点就是解析到一个「wxml」文件时,会同时在同级目录下找到同文件名的「js」和「wxss」文件,所以「js」文件需及时在「app.json」里预先配置好。

编写「wxml」时,根据微信应用号提供的接口编码即可,大部分就是以前的「div」,而现在就用「view」即可。需要用其它子集时,可以根据微信提供的接口酌情选择。

使用「class」名来设置样式,「id」名在这里基本没有什么用处。主要操作数据,不操作「dom」。

7. 以上是示例项目首页的「wxml」编码。从图中就可以看出,实现一个页面代码量非常少。

8. 「Wxss」文件是引入的样式文件,你也可以直接在里面写样式,示例中采用的是引入方式:

9. 修改代码后刷新一次,可以看到未设背景的「view」标签直接变成了粉色。

注意:修改「wxml」和「wxss」下的内容后,直接 F5 刷新就能直接看到效果,修改「js」则需点击重启按钮才能看到效果。

10. 另外,公共样式可以在「app.wxss」里直接引用。

11. 「Js」文件需要在「app.json」文件的「page」里预先配置好。为了项目结构清晰化,博卡君在示例项目中的「index」首页同级目录新建其它四个页面文件,具体如下:

经过以上步骤,案例中的五个底部菜单就全部配置完毕了。

一些感悟

这里记录下平时工作和生活上的一些想法,不定时更新:

  1. 有的时候工作上开不开心取决去你在工作中的地位,以及上头boss是否认可你、是否在乎你,特别是比较敏感的人。
  2. 发现不同的公司,公司文化和环境确实不一样,就拿一点来说,对于老板的问话,以前公司是必须及时回复,回复的技巧也要高级,现在就不一样了。。。

Android开发的前景到底怎么样?

转载:http://geek.csdn.net/news/detail/74896

本文原创发布于微信公众号AndroidDeveloper「googdev」,转载请务必注明出处。

最近公众号有不少人问我这样一个问题:「张哥,我刚接触编程,准备学习下Android开发,但是担心现在市场饱和了,Android开发的前景怎么样?」

想着可能有很多人都有这样的担心,于是就赶紧写篇文章,来跟你们谈下Android开发的前景到底怎么样?

1. 编程语言

众 所周知,Android 开发是基于 Java 编程语言的,而 Java 作为老牌成熟的编程语言,虽然经常被人诟病,但是毫无疑问,Java是目前市场上最成熟、应用最广泛的编程语言,很多成熟的业务系统 Java 都是第一选择,每月的 TIOBE 编程语言排行榜Java一直稳坐榜首位置。所以从编程语言这个点来说,学习 Android 开发你至少掌握了Java编程语言,而目前来看不管是语言的使用范围还是市场需求来说,Java 都看不到被淘汰的影子。

2. 市场需求

这个是很多人关心的,说最近感觉 Android 开发饱和了,找工作很难,所以纠结要不要学习 Android 。

不得不说,前两年的移动开发确实很火,为什么?因为一个行业刚兴起,智能手机发展之快速让人震惊,加上创业的越来越多,基本上创业都需要做一个 App,以上种种原因综合下来导致移动开发人才特别紧缺,可以说随便能鼓捣出来点东西都很容易找到工作。

而现在明显降温了,对人才的要求也越来越苛刻,现在搞一个公众号就可以创业融资了,App 不再是刚需了,所以跟前两年比需求确实少了很多。

但 是跟同类其他编程行业比移动开发依然需求蛮大的,这种现象只能说明以前是「疯狂」,而现在才是「正常」,所以那些说 Android 开发饱和了,我并不认同,因为我看到各大招聘网站 Android 跟 iOS 开发的职位跟其他岗位的招聘比并没有少,所以所谓的「饱和」只是一种从「疯狂」到「正常」的错觉而已。

3. 薪资水平

说到开发前景,工资就不得不谈,这也是很多人关注的一个话题。我今天随意看了下一些招聘网站给各个编程岗位开的价格,姑且以一线城市1-3年工作经验来看:

Android & iOS开发月薪范围大概在10-20k
Java、Php、.NET等月薪范围大概在8-15k
一些小众语言如Ruby、Python等月薪范围大概在10-20k

以上根据个人能力会有差别,而且不同公司也会有差距,上面的数据并不是那么准确,只是我粗略的一个观察,所以别纠结数据层面,但是我感觉这个范围不会差别太大,所以得出结论大概是移动开发依然是目前薪资相对较高的行业。

4. 为什么感觉不好找工作

这个也是很多人的疑问,说投了很多简历,都没有回应,总感觉今年工作特别难找。这个在这里解释下原因:

  1. 不知道我的读者们关注经济不,我老板是做投资出身的,所以跟着他我也了解了不少知识。从15年下半年开始,中国经济特别差,企业亏损严重,股票大 跌,投资人手上也没多少现金了,所以看到去年下半年很多公司倒闭,老板跑路,我亲身经历的一好哥们公司倒闭了,还拖欠了两个月工资没发。这还只是上海这 边,相对影响已经算小了,如果你仔细观察,应该能发现小城市经济更差,我是亲身体验过的,去年回家,家里很多煤矿倒闭,房地产不经济,钢铁厂亏损严重,村 里很多人都没有工作,我是亲眼见证的。

我在去年底今年初的时候还特意在公号发过一篇文章说如果想换工作建议不要裸辞,先找到好的机会再辞职,最近经济很差,不像往年,工作很好找,相信很多那时候关注我的人还有印象。

一直到现在,虽然公布的中国经济数据慢慢有所恢复,但是受到的影响不会那么快消除,还需要点时间恢复,所以经济大环境是工作不好找的一个重要原因。

  1. 因为移动开发前两年的火爆,导致很多新兴的培训机构大举招聘移动开发,所以这两年你会发现培训 iOS、Android 开发的人特别多,我自己是能感受到的,收到的一些简历比以前培训的多多了。而且相对来说培训iOS的人要更多些,我自己也是培训出来的,我这里并不是看不 起培训的。只是人越来越多,质量也是越来越差,现在很多培训的都是伪造简历来求职,所以你会发现应届生、没工作经验的人找工作较难些,因为没工作经验的初 级太多了,而现在企业也优先选择有工作经验的,现在再也不像两年前招不到人需要自己培养了,现在可选的人一大把,我为什么要先招进来再培养你呢?

但是对于一些实习生以及初学者并不是没有需求,大部分企业还是想要招聘一些基础扎实,学习能力强,甚至有点工作经验的同学,而这些可能刚好是很多培训出来的人缺乏的,至于没工作怎么获取工作经验?可以看我这篇文章「自学Android到什么程度才能找到工作?」

5. 如何选择

所以综上,真的别杞人忧天,沉下心来学好基础,提升技能比什么都重要,如果你找不到工作,只能说你学的还不够好,或者能力还不达标,并不能说明没有这个岗位的招聘,以个人能力的不行来否定整个市场的需求是弱者的行为!

还 有很多人纠结到底是选择 Android、iOS、web前端还是后端?虽然我是做Android开发的,但是我必须客观的说,没有什么区别,谁也不知道以后 Android、iOS 谁把谁打败了,谁也不知道web前端还是后端以后更吃香,我能给到你的建议是看你的兴趣所在,别纠结太多,学好了哪一个都能找到一份好的工作,先跟着你内 心的感觉学好、学精一门,而且编程语言都是互通的,以后你都会有机会接触其他领域,目前第一重要的是全身心的投入你现在想要从事的职业上,吃饱饭比什么都 重要!

以上就是我能给到你们的建议,不一定是对的,但是从我自身角度来说都是非常中肯的建议,我能帮到你们的也就以我过来的人经验与见解,帮你们指明方向而已,至于怎么走,怎么选择,全凭自己!

在一个创业型公司做研发总监应该干什么?

转自:http://www.cnblogs.com/Fredric-2013/p/5327777.html

从大公司出来在创业公司做研发总监一年半多,时间虽然不长,但也经历了不少问题和困难。想在本文中谈一谈个人对这个岗位的理解。

因为,我所在的公司是以承接项目为主,因此我也是站在这类岗位的角度来谈的。

1、项目前期(售前阶段):

1.1、需求澄清,引导并与客户一起梳理出一个利于双方的需求边界,识别出客户的关键需求;

备注:这个时候对研发总监的要求就是要和客户聊得开,展示自己的能力以给对方信任感;

1.2、架构设计,能在短期交付和长期演进的平衡下设计架构,划分子系统,在可用、可靠性等方面,前期可以简单,但要保证能扩展;

1.3、技术选型,结合团队自身和‘架构设计’完成语言、数据库、中间件等选型,此时点在于权衡交付压力和团队能力提升;

1.4、关键技术风险,对项目中可能出现风险的技术点进行识别,并在初步计划中预留额外工作量;

1.5、工作量评估及开发计划初步制定,支撑商务谈判和合同签订;

 

2、项目中期(售中阶段)

2.1、制定详细的开发计划,协调终端、前端、后端(大的项目也可能是各个子系统)之间的交付,避免彼此的功能存在依赖而阻塞,同时也要能够照顾到前期识别出的关键需求;

备注:尽可能先定接口;尽可能采用迭代;

2.2、对外控制客户的预期(这个时候项目合同已定,可以适当的和客户谈谈困难,给自己的交付留有余地),包括控制需求变更;

2.3、对内控制项目的进度和质量,包括组织例会、关键的质量活动如检视、测试、持续集成等;

2.4、项目框架搭建及核心代码编写;

3、项目后期(售后阶段)

3.1、妥善处理客户需求变更,保证不过多增加项目成本,而又不影响后续款项的收取;

3.2、版本管理,每次发布后的版本要能在配置库可回溯,出了问题可回滚;

[转载]手机App开发价格5万和50万的差别

转载的,把其中的广告去掉了,内容还是有参考价值。

普通创业者在开展移动端业务布局时,通常咨询的第一个问题是“开发一个App要多少钱?”。这个问题在百度等搜索引擎上搜索出的结果则是几万到几十万不等的回答。那么花费5万元和50万元开发一个App,最根本的区别在哪里呢?

差别一:App开发模式

在讨论App开发费用之前,客户有必要明确自己想要开发的App模式,现在的模式有两种,一种是模板式App,一种是定制型App。

模板式App顾名思义,就是一个App模型,有固定的功能,购买后客户可以修改里面的内容,视觉上做一些简单的色调更改。

但App内的逻辑关系是不能修改的,并且模板的源代码是App开发商所有,不会开放给购买者,企业数据也存放在App开发商的服务器中,需要时可以导出。目前市场上模块App的价格几千到几万不等,一分价钱一分货,价格决定App质量。

模板式App的价格公开透明,而定制型App的报价就有很大空间,有的十几万,有的几十甚至上百万。

差别二:App开发人员成本

通常定制型App的价格构成是:设计方案+UI设计+App功能开发+App后台管理系统+App上线+技术支持和维护, App的功能多而复杂,工作量、人员配置和开发周期就直线上升,价格就下不来。

一个App至少要开发iOS和Android两个系统,通常一个有一两年iOS和Android开发经验的人员成本要1万左右,因为两个系统使用不同的语言开发,所以要组建两个开发团队,那一个App项目基本的人员成本就需要6-7万。如果选择混合开发,这个成本就会降低很多,比如XXXX平台,用标准的web技术就可以同时开发出iOS、Android两个系统的App,并且web开发者的人员成本比iOS和Android开发者要低,从开发时间和人员成本上,就可以为App开发省下一大笔费用。

差别三:App开发功能

定制型App之所以有巨大的报价空间,其核心因素在于App所要实现的功能。

一个App基本的功能有登录注册、消息推送、分享、通讯、验证与安全,如果是社交、电商类App,还涉及到支付、地图、智能识别等功能。这些功能如果都自己一一开发的话,开发周期和工作量肯定直线上升。

现在市场上有很多第三方服务商,他们将这些功能封装成SDK共开发者调用,比如推送有极光推送、个推,通讯有融云、环信、智齿客服,支付有支付宝、微信支付,地图有高德地图、百度地图等等,这些功能都是用原生语言开发,缩短开发周期的同时保证了App的使用体验,但是不同的SDK调用的接口规范不一样,而XXXX平台的聚合API则统一了这些接口调用规范,这样开发时就不用花大量时间去调试接口了。
图片描述

差别四:App开发上线

App开发完成后,上线应用市场也存在一定的费用,iOS只有AppStore,但是Android却有几十个应用市场,分渠道打包的目的是针对不同市场进行数据分析,收集应用信息,为App的营销推广提供数据支持。XXXX提供一键生成12个常用渠道安装包的服务,包括360手机助手、腾讯应用宝、91助手、豌豆荚、小米、应用汇等应用市场,使打包效率进一步提升,节省App开发的时间成本。XXXX的多渠道打包兼容了TalkingData App Analytics数据统计平台的SDK,方便App进行分渠道数据统计。

我们拿一个App案例举例,偶偶足球是一个基于足球运动为核心的社区+电商App,主要功能包括足球资讯、足球装备购物和深度社交。
图片描述
这样一款App,开发团队为2人,demo40天完成,上线120天完成!
所使用到的功能模块包括:
1. iamgeBrowser
2. inputField
3. rongCloud2
等共计16个模块。而使用XXXX平台开发完全免费,偶偶足球花费的只有人员等其他必要成本。

综上所述,一个App的开发报价5万还是50万,取决于企业对App的功能、质量的要求,评估下需要投入的人工成本和工作量就可估算出App的开发成本。XXXX是混合开发平台,使用html5语言开发出媲美iOS、Android原生App用户体验的应用,同时大量节省人工和时间成本,是开发App的不错选择。

Android处理崩溃的一些实践

对于任何程序来说,崩溃都是一件很难避免的事情,当然Android程序也不例外。在Android程序中,引起崩溃的多属于运行时异常或者错误,对于这些异常我们很难做到类似Checked Exception那样显式捕获,因而最终导致了程序崩溃。本文讲介绍一些如何处理崩溃的实践,比如收集崩溃的stacktrace,甚至如何避免出现程序已停止的对话框。

如何收集崩溃信息

收集崩溃信息,可以更好的修复问题,增强程序的稳定性。Android中的崩溃收集沿用了Java的收集机制,实现起来比较简单。

1.实现UncaughtExceptionHandler

我们需要实现UncaughtExceptionHandler接口中的uncaughtException方法。该方法体中最常见的操作就是读取崩溃的stacktrace信息,然后上报到服务器数据便于开发者分析。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SimpleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final String LOGTAG = "SimpleUncaughtExceptionHandler";

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
      //读取stacktrace信息
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        ex.printStackTrace(printWriter);
        String errorReport = result.toString();
        Log.i(LOGTAG, "uncaughtException errorReport=" + errorReport);
    }
}

除此之外,还建议携带以下信息发送到服务器,帮助更快定位和重现问题。

  • 设备唯一ID(基于IMEI或者Android ID等),方便根据用户提供的id,查找崩溃的stacktrace
  • 设备语言与区域 方便重现
  • 应用的版本号
  • 设备的系统版本
  • 设备类型,如平板,手机,TV等
  • 崩溃发生的时间等

注册默认的异常处理

注册默认的异常处理就是最后的一步,很简单,通常建议放在Application的onCreate方法中进行。

1
2
3
4
5
6
7
8
9
10
public class DroidApplication extends Application {
    private static final String LOGTAG = "DroidApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(LOGTAG, "onCreate");
        Thread.setDefaultUncaughtExceptionHandler(new SimpleUncaughtExceptionHandler());
    }
}

验证

当我们刻意触发一个NullPointerException时,过滤日志adb logcat | grep SimpleUncaughtExceptionHandler类似如下信息,则说明成功了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
I/SimpleUncaughtExceptionHandler(22469): uncaughtException errorReport=java.lang.NullPointerException
I/SimpleUncaughtExceptionHandler(22469):  at com.droidyue.avoidforceclosedemo.MainActivity.causeNPE(MainActivity.java:22)
I/SimpleUncaughtExceptionHandler(22469):  at com.droidyue.avoidforceclosedemo.MainActivity.onClick(MainActivity.java:29)
I/SimpleUncaughtExceptionHandler(22469):  at android.view.View.performClick(View.java:4470)
I/SimpleUncaughtExceptionHandler(22469):  at android.view.View$PerformClick.run(View.java:18593)
I/SimpleUncaughtExceptionHandler(22469):  at android.os.Handler.handleCallback(Handler.java:733)
I/SimpleUncaughtExceptionHandler(22469):  at android.os.Handler.dispatchMessage(Handler.java:95)
I/SimpleUncaughtExceptionHandler(22469):  at android.os.Looper.loop(Looper.java:157)
I/SimpleUncaughtExceptionHandler(22469):  at android.app.ActivityThread.main(ActivityThread.java:5867)
I/SimpleUncaughtExceptionHandler(22469):  at java.lang.reflect.Method.invokeNative(Native Method)
I/SimpleUncaughtExceptionHandler(22469):  at java.lang.reflect.Method.invoke(Method.java:515)
I/SimpleUncaughtExceptionHandler(22469):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
I/SimpleUncaughtExceptionHandler(22469):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
I/SimpleUncaughtExceptionHandler(22469):  at dalvik.system.NativeStart.main(Native Method)

不出现应用崩溃对话框

在Android崩溃的时候,我们都会看到类似这样的对话框

app crash

然而,实际上有些情况下是不需要展示这个对话框的,一个常用的例子,我的程序中一个不太重要的推送服务采用了单独的进程,当这个进程崩溃时,实际上是可以允许不让用户感知的。

如果我们采取主进程仍弹出对话框,其他进程不弹出的策略,那么我们的问题,可以总结成如下三个

  • 如何判断进程为主进程还是其他进程,或者某个进程
  • 如何在某些进程不弹出应用崩溃对话框
  • 如何在主进程弹出崩溃对话框

既然问题来了,我们就开动挖掘机深挖吧。

进程判定

进行进程判定也比较容易,首先我们需要获得进程名

1
2
3
4
5
6
7
8
9
10
11
12
public static String getProcessName(Context appContext) {
    String currentProcessName = "";
    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
        if (processInfo.pid == pid) {
            currentProcessName = processInfo.processName;
            break;
        }
    }
    return currentProcessName;
}

判断主进程,则对比进程名是否和包名相同即可

1
mAppContext.getPackageName().equals(processName)

判断为某个进程,在mainifest这样这样声明

1
<service android:name=".DroidService" android:process=":service"></service>

其对应的完整进程名为com.droidyue.avoidforceclosedemo:service,我们判断可以使用如下代码

1
"com.droidyue.avoidforceclosedemo:service".equals(processName);

不弹框的处理

不弹框的需要做的就是不调用Android默认的异常处理,当异常出现时,收集完信息,执行进程kill即可。

1
android.os.Process.killProcess(android.os.Process.myPid());

主进程保持弹窗的处理

想要保持弹窗也比较容易,就是调用Android默认的异常处理。

首先需要获得Android默认的异常处理,在设置自定的异常处理之前,将Android默认处理保存起来。如下是在自定义异常处理的构造方法中获取Android默认处理

1
2
3
4
public DroidUncaughtExceptionHandler(Context context) {
    mAppContext = context.getApplicationContext();
    mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
}

然后在异常处理方法uncaughtException中调用如下方法

1
mDefaultExceptionHandler.uncaughtException(thread, ex);

注意,如果你的应用崩溃后,不调用Android默认的异常处理,也不进行杀死进程,则进程处于不可交互,即UI点击无响应状态。

源码

本示例源码,存放在Github,地址为AvoidForceCloseDemo

文章来源:http://droidyue.com/blog/2015/12/06/practise-about-crash-in-android/