Android部分技术点目录(2)
目录
Toast可以在子线程使用
估值器和插值器
Activity启动过程
Intent能传递的数据类型
Parcelable和Serializable区别
Instrumentation
WMS和AMS的窗口切换过程
ListView局部刷新
Android中App的内存大小、largeHeap的大小
在onCreate中用子线程给TextView属性赋值成功的问题
性能分析工具
线性布局的几个问题
Toast可以在子线程使用
Toast是系统Window,不受ViewTree刷新时checkThread的限制。
但是Toast不能随便在子线程使用,需要子线程启动Looper,或者是handlerthread。
因为toast需要让notification manager service 回调自己,这是通过bindler回调的,回调会进入Bindler线程,不是直接回到发起toast的线程,这就需要该线程有looper,这样才能用handler通知到调用Toast的线程。
估值器和插值器
估值器和插值器是用于属性动画ObjectAnimator的,原理是不断调用get和set方法来重设属性值,比补间动画的形式更加丰富。
属性动画中的估值器和插值器,是实现非匀速动画的,根据当前属性改变的百分比,计算改变后的属性值。
插值器Interpolator决定变化的趋势,可以根据时间流逝的百分比,计算属性改变的百分比。
估值器Evaluator设置变化的具体数值,让属性值从初始值过渡到结束值。
过程插值器根据input的时间,计算出属性变化的百分比fraction,插值器根据百分百fraction,结合动画的初始值startValue和结束值endValue,计算出当下的属性值
大致流程如下:
属性动画中的插值器和估值器
Activity启动过程
1.Activity中最终到startActivityForResult()(mMainThread.getApplicationThread()传入了一个ApplicationThread检查APT)
->Instrumentation#execStartActivity()和checkStartActivityResult()(这是在启动了Activity之后判断Activity是否启动成功,例如没有在AM中注册那么就会报错)
->ActivityManagerNative.getDefault().startActivity()(类似AIDL,实现了IAM,实际是由远端的AMS实现startActivity())
->ActivityStackSupervisor#startActivityMayWait()
->ActivityStack#resumeTopActivityInnerLocked
->ActivityStackSupervisor#realStartActivityLocked()(在这里调用APT的scheduleLaunchActivity,也是AIDL,不过是在远端调起了本进程Application线程)
->ApplicationThread#scheduleLaunchActivity()(这是本进程的一个线程,用于作为Service端来接受AMS client端的调起)
->ActivityThread#handleLaunchActivity()(接收内部类H的消息,ApplicationThread线程发送LAUNCH_ACTIVITY消息给H)
->最终在ActivityThread#performLaunchActivity()中实现Activity的启动完成了以下几件事:
2.从传入的ActivityClientRecord中获取待启动的Activity的组件信息
3.创建类加载器,使用Instrumentation#newActivity()加载Activity对象
4.调用LoadedApk.makeApplication方法尝试创建Application,由于LoadedApk只有package一个单例所以不会重复创建。
5.创建Context的实现类ContextImpl对象,并通过Activity#attach()完成数据初始化和Context建立联系,因为Activity是Context的桥接类,
最后就是创建和关联window,让Window接收的事件传给Activity,在Window的创建过程中会调用ViewRootImpl的performTraversals()初始化View。
6.Instrumentation#callActivityOnCreate()->Activity#performCreate()->Activity#onCreate().onCreate()中会通过Activity#setContentView()调用PhoneWindow的setContentView()
更新界面。
Intent能传递的数据类型
Intent可能需要跨进程传递数据,所以传递的最终类型都是Parcelable或Serializable,Intent开放的Bundle、和Intent自身都是Parcelable对象,向Intent里传值其实都是Bundle处理的,包括CharSequence、Serializable等。
Intent能传递的数据大小
Intent其实是走Binder机制,内存对Binder的mmap限制为4M以内,实际App层限制为1M~8K,所以Bitmap过大就会崩溃。
Parcelable和Serializable的区别
虽然都做序列化,但是实现方式和用途不同:
1.实现方式
Parcelable需要手动实现writeToParcel和CREATOR的createFromParcel。
Serializable只需要实现Serializable接口即可,同时允许扩展功能,比如可以利用transient关键字,声明某些属性不允许序列化;可以利用serialVersionUID属性来区分各版本数据;可以利用readObject和writeObject,介入序列化和反序列化过程,这可以用来规避破坏单例的问题。
2.序列化对象和读写
Parcelable是Android特有的,序列化为Parcel对象,可以marshall或unmarshall为byte[]数组,通过操作native指针来实现快速读写和。
Serializable是Java通用的,序列化为Stream字节流,会有大量的IO操作,还会执行反射,会产生很多中间变量,既有GC压力,又占用内存,速度也慢。
3.应用场景
Parcelable更高效,但通用性差,Parcelable通过native方法操作内存指针,效率很高;但是Parcelable通用性差,不建议存为文件或通过网络传输,虽然Parcelable也能持久化数据,但是Parcelable在各android版本上差异大,在数据转换上会有问题。
Instrumentation
是ContextImpl中的对象,每个App只有一个Instrumentation对象,每个Activity通过引用Context引用这个Instrumentation对象,这是App进程的管家,ActivityThread都是通过Instrumentation来处理创建、暂停Activity等处理。
ListView局部刷新
ListView有时候只有一个item发生了变化,如果要只更新这1个item,需要三步:
1.找到item,用数据列表的index,减去mList.getFirstVisiblePosition(),可以判断是否正在显示区间内,然后用mList.get(index-firstVisiblePosition),可以找到item的View
2.取出ViewHolder,通过view.getTag(),获取到ViewHolder
3.更新界面,直接在ViewHolder中修改item的界面
Android中App的内存大小、largeHeap的大小
查看设备的/system/build.prop文件
dalvik.vm.heapstartsize=8m App初始大小
dalvik.vm.heapgrowthlimit=192m 普通App的上限
dalvik.vm.heapsize=512m largeHeap的App的上限
探究android:largeHeap
在onCreate中用子线程给TextView属性赋值成功的问题
判断主线程UI是在viewRootImpl中的checkThread进行的,如果View还没有绘制出来,就会先做属性赋值,然后才会做invalidate绘制,那么checkThread就是在子线程赋值之后执行,其实还是在主线程绘制的。
所以,因为在onResume之后,Activity的PhoneWindow的DectorView的viewRootImpl才会被生成,所以在onCreate里赋值时不会触发invalidate绘制。
为什么我们可以在非UI线程中更新UI
性能分析工具
一、DDMS(MAT)检测内存泄露;
二、TraceView跟踪代码执行时间;
三、Dump UI Hierarchy for UI Atomator分析UI层级;
四、Systrace追踪系统中各子系统的运行状态;
分别简述:
一、DDMS+MAT检测内存泄露;
Dalvik Debug Monitor Service是一个程序执行查看器,能看到线程和堆栈等信息。
DDMS其实是用端口监听测试终端,用adb建立调试器,发送指令到调试终端。
DDMS本身提供了一个简便的Heap工具查看有没有内存泄露现象,通过CauseGC,检查多次操作后内存是否稳定。
如果发生了内存泄露,就收集内存使用信息,导出为.hprof文件,用MAT工具去打开文件(继续转换一次,转为标准hprof文件),详细分析。
MAT的饼图没啥作用,主要看Histogram,过滤出所有对象,列出内存中的对象列表,检查每个实例的Path to GC Roots,exclude所有的weak/soft引用,找到这些对象都是被哪个对象引用,从而推断出内存泄露的原因。
二、TraceView跟踪代码执行时间;
TraceView是性能分析器,能查询代码执行时间。
1.分析具体代码块
首先,在代码中插入trace标记:
android.os.Debug.startMethodTracing("保存文件名");
android.os.Debug.stopMethodTracing();
注意要在manifest里允许读写文件,系统会把数据保存到文件里/mnt/sdcard/保存文件名.trace'
然后,用Monitor工具(sdk/tools/monitor.bat)导入trace文件,进行分析
2.分析App的某个操作
首先,记录操作数据,利用AS自带的Monitors工具,点击CPU一栏中秒表logo的标记开始trace,然后操作App,操作完成后,结束trace。
然后,分析数据,TraceView提供了调用的函数、各函数的执行时间和次数等。
正确使用Android性能分析工具——TraceView
Android系统性能调优工具介绍
三、Dump View Hierarchy for UI Atomator分析UI层级;
DDMS里在截屏按钮后面有个Dump View Hierarchy按钮,可以在DDMS中刷出当前app的页面,选择某个元素,就能列出其所在的UI层级等信息。
四、Systrace追踪系统中各子系统的运行状态;
Systrace功能非常强大,可以对CPU、Native进程甚至Kernel线程采集性能数据。
在DDMS中练好设备,点击最右侧统计形状的按钮,设置输出文件地址和要采集的内容,输出一个html文件,打开是张统计报表(只能用chrome浏览器打开,其他浏览器如Safari打开是空白)
Android性能优化工具之Systrace
线性布局的几个问题
线性布局中,两个Button,如何分置两边
利用android:layout_weight=“1”来填充剩余空间,可以中间插一个透明,也可以嵌套一层。
五种布局:LinearLayout、RelativeLayout、FrameLayout、AbsoluteLayout、TableLayout