Android第三方网络请求(Retrofit)、权限请求(Permission4M)框架

作者:陆金龙    发表时间:2024-02-25 04:52   

关键词:网络请求  权限请求  Retrofit  Permission4M  

1.网络请求框架

Volley、OkHttp或xUtils?

Retrofit或者Volley+OkHttp

 

参考意见:

公司项目一直在使用xutils发现在网络请求时存在一些bug,调研发下xutils这种聚合型框架一定要放弃,可读性差,耦合高,难扩展,不利于以后代码的迭代和错误纠正,现在重新调研选择了发现,Retrofit或者Volley+OkHttp。

 

网络请求框架总结
1.xutils  
   此框架庞大而周全,这个框架可以网络请求,同时可以图片加载,又可以数据存储,又可以 View 注解,使用这种框架很方便,这样会使得你整个项目对它依赖性太强,万一以后这个库不维护了,或者中间某个模块出问题了,这个影响非常大,所以在项目开发时,一般会更喜欢选择专注某一领域的框架。

2.OkHttp
    Android 开发中是可以直接使用现成的api进行网络请求的,就是使用HttpClient、HttpUrlConnection 进行操作,目前HttpClient 已经被废弃,而 android-async-http 是基于HttpClient的,可能也是因为这个原因作者放弃维护。 而OkHttp是Square公司开源的针对Java和Android程序,封装的一个高性能http请求库,它的职责跟HttpUrlConnection 是一样的,支持 spdy、http 2.0、websocket ,支持同步、异步,而且 OkHttp 又封装了线程池,封装了数据转换,封装了参数使用、错误处理等,api使用起来更加方便。可以把它理解成是一个封装之后的类似HttpUrlConnection的东西,但是在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。

3.Volley
   Volley是Google官方出的一套小而巧的异步请求库,该框架封装的扩展性很强,支持HttpClient、HttpUrlConnection, 甚至支持OkHttp,而且Volley里面也封装了ImageLoader,所以如果你愿意你甚至不需要使用图片加载框架,不过这块功能没有一些专门的图片加载框架强大,对于简单的需求可以使用,稍复杂点的需求还是需要用到专门的图片加载框架。Volley也有缺陷,比如不支持post大数据,所以不适合上传文件。不过Volley设计的初衷本身也就是为频繁的、数据量小的网络请求而生。

4.Retrofit
   Retrofit是Square公司出品的默认基于OkHttp封装的一套RESTful网络请求框架,RESTful是目前流行的一套api设计的风格, 并不是标准。Retrofit的封装可以说是很强大,里面涉及到一堆的设计模式,可以通过注解直接配置请求,可以使用不同的http客户端,虽然默认是用http ,可以使用不同Json Converter 来序列化数据,同时提供对RxJava的支持,使用Retrofit + OkHttp + RxJava + Dagger2 可以说是目前比较潮的一套框架,但是需要有比较高的门槛。 

5.Volley VS OkHttp
  Volley的优势在于封装的更好,而使用OkHttp你需要有足够的能力再进行一次封装。而OkHttp的优势在于性能更高,因为 OkHttp基于NIO和Okio ,所以性能上要比 Volley更快。IO 和 NIO这两个都是Java中的概念,如果我从硬盘读取数据,第一种方式就是程序一直等,数据读完后才能继续操作这种是最简单的也叫阻塞式IO,还有一种是你读你的,程序接着往下执行,等数据处理完你再来通知我,然后再处理回调。而第二种就是 NIO 的方式,非阻塞式, 所以NIO当然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基础上做的一个更简单、高效处理数据流的一个库。理论上如果Volley和OkHttp对比的话,更倾向于使用 Volley,因为Volley内部同样支持使用OkHttp,这点OkHttp的性能优势就没了,  而且 Volley 本身封装的也更易用,扩展性更好些。 

6.OkHttp VS Retrofit
   毫无疑问,Retrofit 默认是基于 OkHttp 而做的封装,这点来说没有可比性,肯定首选 Retrofit。 

7.Volley VS Retrofit 
  这两个库都做了不错的封装,但Retrofit解耦的更彻底,尤其Retrofit2.0出来,Jake对之前1.0设计不合理的地方做了大量重构, 职责更细分,而且Retrofit默认使用OkHttp,性能上也要比Volley占优势,再有如果你的项目如果采用了RxJava ,那更该使用  Retrofit 。所以这两个库相比,Retrofit更有优势,在能掌握两个框架的前提下该优先使用 Retrofit。但是Retrofit门槛要比Volley稍高些, 要理解他的原理,各种用法,想彻底搞明白还是需要花些功夫的,如果你对它一知半解,那还是建议在商业项目使用Volley吧。 

8.总结
  综上,如果以上三种网络库你都能熟练掌握,那么优先推荐使用Retrofit,前提是最好你们的后台api也能遵循RESTful的风格, 其次如果不想使用或者没能力掌握Retrofit ,那么推荐使用Volley ,毕竟Volley不需要做过多的封装,如果需要上传大数据, 那么不建议使用 Volley,该采用 OkHttp 。

2.权限请求框架Permission4M使用

Gradle 依赖

 

project 中的 build.gradle:

allprojects {

  repositories {

    // 请添加如下一行

    maven { url 'https://jitpack.io' }

  }

}

app 中的 build.gradle:

dependencies {

  compile 'com.github.jokermonn:permissions4m:2.1.2-lib'

  annotationProcessor 'com.github.jokermonn:permissions4m:2.1.2-processor'

}

 

Activity、Fragment 中使用

Activity、Fragment中注册回调,并实现回调接口的三个方法permissionGranted(int i)、permissionDenied(int i)、permissionRationale(int i)

 

Permissions4M.get(QhVoiceRecorder.this)
        // 是否强制弹出权限申请对话框,建议设置为 true,默认为 true
        // .requestForce(true)
        // 是否支持 5.0 权限申请,默认为 false
        // .requestUnderM(false)
        // 权限,单权限申请仅只能填入一个
        .requestPermissions(Manifest.permission.RECORD_AUDIO)
        // 权限码
        .requestCodes(CODE_AUDIO)
        // 如果需要使用 @PermissionNonRationale 注解的话,建议添加如下一行
        // 返回的 intent 是跳转至**系统设置页面**
        // .requestPageType(Permissions4M.PageType.MANAGER_PAGE)
        // 返回的 intent 是跳转至**手机管家页面**
        // .requestPageType(Permissions4M.PageType.ANDROID_SETTING_PAGE)
        .requestListener(this) //注册回调
        .request();

 

@Override
public void permissionGranted(int i)
{
    hasAudioPermission = true;
    UIUtils.ShowToast(getActivity(), "权限申请成功!");
    isRequesting = false;
}

@Override
public void permissionDenied(int i)
{
    hasAudioPermission = false;
    AlertPermissionDenied();
    isRequesting = false;
}


@Override
public void permissionRationale(int i)
{
    UIUtils.ShowToast(getActivity(), "请开启录音权限!");
}

的二次权限申请回调

在 Activity 或 Fragment 中,需要手动添加 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法以支持权限申请回调

@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]

        grantResults) {

    Permissions4M.onRequestPermissionsResult(MainFragment.this, requestCode, grantResults);

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}

附:多个权限同步申请

 

Permissions4M.get(NormalFragment.this)

        .requestPermissions(Manifest.permission.BODY_SENSORS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CALENDAR)

        .requestCodes(SENSORS_CODE, LOCATION_CODE, CALENDAR_CODE)

        .requestListener(new Wrapper.PermissionRequestListener() {

            @Override

            public void permissionGranted(int code) {

                switch (code) {

                    case LOCATION_CODE:

                        ToastUtil.show("地理位置权限授权成功 in fragment with annotation");

                        break;

                    case SENSORS_CODE:

                        ToastUtil.show("传感器权限授权成功 in fragment with annotation");

                        break;

                    case CALENDAR_CODE:

                        ToastUtil.show("读取日历权限授权成功 in fragment with annotation");

                        break;

                    default:

                        break;

                }

            }

 

            @Override

            public void permissionDenied(int code) {

                switch (code) {

                    case LOCATION_CODE:

                        ToastUtil.show("地理位置权限授权失败 in fragment with annotation");

                        break;

                    case SENSORS_CODE:

                        ToastUtil.show("传感器权限授权失败 in fragment with annotation");

                        break;

                    case CALENDAR_CODE:

                        ToastUtil.show("读取日历权限授权失败 in fragment with annotation");

                        break;

                    default:

                        break;

                }

            }

 

            @Override

            public void permissionRationale(int code) {

                switch (code) {

                    case LOCATION_CODE:

                        ToastUtil.show("请开启地理位置权限 in fragment with annotation");

                        break;

                    case SENSORS_CODE:

                        ToastUtil.show("请开启传感器权限 in fragment with annotation");

                        break;

                    case CALENDAR_CODE:

                        ToastUtil.show("请开启读取日历权限 in fragment with annotation");

                        break;

                    default:

                        break;

                }

            }

        })

        .requestCustomRationaleListener(new Wrapper.PermissionCustomRationaleListener() {

            @Override

            public void permissionCustomRationale(int code) {

                switch (code) {

                    case LOCATION_CODE:

                        ToastUtil.show("请开启地理位置权限 in fragment with annotation");

                        Log.e("TAG", "permissionRationale: 请开启地理位置权限 ");

 

                        new AlertDialog.Builder(getActivity())

                                .setMessage("地理位置权限权限申请:\n我们需要您开启地理位置权限(in fragment with " +

                                        "annotation)")

                                .setPositiveButton("确定", new DialogInterface

                                        .OnClickListener() {

                                    @Override

                                    public void onClick(DialogInterface dialog, int which) {

                                        Permissions4M.get(NormalFragment.this)

                                                .requestOnRationale()

                                                .requestPermissions(Manifest.permission

                                                        .ACCESS_FINE_LOCATION)

                                                .requestCodes(LOCATION_CODE)

                                                .request();

                                    }

                                })

                                .show();

                        break;

                    case SENSORS_CODE:

                        ToastUtil.show("请开启传感器权限 in fragment with annotation");

                        Log.e("TAG", "permissionRationale: 请开启传感器权限 ");

 

                        new AlertDialog.Builder(getActivity())

                                .setMessage("传感器权限申请:\n我们需要您开启传感器权限(in fragment with " +

                                        "annotation)")

                                .setPositiveButton("确定", new DialogInterface

                                        .OnClickListener() {

                                    @Override

                                    public void onClick(DialogInterface dialog, int which) {

                                        Permissions4M.get(NormalFragment.this)

                                                .requestOnRationale()

                                                .requestPermissions(Manifest.permission

                                                        .BODY_SENSORS)

                                                .requestCodes(SENSORS_CODE)

                                                .request();

                                    }

                                })

                                .show();

                        break;

                    case CALENDAR_CODE:

                        ToastUtil.show("请开启读取日历权限 in fragment with annotation");

                        Log.e("TAG", "permissionRationale: 请开启读取日历权限 ");

 

                        new AlertDialog.Builder(getActivity())

                                .setMessage("日历权限申请:\n我们需要您开启日历权限(in fragment with " +

                                        "annotation)")

                                .setPositiveButton("确定", new DialogInterface

                                        .OnClickListener() {

                                    @Override

                                    public void onClick(DialogInterface dialog, int which) {

                                        Permissions4M.get(NormalFragment.this)

                                                .requestOnRationale()

                                                .requestPermissions(Manifest.permission

                                                        .READ_CALENDAR)

                                                .requestCodes(CALENDAR_CODE)

                                                .request();

                                    }

                                })

                                .show();

                        break;

                    default:

                        break;

                }

 

            }

        })

        .request();