移动周分享-第57期

Java Runtime 动态代理 - 王胜

使用过Spring的朋友应该对AOP『Aspected Oriented Programe』很熟悉,那么是否被里面的前置增强、后置增强、环绕增强所震撼呢?有没有想过这背后的技术实现呢?其实JDK的动态代理就能实现。下面做一个简单的示例,来阐述如何在Runtime时,动态增强原有的方法。

前提环境

假设我们有一个Waiter类,里面含有两个方法:

  • welcome() // 欢迎光临!
  • bye() // 谢谢惠顾!
1
2
3
4
欢迎光临!
谢谢惠顾!

Process finished with exit code 0

后来发现欢迎顾客和送别顾客不够礼貌,需要在欢迎之前说『您好!』,送别顾客后说『祝您愉快!』。但我们不希望动原有的业务类Waiter,那么如何实现呢?

实现步骤

  1. 创建一个业务对象『必须要有接口』, 执行业务方法

    1
    2
    3
    4
    public interface Receptor {
    void welcome();
    void bye();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      public class Waiter implements Receptor{
    @Override
    public void welcome() {
    System.out.println("欢迎光临!");
    }

    @Override
    public void bye() {
    System.out.println("谢谢惠顾!");
    }
    }
  2. 创建一个具有公用代码的对象,并将这些公用代码声明成方法

    1
    2
    3
    4
    5
    6
    7
    8
    public class PoliteWords {
    public void sayHello() {
    System.out.println("您好!");
    }
    public void sayHappy() {
    System.out.println("祝您愉快!");
    }
    }
  3. 创建一个可以使用公用代码对象的对象,这个对象可以对要被处理的目标对象实施方法拦截,执行公用代码对象中的方法。借助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
    27
    public 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;
    }
    }
  4. 创建一个可以获取增强了功能之后对象的代理工厂,工厂的作用就是返回增强后的代理Proxy, 他的类型应该和目标对象的接口类型相同。

    1
    2
    3
    4
    5
    6
    7
    8
    public 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);
    }
    }
  5. 编写测试类(可选)

    1
    2
    3
    4
    5
    6
    7
    8
    public 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
2
3
4
5
6
您好!
欢迎光临!
谢谢惠顾!
祝您愉快!

Process finished with exit code 0

注意: JDK动态代理只能代理接口类型的对象,并且返回接口类型。如果要增强那些没有接口的对象,JDK动态代理不能实现,要依赖 CGLIB 技术。

Android转场动画

  • startActivity()

    1
    2
    3
    	Intent 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
2
3
4
5
6
7
public static void startActivity(Activity activity, Intent intent, @Nullable Bundle options) {
if (Build.VERSION.SDK_INT >= 16) {
ActivityCompatJB.startActivity(activity, intent, options);
} else {
activity.startActivity(intent);
}
}
  • Android 4.1(API16)以下转场动画
1
2
3
AIntent intent = new Intent(MainActivity.this,OtherActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.push_up_in,R.anim.push_up_out);
  • 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
        3
        Added 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_CLOSE

      1
      - 底层源码:什么动画实现?

      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;
      SparseArray firstOutFragments = null;
      SparseArray lastInFragments = 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动画