[Android] gradient中设置angle角度问题

在设置gradient时,经常需要设置角度,android:angle=”90″,比如:

<shape
xmlns:android=”http://schemas.android.com/apk/res/android”
android:shape=”rectangle”>
<gradient
android:startColor=”@color/mainPinkStart”
android:endColor=”@color/mainPinkEnd”
android:angle=”90″ />
</shape>

这里需要注意的是,angle值只能是45的倍数,否则会出现下面的错误log:

Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #5<gradient> tag requires ‘angle’ attribute to be a multiple of 45
at android.graphics.drawable.GradientDrawable.updateGradientDrawableGradient(GradientDrawable.java:1354)
at android.graphics.drawable.GradientDrawable.inflateChildElements(GradientDrawable.java:1176)

[Android] FragmentPagerAdapter or FragmentStatePageAdapter获取Fragment

ViewPager和Fragment搭配使用,是很多APP经常采用的方法,但其中有很多“坑”要注意。比如这里有个需求,需要获取某个Fragment的实例,来进行一些操作。这里又分FragmentPagerAdapter 和 FragmentStatePageAdapter,两者区别见

1.对于FragmentPagerAdapter,经常用的是

"android:switcher:" + viewId + ":" + position

2.对于FragmentStatePageAdapter,不支持上面方式,这里有个简单的办法:

myFragmentStatePageAdpater.instantiateItem(null, position)

参考:http://stackoverflow.com/questions/12384971/android-fragmentstatepageradapter-how-to-tag-a-fragment-to-find-it-later

http://stackoverflow.com/questions/14035090/how-to-get-existing-fragments-when-using-fragmentpageradapter#

http://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager

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

[Android] 不通过usb调试,使用网络

当手机需要充电或usb口被其他占用时,可以通过设置网络来进行连接电脑调试,前提是手机和电脑已经联网。

  1. 首先手机通过usb连接电脑
  2. 在命令行输入:adb tcpip 5678(5678为端口号,可随意指定)
  3. 断开usb数据线连接
  4. 在命令行输入:adb connect <手机IP地址>:5678
  5. 然后就可以在电脑端使用adb命令操作手机了,例如adb install apk等,由于通过网络传输,速度没有直接连接usb快。
  6. 如果要恢复usb,使用命令:adb usb即可。

注意:手机IP可以在手机的“设置”->”关于手机”->“状态信息”里查看,不同手机路径稍微有差别。

这个方法在实际使用中有时会慢,根据当前网络情况会有很大差别。所以比较稳定的办法还是通过usb调试吧。

JNI崩溃问题定位

一般native代码导致的崩溃问题,奔溃日志提示大概类似这样:

Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 13261

只有这样而没有详细的调用栈信息,这样我们开发者无法定位到JNI中到底哪一行导致程序崩掉的。根本无法定位问题所在,就更不用说解决问题了。

好在NDK给开发者们提供了ndk-stack工具(在NDK根目录下),我们可以通过ndk-stack工具来查看so库中崩溃的堆栈信息。

NDK编译时已DEBUG模式编译

如果是使用命令行编译,则使用如下语句:

ndk-build clean all NDK_DEBUG=1

clean all 的意思是编译之前先清理全部上次编译生成的内容。NDK_DEBUG=1 意思是生成调试版本的文件。加了这个参数后 调试的时候能定位到源码行数。

如果是使用gradle,则写法如下(注意这里已经覆盖了gradle默认的NDK编译,详细请前往《Android Studio覆盖了gradle默认的NDK编译》):

task ndkBuild(type: Exec) {
    commandLine 'ndk-build', '-C', file('src/main/jni').absolutePath, 'clean','all', 'NDK_DEBUG=1'
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

最后记得在AndroidManifest.xml设置debuggable为true ,在Application节点中。

ndk编译so库并运行程序

前提是要搭建好NDK开发环境并在项目中集成NDK,不会的可以参考Ubuntu下NDK编译环境搭建及在Android Studio中集成NDK

为了演示,我这里先模拟一个错误:

JNIEXPORT jstring JNICALL Java_com_liuling_ndkjnidemo_JniUtils_getStringFromC
        (JNIEnv *env, jclass obj) {
    int * p = NULL;
    *p = 1;    //这里会导致程序崩溃
    return (jstring)(*env)-> NewStringUTF(env, "I am string from jni");

}

使用ndk-stack工具定位崩溃信息

在命令行中执行如下命令:

adb logcat | 你的NDK所在的路径/ndk-stack -sym 你的项目所在的路径/app/src/main/obj/local/armeabi

这里要确定,ndk编译后生成了”你的项目所在的路径/app/src/main/obj/local/armeabi”目录,也就是这个目录要存在。

执行完这个命令之后,终端会阻塞在那,一旦程序崩溃,就会在终端打印出崩溃信息栈。如图所示:

从崩溃信息可以看出导致崩溃的代码是在com_liuling_ndkjnidemo_JniUtils.c中的13行。

打开com_liuling_ndkjnidemo_JniUtils.c文件查看代码,确实是在13行出的问题。

能够定位崩溃所在的位置,就对于我们排查问题来说有很大的帮助,其实修复bug大部分时间都是在找哪里出的问题,能够快速找出哪里出的问题,问题也就很快修复了。

转自:http://liuling123.com/2016/06/ndk-stack.html?utm_source=tuicool&utm_medium=referral

Android Development : Some of the best practices

英文链接:https://medium.com/@laanayabdrzak/android-development-some-of-the-best-practices-27722c685b6a#.5balzou11

Hi! After a modest number of projects I’ve decided to share with you some of the things that experience made me learn, I mean the hard way.

Here is the selection:

  • Think twice before adding any third party library, it’s really a serious commitment
  • Don’t use a database unless you really need to
  • You can think about realm it’s really awesome!
  • Hitting the 65k method count mark is gonna happen fast, I mean really fast! And Multidexing can save you
  • RxJava is the best alternative to AsyncTasks and so much more
  • Retrofit is the best networking library there is
  • Don’t write your own HTTP client, use Volley or OkHttp libraries
  • Shorten your code with RetroLambda
  • Combine RxJava with Retrofit and RetroLambda for maximum awesomeness!
  • I use event Bus and it’s great, but I don’t use it too much because the codebase would get really messy
  • Package by Feature, not layers
  • Move everything off the application thread, that’s can save you fromANR
  • lint your views to help you optimize the layouts and layout hierarchies so you can identify redundant views that could perhaps be removed
  • Use Gradle and its recommended project structure
  • Put passwords and sensitive data in gradle.properties
  • Use styles to avoid duplicate attributes in layout XMLs
  • Do not make a deep hierarchy of ViewGroups
  • Monitor power source and battery more data updates while charging? Suspend updates when battery is low?
  • You can think about JobScheduler
  • onLowMemory() it will be called when the whole System runs low on memory, not your App, so you can’t exactly avoid OOMs with it.
  • Drain battery is 30% (image, animation, …), and 70% (Analytics, ads, maps, gps)
  • Monitor connectivity and type of connection (more data updates while on wifi?)
  • Use the Account Manager to suggest login usernames and email addresses
  • Give your methods a clear name for what they are going to do
  • The launch screen is a user’s first experience of your application
  • Do not show the launch screen if you don’t have to
  • Tests are great for performance: Write slow (but correct) implementation then verify optimizations don’t break anything with tests
  • Keep your colors.xml short and DRY, just define the palette
  • Also keep dimens.xml DRY, define generic constants
  • In reality perfExternal is rarely used as an application on theexternal storage is stopped once the device is connected to a computer and mounted as USB storage
  • Use StringBuffer or Stringbuilder classes when there is a lot of modifications to string of characters
  • To avoid Memory Leaks :

1. Don’t reference View inside AsyncCallback

2. Don’t reference View from static object

3. Avoid putting views inside collection that’s don’t have clear memory pattern, you can use WeakHasMap

  • FlatBuffers is an efficient cross platform serialization library, so use it
  • Serializable it’s simple to implement, but in term of performance it’s really bad 🙁

Android Project Structure — alternative way

We all know how android project structure looks like — place all images inside this folder and all layouts inside that folder. But… during project development the number of files grow up rapidly and it becomes hard to navigate and search needed file.

Typical android project structure

Resource folder — per screen

In case if your screens consist of big amount of layouts, drawables, dimensions — it make sense to create separate resource folder for every screen.

Resource folder — per screen android project structure

As you can see on image above we have two root folders inside main folder:

  • res-main contain all common resources which are used on more than one screen.
  • res-screen contain resource folders for every screen e.g. about, chat,event details, event list, home, login

Let’s take a look what we have inside chat screen resource folder.

Resource folder

Chat itself consist of several xml layouts files so we created chat layoutfolder and moved all those files here. It also has a lot of .png images which are used only on chat screen, so we moved all those images files to chat drawable-hdpi, drawable-xhdpi, drawable-xxhdpi and drawable-xxxhdpifolders.

When times come to implement landscape layout or tablet version of chatwe will make layout-land and layout-sw720dp folders inside chat screen resource folder.

How declare screen resource folder?

Open app.gradle file and declare sourceSets inside android section. More about resource merging here.

sourceSets {
    main {
        res.srcDirs = [
                'src/main/res-main',
                'src/main/res-screen/about',
                'src/main/res-screen/chat',
                'src/main/res-screen/event-detail',
                'src/main/res-screen/event-list',
                'src/main/res-screen/home',
                'src/main/res-screen/login',
        ]
    }
}

Instead of declaring all resource files explicitly you can write simple script which will add all sub folders of given folder to srcDirs.

sourceSets {
    main {
        file('src/main/res-screen')
                .listFiles()
                .each { res.srcDirs += it.path }
    }
}

Note: above works only if you are in Project view

Conclusion

If you have a big project and want to arrange your folders, quickly see which layout, drawables, values, etc. belongs to which screen try to use resource folder — per screen android project structure.

Android Development for the Rural World: A How To Guide

英文地址:https://medium.com/android-news/android-development-for-the-rural-world-a-how-to-guide-b2da153e0612#.796qheegp

Building for the “next billion” is a challenge, thanks to difficulties posed by the lack of internet, electricity, and low-cost devices. At SocialCops, we set out to build an Android data collection application that could work in the most remote, rugged parts of the world. We have been successful in building an Android application, Collect, that works without internet, on phones that cost INR 2,000 (USD 30), on 512 MB memory, and 1 GB storage. We still managed to include features such as voice to text, media capture, and location-based verification. Here’s how we did it.

Only 1 out of 3 people can access the Internet today. In a country like India, an estimated 75% of the population doesn’t have access to Internet facilities. Today, most of the data that we “mine” in the big data revolution comes from the 30% of the world’s population that can access the Internet. So much for #bigdata. At SocialCops, we aim to drive the most important decisions facing mankind through data — in most cases, these decisions heavily impact the 70% non-internet-accessing, non-Facebook-using population.

We set out to map the remotest of areas — along with the people living in them — by providing a free data collection and monitoring tool for non-profits around the world. By building a kickstarter for data collection, we aimed to empower non-profits and public sector organizations to effectively collect and manage their data. We decided to build the tool on Android. Given the Android revolution and the decreasing cost of phones, it was the only logical option. It wasn’t the easiest task though.

Challenge 1: Working with Memory Constraints in Low-Cost Android Devices

Most low-cost Android devices have a limited memory (RAM), usually 512 MB or 1 GB of RAM. This makes developing a “data collection device” for Android a difficult task. The data structure needs to be designed with the memory consumption of the application in mind. No user enjoys an “OUT OF MEMORY” warning message flashing on their screens.

To optimize memory consumption, we defined objects for each entity like surveys, questions, and other parameters. We carefully controlled each object to prevent memory leaks and to ensure the object was deleted while not in use. This saved us tons of memory and increased the speed of the application.

These are some tips to ensure your Android application can function with low memory:

1. Avoid memory leaks: Memory leaks are usually caused by holding on to object references in global members. Remember to release any reference objects at the appropriate time.

2. Use services sparingly: If your app needs a service to perform work in the background, do not keep it running unless it’s actively performing a job.

3. Release memory as it becomes tight: The onTrimMemory callback can be used to check when the overall device memory is getting low.

4. Check how much memory you should use: Call getMemoryClass to get an estimate of your app’s available heap in megabytes.

5. Release memory when your user interface is hidden: Another cool trick is to release any resources that are used only by your UI when the user navigates to a different app and your UI is no longer visible.

6. Avoid wasting memory with bitmaps: When you load a bitmap, keep only the resolution you need for the current device’s screen.

7. Use optimized data containers: Use SparseArrray, instead of generic HashMap.

8. Stay aware of memory overheads: Make sure you have sufficient knowledge about the cost and overhead of the language and libraries you are using, then keep this in mind at every step when you design your app. The less the RAM you use, the greater the efficiency of your Android application. This allows more data to be processed.

Memory is especially important while making an app usable without internet. You will need to store and process all your data at the same time, since you can’t fetch the required data and process it.

Challenge 2: Working with Storage Constraints in Low-Cost Android Devices

Given the limited storage on Android devices (e.g. 1 GB, no internal storage space, or 1 GB — 16 GB external memory), we had to leverage innovative solutions to ensure that field workers could collect data and store it locally until they could access internet at district centers. There are many alternatives available to store data, which you can choose from depending on your needs. Here are some storage parameters that you can use in Android:

1. Shared Preferences: This is a key/value store where you can save data under a certain key and easily access it by entering the key. This is very useful when you need to store a small amount of data, but storing and reading large structured data is extremely difficult as you need to define a key for every single data point. Furthermore, you can’t search within the data except when you have a certain concept for naming the keys. This is perfect for the NOSQL concept (JSON).

2. SQLite: Large amounts of similarly structured data should be stored in a SQLite database. Since the data is structured and managed by the database, the data can be queried using a query language like SQL. This makes it possible to search within the data and get a subset of the data that matches a certain criteria. Managing and searching large sets of data influences performance, so reading data from a database can be slower than reading data from Shared Preferences.

3. Files: Android uses a file system that is similar to disk-based file systems on other platforms. File objects are well-suited to reading or writing large amounts of data in start-to-finish order without skipping around. For example, Android files are perfect for image files or anything exchanged over a network, and these can be stored in internal or external storage before they are uploaded.

4. Realm Database: Realm provides an awesome database to manage JSON-formatted data. Realm is an open-source project with great issue tracking and support. Read more about our experience with Realm.

In the first version of Collect, we used Shared Preferences to store JSON data and Files to store media files until they can be uploaded to the server. For the next version, we are exploring Realm for storing JSON-formatted data.

Note: Don’t forget to delete local data from storage once it has been uploaded on the server.

Challenge 3: Dealing with Performance and Speed Issues in Low-Cost Android Devices

The speed of your application is one of your users’ top priorities, so you should be sure to optimize speed wherever possible. One classic way of improving speed is to fetch information in one go and restrict updates to when there is a worthy change.

Some of the other ways to optimize for speed are:

  1. Avoid creating unnecessary objects
  2. Prefer static over virtual
  3. Use enhanced for Loop Syntax
  4. Avoid using floating point
  5. Know and use the Libraries
  6. Use Native methods carefully

Using these simple methods, you can optimize performance and increase speed. This will ensure that the application runs in the same way both online and offline.

Challenge 4: Designing a User Interface for a Population That Has Never Been Online

Applications for rural India require a simple, intuitive user experience to ensure that rural users can actually use the platform. Designing for the next billion poses a whole new level of challenges, which we explored in depth ina separate blog post.

On the mobile front — we had to build the mobile application keeping in view the erratic internet connectivity in remote areas. The one mantra we religiously followed was to keep things simple and show users information up front.

Challenge 5: Optimizing Battery Use

The key limitation to using a phone in the rural world is battery — once a phone runs out of battery, data collection is done for the day. This is why it’s essential to be careful about how much battery an app uses.

While developing an Android application for the offline world, you need to be specific about everything you’re storing offline, the space you’re providing to every module, and the processes you’re running. Your user will most likely be using a low-cost phone, so make sure you know how to store information, change the frequency of storage, and notify the user if they need more storage space.

To optimize battery, optimize the way you fetch locations for the use case of your application. For example, if the application is navigation based, then the location has to be fetched faster. If we need the location at one point, you can use a single fetch. You can also decrease the location accuracy, since higher accuracy uses more battery.

We use Battery Historian (a tool in the Android studio) to observe an application’s battery consumption.


Most Android developers love the thrill of coding for the latest gadgets in the market, with rich graphics and lots of heavyweight features. However, this changed when I visited Mewat in Haryana to observe a field pilot with our partner NGO. I saw my product in the hands of people who had never used technology before and the sheer joy they derived from simply tapping on answers. I experienced a strange high for having conquered the challenge of developing for a world that hasn’t seen the internet age. Trust me — that feeling of building something that could potentially change someone’s life is indescribable.


This article was first published on the SocialCops blog on 26 December 2014 and updated on 19 May 2016.

Stub driven mobile app development

英文链接:https://medium.com/cobe-mobile/stub-driven-mobile-app-development-8dea10459621#.65ngg6htk

For the last month I’ve been developing an app that relies heavily on a server-side API. The whole process was a mess. This is what a typical morning looked like: I would start working on a feature in the UI, realise I have a bug in my network layer, patch it up, continue working on the feature, realise there’s data I need that’s not in the server response, tell the backend guy it’s missing, have lunch, then forget what I was doing because I’m working on five things at once! That is no way to live.

So I got an idea. Why not just make a stub backend. That is, make a fake version of the service your app relies on, before you even start working on a single feature. If your app relies on some sort of server-side API to fetch data, write a class that will give you stub data. Make that class implement an interface, and then make the rest of your app talk to the interface.

Why?

Easier development

I probably spent about 20% of my development time of this app going trough the whole flow of the app until I get to the point I want to test, just because that’s the way our backend is set up. Had I worked with dummy data and stubs from the beginning, I would have been able to test each part of the app separately and have complete control over the whole process.

Faster development

Overhead doesn’t always mean slower. Construction workers working on your building would sure as hell have a harder time without their scaffolding. In this case, the (usually small) time it takes to write a dummy network service is minimal compared to the amount of time you’ll be waiting for your backend team to fix a server-side bug.

Switch cost

A programmer coding at full throttle is keeping zillions of things in their head at once: everything from names of variables, data structures, important APIs, the names of utility functions that they wrote and call a lot, even the name of the subdirectory where they store their source code. If you send that programmer to Crete for a three week vacation, they will forget it all. — Joel Spolsky

This is a real thing. It means that humans are more efficient at working on one thing at a time because it takes us time to get used to new ways of thinking. Constantly switching from working in your network layer to writing a new feature in the view layer means you’re always in that ‘getting started’ phase.

Define functionality early on

Crash Early — A dead program normally does a lot less damage than a crippled one. — The Pragmatic Programmer

This is a popular saying in programming. But it doesn’t apply to just design — it also applies to the whole development process. You want to define your architecture as early as possible. And then, no matter how good you are, you will have made a ton of mistakes. This is why you start with a stub and make your app around it. This will show you the shortcomings of the way you’ve designed your app early on, and give you the opportunity for very easy change.

Parallel development

If you set up your architecture correctly, (layers decoupled from one-another and using interfaces for communication) your app won’t actually care whether it’s the server or a class from its own bundle supplying the data. So, your backend team can work on their end, and you can work on yours, and when it comes time you just join forces.

Makes you think of future changes

Software development is a whole series of changes. The whole point of using an SVN is to track changes. So, you want to make changes as easy and painless as possible. Using stubs will force you to design your architecture in a way where you can change the underlying model and network service without having to change your UI layer. This will make your life a lot easier down the line.

Sets you up for UI testing

UI testing is a huge pain in the ass. But it’s also a huge lifesaver. Using stubs will make your UI tests run much faster, and you’ll have much more control over them.

How?

  1. Make an interface (protocol) that the object (only one object) that will talk to the server should implement.
  2. Make an implementation for the happy path (return dummy data, process requests with the correct response, etc.)
  3. Make an implementation for the sad path (return no data, junk, incorrect responses, etc.)
  4. Create your app. Never talk to the implementations above directly, always use the interface.
  5. Switch between the happy and sad implementations to check if your app handles both correctly.
  6. When you’re done with the app, create your real network service.
  7. Wohoo! 🎊

Enjoy your stress-free development process!