Simple Implementation of Responsibility Chain Model in Android

Simple Implementation of Responsibility Chain in Android (Scenario: View to Bitmap)

Chain of responsibility

The responsibility chain model enables each processor in the chain to process the corresponding requests until a node in the chain can correctly respond to the requests.
Seeing if you are familiar with it, in fact, the most common event distribution on android and the implementation of interceptor on okhttp are all based on responsibility chain mode.

Use scenarios

When we use this scenario to convert view to Bitmap, we can consider adding responsibility chain. In fact, when we really use responsibility chain model, we do not need to use responsibility chain model to construct our transformation logic in this scenario, because there is a lot of fuss, and in this scenario, responsibility chain model is not applicable to construct. But when I was about to write, I suddenly had a flash of inspiration. I thought that this scenario could be used to practice the implementation of the responsibility chain mode. In fact, the implementation of our responsibility chain refers to the implementation of okhttp interceptor.
Consider this situation. Our WebView is converted to bitmap. If our WebView shows a very long and long content, then when we get bitmap of webview, oom may occur when we generate bitmap. So if this happens, we need to find a second solution, that is, the first one. When one node can't get the bitmap we want, we pass the conversion request to the next node for processing, and so on, until one node can get the correct bitmap.

Abstract acquisition class

//Abstract acquirer
public interface Capture {
	//Get the bitmap of view
    Bitmap capture(Chain chain);
	//Abstract Chain of Responsibility
    interface Chain {
    	//Next Node Method of Execution Chain
        Bitmap proceed(View view);
        //Target view
        View view();
    }
}

Get the implementation class

//Initial classes can be used to initialize our target view, or to wrap the bitmap we eventually get
public class InitCapture implements Capture {
    @Override
    public Bitmap capture(Chain chain) {
        View view = chain.view();
        //For example, let's make an empty judgment here.
        if (view == null) new RuntimeException("view must not be null");
        //For example, when our view hasn't been added to the window, we can't get the measurement width and height of view, so we need to call the measurement function manually.
        if (view.getMeasuredHeight() == 0 || view.getMeasuredWidth() == 0) {
            int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            view.measure(measureSpec, measureSpec);
        }
        //Deliver the request to the next node for processing
        Bitmap bitmap = chain.proceed(view);
        //Of course, we can wrap the bitmap after we get it and return it.
        return bitmap;
    }
}

The above class is used to initialize the initial view and so on. Of course, we just want to practice here. The added class is just to illustrate the handling of requests by the chain of responsibility mode. The actual situation may be different from what we write now.

//Implementation Class of bitmap for Getting view at One Time
public class FullCapture implements Capture {

    @Override
    public Bitmap capture(Chain chain) {
        Bitmap bitmap = capture(chain.view());
        //If we can't get the bitmap this time, we'll leave it to the next node, otherwise we'll send back the bitmap we've processed.
        return bitmap != null ? bitmap : chain.proceed(chain.view());
    }
    public Bitmap capture(View view) {
        return getBitmap(view);
    }

    private Bitmap getBitmap(View view) {
        Bitmap bitmap;
        try {
        	//Create bitmap of view size
            bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565);
        } catch (OutOfMemoryError error) {
        	//If oom manually gc
            System.gc();
            try {
            	//Try again
                bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565);
            } catch (OutOfMemoryError error2) {
                return null;
            }
        }
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }
}

The above acquisition implementation class, we use view.draw(canvas) method to get the entire bitmap at one time. Of course, oom may occur at this time. We can try again after manually gc. If it is still oom, we will pass the request to the next processor for processing.

//Get the bitmap class in segments, which is not yet implemented here
public class SubsectionCapture implements Capture {

    public Bitmap capture(View view) {
    	........
    	......
        return bitmap;
    }


    @Override
    public Bitmap capture(Chain chain) {
        Bitmap bitmap = null;
        try {
            bitmap = capture(chain.view());
        } catch (OutOfMemoryError error) {
			
        }
        //Assuming that this is the last processing class, whether or not we succeed in processing, we will return our processing results.
        return bitmap;
    }
}

The logic here is to use view. setDrawing CacheEnabled (true) and other view caching functions to construct our section bitmap and merge it into a large bitmap. In fact, even if we get a section of bitmap, we need to save these bitmaps as local files first, but not directly return them to our big Bitm map. Ap, or oom will happen. This is just to illustrate the implementation of the responsibility chain model.
As the last node in our chain, Subsection Capture returns our processing results regardless of our success.

Chain of Responsibility Implementation Class

There are several implementations of capture classes mentioned above. They are all nodes in the chain, and our abstract method of acquisition is Bitmap capture(Chain chain).
So where does this Chain come from and how does it link up each node?

//Chain of Responsibility Implementation Class
public class CaptureChain implements Capture.Chain {
	//Save every node in our chain
    private List<Capture> captures;
    //Target view
    private View view;
    //Progressive Number of Nodes
    private int index;

    public CaptureChain(List<Capture> captures, View view, int index) {
        this.captures = captures;
        this.view = view;
        this.index = index;
    }

    @Override
    public Bitmap proceed(View view) {
        if (index >= captures.size()) throw new RuntimeException("captures size is " + captures.size());
        //Here we build a chain, move the node forward, and then call the capture method of the current node.
        CaptureChain captureChain = new CaptureChain(captures, view, index + 1);
        return captures.get(index).capture(captureChain);
    }

    @Override
    public View view() {
        return view;
    }
}

Chain implementation is also simple, that is to wrap a new chain, the processing node on the chain points to the next node, that is, if there is a call to the chain. proceed (chain. view () method in the Capture, the event will be passed to the next Capture processing until the final processing and return.

ViewCapture's Real Transform Request Class

After doing the above preparation, we prepare a class that is processing the request to respond to the user's request.

public class ViewCapture {
    private static volatile ViewCapture instance;
    private List<Capture> captures;
    private ViewCapture() {
        captures = new ArrayList<>();
        captures.add(new InitCapture());
        captures.add(new FullCapture());
        captures.add(new SubsectionCapture());
    }

    public static ViewCapture get() {
        if (instance == null) {
            synchronized (ViewCapture.class) {
                if (instance == null) {
                    instance = new ViewCapture();
                }
            }
        }
        return instance;
    }

    public Bitmap getBitmap(View view) {
    	//Constructing the initial responsibility chain, the node of the chain passes 0, that is, the first node will be executed.
        CaptureChain captureChain = new CaptureChain(captures,view,0);
        //Start execution
        return captureChain.proceed(view);
    }
}

Our processing class is also very simple. We build a processing class by singleton, then build our chain and start processing requests. When we execute captureChain.proceed(view), the first Capture will process our request, which is InitCapture - > FullCapture - > SubsectionCapture.
Then bitmap from Subsection Capture - > Full Capture - > InitCapture finally gives our caller.

summary

Chain of responsibility is clear through the example above, of course, our scenario may not use the chain of responsibility, but use this example to learn the realization of the chain of responsibility.

Keywords: Android OkHttp

Added by YodaOfCamarilla on Fri, 19 Jul 2019 16:14:29 +0300