TakePhoto框架源码流程解析(一)

在我最近一年接触的项目中,一直都会接触到一个功能就是图片的拍摄与选取,对于Android系统,各个厂家对于手机系统都会有不同的功能定制,导致碎片化一直很严重,写出的同一套程序,也许两个不同的手机上都会有不同的效果,也许有的手机上根本无反应,这一直是需要解决的问题。


TakePhoto简介

我在做这一功能时候,一直都是用的github上的一框架TakePhoto,这框架对于android的拍照和图片的选取都做了一层封装,并且兼容低版本系统,6.0和7.0的系统,可以实现图片的拍摄,单选,多选,压缩和裁剪功能。官网上的最新功能是4.0.3,下图是它的支持功能

这里写图片描述


TakePhoto的使用

基本使用就不多说了,官网上有使用,针对这一功能,重新封装了一个BaseTakePhotoActivity, 用户一般只需要将其继承自自己的BaseActivity就可以了。

public abstract class BaseTakePhotoActivity extends BaseActivity implements TakePhoto.TakeResultListener,
        InvokeListener {
    protected TakePhoto takePhoto;
    protected InvokeParam invokeParam;

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

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        getTakePhoto().onSaveInstanceState(outState);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        getTakePhoto().onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) {
        PermissionManager.TPermissionType type= PermissionManager.checkPermission(TContextWrap.of(this),invokeParam.getMethod());
        if(PermissionManager.TPermissionType.WAIT.equals(type)){
            this.invokeParam=invokeParam;
        }
        return type;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionManager.TPermissionType type= PermissionManager.onRequestPermissionsResult(requestCode,permissions,grantResults);
        PermissionManager.handlePermissionsResult(this,type,invokeParam,this);
    }

    /**
     *  获取TakePhoto实例
     * @return
     */
    public TakePhoto getTakePhoto(){
        if (takePhoto==null){
            takePhoto= (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this,this));
        }
        return takePhoto;
    }

    protected void configTakePhotoOption(TakePhoto takePhoto) {
        TakePhotoOptions.Builder builder = new TakePhotoOptions.Builder();
        builder.setWithOwnGallery(false);
        builder.setCorrectImage(true);
        takePhoto.setTakePhotoOptions(builder.create());
    }

    protected void configCompress(TakePhoto takePhoto){
        int maxSize = 1048576;
        boolean showProgressBar = false;
        boolean enableRawFile = true;
        CompressConfig config;
        LubanOptions option = new LubanOptions.Builder()
                .setMaxSize(maxSize)
                .create();
        config = CompressConfig.ofLuban(option);
        config.enableReserveRaw(enableRawFile);
        takePhoto.onEnableCompress(config, showProgressBar);
    }

    @Override
    public void takeSuccess(TResult result) {

    }

    @Override
    public void takeFail(TResult result, String msg) {
        ToastUtils.showShort(msg);
    }

    @Override
    public void takeCancel() {
        ToastUtils.showShort("用户取消");
    }
}

源码调用过程

针对源码的调用过程,画了一个简易流程的调用过程

这里写图片描述

这是针对onPickMultiple画的一个流程图,接下来详细介绍下其内部调用过程。

先从takephoto说起,我们在调用它的api时候首先需要实例化takephoto,看BaseTakePhotoActivity

/**
 *  获取TakePhoto实例
 * @return
 */
public TakePhoto getTakePhoto(){
    if (takePhoto==null){
        takePhoto= (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this,this));
    }
    return takePhoto;
}

takephoto的实例化是通过TakePhotoInvocationHandler获取,在TakePhotoInvocationHandler中绑定一个TakePhotoImpl

/**
 * 绑定委托对象并返回一个代理类
 * @param delegate
 * @return
 */
public Object bind(TakePhoto delegate) {
    this.delegate = delegate;
    return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
}

从这里可以看出我们获取的takephoto只是一个代理对象,所有我们调用的takephoto的api都会走TakePhotoInvocationHandlerinvoke方法,看下其实现

@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    PermissionManager.TPermissionType type=listener.invoke(new InvokeParam(proxy,method,args));
    if(proxy instanceof TakePhoto){
        if(!PermissionManager.TPermissionType.NOT_NEED.equals(type)){
            ((TakePhoto)proxy).permissionNotify(type);
        }
    }
    return method.invoke(delegate, args);
}

当在走这个方法的时候,首先会调listener.invoke(new InvokeParam(proxy,method,args))方法,并且将参数封装到InvokeParam里面,并传给listener.invoke方法,其中listener.invoke方法,我们在BaseTakePhotoActivity,中已经实现,看下实现

@Override
public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) {
    PermissionManager.TPermissionType type= PermissionManager.checkPermission(TContextWrap.of(this),invokeParam.getMethod());
    if(PermissionManager.TPermissionType.WAIT.equals(type)){
        this.invokeParam=invokeParam;
    }
    return type;
}

在该方法中首先调用PermissionManagercheckPermission方法

public static TPermissionType checkPermission(@NonNull TContextWrap contextWrap, @NonNull Method method) {
    String methodName = method.getName();
    boolean contain=false;
    for(int i=0,j=methodNames.length;i<j;i++){
        if(TextUtils.equals(methodName,methodNames[i])){
            contain=true;
            break;
        }
    }
    if(!contain)return TPermissionType.NOT_NEED;

    boolean cameraGranted = true, storageGranted = ContextCompat.checkSelfPermission(contextWrap.getActivity(), TPermission.STORAGE.stringValue()) == PackageManager.PERMISSION_GRANTED ? true : false;

    if (TextUtils.equals(methodName, "onPickFromCapture") || TextUtils.equals(methodName, "onPickFromCaptureWithCrop")) {
        cameraGranted = ContextCompat.checkSelfPermission(contextWrap.getActivity(), TPermission.CAMERA.stringValue()) == PackageManager.PERMISSION_GRANTED ? true : false;
    }

    boolean granted = storageGranted && cameraGranted;
    if (!granted) {
        ArrayList<String> permissions = new ArrayList<>();
        if (!storageGranted) permissions.add(TPermission.STORAGE.stringValue());
        if (!cameraGranted) permissions.add(TPermission.CAMERA.stringValue());
        requestPermission(contextWrap,permissions.toArray(new String[permissions.size()]));
    }
    return granted ? TPermissionType.GRANTED : TPermissionType.WAIT;
}

在该方法中首先会去检测存储权限,接着如果是onPickFromCapture或者onPickFromCaptureWithCrop,回去检测是否有相机权限,如果那个没有就去申请那个的权限,最后根据是否有权限将,TPermissionType的状态改为GRANTED或者WAIT状态,如果已经有权限直接调TakePhotoInvocationHandler.invoke的最后method.invoke方法,也就是调takephoto的api方法。如果在申请权限,就会回调BaseTakePhotoActivity.onRequestPermissionsResult方法,该方法中会根据用户是否同意或拒绝权限来是否调用的TakePhoto的相应方法。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    PermissionManager.TPermissionType type= PermissionManager.onRequestPermissionsResult(requestCode,permissions,grantResults);
    PermissionManager.handlePermissionsResult(this,type,invokeParam,this);
}

在该方法中根据用户是否同意权限,将结果封装到TPermissionType 中,然后调用PermissionManagerhandlePermissionsResult方法

 public static void handlePermissionsResult(Activity activity, TPermissionType type, InvokeParam invokeParam, TakePhoto.TakeResultListener listener){
              String tip=null;
              switch (type){
                  case DENIED:
                      listener.takeFail(null,tip=activity.getResources().getString(R.string.tip_permission_camera_storage));
                      break;
                  case ONLY_CAMERA_DENIED:
                      listener.takeFail(null,tip=activity.getResources().getString(R.string.tip_permission_camera));
                      break;
                  case ONLY_STORAGE_DENIED:
                      listener.takeFail(null,tip=activity.getResources().getString(R.string.tip_permission_storage));
                      break;
                  case GRANTED:
                      try {
                          invokeParam.getMethod().invoke(invokeParam.getProxy(),invokeParam.getArgs());
                      } catch (Exception e) {
                          e.printStackTrace();
                          listener.takeFail(null,tip=activity.getResources().getString(R.string.tip_permission_camera_storage));
                      }
                      break;
          default:
              break;
      }
      if(tip!=null)Toast.makeText(activity,tip,Toast.LENGTH_LONG).show();

  }

在该方法中,通过TPermissionType 方法回调处理相应的结果,如果是同意,通过反射去调用takephoto的方法,有一点是需要提示一下的,就是我们在调用takephoto的方法时候,

this.fromType = TImage.FromType.CAMERA;
if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) return;

都会有这样的判断,这个判断是给6.0运行时权限设计的,如果在申请权限的时候,对应的方法不是由takephoto调用,而是由PermissionManagerhandlePermissionsResult方法反射调用,这是一种非常值得借鉴的思想。


总结

基本流程就这多了,看了它的源码过程,自己也有了一定的架构思路,对于不同模块最好规范一个接口,模块之间可以通过动态代理连接,这样可以自己干自己的事,互不干扰。

这一篇介绍了流程,下一篇打算根据各个详细的调用过程来写几篇文章。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值