返回首页 Android&Java 技术笔记

基础

构建

测试

定制

视野

最佳实践

深入

杂谈

后端

前端

一些很棒的点子

系统API

  • 自Android 5.0之后,用户的“最近任务”(recent tasks)视图,可以自定义了,支持自定义图标、标题、顶栏色彩;参考:developersblog
  • 全新的Android编译系统:Jack & Jill
  • 使用LinearLayout的divider属性,设置为shape drawable,控制其子元素之间的间距
  • 使用wedget,在桌面上显示内容。示例
  • Android 5.0引入TextView的CSS样式fontFeatureSettings
  • 利用Action Intent尽可能利用用户手机上已有的APP功能,还不需要相关的权限
  • TextView的高级玩法:CompoundDrawable,shadow,Typeface自定义字体,Shader,HTML渲染(支持自定义tag)、Span(SpannableString:字符级别、段落级别、对其),自定义Span(立式分数、彩虹效果、彩虹动效、可点击URL、Emoji...)用xml定义drawable动画

    AdvancedTextView.png

  • Android integration of multiple icon providers such as FontAwesome, Entypo, Typicons,...
  • Shape Drawable:形状、圆角、边框、填充、渐变色填充等
  • View绘制时加上特效:Shader,图像渲染、线性渐变、环形渐变、扫描渐变、组合渐变
  • 安卓系统的“售货亭模式”
    • 只允许用户在一个应用程序内使用,不能接收到系统通知,状态栏,退出程序,类似于ATM机,只能使用一个应用程序。
    • 5.0:设置菜单内Screen pinning mode;startLockTask()
    • pre 5.0:
      • 自启动:监听启动事件android.intent.action.BOOT_COMPLETED,随系统启动APP;
      • 监听返回键,并且不返回;
      • manifest文件启动activity添加三个category:android.intent.category.HOMEandroid.intent.category.LAUNCHERandroid.intent.category.DEFAULT
      • 电源键:只在4.0以下的系统可以达到效果
        @Override  
        public void onAttachedToWindow() {  
          getWindow().addFlags(  
              WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
          getWindow().addFlags(  
              WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);  
        }
      • 系统对话框:监听失去焦点事件,然后发广播关掉所有系统对话框
        @Override  
        public void onWindowFocusChanged(boolean hasFocus) {  
        super.onWindowFocusChanged(hasFocus);  
        if(!hasFocus) {  
          Intent closeDialog =   
                new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);  
          sendBroadcast(closeDialog);  
        }  
        }  
      • 虚拟键盘
      • 状态栏:全屏、TYPE_SYSTEM_ALERT、截取状态栏区域的点击事件
  • onResumeFragments
    • FragmentActivity的子类(AppCompatActivity等)均有此lifecycle方法
    • FragmentActivity的onResume函数调用的时候,所有的Fragment都还没有onResume,Activity并不能保证保存的状态已被恢复,而在这种情况下,是不能进行fragment的transaction的,而在onResumeFragments则能保证调用的时候activity已经恢复了状态
    • read more
  • 使用ViewPager的同时不用Fragment作为显示的内容
  • Notification中加入联系人信息之后,通知消息的显示将有更高的优先级
  • xml中使用tools属性来辅助IDE预览
    • xml需要预览的内容,统统用tools:属性,否则会有运行时开销,参考
    • 辅助lint:类似于@SuppressWarnings(tools:ignore),@TargetApi(tools:targetApi),指定locale(tools:locale)
    • 辅助预览layout:tool:context, tools:showIn, tools:menu, tools:actionBarNavMode, 指定frament的layout(tools:layout),tools:listheader/listitem/listfooter
    • Support Annotations: @Nullable/NonNull, resources ids(), range, collection size, TypeDef, Thread(MainThread/UiThread/BinderThread/WorkerThread), "Architecture"(CallSuper/CheckResult/VisibleForTesting), Permission{ RequiresPermission(Manifest.permission.BLUTOOTH) }, proguard(Keep)
    • ViewDebug: @ViewDebug.ExportedProperty
  • Percent layout library

    • 为什么要有Percent?
      • LinearLayout的layout_weight属性可以实现按需+比例分配空间,但是当相对定位与比例分配都需要使用的时候,就不得不使用两层Layout来实现了,有损性能;另注:当LinearLayout只有一个子View使用layout_weight属性时,将需要按比例分配的长/宽属性置为0,可以提高性能,因为layout_weight被使用时会有两遍measure,而如果置为0,使用layout_weight的子View第一遍measure就可以省略。
      • 嵌套的layout_weight使用将会导致性能下降,指数级
    • PercentRelativeLayout/PercentFrameLayout
    • 应该还是会需要两次measure,还是应该尽力避免嵌套Percent,此外,减少Layout的层数是一个常识
    • 例子

      <android.support.percent.PercentRelativeLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
      
        <View
          android:id="@+id/first"
          android:background="@color/sa_green_dark"
          app:layout_heightPercent="50%"
          app:layout_marginLeftPercent="25%"
          app:layout_marginTopPercent="25%"
          app:layout_widthPercent="50%" />
      
        <View
          android:layout_width="0dp"
          android:layout_height="32dp"
          android:layout_alignLeft="@id/first"
          android:layout_alignStart="@id/first"
          android:layout_alignRight="@id/first"
          android:layout_alignEnd="@id/first"
          android:layout_below="@id/first"
          android:layout_marginTop="8dp"
          android:background="@color/light_grey" />
      
      </android.support.percent.PercentRelativeLayout>

      PercentRelativeLayout.png

    • pitfalls
      • 当子View需要的长/宽大于给定的percent时,可以通过指定layout_width/height为wrap_content来实现大小扩展,然而似乎不起效?
      • Percent*Layout中不要使用padding,否则总大小将小于100%,可能会导致对其问题
  • Layout animations on RecyclerView
    • RecyclerView的子类,重写protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count)方法,控制layout动画的播放
    • 使用xml定义animation
  • Chrome custom tabs
  • 执行定时任务,可能的实现方式有:Alarm, JobScheduler, API 21+, JobSchedulerCompat API 10+, GcmNetworkManager, 分享
  • RenderScript例子:HealingBrush
  • 安卓6.0引入的运行时权限系统,如果APP使用ACTION_SEND分享文件,并使用了file://形式的Uri,那么接收APP将需要READ_EXTERNAL_STORAGE权限,否则将会崩溃;而如果使用content://形式Uri,接收APP将不需要权限;所以应该使用后者形式;
  • 安卓6.0引入了App Links,将限制一个Intent只能被通过App link验证的APP打开,具体验证方式可以参考官方文档;packageManager.queryIntentActivities(intent, MATCH_DEFAULT_ONLY);将至多返回一个结果;MATCH_ALL这个flag起作用的前提是尚无通过验证的APP,否则也只会有一个结果;详见
  • 在manifest中设置android:windowSoftInputMode="adjustResize"后,activity内的“可折叠”ViewGroup,例如ScrollView会在键盘弹起时减小其高度,然而如果在activit的theme中设置android:windowFullscreen="true"或者android:fitsSystemWindows="false",那么adjustResize都将不起作用。
  • 如果Activity使用Theme.NoDisplay,并且没有立即finish,那APP将会ANR,详见

Material design

构建/工具

  • 利用buildSrc工程和Codemodel自动生成代码,buildSrc目录下的代码将作为gradle插件被编译,并自动添加到工程的依赖中
  • 第三方库在manifest中声明的权限,可能app中并不会使用,可以通过uses-permission标签的tools:node="remove"属性,使得gradle在进行manifest merge时,移除该权限,例子:
    <uses-permission 
      android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
    <uses-permission 
      android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
  • MultiDex

    MultiDex会导致build变慢,在Dalvik虚拟机上(未使用ART技术时),APP启动速度也会变慢,因为ClassLoader要从第二个(甚至更多个)dex文件中加载类;
    有时候还会导致build过程中的dex步骤报OOM错误;新提出的Jack&Jill构建技术将解决这一问题;

      UNEXPECTED TOP-LEVEL ERROR:
      java.lang.OutOfMemoryError: GC overhead limit exceeded
        at com.android.dx.cf.code.ExecutionStack.copy(ExecutionStack.java:66)
        at 
        ...

    在build.gradle中加入以下片段即可解决:

      android {
        // ...
        dexOptions {
          javaMaxHeapSize “2048M”
        }
      }
  • 缩小APK包体积的Tips
    • Proguard
    • Lint
    • 不必为每种dpi打包资源文件(图标)
    • 移除第三方库中不必要的资源文件
      defaultConfig {
          resConfigs "en", "de", "fr", "it"
          resConfigs "nodpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
      }
    • 图片压缩,9-patches
    • Limit the number of architectures, armabi, x86 is enough
    • Reuse whenever possible:图标如果只是颜色不同、旋转,则可以只打包一个,然后通过tint/tintMode/ColorFilter/RotateDrawable来重复利用
    • Render in code when appropriate
    • Going even further? Server side packaging,根据设备具体细节打包资源,但是有一定风险。
  • 使用pre-dex jar来减小最终打包app的大小,避免multi-dex的发生,但需要保证在使用某个库的类之前,pre-dex jar已经被加载,详情
  • 通过gradle配置sourceSets让单元测试和集成测试共享代码,受此启发,可以更加高度定制化代码路径。详见

有意思的第三方库/教程

Google API

最佳实践

  • 使用Headless Fragment把部分Activity公用的逻辑封装起来,避免将只被部分Activity公用的逻辑加到所有Activity的父类中。
  • 通过Intent调起其他应用时,需要先检查是否有应用可以响应此Intent:

      if (sendIntent.resolveActivity(getPackageManager()) != null) {
          startActivity(sendIntent);
      }
上一篇: 安卓系统动效 下一篇: Android项目架构