Android 输入法导致内存泄露以及WebView内存泄露解决方案

如果开发到一定程度肯定会去处理内存的问题,因为不处理该问题的话,轻一点会导致应用频繁GC,严重的情况下会产生内存溢出导致崩溃。而只要讲到内存溢出经常会遇到一句话,使用Activity的Context导致,而Context被引用,最后导致Activity泄露。

1、查找泄露原因

既然是查询View持有Activity的引用导致的内存泄露,那么就从Activity的setContentView看起。

1
2
3
4
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

通过代码一步一步查找,发现最后是实现了PhoneWindow的setContentView的方法。PhoneWindow位于\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWondow.java;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//最后还是在这里实现,如果mContentParent不为null,mContentParent直接把layoutResID的布局添加到根布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}

可以看见Activity的setContentView(int layoutRes)最后还是用LayoutInflater实现了。那我们再看mLayoutInflater是什么时候生成的,代码如下:

1
2
3
4
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}

可以看出是在PhoneWindow生成时一起生成的,那么PhoneWindow什么生成的,继续看Activityd的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//这里可以看出把Activity当成Context传给PhoneWindow了,并用这个Context生成了LayoutInflater。
mWindow = new PhoneWindow(this);
....
省略
....
}

这里我们可以看到Activity传递到了PhoneWindow,并通过Activity生成了LayoutInflater。这里记住,下面分析时要用到。

既然是Activity被View持有,那么我们就从View何时被赋值Context出发。那么我先列出View有多少种生成方法。
1、构造函数生成

1
2
public View(Context context) {
}

对构造函数生成,可以看出在这个方法中我们可以直接控制Context,也就是说我们自己可以控制是否直接使用Activity,可以直接使用Application,当然会出些问题,原因以及解决方法下面再说。

2、LayoutInflater方法生成

然后再看LayoutInflater如何生成View,在这之前我先找到LayoutInflater的实现类,因为你会发现LayoutInflater是抽象的。现在从LayoutInflater的生成方法开始看起。

1
2
3
4
5
6
7
8
9
10
11
/**
* Obtains the LayoutInflater from the given context.
*/
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的getSystemService方法,一直往上找会发现最后调用了ContextImpl的getSystemService的方法。
LayoutInflater实例化的地方为ContextImpl,位于\frameworks\base\core\java\android\app\ContextImpl.java。代码为:

1
2
3
4
5
6
7
8
9
10
11
12
static {
....
省略
....
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
....
省略
....
}

通过一步一步的代码跟踪,发现了LayoutInflater的实现类BridgeInflater,位于frameworks\base\tools\layoutlib\bridge\src\android\view\BridgeInflater.java,在这我们只需要关心onCreateView方法。
最后调用PolicyManager的makeNewLayoutInflater方法生成,同时传入ctx.getOuterContext(),这就是LayoutInflater所持有的Context,这就是ContextImpl生成时传入ContextImpl的Context。ContextImpl的对象生成代码位于\frameworks\base\core\java\android\app\ActivityThread.java:

1
2
3
4
5
6
7
8
9
10
11
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
生成ContextImpl并把Activity通过setOuterContext方法赋值给ContextImpl。
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
....
省略
....
return baseContext;
}

可以看出Activity和ContextImpl互相持有,那么Application和ContextImpl也互相持有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
public final class BridgeInflater extends LayoutInflater {
private final IProjectCallback mProjectCallback;
private boolean mIsInMerge = false;
private ResourceReference mResourceReference;
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit."
};
protected BridgeInflater(LayoutInflater original, Context newContext) {
//把Context传递给LayoutInflater父类
super(original, newContext);
mProjectCallback = null;
}
public BridgeInflater(Context context, IProjectCallback projectCallback) {
//把Context传递给LayoutInflater父类
super(context);
mProjectCallback = projectCallback;
mConstructorArgs[0] = context;
}
@Override
public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
View view = null;
try {
for (String prefix : sClassPrefixList) {
try {
//调用LayoutInflater的createView方法
view = createView(name, prefix, attrs);
if (view != null) {
break;
}
} catch (ClassNotFoundException e) {
// Ignore. We'll try again using the base class below.
}
}
try {
if (view == null) {
view = super.onCreateView(name, attrs);
}
} catch (ClassNotFoundException e) {
// Ignore. We'll try again using the custom view loader below.
}
// Finally try again using the custom view loader
try {
if (view == null) {
view = loadCustomView(name, attrs);
}
} catch (ClassNotFoundException e) {
// If the class was not found, we throw the exception directly, because this
// method is already expected to throw it.
throw e;
}
} catch (Exception e) {
// Wrap the real exception in a ClassNotFoundException, so that the calling method
// can deal with it.
ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
throw exception;
}
setupViewInContext(view, attrs);
return view;
}
@Override
public View createViewFromTag(View parent, String name, AttributeSet attrs) {
View view = null;
try {
//调用LayoutInflater的createViewFromTag方法
view = super.createViewFromTag(parent, name, attrs);
} catch (InflateException e) {
// try to load the class from using the custom view loader
try {
view = loadCustomView(name, attrs);
} catch (Exception e2) {
// Wrap the real exception in an InflateException so that the calling
// method can deal with it.
InflateException exception = new InflateException();
if (e2.getClass().equals(ClassNotFoundException.class) == false) {
exception.initCause(e2);
} else {
exception.initCause(e);
}
throw exception;
}
}
setupViewInContext(view, attrs);
return view;
}
@Override
public View inflate(int resource, ViewGroup root) {
Context context = getContext();
if (context instanceof BridgeContext) {
BridgeContext bridgeContext = (BridgeContext)context;
ResourceValue value = null;
Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
if (layoutInfo != null) {
value = bridgeContext.getRenderResources().getFrameworkResource(
ResourceType.LAYOUT, layoutInfo.getSecond());
} else {
layoutInfo = mProjectCallback.resolveResourceId(resource);
if (layoutInfo != null) {
value = bridgeContext.getRenderResources().getProjectResource(
ResourceType.LAYOUT, layoutInfo.getSecond());
}
}
if (value != null) {
File f = new File(value.getValue());
if (f.isFile()) {
try {
XmlPullParser parser = ParserFactory.create(f);
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, bridgeContext, false);
//调用LayoutInflater的inflate方法
return inflate(bridgeParser, root);
} catch (Exception e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + f.getAbsolutePath(), e, null /*data*/);
return null;
}
}
}
}
return null;
}
....
省略
....
}

最后还是会调用LayoutInflater的inflate的方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//获取页面的xml资源解析
final XmlResourceParser parser = res.getLayout(resource);
try {
//解析xml资源文件
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
//设置View构造函数的参数,设置Context参数为LayoutInflater自带的context,下面createView方法会使用到。
mConstructorArgs[0] = inflaterContext;
....
省略
....
//创建View
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
....
省略
....
}

上面代码就是创建的一下流程,可以看出View是LayoutInflater创建的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
....
省略
....
//View 最后调用了onCreateView方法生成了View,mFactory2、mFactory默认为null,最后会调用onCreateView方法。
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
....
省略
....
}

最后还是到了createView方法。

1
2
static final Class<?>[] mConstructorSignature = new Class[] {
Context.class, AttributeSet.class};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
....
省略
....
//获取默认构造,使用View(Context context, @Nullable AttributeSet attrs)
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
....
省略
....
Object[] args = mConstructorArgs;
args[1] = attrs;
//此处args参数中args[0]已结设置为LayoutInflater,上面中已经提及。
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
....
省略
....
}

从上面分析可以看出最后和View绑定的Context就是LayoutInflater所持有的Context。这时我们就要着重分析LayoutInflater持有的Context到底是不是Context,如果是那么是什么时候赋值的,那我们再研究一下LayoutInflater的生成方法:

1
2
3
4
5
6
7
8
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;
}

如果研究过Activity和Application的源码我们会发现Activity的源码继承类比Activity多了一层,那就是Activity继承于ContextThemeWrapper,而Application继承于ContextWrapper,他们都对getSystemService方法进行了重写,ContextThemeWrapper的重写代码为:

1
2
3
4
5
6
7
8
9
@Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}

而ContextWrapper重写的代码为:

1
2
3
4
@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}

ContextThemeWrapper继承于ContextWrapper,而getBaseContext()方法如下:

1
2
3
public Context getBaseContext() {
return mBase;
}

可以看出ContextThemeWrapper和ContextWrapper最终实现除了cloneInContext方法外都一样,最终都是通过自己持有的ContextImpl对象执行getSystemService方法来获取LayoutInflater对象。而前面分析可知最终都会用ContextImpl对象所持有的mOuterContext对象,即Application和Activity生成LayoutInflater对象,进而LayoutInflater对象持有Activity或Application。我们在看一下ContextThemeWrapper重写中用到的cloneInContext方法。

1
2
3
4
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new BridgeInflater(this, newContext);
}

可以看出该方法的作用就是克隆,只是把新生成LayoutInflater对象的Context对象替换掉。其实我有点不明白,因为ContextImpl方法中生成LayoutInflater用到的Context就是Activity本身,为什么还要这一步。到这一步可以很容易的看出View为什么会持有Activity了。逻辑整理如下:

PhoneWindow 用Activity生成LayoutInflater

LayoutInflater对象持有Activity;

LayoutInflater在生成View对象时把Activity赋值给View,

View 持有Activity的引用。

##2、解决方案##

我的思路很简单既然是因为View持有Activity的引用,那就不让Activity的强引用就可以了。

从以上代码分析可以看出Activity之所以会被持有都是因为PhoneWindow中生成LayoutInflater对象调用了Activity的getSystemService方法。该方法生成的对象持有Activity对象的引用。一开始我准备直接在BaseActivity中重写的getSystemService方法。最早为:

1
2
3
4
5
6
7
8
9
10
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
//使用代理类
baseContextWrappernew = new BaseContextWrapper(this);
mInflater = LayoutInflater.from(BaseApplication.getInstance());
}
return mInflater;
}
return super.getSystemService(name);

但这种方法有个严重的问题,就是Activity的很多主题样式不能使用了,所以有看了ContextThemeWrapper的方法,自己继承ContextWrapper类,重写其中的几个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//创建代理类
public class BaseContextWrapper extends ContextWrapper {
static Field field;
static {
try {
field = ContextWrapper.class.getDeclaredField("mBase");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
Context mContext;
public BaseContextWrapper(Context context) {
super(null);
mContext = context;
if(context instanceof ContextWrapper){
context = ((ContextWrapper)context).getBaseContext();
}
attachBaseContext(context);
}
//当获取主题信息时直接调用Activity的getTheme方法。
@Override
public Resources.Theme getTheme() {
if(mContext != null){
return mContext.getTheme();
}
return super.getTheme();
}
@Override public Object getSystemService(String name) {
if(mContext == null){
return super.getSystemService(name);
}
return mContext.getSystemService(name);
}
//释放Activity的持有。
public void recycle(){
try {
field.set(this,null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public Activity getActivityContext(){
Activity context = (Activity) mContext;
return context;
}
}

这样View就只会持有BaseContextWrapper对象,当Activity销毁时会执行recycle方法释放Activity已经ContextImpl。这样View已经不能直接持有Activity了,但还有一个问题就是View本身持有包含它的ViewGroup就是ViewParent,这里只需要去除持有关系即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ViewGroup viewGroup = (ViewGroup) getWindow().getDecorView();
//去除方法要在BaseContextWrapper的recycle方法之前执行。
clearView(viewGroup);
if(baseContextWrappernew != null){
baseContextWrappernew.recycle();
}
private void clearView(View viewClear){
if(viewClear instanceof ViewGroup){
int length = ((ViewGroup)viewClear).getChildCount();
View view = null;
for(int i=0;i< length;i++){
view = ((ViewGroup) viewClear).getChildAt(i);
clearView(view);
}
try {
((ViewGroup) viewClear).removeAllViews();
} catch(Exception e){
}
}
}

这些只是清楚了VIew和Activity的基本持有关系,但各种事件监听之类的要自己注意去除。

##3、LayoutInflater 的Activity or Application##
从我上面的分析其实已经可以看出了部分区别。

1、就是用Activity那么View持有的Context就是Activity,如果用的是Application那么View持有的Context就是Application,而有些人比较喜欢直接用View的Context转为Activity,如果用Application那时就会出错。
2、Application继承自ContextWrapper,而Activity继承自ContextThemeWrapper,而ContextThemeWrapper有时对ContextWrapper的继承,对其中一部分方法进行了修改,这也就是不同所在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//在Activity生成时会获取Activity的主题,并调用该方法
@Override public void setTheme(int resid) {
mThemeResource = resid;
initializeTheme();
}
/** @hide */
@Override
public int getThemeResId() {
return mThemeResource;
}
@Override public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
//Activity的主题产生作用。覆盖Application的相同属性
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
theme.applyStyle(resid, true);
}
//生成主题并获取Application的主题赋值给新主题,并使Activity的主题产生作用。
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
Resources.Theme theme = mBase.getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
//Activity的主题产生作用。
onApplyThemeResource(mTheme, mThemeResource, first);
}

这里就已经产生基本的不同了,比如EditText 的字体颜色 设置属性为?textcolor,这时如果Application设置为黑色,而Activity的主题中设置为红色,那么如果这个Activity的某个EditText用了Application,那么这时字体会显示黑色而不是预期的红色。

Android内存泄露

做过Android的应该知道,如果不关心内存的泄露问题,就会导致内存溢出,那么应用就会出现随时崩溃的问题,用户体验很差,就比如我最早做的一个项目,其中包含考试的模块,一开始也没有做考试备份的功能,也就是放弃考试必须重新考试。有一次测试中,100道题快要做完时,因为有一题包含了图片,所以崩溃了,然后所有的都完蛋了,我自己作为开发者都快崩溃了,更不要说那些认真答题的使用者了。所以为了用户的满意度必须解决内存泄露的问题。因为Android上层使用的是Java,所以大家知道Java的特性就应该知道Android为什么会存在内存泄露了。

##1、static静态变量内存泄露##

问题:
有时我们为了方便各个组件之间传递大数据不得不使用static变量,因为static变量是属于类的,所以可以之间使用类调用,还是那个包含考试的项目,因为需要把试题传递到另一个Activity使用,但数据本身太大超过了Intent传递数据的上限。所以使用了静态变量,最后也没有释放,这就产生了泄露,还有就是类似于Activity的静态变量,也是一个很早的项目里面涉及到电话监听,我忘了是监听电话开始还是结束状态,但监听会调用两次,然后会启动两次Activity,这时问题就出来了,当时没什么技术,也没解决问题的理念,所以竟然把Activity当成了静态变量保存在了那个Activity中,具体的我已经忘了,但是我知道最后Activity中的静态对象没有释放,也就是永远会把最后一个实例化的Activity放在静态变量中,没有释放。
解决方案:
这样做本身没有太大的问题,但如果不释放内存就有问题了,在这中情况下,只需要在不需要的时候释放内存就行了,比如上面两个问题,其实只要在Activity的onDestory方法中设置为null,就把引用释放了。

##2、资源未关闭或未回收导致的内存泄露##
问题:
数据库Cursor没关闭,IO流没关闭,当使用SQLite数据库没有关闭Cursor,以及文件流没有关闭都会导致内存泄露。
解决方案:
调用Cursor.close(),以及IO的close方法。

##3、未取消注册广播接收者##
问题:
使用广播时只注册没有取消注册。
解决方案:
registerReceiver()和unregisterReceiver()要成对出现,通常需要在Activity的onDestory()方法去取消注册广播接收者。

##4、第三方jar包使用不当导致的内存泄露##
问题:
使用第三方jar包没有注意使用细节出现,比如eventBus只注册而忘记取消注册,百度地图或高德地图没有调用map的onDestory的方法等等。
解决方案:
使用第三方是要注意其使用注意事项及时释放引用。当然也有的第三方jar包本身就有问题,不提供释放方法,比如我就遇到过一个gif播放jar包,使用过后肯定会导致泄露,最后只能用反射把引用对象设置为null才把泄露解决,这也就要求我们选择jar包时要经过测试,不能遇到解决方法就用,当然还是我当时水平不行,其实当时应该修改源码再打成jar的。

##5、Context使用不当导致的内存泄露##
问题:
我以前写了并收集了一些公共方法类,然后在项目中使用而这些方法类中使用了Context,而且只要是Context就可以。当时觉得每次传一个Context参数太麻烦然后写了一个UtilsConfig类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UtilsConfig{
private static Context mContext;
private UtilsConfig(){
throw new RuntimeException("UtilsConfig不能实例化");
}
public static init(Context mContext){
UtilsConfig.mContext = mContext;
}
public static Context getContext(){
if(mContext == null){
throw new RuntimeException("UtilsConfig 未初始化");
}
return mContext;
}
}

可以看出如果在一个Activity中调用该方法,肯定会产生泄露。
解决方案:
该方法其实使用的是ApplicationContext,所以只要在Application类中初始化就可以了。当然我们要看到另一个问题,就是如果所需要的参数Application本身就可以那么就不要使用Activity等,因为Application的生命周期是全局的所以不会产生内存泄露。一个原则就是能用Application就用Application,当然我的上一个方法后来也进行了改进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UtilsConfig{
private static Context mContext;
private UtilsConfig(){
throw new RuntimeException("UtilsConfig不能实例化");
}
public static init(Context mContext){
UtilsConfig.mContext = mContext.getApplicationContext();
}
public static Context getContext(){
if(mContext == null){
throw new RuntimeException("UtilsConfig 未初始化");
}
return mContext;
}
}

##6、Handler导致内存泄露##
问题:
知道Handler原理的都清楚,Handler模块主要有三个类组成,分别是Handler、Looper以及MessageQueue三部分组成。Handler负责消息处理,Looper主要负责消息循环获取,MessageQueue就是消息队列管理,而每个线程只能有一个Looper以及MessageQueue,可以有多个Handler,而我们使用时基本上是在主线程上使用,也就是说我们是在使用主线程的Looper、MessageQueue。而这个MessageQueue的生命周期是全局的,所以如果对象被MessageQueue持有引用则无法被回收。如果是使用其他线程的Handler,如果那个线程持续运行,而Looper也没停止·,结果是一样的。引用链为
MessageQueue 持有 Message ,
Message 持有 Handler,
Handler 持有 View 或 Activity。
从而导致内存泄露。
解决方案:
如果只是MessageQueue导致的内存泄露。只需要调用。Handler.removeCallbacksAndMessages(null);即可,这会请求MessageQueue中所有该Handler的Message。如果是线程持有该对象,该对象又是内部类导致的泄露放在下面。

##7、内部类或匿名类导致内存泄露##
问题:
我们知道非静态内部类持有外部类的引用,匿名类同样会持有定义它们的对象的引用。而匿名类或内部类对象又被其他类持有引用导致内存泄露,比如我写了一个天气预报的模块,我直接使用的中国天气网的数据,所以网络请求做了单独写,然后我用匿名类对象做了网络请求回调,然后在请求结束之前退出,网络请求继续,从而Activity也泄露了。
解决方案:
内部类泄露一般解决方案是该为静态内部类,然后静态内部类持有Activity的弱引用。匿名类也可以先该为静态类,然后再实例化,其他和内部类处理方式一致,其实如果在Activity执行onDestory时把持有内部类或匿名类对被持有引用的地方置为null即可。

##8、异步任务导致内存泄露##
问题:
这里的异步任务我把线程以及定时器都作为了异步任务。
解决方案:

Bundle、Parcel解析

Bundle、Parcel解析

Bundle 被我们经常用来进行启动Activity、service以及传递数据.Bundle支持的数据类型有原始类型以及其封装类,Parcelable 类型、Size类型、SizeF类型以及Serializable类型。

Bundle基本使用

Intent添加数据

我们先看一下Bundle的添加传递信息的方法,
添加Parcelable类型参数。

1
2
3
4
5
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFdsKnown = false;
}

添加float类型参数。

1
2
3
4
5
6
7
8
9
@Override
public void putFloat(@Nullable String key, float value) {
super.putFloat(key, value);
}
//调用父类方法保存参数
void putFloat(@Nullable String key, float value) {
unparcel();
mMap.put(key, value);
}

通过putParcelable 方法和putFloat 方法可以看出最后数据都会保存在mMap对象中。

1
2
3
4
ArrayMap<String, Object> mMap = null;
.....
mMap = capacity > 0 ?
new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();

mMap的定义可以看出mMap就是一个ArrayMap,存放键值对,而且value的类型是Object类型,就是所有参数的类型都被转换为Object类型保存在mMap中。而系统如何使用这些数据以下再说。

Bundle数据储存

找到Intent数据保存的切入点

通过研究activity的startActivity方法,发现最后会调用ActivityManagerProxy的startActivity方法,该类是ActivityManagerNative的内部类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
参数重点是intent,就是我放入startActivity的intent
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
//使用该方法把Intent的Bundle的参数导入Parcel的data中,
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

可以看出最后Intent调用了writeToParcel方法,把Bundle中的数据导入进Parcel类型数据中,再看writeToParcel的方法:

1
2
3
4
5
6
7
public void writeToParcel(Parcel out, int flags) {
....
省略
....
//mExtras就是保持的Bundle类型数据,也就是我们要保存的数据
out.writeBundle(mExtras);
}

顺着代码调用进入到了Parcel的writeBundle方法,

1
2
3
4
5
6
7
8
public final void writeBundle(Bundle val) {
if (val == null) {
writeInt(-1);
return;
}
//调用需要传递数据的Bundle的writeToParcel方法
val.writeToParcel(this, 0);
}

最后还是回到Bundle类本身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//Bundle的方法
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds);
try {
//调用父类的方法写入数据
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
//BaseBundle把本身数据写入parcel中
void writeToParcelInner(Parcel parcel, int flags) {
if (mParcelledData != null) {
if (mParcelledData == EMPTY_PARCEL) {
parcel.writeInt(0);
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(BUNDLE_MAGIC);
parcel.appendFrom(mParcelledData, 0, length);
}
} else {
// Special case for empty bundles.
if (mMap == null || mMap.size() <= 0) {
parcel.writeInt(0);
return;
}
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
//核心代码,mMap就是上面一开始保存数据的对象,也就是所有需要传输的数据都在该对象中。又返回到了Parcel的方法中
parcel.writeArrayMapInternal(mMap);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
}

Parcel最后都都调用write方法保存,最后都会调用本地方法,因为Parcel的数据结构都在c层,所以不做深入研究。有兴趣的可以看看,路径为frameworks\base\core\jni\android_os_Parcel.cpp,以及frameworks\native\libs\binder\Parcel.cpp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void writeArrayMapInternal(ArrayMap<String, Object> val) {
....
省略
....
int startPos;
for (int i=0; i<N; i++) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
// 写入mMap的key值
writeString(val.keyAt(i));
// 写入mMap的value值
writeValue(val.valueAt(i));
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+ " " + val.keyAt(i));
}
}

走到这里,基本可以理清mMap的数据最后都会写入Parcel中,也就是说Bundle也是使用Parcel传递的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public final void writeValue(Object v) {
if (v == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
} else if (v instanceof Map) {
writeInt(VAL_MAP);
writeMap((Map) v);
} else if (v instanceof Bundle) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof Parcelable) {
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} else if (v instanceof Short) {
writeInt(VAL_SHORT);
writeInt(((Short) v).intValue());
} else if (v instanceof Long) {
writeInt(VAL_LONG);
writeLong((Long) v);
} else if (v instanceof Float) {
writeInt(VAL_FLOAT);
writeFloat((Float) v);
} else if (v instanceof Double) {
writeInt(VAL_DOUBLE);
writeDouble((Double) v);
} else if (v instanceof Boolean) {
writeInt(VAL_BOOLEAN);
writeInt((Boolean) v ? 1 : 0);
} else if (v instanceof CharSequence) {
// Must be after String
writeInt(VAL_CHARSEQUENCE);
writeCharSequence((CharSequence) v);
} else if (v instanceof List) {
writeInt(VAL_LIST);
writeList((List) v);
} else if (v instanceof SparseArray) {
writeInt(VAL_SPARSEARRAY);
writeSparseArray((SparseArray) v);
} else if (v instanceof boolean[]) {
writeInt(VAL_BOOLEANARRAY);
writeBooleanArray((boolean[]) v);
} else if (v instanceof byte[]) {
writeInt(VAL_BYTEARRAY);
writeByteArray((byte[]) v);
} else if (v instanceof String[]) {
writeInt(VAL_STRINGARRAY);
writeStringArray((String[]) v);
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
writeInt(VAL_CHARSEQUENCEARRAY);
writeCharSequenceArray((CharSequence[]) v);
} else if (v instanceof IBinder) {
writeInt(VAL_IBINDER);
writeStrongBinder((IBinder) v);
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
} else if (v instanceof long[]) {
writeInt(VAL_LONGARRAY);
writeLongArray((long[]) v);
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
} else if (v instanceof SizeF) {
writeInt(VAL_SIZEF);
writeSizeF((SizeF) v);
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
// Only pure Object[] are written here, Other arrays of non-primitive types are
// handled by serialization as this does not record the component type.
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);
} else {
throw new RuntimeException("Parcel: unable to marshal value " + v);
}
}
}

要写入Parcel的对象要先判断类型,然后先写入该对象的类型标志,再调用对应类型的方法写入要保存数据。这里需要关注的只有一点,就是Map类型的对象保存,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final void writeMap(Map val) {
writeMapInternal((Map<String, Object>) val);
}
void writeMapInternal(Map<String,Object> val) {
if (val == null) {
writeInt(-1);
return;
}
Set<Map.Entry<String,Object>> entries = val.entrySet();
writeInt(entries.size());
for (Map.Entry<String,Object> e : entries) {
writeValue(e.getKey());
writeValue(e.getValue());
}
}

Parcel只是获取Map的键值对,然后保存,该方法没有保存Map类的信息,也就是类型擦除,最后经过startActivity跳到其他Activity时,再获取时所以Map类型对象都会转换为HashMap对象,所以Bundle传递数据不能使用其他Map类型,比如TreeMap。

Parcel转换为类型对象

这里还是从Intent类看起,Intent实现了Parcelable,ActivityManagerProxy的startActivity方法中intent.writeToParcel(data, 0);通过这个方法Intent把对象的具体信息写入Parcel中,通过一连串的传输最后又通过序列化转为Intent对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
protected Intent(Parcel in) {
readFromParcel(in);
}
public void readFromParcel(Parcel in) {
//恢复action属性
setAction(in.readString());
//恢复Data属性
mData = Uri.CREATOR.createFromParcel(in);
//Type属性
mType = in.readString();
//Flag属性,启动模式
mFlags = in.readInt();
mPackage = in.readString();
mComponent = ComponentName.readFromParcel(in);
if (in.readInt() != 0) {
mSourceBounds = Rect.CREATOR.createFromParcel(in);
}
int N = in.readInt();
if (N > 0) {
mCategories = new ArraySet<String>();
int i;
for (i=0; i<N; i++) {
mCategories.add(in.readString().intern());
}
} else {
mCategories = null;
}
if (in.readInt() != 0) {
mSelector = new Intent(in);
}
if (in.readInt() != 0) {
mClipData = new ClipData(in);
}
mContentUserHint = in.readInt();
//传递的数据
mExtras = in.readBundle();
}

使用过Parcelable的都知道反序列化最后都会调用包含Parcel参数的构造函数,对于Intent就是Intent(Parcel in),然后调用readFromParcel方法,把Intent的相关属性恢复。对于Bundle数据的恢复是调用了Parcel的readBundle方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//Parcel.java
public final Bundle readBundle() {
return readBundle(null);
}
//Parcel.java
public final Bundle readBundle(ClassLoader loader) {
//获取Bundle保存数据的长度
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
return null;
}
final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}
//Bundle.java
Bundle(Parcel parcelledData, int length) {
super(parcelledData, length);
mHasFds = mParcelledData.hasFileDescriptors();
mFdsKnown = true;
}
//BaseBundle.java
BaseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
//BaseBundle.java
private void readFromParcelInner(Parcel parcel, int length) {
if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = EMPTY_PARCEL;
return;
}
//获取数据类型魔数,若不为BUNDLE_MAGIC,则抛出异常。
int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
//noinspection ThrowableInstanceNeverThrown
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(offset + length);
Parcel p = Parcel.obtain();
p.setDataPosition(0);
//通过传入的Parcel对象生成一个新的Parcel对象,也就是截取Parcel的数据组成新的Parcel对象。
p.appendFrom(parcel, offset, length);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
//然后把新生成的Parcel赋值给Bundle。
mParcelledData = p;
}

Intent的readBundle方法没有直接的把Parcel数据解析为对象数据,还是以Parcel形式保存着,那Parcel是什么时候解析的呢?然后我查看了一下Bundle的get方法,发现最后都调用了unparcel()。

1
2
3
4
5
6
7
8
9
@Override
public byte getByte(String key) {
return super.getByte(key);
}
byte getByte(String key) {
unparcel();
return getByte(key, (byte) 0);
}

然后就开始研究unparcel()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
synchronized void unparcel() {
//没有Parcel数据,不创建mMap对象,直接返回
if (mParcelledData == null) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
return;
}
//mParcelledData为默认的EMPTY_PARCEL,创建或清理mMap对象,不解析数据,直接返回
if (mParcelledData == EMPTY_PARCEL) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": empty");
if (mMap == null) {
mMap = new ArrayMap<String, Object>(1);
} else {
mMap.erase();
}
mParcelledData = null;
return;
}
//解析Parcel数据,或取Parcel含有多少键值对数据
int N = mParcelledData.readInt();
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + N + " maps");
if (N < 0) {
return;
}
if (mMap == null) {
mMap = new ArrayMap<String, Object>(N);
} else {
mMap.erase();
mMap.ensureCapacity(N);
}
//核心功能读取数据放入mMap中
mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
mParcelledData.recycle();
mParcelledData = null;
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}

unparcel方法的就是判断当前的Bundle是否含有Parcel数据,然后根据结果创建mMap对象,然后调用readArrayMapInternal把Parcel中的数据发序列化为对象。然后存放在mMap中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void readArrayMapInternal(ArrayMap outVal, int N,
ClassLoader loader) {
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
}
int startPos;
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
outVal.append(key, value);
N--;
}
outVal.validate();
}

这里我们可以看到先读取了key,然后调用readValue获取value的值,然后存储到mMap中。我在这里主要分析一下Map类型数据的获取,可以很明了的知道为什么强制为TreeMap会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final Object readValue(ClassLoader loader) {
int type = readInt();
switch (type) {
case VAL_NULL:
return null;
...
省略
...
case VAL_MAP:
return readHashMap(loader);
...
省略
...
}
}
public final HashMap readHashMap(ClassLoader loader)
{
int N = readInt();
if (N < 0) {
return null;
}
HashMap m = new HashMap(N);
readMapInternal(m, N, loader);
return m;
}

一切都很明了,Parcel对Map类型的数据进行反序列化时,获取key\value的值都存放在HashMap中,然后返回HashMap,也就是说所有Map类型数据最后都会转为HashMap类型数据。强制当然会报错。

Java 动态代理

动态代理第一次遇到是研究Retrofit源码时,经过代码分析发现retrofit巧妙的运用了动态代理把请求接口类的方法的注解信息以及参数按照既定的规则组合成完整的请求。

动态代理的简单用例

一个简单的接口类

1
2
3
4
public interface Person {
void doThis(String thing);
void doThat(String thing);
}

可以看出该类只有一个简单的方法,

InvocationHandler 类的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PersonInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args){
if(method.getName().equals("doThis")){
System.out.println(args[0]);
return null;
}
if(method.getName().equals("doThat")){
System.out.println(args[0]);
return null;
}
return null;
}
public static void main(String[] args) {
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new PersonInvocationHandler());
person.doThis("I do this thing");
person.doThat("I do that thing");
}
}

输出结果为

1
2
I do this thing
I do that thing

通过输出接口分析,动态代理生成的对象调用方法时最终都会调用InvocationHandler的invoke方法,invoke(Object proxy, Method method, Object[] args) 可以看出 proxy是代理对象本身,method是调用的方法属性,而args数组就是方法参数。

动态代理的源码解析

我就从对象生成这句代码看起:

1
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new PersonInvocationHandler());

从而找到实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
if (invocationHandler == null) {
throw new NullPointerException("invocationHandler == null");
}
Exception cause;
try {
return getProxyClass(loader, interfaces)
.getConstructor(InvocationHandler.class)
.newInstance(invocationHandler);
} catch (NoSuchMethodException e) {
cause = e;
} catch (IllegalAccessException e) {
cause = e;
} catch (InstantiationException e) {
cause = e;
} catch (InvocationTargetException e) {
cause = e;
}
AssertionError error = new AssertionError();
error.initCause(cause);
throw error;
}

可以看出该方法主要是对异常情况进行处理,没有涉及到动态代码的原理。可以看出主要实现是getProxyClass方法,getProxyClass作用是获取代理类Class对象。getConstructor方法属于Class的方法,作用是获取构造函数,而newInstance的作用只是为了实例化一个代理类对象。以下查看getProxyClass方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//从interfaces可以看出,动态代理可以同时实现多个接口
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException {
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
if (interfaces == null) {
throw new NullPointerException("interfaces == null");
}
final List<Class<?>> interfaceList = new ArrayList<Class<?>>(interfaces.length);
Collections.addAll(interfaceList, interfaces);
final Set<Class<?>> interfaceSet = new HashSet<Class<?>>(interfaceList);
if (interfaceSet.contains(null)) {
throw new NullPointerException("interface list contains null: " + interfaceList);
}
if (interfaceSet.size() != interfaces.length) {
throw new IllegalArgumentException("duplicate interface in list: " + interfaceList);
}
//查看缓存中是否已经存在代理类
synchronized (loader.proxyCache) {
Class<?> proxy = loader.proxyCache.get(interfaceList);
if (proxy != null) {
return proxy;
}
}
String commonPackageName = null;
for (Class<?> c : interfaces) {
//被代理类必须是接口类,这点感觉不好,如果任何类都可以代理那就很爽了
if (!c.isInterface()) {
throw new IllegalArgumentException(c + " is not an interface");
}
if (!isVisibleToClassLoader(loader, c)) {
throw new IllegalArgumentException(c + " is not visible from class loader");
},
if (!Modifier.isPublic(c.getModifiers())) {
String packageName = c.getPackageName$();
if (packageName == null) {
packageName = "";
}
if (commonPackageName != null && !commonPackageName.equals(packageName)) {
throw new IllegalArgumentException(
"non-public interfaces must be in the same package");
}
commonPackageName = packageName;
}
}
//可以看出改代码是获取所有接口的方法属性
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
//生成代理类的包名
String baseName = commonPackageName != null && !commonPackageName.isEmpty()
? commonPackageName + ".$Proxy"
: "$Proxy";
Class<?> result;
synchronized (loader.proxyCache) {
result = loader.proxyCache.get(interfaceList);
if (result == null) {
String name = baseName + nextClassNameIndex++;
//生成Class文件并生成代理类Class对象。
result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
loader.proxyCache.put(interfaceList, result);
}
}
return result;
}
1
2
3
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);

从以上代码可以看出动态代理最后的核心就是generateProxy,但它是本地方法,而且涉及动态产生Class文件,暂时不去涉猎,但基本已经搞清了动态代理的基本流程,如下;

1、调用 Proxy 的newProxyInstance 方法传入加载器ClassLoader对象,需要被代理的接口类的Class对象,以及InvocationHandler 的实例。
2、调用getProxyClass方法生成代理类Class文件,并加载生成Class对象,并返回。
3、然后通过Class对象进行实例化,产生代理类对象。
4、当调用代理类实例化对象之后,动态类对象就会回调InvocationHandler的invoke方法,并将方法属性、代理类对象已经参数传人,我们就可以在invoke方法中做统一处理。