美文网首页Android学习Android专题
Android设计模式实战-代理模式

Android设计模式实战-代理模式

作者: 九心_ | 来源:发表于2019-04-15 20:42 被阅读17次

前言

设计模式在Android进阶中不可避免,对一个Android工程师来说,学习好设计模式会给我们带来很多好处:

  • 方便阅读和理解Android源码
  • 写出优质的代码,易于拓展和他人阅读

最近在看《Android源码设计模式解析与实战》,一直想深入了解Binder机制,奈何愚笨,读完《Android开发艺术探索》中的第二章节之后仍感觉云里雾里,看见Binder机制使用了代理模式,所以决定先学习代理模式。

目录

目录

一、定义

代理模式(Proxy Pattern)也称委托模式,我们引用《Android源码设计模式与实战》中的定义:

为其他对象提供一种代理以控制对这个对象的访问

二、使用场景

1. 举个栗子

定义好像说的不是那么明白?没关系,我们来举个例子:
我们称小王为著名演员,想要去国外,得去买飞机票吧,如果他自己去买票,就会被很多人认出,周围群众就会请求与他合影。为了不让自己暴露在大众面前,就让助理代替他去买票,他的助理就称为他的代理。我们用类图表示一下:

小王买票
著名演员小王就是这个Actor对象,助理就是Assistant对象,Buyer是我们买票要实现的接口,售票员就是Seller对象,演员的粉丝就是Fans,为了避免粉丝调用小王的合影方法takePhoto(),小王选择让助理代替其去买票,只不过最终还是小王付钱买票罢了。

2. 思考

那么我们来思考一下使用场景:
当我们不想让别人直接访问我们的对象(因为这样可能会暴露我们对象,别人可以直接调用我们对象的某些方法,我们不想让别人调用),又或者别人不能直接访问的对象,需要一个代理去接触我们的对象。

三、代理分类

如果我们单纯的从code分类,代理模式可以区分为:

  • 静态代理:上述的类图用代码实现后就是一个典型的静态代理,编译前我们就知道Assistant是我们的代理类,并且已经存在。
  • 动态代理:动态代理与静态代理相反,编译前我们并不知道谁是我们的代理类,它是通过反射机制实现的,Java给我们提供了便捷的动态代理接口InvocationHandler,具体的使用可以查看Java动态代理

那么从适用范围来讲,我们又可以区分为:

  • 远程代理:为某个对象在不同的地址空间提供局部代理。使系统可以将Server部分隐藏,以便Client不需要考虑Server的存在。
  • 虚拟代理:使用一个代理对象表示一个十分耗资源的对象,并在真正需要的时候创建。
  • 保护代理:使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况。
  • 智能引用:在访问原始对象时执行一些自己的附加操作,并对指向原始对象的引用计数。

四、实战

学完了代理模式之后,我对我的照片库PhotoPagerView进行了重构,PhotoPagerView代码并不复杂,只是因为公司的代码日常需要,所以就封装起来,主要功能就是加载多个Bitmap通过ViewPager展示出来。为了好(zhuang)看(bi),特意选择了QQ主题的图片预览,其实普通主题的代码特别简单,如果单纯的学习代理模式,就不需要看QQ主题的Dialog.

qq主题

1. 需求

  • 显示显示大图的Dialog
  • Dialog暂时分为两种类型:普通类型(NormalPager)和QQ风格类型(QQPager)
  • 简化操作,不需要让使用者知道这两种具体的实现,只知道有这两种类型即可

2. 实现

为了讲解,细节都被省略了,具体可查看代码
第一步:创建IPhotoPager的接口

public interface IPhotoPager {
    void show();
    void dismiss();
    void setConfig(Config config);

    /*
        config 构成图片的一些参数
     */
    class Config {
        ...
    }
}

第二步:创建基础的BasePager,供QQPagerNormalPager实现,这边只是简单的业务代码

public abstract class BasePager extends Dialog
        implements ViewPager.OnPageChangeListener,IPhotoPager {

    protected Context mContext;
    // all base info
    private IPhotoPager.Config mConfig;

    ... 

    public BasePager(@NonNull Context context) {
        this(context, R.style.Dialog);
    }

    public BasePager(@NonNull Context context, int themeResId) {
        super(context, themeResId);

        mContext = context;
    }

    @Override
    public void setConfig(Config config) {
        this.mConfig = config;
        initParams();
    }

    /*
        init parameter
     */
    private void initParams() {
         ...
    }

    @Override
    public void show() {
        if(bitmaps == null || bitmaps.size() == 0){
            throw new RuntimeException("bitmaps can't be null");
        }

       ...
    }
}

第三步:实现具体类型的IPhotoPagerQQPagerNormalPagerQQPager加入了更多的效果和弹幕功能,比较复杂,这里不作过多介绍,感兴趣可以看代码

public class NormalPager extends BasePager
    implements View.OnClickListener{
    private String TAG = "NormalPager";

    private MyViewPager mPhotoPager; // 继承自ViewPager,仅仅捕获异常
    private ImageView mDelete;
    private TextView mPosition;
    private ImageView mBack;

    private PhotoPagerAdapter mAdapter;

    public NormalPager(@NonNull Context context) {
        super(context);
    }

    public NormalPager(@NonNull Context context, int themeResId) {
        super(context, themeResId);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        View root = LayoutInflater.from(mContext).inflate(R.layout.layout_normal_pager, null);
        setContentView(root);
        initWidget(root);
    }

    private void initWidget(View root){
        ...
        // 初始化布局
    }


    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.iv_back) {
            dismiss();
        } else {
            deleteCurrentPosition();
        }
    }

    private void deleteCurrentPosition() {
        // 省略具体实现 删除当前部分触发
        ...
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        Log.e(TAG,"pos:"+position+",offset:"+positionOffset);
    }

    @Override
    public void onPageSelected(int position) {
        mPosition.setText(String.format(Locale.getDefault(), "%d/%d", position + 1, bitmaps.size()));
        curPosition = position;
    }
}

第四步:设置代理,通过类型创建不同主题的Pager

public class PhotoPagerViewProxy implements IPhotoPager {
    public static final int TYPE_NORMAL = 1;
    public static final int TYPE_QQ = 2;
    public static final int TYPE_WE_CHAT = 3;

    public static final int ANIMATION_SCALE_ALPHA = 1;
    public static final int ANIMATION_TRANSLATION = 2;

    private IPhotoPager photoPageView;

    private PhotoPagerViewProxy(Context context, int type, Config config) {
        switch (type) {
            case TYPE_QQ:
                photoPageView = new QQPager(context,R.style.Dialog);
                break;
            case TYPE_WE_CHAT:
                break;
            default:
                photoPageView = new NormalPager(context, R.style.Dialog);
                break;
        }
        setConfig(config);
    }

    @Override
    public void show() {
        photoPageView.show();
    }

    @Override
    public void dismiss() {
        photoPageView.dismiss();
    }

    @Override
    public void setConfig(Config config) {
        photoPageView.setConfig(config);
    }

    public static class Builder {
        private Activity context;
        private IPhotoPager.Config config;
        private int type;

        public Builder(Activity context, int type) {
            this.context = context;
            this.config = new IPhotoPager.Config();
            this.type = type;
        }

        public Builder(Activity context) {
            // default type is TYPE_NORMAL
            this(context, TYPE_NORMAL);
        }

       ... // 具体设置参数都省略

        public PhotoPagerViewProxy create() {
            return new PhotoPagerViewProxy(context, type, config);
        }
    }
}

大功告成,具体代码地址:PhotoPagerView

五、总结

总结

本人水品有限,难免有误,如有错误,欢迎指出。
Over~
引用:

《Android源码设计模式》
《代理模式》

相关文章

网友评论

    本文标题:Android设计模式实战-代理模式

    本文链接:https://www.haomeiwen.com/subject/jipybqtx.html