最近在读《Android源码设计模式解析与实战》,会尝试自己阅读、整理android源码。
单例模式
1.饿汉模式--类声明时初始化
public class MyObj{
private static final MyObj sObj=new MyObj();
private MyObj(){}
public static MyObj getInstance(){
return sObj;
}
}
2.懒汉模式 ---使用时才被初始化,劣势:同步锁会导致访问速度慢
public class MyObj{
private static MyObj sObj;
private MyObj(){}
public static synchronized MyObj getInstance(){
if(sObj==null){
sObj=new MyObj();
}
return sObj;
}
}
3.双重锁定检查DCL模式---
缺点 “sObj=new MyObj(); ”这句包含三步:1给MyObj的实例分配内存;2.初始化MyObj;3将sObj指向分配的内存空间。无法保证2、3的顺序,1-3-2会出现DCL失效问题。sObj非空,却未完成初始化,使用会出错。
JDK是1.5或之后的版本,只需要将sObj的定义改成“private volatile static MyObj sObj = null;”就可以保证每次都去instance都从主内存读取
public class MyObj{
private static MyObj sObj;
private MyObj(){}
public static MyObj getInstance(){
if(sObj==null){
synchronized(MyObj.class){
if(sObj==null){
sObj=new MyObj();
}
}
}
return sObj;
}
}
4.静态内部类单例模式
public class MyObj{
private MyObj(){}
public static synchronized MyObj getInstance(){
return MyObjHolder.sObj;
}
private static class MyObjHolder{ // Note:(static)inner classes cannot have static declarations
static final MyObj sObj=new MyObj();
}
}
LayoutInflater的加载过程
1.LayoutInflater是通过LayoutInflater.from(context)来获取到一个LayoutInflater实例的。
在from方法中会调用 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);也就是说最终的初始化是context的getSystemService方法来做的。
LayoutInflater.java
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
那么context是哪个呢?以Activity为例,继承的是父类ContextThemeWrapper的getSystemService方法。
Activity.java
@Override
public Object getSystemService(String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
可以看到在ContextThemeWrapper里有mInflater = LayoutInflater.from(mBase).cloneInContext(this);这行代码。最终传入的是一个mBase变量(其实是个Context),那么这个context是哪里来的呢?
ContextThemeWrapper.java
private Context mBase;
@Override protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
mBase = newBase;
}
@Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(mBase).cloneInContext(this);
}
return mInflater;
}
return mBase.getSystemService(name);
}
2.来看ActivityThread这个类,main方法作为入口。 里面有行代码thread.attach(false);
(ActivityThread类无法直接搜索到,是因为是个隐藏类)
package android.app;
* {@hide}
*/
public final class ActivityThread {
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Process.setArgV0("
");
Looper.prepareMainLooper();//NOTE:这里还调用了我们熟悉的Looper的prepare及loop()方法,以后分析
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private void attach(boolean system) {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
.....
}
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mPackages) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (applyConfigurationToResourcesLocked(newConfig, null)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;
queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
public void onLowMemory() {
}
public void onTrimMemory(int level) {
}
});
}
通过 mgr.attachApplication(mAppThread);建立通信???TODO:此处待详细了解。
最终在activity.attach方法里传入一个ContextImpl对象。而activity.attach方法最终调用了父类ContextThemeWrapper的attachBaseContext方法,将ContextImpl对象赋值给mBase。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
...
if (activity != null) {
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
return activity;
}
那么ContextImpl里是如何处理getSystemService方法的呢?原来是初始化了一个static的hashMap,当ContextImpl类被加载时,static定义的一段静态代码块会被执行,然后各种service调用registerService方法,以名称作为key,ServiceFetcher作为value,保存在hashMap里;在外部调用getSystemService方法的时候,就以名称来取出对应的ServiceFetcher,然后返回fetcher.getService(this),fetcher.getService(this)第一次会调用createService创建一个service对象,缓存到列表中,下次再取时直接从缓存列表中拿。这样就保证了服务的单例。
ContextImpl.java
package android.app;
private static final HashMap SYSTEM_SERVICE_MAP =
new HashMap();
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
static {
//NOTE:这里会注册各种service
...
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
}
}
可以看到LAYOUT_INFLATER_SERVICE的createService是调用了PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());来实例化的。那PolicyManager.makeNewLayoutInflater(ctx.getOuterContext())里又做了什么呢?
3.PolicyManager通过反射实例化了一个com.android.internal.policy.impl.Policy类,调用它的sPolicy.makeNewLayoutInflater(context);方法。
PolicyManager.java
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
}
...
}
// Cannot instantiate this class
private PolicyManager() {}
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
来看看com.android.internal.policy.impl.Policy类的makeNewLayoutInflater方法。创建并返回了一个PhoneLayoutInflater对象。
Policy.java
public LayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
也就是说PhoneLayoutInflater才是真正的LayoutInflater实现类。看看它做了什么。
PhoneLayoutInflater.java
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit."
};
/** Override onCreateView to instantiate names that correspond to the
widgets known to the Widget factory. If we don't find a match,
call through to our super class.
*/
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}
return super.onCreateView(name, attrs);
}
主要就是重写了LayoutInflater的onCreateView方法,主要目的是在传递进来的View名字前面加上"android.widget."或者"android.webkit."前缀,以此构造对应的View对象。
具体是什么流程呢?以Activity的setContentView为例来看。
Activity.java
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
对于Activity的getWindow()调用的是mWindow = PolicyManager.makeNewWindow(this);实例化是用Policy的makeNewWindow方法,返回一个PhoneWindow对象。最终会调用mLayoutInflater.inflate(layoutResID, mContentParent);方法。
PhoneWindow.java
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
4.
一个Window的View层级图
--Activity
--PhoneWindow
--DecorView
--DefaultLayout
--ViewGroup
--Xml布局
来看看LayoutInflater的inflate方法
LayoutInflater.java
public View inflate(XmlPullParser parser, ViewGroup root) {
//root不为空,则会从resource布局解析到view,并添加到root中
return inflate(parser, root, root != null);
}
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
//获取xml资源解析器
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
//参数1为xml解析器,参数2为父视图,参数3为是否要将解析到视图添到父视图中
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
//初始化result为父视图
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
final String name = parser.getName();
...
//解析merge标签
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
// 解析其父视图root下的所有子View
rInflate(parser, root, attrs, false);
} else {
// 不是merge标签直接解析布局视图
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
// 这里通过xml的tag来解析layout的根视图,name就是要解析的视图的类名,如RelativeLayout
// TODO:暂时未查看createViewFromTa方法的实现
temp = createViewFromTag(root, name, attrs);
}
ViewGroup.LayoutParams params = null;
if (root != null) {
// 生成布局参数
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// 如果不添加视图到父布局,将布局参数设给temp
temp.setLayoutParams(params);
}
}
// 解析temp视图下的所有子View
rInflate(parser, temp, attrs, true);
// 如果父视图不为null并且attachToRoot为true,将temp添加到父视图
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//如果父视图为null或者attachToRoot为false,返回结果为temp
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
...
}
return result;
}
}
上述inflate方法中,主要有下面几步:
1. 解析xml中的根标签(第一个元素)
2. 如果根标签是merge,那么调用rInflate进行解析,rInflate会将merge标签下的所有子View直接添加到根标签中;
3. 如果标签是普通元素,那么调用createViewFromTag对该元素进行解析;
4. 调用rInflate解析temp根元素下的所有子View,并且将这些子View添加到temp下;
5. 返回解析到的根视图;
看一下XmlResourceParser parser = getContext().getResources().getLayout(resource);获取的XmlResourceParser是什么
调用的是Resources类的getLayout方法,一步步追踪下去,可以发现最终是实例化了XmlBlock.Parser这个类。
Resources.java
public XmlResourceParser getLayout(int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
XmlResourceParser loadXmlResourceParser(String file, int id,
int assetCookie, String type) throws NotFoundException {
...
return block.newParser();
...
}
XmlBlock.java
public XmlResourceParser newParser() {
synchronized (this) {
if (mNative != 0) {
return new Parser(nativeCreateParseState(mNative), this);
}
return null;
}
}
这里理解下createViewFromTag方法。
LayoutInflater.java
View createViewFromTag(View parent, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
try {
View view;
//可以通过设置factory解析View,默认这些Factory都为空,可以忽略这段
if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
else view = null;
...
if (view == null) {
if (-1 == name.indexOf('.')) {
//系统自带的view的解析(如 view = onCreateView(parent, name, attrs);
} else {
//自定义View的解析(如 view = createView(name, null, attrs);
}
}
return view;
} catch (InflateException e) {
...
}
}
可以看到代码里针对系统自带的view调用的是onCreateView方法,对自定义的view调用的是createView方法,onCreateView与createView的区别是什么呢?
其实之前已经看过PhoneLayoutInflater的onCreateView方法,对于系统自带的view,name不包含完整包名,只有类名。因此在onCreateView方法作出如下处理:如果以"android.widget."或"android.webkit."为前缀参数调用createView(name, prefix, attrs)创建出view,就返回该view,否则调用父类LayoutInflater的onCreateView方法,以"android.view."为前缀参数调用createView(name, prefix, attrs)创建出view。
而自定义的View调用createView(name, null, attrs)传的name参数是类名的完整路径。
5.
现在再来看看createView方法吧。
LayoutInflater.java
//根据完整类名,通过反射构造View对象
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
//缓存获取构造函数
Constructor extends View> constructor = sConstructorMap.get(name);
Class extends View> clazz = null;
try {
if (constructor == null) {
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
Object[] args = mConstructorArgs;
args[1] = attrs;
return constructor.newInstance(args);
...
}
发现其实是根据完整的类名,通过反射创建对应的实体类,然后对应的View就被创建出来了。
6.
有了单个View的实例化,那View所包含的子view是如何被实例的呢?
现在来关注下LayoutInflater的rInflate方法
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
//获取深度, 在parser.next()方法里,mDepth是递增的(type== XmlPullParser.START_TAG)或者递减的
final int depth = parser.getDepth();
int type;
//循环每个元素
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else if (TAG_1995.equals(name)) {
final View view = new BlinkLayout(mContext, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else {//普通view的处理
//先实例化自身,
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//然后递归实例化子View
rInflate(parser, view, attrs, true);
//将view添加到父view
viewGroup.addView(view, params);
}
}
if (finishInflate) parent.onFinishInflate();
}
rInflate方法通过深度优先遍历(?这个名词很高大上)来构造视图树,每解析到一个view元素就递归调用rInflate,直到该路径的最后一个元素,然后回溯过来把每个view元素添加到它们的parent中,整个过程结束后,整颗视图树就构建完成,通过setContentView设置的内容就会在界面上显示。