This blog will describe a usage scenario for dynamic proxies in Android applications.
Proxy mode
The role of proxy mode is to provide a proxy for other objects to control access to this object. For example, users call a "eat" method, if you do not rely on the agent, the user may eat their own bowl of rice, and if through the agent, may not even bowl do not need the user to take, the user only need to open the mouth, agent to feed on the line, it is necessary to note that Here agent in addition to take the bowl and feeding outside can do anything else, such as help you to cool some of the food, or worry about your weight and secretly help you pour out half of the meal, or to add something strange things, who know, this is the agent of the job. In fact, in Java also provides a proxy this magic mode, but also divided into static and dynamic two, the difference is that the static agent structure before the program has been scheduled to run, and dynamic agent is in the process of running the program is specified, this article is the use of dynamic proxy method.
Mirror Flip
In a previous blog "Android" Android image rollover analyzed how to mirror a view to flip, that is to achieve the following effect:
Mirror horizontal rollover before and after effects
For general view, this effect is achieved directly with View.setscaley (-1), and for custom Surfaceview, you can also use
Canvas.scale ( -1,1,canvas.getwidth ()/2,canvas.getheight ()/2)
To flip them. However, if you are facing a third-party surfaceview and cannot get directly to the canvas object that Surfaceview uses to draw. For example, the implementation code on the left is shown below.
public class Testsurfaceview extends Surfaceview implements Surfaceholder.callback {public Testsurfaceview ( Context context, AttributeSet Attrs) { Super (context, attrs); Getholder (). Addcallback (this); } @Override public void surfacecreated (Surfaceholder holder) { //get canvas canvas canvas = Holder.lockcanvas (); Canvas.drawcolor (Color.rgb (220,220,220)); Paint paint = new paint (); Paint.settextsize (); Draw text canvas.drawtext ("Hello, this is Surfaceview", 200,600,paint); Draw round canvas.drawcircle (300,800,100,paint); Display holder.unlockcanvasandpost (canvas); } @Override public void surfacechanged (surfaceholder holder, int format, int width, int height) {} @Override
public void surfacedestroyed (Surfaceholder holder) {}}
Assume that the Testsurfaceview is in a third-party jar package and cannot be modified. So how to mirror the interface to flip it, the following is given by the dynamic Agent implementation of the scheme
SOURCE Analysis
In combination with the above code, we know that the Surfaceview drawing process is as follows:
Surfaceview Drawing Process
The usual calling code is as follows:
Get canvas canvas for canvas canvas = Holder.lockcanvas ();//Draw content ...//unlock canvas, display canvas content holder.unlockcanvasandpost (canvas);
What is holder, we can find Surfaceholder instance msurfaceholder and implement in the source code of Surfaceview.
public class Surfaceview extends View {... Private final Surfaceholder Msurfaceholder = new Surfaceholder () { private static final String Log_tag = "Surfaceholde R "; @Override Public Boolean iscreating () { return miscreating; } ...... @Override public Canvas Lockcanvas () { return Internallockcanvas (null); } @Override public Canvas Lockcanvas (Rect inoutdirty) { return Internallockcanvas (Inoutdirty); } ...... };}
In the source code, we can see Msurfaceholder implemented the Lockcanvas method, and returned to the canvas, here is the breach.
Agent implementation
The goal of the agent is to monitor the Lockcanvas method in the Surfaceholder and add some action to its return value, that is, to change the original process to the following structure:
Dynamic Proxy Add access control
The first thing you need to implement is the agent processor, whose code is as follows:
public class Testinvocation implements Invocationhandler { Object mobject; Public Testinvocation (Object object) { Mobject = object; } @Override Public object Invoke (Object proxy, Method method, object[] args) throws Throwable { //intercept Lockcanvas method call if ("Lockcanvas". Equals (Method.getname ())) { ///Lockcanvas method return value is the canvas canvas canvases canvas = (canvas) Method.invoke (Mobject,args); Add Mirror Canvas.scale ( -1,1,canvas.getwidth ()/2,canvas.getheight ()/2); return canvas; } Return Method.invoke (Mobject,args);} }
The proxy processor does this by filtering out the Lockcanvas method at invoke, then executing the Lockcanvas method, and getting the return value, which is the canvas object, which is what Surfaceview is going to paint on. So here we can add a mirror rollover effect to the agent beforehand, and then return to the normal process to continue execution after the addition is complete.
After completing the agent processor, you can add the dynamic agent for Surfaceholder, it is important to note that surfaceholder in Surfaceview, the need to take out first, and then add the agent, the code is as follows:
Get Surfaceholdersurfaceholder msurfaceholder in Surfaceview = Mtestsurfaceview.getholder ();// Create an implementation of the Proxy interface testinvocation testinvocation = new Testinvocation (msurfaceholder);//Add a dynamic proxy for msurfaceholder and get the add agent Newsurfaceholdersurfaceholder Newsurfaceholder = (surfaceholder) proxy.newproxyinstance (MSurfaceHolder.getClass (). getClassLoader (), Msurfaceholder.getclass (). Getinterfaces (), testinvocation);
The newly generated newsurfaceholder is the surfaceholder that has been added on the dynamic proxy because Surfaceholder is a private property in Surfaceview and cannot be replaced directly. So here we need to use the reflection mechanism to Newsurfaceholder replace the original Msurfaceholder in Surfaceview, the code is as follows:
Get msurfaceholder Fieldfield Fieldholder = SurfaceView.class.getDeclaredField ("Msurfaceholder");// Change to accessible permission fieldholder.setaccessible (true);//replace Msurfaceholderfieldholder.set with Newsurfaceholder after adding the agent ( Mtestsurfaceview,newsurfaceholder);
After adding the dynamic agent, the Lockcanvas method that calls holder in Testsurfaceview gets the canvas that is testinvocation transpose, which realizes this strange need, that is, the effect of the picture on the right.
Source code Download the entire project source code is as follows
Android Dynamic Agent Practice
"Android" Android dynamic agent adds hooks for Surfaceholder