Android使用总结2

不知不觉,在小米工作快两年了。


View

onMeasure\onLayout\onDraw

  • View measure方法传递过来的MeasureSpec是父View希望你的大小,通过重写onMeasure方法来使用MeasureSpec和自己的特性来决定自己的大小,View默认的onMeasure实现是依靠MeasureSpec和minWidth属性来确定大小。onMeasure方法最后调用setMeasuredDimension方法,这样View的getMeasuredWidth就有返回值了。
  • ViewGroup getChildMeasureSpec方法根据自己的MeasureSpec、Padding、子View的LayoutParams来决定子View的MeasureSpec,然后调用子View的measure方法将MeasureSpec传递过去。生成子View的MeasureSpec时,父View是wrap_content,则父View的mode是AT_MOST,子View是match_parent还是wrap_content的结果相同,都是mode是AT_MOST,size是父的size。在这种情况下对于一个直接继承自View的自定义View来说,它使用View默认的onMeasure方法会使wrap_content和match_parent属性的效果是一样的,因此如果要实现自定义View的wrap_content,则要重写onMeasure方法。比如TextView、ImageView均实现了自己的onMeasure。
    532901-20170305163306735-2118101905.png

  • ContentFrameLayout的mode是Exactly,size是屏幕宽度,它的子View只有一个,就是我们写在xml里的根ViewGroup,当我们的根ViewGroup是wrap_content时,则它的MeasureSpec的mode是AT_MOST,否则是Exactly。
  • FrameLayout的Measure效率最高,RelativeLayout因为有横向的Rule有纵向的Rule,所以需要Measure两遍,效率最低。LinearLayout少使用weight。FrameLayout为wrap_content时子View中match_parent的个数不超过1个。
  • FrameLayout不断生成子View的MeasureSpec,遍历测量子View,然后用子View的maxWidth和maxHeight和传给自己的MeasureSpec来最终决定自己的宽高。
  • 当FrameLayout设置wrap_content,只有一个子View设置成match_parent,则这个子View的MeasureSpec为mode AT_MOST,size是父Size,子View通过自己的onMeasure测量出自己的宽高,只measure一遍。如果有两个子View设成match_parent,则在父View measure结束后会把这两个子View重新measure一遍,MeasureSpec为mode Exactly,size是父View的measuredWidth。比如一个TextView第一遍measure的宽高是100x80,另一个TextView的第一遍measure宽高是80x100,经过第二遍measure两个TextView的宽高均变成100x100。
  • getMeasuredWidth方法获得的值是setMeasuredDimension方法设置的值,它的值在measure方法运行后就会确定
    getWidth方法获得是layout方法中传递的四个参数中的mRight-mLeft,它的值是在layout方法运行后确定的
    一般情况下在onLayout方法中使用getMeasuredWidth方法,而在除onLayout方法之外的地方用getWidth方法。
  • View的onLayout方法是空实现,即View不需要做额外的Layout
    ViewGroup的onLayout方法需要Layout子View的位置,layout(int l, int t, int r, int b)里传递过来的左上右下参数是相对于父View左上角点的左上右下,即r=l+measuredWidth,b=t+measuredHeight
    532901-20170306011639813-1524272665.png

  • View的onDraw方法是空实现,自定义View均需要实现自己的onDraw,View没有子View,所以dispatchDraw是空实现
    ViewGroup先绘制背景,绘制自己,然后不断dispatchDraw来绘制child,最后绘制装饰
    View dispatchDraw的时候先把canvas clip成父View的大小,即使子View的onMeasure、onLayout比父View大,draw的时候canvas也不能比父View大,所以画不到额外的区域,但是如果子View设置了CLIP_CHILDREN属性,则在dispatchDraw时,canvas可以大于父View的canvas。
  • 当一个View不需要绘制内容时,系统进行相应优化。默认情况下:View 不启用该标记位(设置为true);ViewGroup 默认启用(设置为false)
    setWillNotDraw参数设置为true:当自定义View继承自 ViewGroup 、且本身并不具备任何绘制时,设置为 true 后,系统会进行相应的优化。
    setWillNotDraw参数设置为false:当自定义View继承自 ViewGroup 、且需要绘制内容时,那么设置为 false,来关闭 WILL_NOT_DRAW 这个标记位。
    LinearLayout的setWillNotDraw取决于是否有divider
  • Android 动画就是通过ParentView 来不断调整 ChildView 的画布坐标系来实现的,下面以平移动画来做示例,假设在动画开始时 ChildView 在 ParentView 中的初始位置在 (100,200) 处,这时 ParentView 会根据这个坐标来设置 ChildView 的画布,在 ParentView的 dispatchDraw 中它发现 ChildView 有一个平移动画,而且当前的平移位置是 (100, 200),于是它通过调用画布的函数traslate(100, 200) 来告诉 ChildView 在这个位置开始画,这就是动画的第一帧。如果 ParentView 发现 ChildView 有动画,就会不断的调用 invalidate() 这个函数,这样就会导致自己会不断的重画,就会不断的调用 dispatchDraw 这个函数,这样就产生了动画的后续帧,当再次进入 dispatchDraw 时,ParentView 根据平移动画产生出第二帧的平移位置 (500, 200),然后继续执行上述操作,然后产生第三帧,第四帧,直到动画播完。
  • View的measure方法是final方法,不可重写,layout和draw方法都不是final方法

卡顿

  • 用户容易在UI执行动画或者滑动ListView的时候感知到卡顿不流畅,是因为这里的操作相对复杂,容易发生丢帧的现象,从而感觉卡顿。有很多原因可以导致丢帧,也许是因为你的layout太过复杂,无法在16ms内完成渲染,有可能是因为你的UI上有层叠太多的绘制单元,还有可能是因为动画执行的次数过多。这些都会导致CPU或者GPU负载过重。
    我们可以通过一些工具来定位问题,比如可以使用HierarchyViewer来查找Activity中的布局是否过于复杂,也可以使用手机设置里面的开发者选项,打开Show GPU Overdraw等选项进行观察。你还可以使用TraceView来观察CPU的执行情况,更加快捷的找到性能瓶颈。
  • 如果Activity要求背景色是白色,在layout中去设置了背景色白色,Activity的布局最终会添加在DecorView中,DecorView的背景就没有必要了, 所以调用mDecor.setWindowBackground(drawable);那么可以在Activity调用getWindow().setBackgroundDrawable(null);也可以写在Activity的Theme里

Renderscript

  • Android Api 11 中 引入的 Renderscript 可以用 GPU 来提高图片处理的速度,而在 API 17(4.2.1) 中,Renderscript 把一些常用的图片处理功能添加到系统中了,这些功能被称之为 Intrinsics 。包含 图片 模糊、混合、Matrix Convolution (矩阵卷积) 等常用操作。
    Read more: http://blog.chengyunfeng.com/?p=596#ixzz3lz3Rg5e5

RecyclerView

  • RecyclerView是ListView和GridView的升级版,系统实现了线性布局、网格布局、瀑布流布局,自定义横向滚动、纵向滚动, 规范了ViewHolder的使用,支持局部刷新notifyItemChange,支持增加删除Item时的动效,更优化的回收机制
  • 把onMeasure和onLayout过程交给LayoutManager处理,能实现复杂的布局
  • ItemTouchHelper|OnItemTouchListener|ItemAnimator|ItemDecoration
  • 与ListView的区别:没有HeaderView、FooterView,没有EmptyView,没有onItemClickListener,没有Divider,但均可通过其他方式实现
  • 实现了NestedScrollingChild,可以配合实现嵌套滚动

LayoutManager

  • 实现 generateDefaultLayoutParams()
  • 实现 onLayoutChildren() 相当于ViewGroup的onLayout()方法,所以我们需要在里面layout当前屏幕可见的所有子View
  • 竖直滚动需要 重写canScrollVertically()和scrollVerticallyBy(),dy>0是向下滑,注意修正边界,当返回的dy与传入的dy不同时系统会判断滑到边界实现发光效果。先做fill,回收不可见,增加可见,再做滑动offsetChildrenVertical(-dy);
  • 实现自己的fill方法,在考虑滑动位移的情况下,回收所有屏幕不可见的子View,layout所有可见的子View
  • 向下fill时可以缓存child的位置信息,向上fill时直接拿出缓存的位置信息
  • 布局API:
    addView\measureChild\layoutDecoratedWithMargins
  • 回收API:
    detachAndScrapView(view,recycler);//detach轻量回收指定View
    removeAndRecycleView(View child, Recycler recycler) // recycle真的回收一个View,该View再次回来需要执行onBindViewHolder方法

布局优化

  • 使用AndroidStudio内存分析工具MemoryMonitor,使用traceView来获得每个方法花费的时间
  • 使用开发者选项Show GPU Overdraw来观察UI上的Overdraw情况,Overdraw就是指屏幕上某个像素的同一帧被绘制了多次。紫色、绿色、淡红、深红代表四种不同程度的OverDraw情况,小块的红色是可以接受的,要避免大块红色的出现
    如何解决Overdraw:把XML非必需的Background移除、在自定义View onDraw方法使用canvas.clipRect()来帮助系统识别可见区域
  • 使用开发者选项的Profile Gpu Rendering来观察Gpu渲染情况,横线代表16ms,要确保每一帧的总花费时间低于这条横线,这样才能避免卡顿

    新API

  • SparseArray是android里为'Interger,Object'这样的Hashmap而专门写的类,目的是提高效率,其核心是折半查找函数(binarySearch)。
  • EventBus 全局通信,EventBus发送Event的是什么线程,收到的就是什么线程
  • SuperSLim StickyHeaderGrid的升级版,用于RecyclerView的LayoutManager
  • PathMeasure DashPathEffect 对Path升级加工
    http://www.jianshu.com/p/3efa5341abcc
    http://www.curious-creature.com/2013/12/21/android-recipe-4-path-tracing/
  • ButterKnife 懒人写View
  • JobService(5.0) 某个时间点或者当满足某个特定的条件时执行一个任务
  • VectorDrawable(5.0)
  • CoordinatorLayout(6.0) 约束布局,实现两个TextView的文字底端对齐,两个View垂直中心对齐
    http://blog.csdn.net/tiankong1206/article/details/48394393
  • DiffUtil(7.0) 更新RecyclerView
  • Lottie 将AE动画转成Json直接执行
  • Jack 兼容Java8 Lamda表达式


ImageLoader

  • preProcess(rotate) postProcess(blur faceposition)
  • diskcache(360*360) uri hack加载原图
  • BitmapRegionDecoder加载原图


Android6.0 Runtime Permission

  • http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/
    http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/
  • TargetSdkVersion : 测试通过的最高版本的AndroidVersion
    在6.0的手机上:
    1.如果app的targetSdkVersion 低于 23,那将被认为app没有用23新权限测试过,那将被继续使用旧有规则:用户在安装的时候不得不接受所有权限,安装后app就有了那些权限
    2.targetSdkVersion < 23的app调用一个需要权限的函数时,这个权限如果被用户手动取消授权了的话,不抛出异常。但是他将啥都不干,结果导致函数返回值是null或者0,可能导致NPE
    targetSdkVersion = 23,如果没有权限,强制访问需要权限的代码,会抛出SercurityException:Permission Deny XXX
    运行时权限对于应用影响比较大的权限有两个,他们分别是
    READ_PHONE_STATE
    WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE
    其中READ_PHONE_STATE用来获取deviceID,即IMEI号码。这是很多统计依赖计算设备唯一ID的参考。如果新的权限导致读取不到,避免导致统计的异常。建议在完全支持运行时权限之前,将对应的值写入到App本地数据中,对于新安装的,可以采取其他策略减少对统计的影响。
    WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE这两个权限和外置存储(即sdcard)有关,对于下载相关的应用这一点还是比较重要的,我们应该尽可能的说明和引导用户授予该权限。


易错点

  • Map的Key是String时,get时必须传String类型的Key.
  • String调用indexOf(100)时会找不到,数字也必须转成String,String.indexOf(String.valueOf(100)).
  • Java不要在Finally里return,否则try里的return失效
  • Intent传递是强类型的,intent.putBooleanExtra则getStringExtra为null
  • Android5.0 Intent必须显示调用,通过setComponent,如无必要无需setPackage
  • JSONObject obj = new JSONObject();
    obj.put("data", new Data());
    Data data = (Data) obj.get("data");
    JSONObject result = new JSONObject(obj.toString());
    Data data2 = (Data) result.get("data");
    data能读出来,data2不能读出来,JSONObject的toString方法会把Object转成String
  • JSONObject对于大的Long用getLong方法有Bug,使用Long.parseLong(params.getString("id"));
  • Android Studio将XML的id Refactor,会将别的XML里同样的id也Refactor掉
  • 对与红米手机,不同Module里同名的res图片 会错乱
  • 米5无法mount:adb disable-verity
  • android 6.0(api 23) SDK,不再提供org.apache.http.*(只保留几个类)
  • Parcel:对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象
  • 1.双卡问题(无卡问题)
    2.网络问题
    3.国际版问题
    4.安卓版本问题
    5.OTA升级
    6.数据库升级
    7.账号切换\会员非会员切换
  • volatile关键字在Java语言中仅仅保证变量的可见性,而不保证原子性
    一个易变域上的不受锁保护的非原子操作可能会产生一个竞争状况 — 但是只有在多个线程并发访问非原子操作时才可能出现。
  • NoSuchMethodError:使用了Deprecated或Hide的方法,而方法实现变了;引包是引用了不匹配的包版本;开发环境和运行环境的不一致.使用javap看方法签名。
  • Unchecked call to Pair(F,S) as a member of a raw type
    Pair 用于组合一对Value,必须使用Pair.create(A,B),才做Param类型check,new Pair(A,B)不做类型check
  • method的param要用Integer\Long\String\Object,不能用int\long
    Pair<String, Object> pair.second匹配到Object
    重载方法是编译时,所以Pair<String, T>的Pair.Second会匹配到Object
    正确写法:Pair<String, Object> 判断pair.second instanceOf Integer\Long\String\Object,分别调用重载的四个方法


HTTP

  • HTTP是无状态的,Web程序引入了Cookie机制来维护状态.
    GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456.
    POST方法是把提交的数据放在HTTP包的Body中.Get方法不带Body.
  • 网络Response返回的是Utf-8,String默认是utf-16
  • JSONObject.NULL
    Gson 构建JSON:JSONObject.fromObject(map); JSONArray.fromObject(array);//需要Jar包 jsonobject.put(key,value); JSONObject obj = new JSONObject(bean); JSONObject obj2 = new JSONObject(map);
    使用反射机制构建和解析JSON
  • gson用int数组表示Stack比ArrayList save 20%
    JsonObject使用LinkedTreeMap,JSONObject使用HashMap
  • Base64并不是编码,实际上是把一段字节数组转换成另一段字节数组,只涉及字节的转化,原有三字节变成四字节,长度增加三分之一,转换后的字节数组使用ASCII码编码变成包含64个可打印字符的字符串,默认使用ASCII码编码
    Base64算法编码结果很难用肉眼识别解码,但它仍可以极为轻松地被计算机所解码,就像其容易编码一样。编码这一步骤的目的并不是安全与隐私,而是为将用户名和口令中的不兼容的字符转换为均与HTTP协议兼容的字符集。


Proguard

http://www.cnblogs.com/renkangke/archive/2013/05/31/3110635.html
http://youngflying.com/2012/07/30/android-proguard/
找不到引用的这个类是第三方包里面的,很多时候我们只需要打乱自己的代码就行了,第三方包的代码就是否要打乱就不要管了。
我们可以使用
-dontwarn com.xx.bbb.
-keep class com.xx.bbb.
{ *;}
参数来保持第三方库中的类而不乱,-dontwarn和-keep 结合使用,意思是保持com.xx.bbb.这个包里面的所有类和所有方法而不混淆,接着还叫ProGuard不要警告找不到com.xx.bbb.这个包里面的类的相关引用。

-keep public class rongqin.util.SongJson
-keep public class rongqin.util.SongJson.** { ; }
-keepclassmembers class rongqin.util.SongJson {
public
;
}
-keepclasseswithmembernames class rongqin.util.SongJson {
public *;
}

ProGuard Examples:http://proguard.sourceforge.net/manual/examples.html#androidactivity
Most importantly, we're keeping all fundamental classes that may be referenced by the AndroidManifest.xml file of the application. If your manifest file contains other classes and methods, you may have to specify those as well.

program code (specified with -injars) and library code (specified with -libraryjars)
-injars指定静态Jar包和输入文件,-libraryjars指定动态Jar包(比如android.jar 不做混淆)
Android默认Proguard:LibraryProject默认不混淆; AppProject混淆, SharedLibrary不混淆,StaticLibrary当成代码的一部分混淆。混淆前做Check检查。
遇到的一个问题:
Warning: library class com.xiaomi.thrifty.data.ThriftyCommonRequestInfo depends on program class org.apache.thrift.protocol.TProtocol
原因是动态引用了Cloud-Common又静态引用了push,push里有TProtocol,这个静态引用被认成program class,动态引用ThriftyCommonRequestInfo被认成libarary class.所以报错.Jar包内的类不能依赖Jar包外的类。


其他

分辨率

density = logicalDensityDpi / DENSITY_DEFAULT
mdpi 320px480px density=1 320dp480dp 160dpi(DENSITY_DEFAULT)
hdpi 480px854px 米1 米1s density=1.5 320dp569dp 240dpi
xhdpi 720px1280px(720p) 米2 米2s density=2 360dp640dp 320dpi
xxhdpi 1080px1920px(1080P) 米3 米4 density=3 360dp640dp 480dpi
1080px1920px(1080P) 米Note density=2.75 392dp698dp 440dpi
xxxhdpi 1440px2560px(2K屏) 米Note顶配 density=4 360dp640dp 640dpi
米PAD 1536px2048px density=2 768dp1024dp 320dpi (sw600dp-xhdpi)
米PAD2 1536px2048px density=2 768dp1024dp 320dpi (sw600dp-xhdpi)
values-v19代码Android版本为19的机型使用的XML
建立res/values-xxhdpi-1080x1920/dimens.xml目录时1080x1920中间是字母x

PX|DP|SP

http://blog.jeswang.org/blog/2013/08/07/ppi-vs-dpi-you-shi-yao-qu-bie/

时间

  1. 三种时间的相同点与不同点

    相同点:都是相对时间。
    不同点:比较的基准不同。currentTimeMillis可以被用户通过时钟设置,也可以网络校时,反正是要设置,如果不设置,计算机怎么会知道现在距离1970年1月1号有多长时间。另外两个是从操作系统被引导后开始计算的,只是一个计算深度睡眠时间,另一个不计算。所以第一种可以通过改时钟来处罚,后两种不可以。
    System#currentTimeMillis
    Returns the current time in milliseconds since January 1, 1970 00:00:00.0 UTC.
    android.os.SystemClock#uptimeMillis
    Returns milliseconds since boot, not counting time spent in deep sleep.
    android.os.SystemClock#elapsedRealtime
    Returns milliseconds since boot, including time spent in sleep.

  2. 应用场景

    AlarmManager支持currentTimeMillis和elapsedRealtime这两种方式。
    Thread.sleep(millis),Object.wait(millis),SystemClock.sleep(millis) 和Handler都是使用uptimeMills。

  3. 时间与时区
    System.currentTimeMillis:时间戳,是将当前时间转换成GMT,再做1970.1.1 0点的差值,所以东八区的话相当于现在距离1970.1.1 8点的时间
    改时区后已设置的闹钟触发方式:东八区8点触发,改时区到东七区7点触发,因为Alarm设定的是UTC时间
    存在问题的是: 对不同时间的闹钟如何设置保证同一时间触发,比如设置2点到6点触发闹钟,则东八区是东八区的2点到6点,东七区是东七区的2点到6点
    Time、Date、SimpleDateFormat、Calendar类都带有时区概念(使用了默认TimeZone,即id=Asia/Shanghai),但只有Calendar和SimpleDateFormat能setTimeZone
    Date date = new Date(0); toString为Thu Jan 01 08:00:00 CST 1970 Date date = new Date(System.currentTimeMillis()); date.getHour() getMiuite()时会做初始化,初始化时区为默认时区,所以date.toString()为Wed Jan 13 17:01:33 CST 2016 带时区概念 Date date2 = new Date((2016 - 1900), 1, 13, 16, 38, 30); 即设置的时间为默认时区(东八区)的2016/1/13/16:38:30

输入法

http://www.jianshu.com/p/2a9a4fc9dda2

Fiddler抓包

SQL

SQL语句的LIMIT的用法
group by a,b 即a和b两列均相等的做合并

Intent

如果是implicit(隐式)intent,android默认给加上一个CATEGORY_DEFAULT,这样的话如果intent filter中没有android.intent.category.DEFAULT这个category的话,匹配测试就会失败。所以,如果你的activity支持接收implicit intent的话就一定要在intent filter中加入android.intent.category.DEFAULT。设置的无需再设置android.intent.category.DEFAULT
intent传递Parcel intent.putStringArrayListExtra intent.putParcelableArrayListExtra
Intent可以传递ArrayList和HashMap

string.xml

String.xml:
\u2026 的意思是表示省略号,是unicode的16进制表示
\u0020的意思是表示空格,是unicode的16进制表示
在XML中写字符串时,可能需要在字符串后面加空格,A_B(中间有空格的情况)是没有问题,用不到转义字符;但是AB_(后面有空格的)时候,就需要转个义。XML转义字符,\u0020表示空格
另一种方法是XML将字符串用引号""引起来,不用其他转义字符

Quantity Strings:getQuantityString()
资源引用
Java代码::R.plurals.plural_name
语法
<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="plural_name"> <item_plurals quantity=["zero" | "one" | "two" | "few" | "many" | "other"] >text_string</item> </plurals> </resources>
因为中文没有复数语法,所以当语言是中文时,将总是获得quantity为other的字符串

代码

Setting&MiuiSettings:
设计技巧:
Setting分两个部分,主页(Setting)和内容页(SubSetting),主页对应dashboard,每个内容页对应一个PreferenceFragment。
进入Setting两种途径:1.从设置应用进入,Setting本身切换只需要replace fragment,配置header.xml里,指定各个Header点击时需要跳转的Fragment。
2.从外部应用跳转各个内容界面,就需要特定的Action找到对应的Activity,这些Activity具有共同的操作,把共同操作封装在通用父类中,于是这些Activity就简单写成一个static变量存在父类Settings里,每个Activity对应的Fragment通过Meta-data配置在Manifest里
跳转到系统应用的设置项:各个系统应用的设置项写在各个应用的APP里,具有相同的Intent-filter,通过找到对应Package的Intent来实现跳转
界面相关:
PAD设置项通过NavigationLayout实现主页(Menu)和内容页(Content)两个Fragment同时的展现,默认内容展示的Fragment的关于平板页(deviceInfo)
Miui定制:
Miui修改了Setting的Launcher Activity,增加了MiuiSetting这个Package,里面包含src和res,不包含mk和manifest

图库使用了PhotoView scaleType = matrix 在onScale回调增加ScaleToExit缩小时退出

转载于:https://www.cnblogs.com/xtanmy/p/6414449.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值