Java Runtime 动态代理 - 王胜
使用过Spring的朋友应该对AOP『Aspected Oriented Programe』很熟悉,那么是否被里面的前置增强、后置增强、环绕增强所震撼呢?有没有想过这背后的技术实现呢?其实JDK的动态代理就能实现。下面做一个简单的示例,来阐述如何在Runtime时,动态增强原有的方法。
前提环境
假设我们有一个Waiter
类,里面含有两个方法:
- welcome() // 欢迎光临!
- bye() // 谢谢惠顾!
1 | 欢迎光临! |
后来发现欢迎顾客和送别顾客不够礼貌,需要在欢迎之前说『您好!』,送别顾客后说『祝您愉快!』。但我们不希望动原有的业务类Waiter
,那么如何实现呢?
实现步骤
创建一个业务对象『必须要有接口』, 执行业务方法
1
2
3
4public interface Receptor {
void welcome();
void bye();
}1
2
3
4
5
6
7
8
9
10
11public class Waiter implements Receptor{
@Override
public void welcome() {
System.out.println("欢迎光临!");
}
@Override
public void bye() {
System.out.println("谢谢惠顾!");
}
}创建一个具有公用代码的对象,并将这些公用代码声明成方法
1
2
3
4
5
6
7
8public class PoliteWords {
public void sayHello() {
System.out.println("您好!");
}
public void sayHappy() {
System.out.println("祝您愉快!");
}
}创建一个可以使用公用代码对象的对象,这个对象可以对要被处理的目标对象实施方法拦截,执行公用代码对象中的方法。借助JDK提供的API『InvocationHandler』
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
27public class ReceptorHandler implements InvocationHandler {
// 省略...
/**
* 重写invoke方法,从而达到增强目标对象的目的
*
* @param proxy 增强后的对象
* @param method 目标对象上正在被调用的方法
* @param args 目标对象上正在被调用的方法所传递的参数
* @return 目标对象正在被调用的方法执行的结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
if (method.getName().equals("welcome")) {// welcome之前sayHello
interceptor.sayHello();
result = method.invoke(target, args);
} else if (method.getName().equals("bye")) {// bye之后sayHappy
result = method.invoke(target, args);
interceptor.sayHappy();
} else {
result = method.invoke(target, args);
}
return result;
}
}创建一个可以获取增强了功能之后对象的代理工厂,工厂的作用就是返回增强后的代理Proxy, 他的类型应该和目标对象的接口类型相同。
1
2
3
4
5
6
7
8public class ReceptorProxyFactory {
public static Object getProxy(Object target) {
ReceptorHandler handler = new ReceptorHandler();
handler.setTarget(target);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
}编写测试类(可选)
1
2
3
4
5
6
7
8public class TestProxy {
public static void main(String[] args) {
Waiter target = new Waiter();
Receptor receptor = (Receptor) ReceptorProxyFactory.getProxy(target);
receptor.welcome();
receptor.bye();
}
}
具体的示例源码,移步 Github-helloJavaSE
执行结果
1 | 您好! |
注意: JDK动态代理只能代理接口类型的对象,并且返回接口类型。如果要增强那些没有接口的对象,JDK动态代理不能实现,要依赖 CGLIB 技术。
Android转场动画
startActivity()
1
2
3Intent intent = new Intent(MainActivity.this,OtherActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.push_up_in,R.anim.push_up_out);ActivityCompat
1 | ActivityCompat.startActivity((Activity) context, intent, null); |
1 | public static void startActivity(Activity activity, Intent intent, @Nullable Bundle options) { |
- Android 4.1(API16)以下转场动画
1 | AIntent intent = new Intent(MainActivity.this,OtherActivity.class); |
Android 4.1(API16)以上转场动画
- ActivityOptions
- Android 4.1(API16)提供了一个新类ActivityOptions,用来实现Activity的切换动画。
ActivityOptions常用使用
makeClipRevealAnimation(View source, int startX, int startY, int width, int height)
1
2
3Added in API level 23
````
- makeCustomAnimation(Context context, int enterResId, int exitResId)Added in API level 16
1
- makeScaleUpAnimation(View source, int startX, int startY, int width, int height)
Added in API level 16
1
- makeSceneTransitionAnimation(Activity activity, Pair...<View, String> sharedElements)
Added in API level 21
1
- makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)
Added in API level 21
1
- makeTaskLaunchBehind()
Added in API level 21
1
- makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)
Added in API level 16
1
2
3
- ActivityOptions兼容包:ActivityOptionsCompat
- 自定义动画:makeCustomAnimation
public static ActivityOptions makeCustomAnimation(Context context,
int enterResId, int exitResId) {
return makeCustomAnimation(context, enterResId, exitResId, null, null);
}
1
2
3
4
5
6
7
8
9
10
11
12
enterResId:开启的动画
exitResId:退出的动画
- makeThumbnailAspectScaleDownAnimation
- makeClipRevealAnimation
- Android API19以上使用
- makeScaleUpAnimation
- 放大动画,从指定view的指定位置开始放大
- makeSceneTransitionAnimation
- 共享view放大效果,仅支持API>=21
- 底层实现动画:什么动画实现?ActivityTransitionState
1
public void startExitOutTransition(Activity activity, Bundle options) {
if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {return;
}
ActivityOptions activityOptions = new ActivityOptions(options);
mEnterTransitionCoordinator = null;
//是否转场动画
if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {int key = activityOptions.getExitCoordinatorKey(); int index = mExitTransitionCoordinators.indexOfKey(key); if (index >= 0) { mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get(); mExitTransitionCoordinators.removeAt(index); if (mCalledExitCoordinator != null) { mExitingFrom = mCalledExitCoordinator.getAcceptedNames(); mExitingTo = mCalledExitCoordinator.getMappedNames(); mExitingToView = mCalledExitCoordinator.copyMappedViews(); mCalledExitCoordinator.startExit(); } }
}
}1
2
3
4
5- Transition解析
- [Android最新动画框架完全解析(二)——Transitions Framework](http://blog.csdn.net/l664675249/article/details/50195847#t0)
- [Android最新动画框架完全解析(一)—— Animator(Property Animation)](http://blog.csdn.net/l664675249/article/details/50204503)
- FragmentTransaction动画
- setCustomAnimations(int enter, int exit, int popEnter, int popExit)Added in API level 13
1
- setCustomAnimations(int enter, int exit)
Added in API level 11
1
- setTransition(int transit)
Added in API level 1
TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, or TRANSIT_FRAGMENT_CLOSE1
- 底层源码:什么动画实现?
BackStackState
1
public void run() {
if (FragmentManagerImpl.DEBUG) Log.v(TAG, “Run: “ + this);if (mAddToBackStack) {
if (mIndex < 0) { throw new IllegalStateException("addToBackStack() called after commit()"); }
}
bumpBackStackNesting(1);
TransitionState state = null;
SparseArrayfirstOutFragments = null;
SparseArraylastInFragments = null;
if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {firstOutFragments = new SparseArray<Fragment>(); lastInFragments = new SparseArray<Fragment>(); calculateFragments(firstOutFragments, lastInFragments); state = beginTransition(firstOutFragments, lastInFragments, false);
}
int transitionStyle = state != null ? 0 : mTransitionStyle;
int transition = state != null ? 0 : mTransition;
Op op = mHead;
while (op != null) {int enterAnim = state != null ? 0 : op.enterAnim; int exitAnim = state != null ? 0 : op.exitAnim; switch (op.cmd) { case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.addFragment(f, false); } break; case OP_REPLACE: { Fragment f = op.fragment; int containerId = f.mContainerId; if (mManager.mAdded != null) { for (int i = mManager.mAdded.size() - 1; i >= 0; i--) { Fragment old = mManager.mAdded.get(i); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "OP_REPLACE: adding=" + f + " old=" + old); if (old.mContainerId == containerId) { if (old == f) { op.fragment = f = null; } else { if (op.removed == null) { op.removed = new ArrayList<Fragment>(); } op.removed.add(old); old.mNextAnim = exitAnim; if (mAddToBackStack) { old.mBackStackNesting += 1; if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + old + " to " + old.mBackStackNesting); } mManager.removeFragment(old, transition, transitionStyle); } } } } if (f != null) { f.mNextAnim = enterAnim; mManager.addFragment(f, false); } } break; case OP_REMOVE: { Fragment f = op.fragment; f.mNextAnim = exitAnim; mManager.removeFragment(f, transition, transitionStyle); } break; case OP_HIDE: { Fragment f = op.fragment; f.mNextAnim = exitAnim; mManager.hideFragment(f, transition, transitionStyle); } break; case OP_SHOW: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.showFragment(f, transition, transitionStyle); } break; case OP_DETACH: { Fragment f = op.fragment; f.mNextAnim = exitAnim; mManager.detachFragment(f, transition, transitionStyle); } break; case OP_ATTACH: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.attachFragment(f, transition, transitionStyle); } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } } op = op.next;
}
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
````- 为什么不建议用FragmentTransaction动画
- Android动画
- ActivityOptions