PropertyValuesHolder
void setupSetter(Class targetClass) {
Class propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
PropertyValuesHolder
/**
* Utility function to get the getter from targetClass
*/
private void setupGetter(Class targetClass) {
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
Object value = convertBack(mGetter.invoke(target));
mGetter.invoke
private View[] mSortedHorizontalChildren;
private View[] mSortedVerticalChildren;
applyHorizontalSizeRules(params, myWidth, rules);
measureChildHorizontal(child, params, myWidth, myHeight);
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
measureChildHorizontal
measureChild
LinearLayout也需要两次measure。
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
return;
}
if (mOrientation == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}
VERTICAL drawDividersHorizontal drawDividersVertical canvas
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
mDivider.draw(canvas);
mDivider.setBounds
mDivider.draw
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int top = child.getTop() - lp.topMargin - mDividerHeight;
LayoutParams lp = child.getLayoutParams
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
bottom = child.getBottom() + lp.bottomMargin;
bottom = child.getBottom + lp.bottomMargin
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
measureVertical measureHorizontal
getChildMeasureSpec
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
measure
makeMeasureSpec measure
lp.weight > 0;
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
child.measure(
MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),//resultSize,resultMode
MeasureSpec.makeMeasureSpec(largestChildHeight,
MeasureSpec.EXACTLY));
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
public interface SharedPreferences {
/**
* Interface definition for a callback to be invoked when a shared
* preference is changed.
*/
public interface OnSharedPreferenceChangeListener {
public interface Editor {
final class SharedPreferencesImpl implements SharedPreferences {
public class PrefState implements OnSharedPreferenceChangeListener {
public final class EditorImpl implements Editor {
ContextWraper
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}
EditorImpl SharedPreferencesImpl
Interpolator 插值器
它表示的是时间流逝的百分比,它的作用就是根据时间流逝的百分比计算出当前属性值改变的百分比
TypeEvaluator 估值器
这个时候就要用到估值器了,它的作用是根据当前属性改变的百分比来计算改变后的属性值
LinearInterpolator
getInterpolation 0,1
IntEvaluator TypeEvaluator
evaluate fraction startValue endValue
fraction = getInterpolation = input
public float getInterpolation(float input) {
return input;
}
input = 20 /40
IntEvaluator这个估值器中的evaluate的三个分别为:0.5,0,40。代入后计算即:0+0.5*(40-0)=20。
public T evaluate(float fraction, T startValue, T endValue);
startValue + fraction * (endValue - startValue)
ValueAnimator.ofObject TypeEvaluator
setInterpolator LinearInterpolator
onAnimationUpdate
Interpolator
getInterpolation
haredPreferences内部工作原理:
1、初始化:调用getSharedPreferences()创建一个SharedPreferences对象。
在多进程模式或者目标sdk版本在HONEYCOMB以下版本每次读取缓存了的sp,Android会检查xml文件是否已经被重写了。
首次使用 getSharedPreferences 时,内存中不存在 SP 以及 SP Map 缓存,需要创建 SP 并添加到 ContextImpl 的静态成员变量(sSharedPrefs)中。下面分析上面这段代码:
首先判断sSharedPrefs是否为空,如果为空则给他初始化。sSharedPrefs是一个用来缓存SharedPreferences的ArrayMap,它的key为包名,它的value为ArrayMap,这个ArrayMap保存的键值对是SharedPreferences文件名和对应的SharedPreferencesImpl(是SharedPreferences的实现类)。如果SharedPreferencesImpl已经存在,它会直接返回已经存在的SharedPreferencesImpl。如果是在多进程模式下,或者目标版本低于HONEYCOMB的时候,会检查是否需要重新从磁盘中加载文件。但是需要说的是MODE_MULTI_PROCESS模式已经被deprecated了,官方建议使用ContentProvider来处理多进程访问。
在重新创建SharedPreferencesImpl的时候,getSharedPreferences会调用getSharedPrefsFile来获取存储的xml文件,这个函数对xml文件名进行了组装:
SharedPreferences在第一次创建后会一直维持一个Singleton,每次调用getSharedPreferences()都返回唯一的一个实例。由于应用版本升级时并不会删除SharedPreferences文件,所以可以加个版本判断,来进行一些数据更新,从上面看来,由于每一次调用getSharedPreferences()都会有IO操作,当内容比较多时,那么就不适宜在Application的onCreate中进行SharedPreferences文件初始化了,最好的办法是开个子线程去完成它的创建和数据的预加载!!!
通过getPreferencesDir()来获取shared_prefs目录,然后根据文件名加上xml后缀。Android没有提供直接访问shared_prefs目录的API,getPreferencesDir是一个私有类,我们如果想要直接访问这个目录,可以通过下面这段代码访问:
String sharedPrefsDir = context.getCacheDir().getParent().getAbsolutePath()+"/shared_prefs";
SharedPreferences具体的实现者是SharedPreferencesImpl。我们都知道Android的SharedPreferences对XML操作是使用DOM方式解析的(一开始就把整个XML给读取出来)。下面看下SharedPreferencesImpl构造函数
makeBackupFile 用来定义备份文件,该文件在写入磁盘时会用到
其中startLoadFromDisk就是将xml给读取出来的。
它使用了一个异步线程来读取xml,最终实现的函数是loadFromDiskLocked()
调用XmlUtils.readMapXml来调用,读取整个xml的内容,放到mMap当中
会先判断是否存在对应xml文件。如果发现存在则会有一个预加载操作,这个操作是把xml文件的内容通过I/O操作和XmlUitl解析后存入一个map对象中。
2、get 操作:如getString
当调用SharedPreferences的getString()方法。get操作实际上是不会对文件做I/O操作,而是直接访问刚刚的map集合的内容(注意同步问题),这提高了效率,如果对应的xml不存在则重新创建一个对应的xml文件。
其他方法如getInt等类似
首先取得 SharedPreferencesImpl 对象锁,然后同步等待从磁盘加载数据完成,最后返回数据。但如果 SP 存储的内容过多,导致我们使用 getXXX 方法的时候阻塞,特别是在主线程调用的时候,所以建议在单个 SP 中尽量少地保存数据,虽然操作时间是毫秒级别的,用户基本上感觉不到。
3、put 操作
SP 写入数据的操作是通过 Editor 完成的,它也是一个接口,实现类是 EditorImpl,是 SharedPreferencesImpl 的内部类。
通过 SP 的 edit 方法获取 Editor 实例,等到加载完毕直接返回一个 EditorImpl 对象。
比如我们要保存某个 String 的值,调用 putString 方法。
mModified 是一个 editor 中的一个 Map,保存着要修改的数据,在将改动保存到 SP 的 Map(变量 mMap,里面保存着使用中的键值对 ) 后被清空。put 完成后就要调用 commit 或者 apply 进行保存。
写操作也有两步,一是把数据先写入内存中,即map集合,二是把数据写入硬盘文件中。这样才能保证数据的完整性,写操作有两个提交的方式:commit():线程安全,性能慢,一般来说在当前线程完成写文件操作 apply():线程不安全,性能高,异步处理IO操作,一定会把这个写文件操作放入一个SingleThreadExecutor线程池中处理。
可以看到,commit 和 apply 操作首先执行了 commitToMemory,顾名思义就是提交到内存,返回值是 MemoryCommitResult 类型,里面保存着本次提交的状态。然后 commit 调用 enqueueDiskWrite 会阻塞当前线程,而 apply 通过封装 Runnable 把写磁盘之后的操作传递给 enqueueDiskWrite 方法。
for (Map.Entry e : mModified.entrySet()) { // 在这开始将修改的内容写入到mMap当中。
MemoryCommitResult 类,主要用于提交到内存后返回结果,然后在写入磁盘时作为参数传递
保存到磁盘的操作:enqueueDiskWrite
参数有 MemoryCommitResult、Runnable,在commit 方法中调用 enqueueDiskWrite 方法是传入的 Runnable 是null,它会在当前线程直接执行写文件的操作,然后返回写入结果。而如果 Runnable 不是 null,那就使用 QueueWork 中的单线程执行。apply 和 commit 的根本区别:一个同步执行,有返回值;一个异步执行,没有返回值。官方推荐:使用 apply 。
writeToFile 方法:备份 → 写入 → 检查 → 善后,这样保证了数据的安全性和稳定性
最终实现的函数是loadFromDiskLocked.xml
shared_prefs
await EditorImpl put
commit apply
mcr.writtenToDiskLatch.await();
commitToMemory,顾名思义就是提交到内存,返回值是
enqueueDiskWrite
mObjectAnimator3 = ObjectAnimator.ofFloat(mImage3, "scaleY", 8f / 12f, 9f / 12f, 10f / 12f,
12f / 12f, 11f / 12f, 9f / 12f, 7f / 12f, 5f / 12f, 7f / 12f, 8f / 12f, 9f / 12f,
12f / 12f, 11f / 12f, 12f / 12f, 9f / 12f, 7f / 12f, 9f / 12f, 11f / 12f, 12f / 12f,
10f / 12f, 8f / 12f, 9f / 12f, 7f / 12f, 5f / 12f, 3f / 12f, 8f / 12f);
PropertyValuesHolder
void setupSetter(Class targetClass) {
Class propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
PropertyValuesHolder
/**
* Utility function to get the getter from targetClass
*/
private void setupGetter(Class targetClass) {
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
Object value = convertBack(mGetter.invoke(target));
mGetter.invoke
private View[] mSortedHorizontalChildren;
private View[] mSortedVerticalChildren;
applyHorizontalSizeRules(params, myWidth, rules);
measureChildHorizontal(child, params, myWidth, myHeight);
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
measureChildHorizontal
measureChild
LinearLayout也需要两次measure。
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
return;
}
if (mOrientation == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}
VERTICAL drawDividersHorizontal drawDividersVertical canvas
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
mDivider.draw(canvas);
mDivider.setBounds
mDivider.draw
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int top = child.getTop() - lp.topMargin - mDividerHeight;
LayoutParams lp = child.getLayoutParams
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
bottom = child.getBottom() + lp.bottomMargin;
bottom = child.getBottom + lp.bottomMargin
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
measureVertical measureHorizontal
getChildMeasureSpec
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
measure
makeMeasureSpec measure
lp.weight > 0;
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
child.measure(
MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),//resultSize,resultMode
MeasureSpec.makeMeasureSpec(largestChildHeight,
MeasureSpec.EXACTLY));
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
public interface SharedPreferences {
/**
* Interface definition for a callback to be invoked when a shared
* preference is changed.
*/
public interface OnSharedPreferenceChangeListener {
public interface Editor {
final class SharedPreferencesImpl implements SharedPreferences {
public class PrefState implements OnSharedPreferenceChangeListener {
public final class EditorImpl implements Editor {
ContextWraper
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}
EditorImpl SharedPreferencesImpl
PhoneWindow ViewRoot
http://www.androidos.net.cn/android/5.0.1_r1/xref//frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
ViewRootImpl
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
public void addView(View view) {
synchronized (this) {
mViews.add(view);
postIfNeededLocked();
}
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// ViewAncestor never intercepts touch event, so this can be a no-op
}
}
public class DecorView extends FrameLayout
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ViewRootImpl getViewRootImpl() {
if (mDecor != null) {
ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
if (viewRootImpl != null) {
return viewRootImpl;
}
}
throw new IllegalStateException("view not added");
}
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
}
return new DecorView(context, featureId, this, getAttributes());
class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
@Override
public Object getSystemService(String name) {
if (Context.WINDOW_SERVICE.equals(name)) {
if (mWindowManager == null) {
WindowManagerImpl wm =
(WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
}
return mWindowManager;
}
return super.getSystemService(name);
}
;
}
PhoneWindow ,DecorView ViewRootImpl
DecorContext WindowManagerImpl
http://androidxref.com/4.4.2_r1/xref/frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowManager getWindowManagerService() {
134 synchronized (WindowManagerGlobal.class) {
135 if (sWindowManagerService == null) {
136 sWindowManagerService = IWindowManager.Stub.asInterface(
137 ServiceManager.getService("window"));
138 }
139 return sWindowManagerService;
140 }
141 }
public void addView(View view, ViewGroup.LayoutParams params,
190 Display display, Window parentWindow) {
248 root = new ViewRootImpl(view.getContext(), display);
249
250 view.setLayoutParams(wparams);
251
252 mViews.add(view);
253 mRoots.add(root);
254 mParams.add(wparams);
255 }
256
257 // do this last because it fires off messages to start doing things
258 try {
259 root.setView(view, wparams, panelParentView);
}
private final ArrayList
113 private final ArrayList
ActivityInfo WindowManager
Activity
attach中会实例化PhoneWindow,并拿到WindowManager设置到自身当中。
PhoneWindow install
PhoneWindow setContentView installDecor generateDecor(-1) DecorView
DecorView中包含了我们的setContentView所需要的布局,而PhoneWindow中又包含了DecorView。PhoneWindow由
PhoneWindow WindowManager去管理。
setContentView WIndowManagerGolobal addView
ViewRootImpl
WindowManagerImpl addView
WIndowManagerGolobal
mContentParent.addView(view, params);
setContentView new DecorView
ViewParent 是
ViewGroup
addViewInner
requestLayout
mParent.requestLayout ViewRootImpl.requestLayout
scheduleTraversals
mTraversalRunnable . doTraversal
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout
performDraw