老套路,先上图(没效果图的博客,我一般都不想看):
Demo源码:下载源码
图中的效果很简单,就三个按钮,前面的两个按钮是一个组合,从获取验证码再到注册账号(因为,在测试的时候我的手机号已经注册过了,所以,这时候再注册就会提示手机号已被注册),当然这不是重点,重点是我们的请求方式,这两按钮的请求我用的是post请求,并且参数也按照规则进行了加密,有兴趣的可以自己去下载看看!!
最后一个按钮就是简单的get请求,效果就介绍到这里了,正所谓没有对比就没有伤害,在欣赏新作品之前还是先看看以前我封装的Retrofit吧,连接地址:单纯的get请求 下面看看更新后的封装吧:
首先还是依赖:
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.squareup.retrofit2:converter-scalars:+' compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
仔细对比会知道,前后的依赖并没有发生变化,这里也就不再解释了。
接着再看封装的Retrofit工具类:
public class RetrofitFacety { /** * * @param file 设置数据缓存的路径 * @param url 接口路劲 * @param mvpJieKou 回调数据的接口 */ //使全局就一个OKHttpClient对象 public static OkHttpClient okHttpClient = new OkHttpClient.Builder() // .cookieJar(new CookiesManager()) .connectTimeout(20,TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) // .addInterceptor(new LoggingInterceptor()) .build(); //使全局就一个Retrofit对象,设置基础Url public static ApiService apiService = new Retrofit.Builder() .baseUrl(APIs.debug) //使我们能高度自定义转化器 .addConverterFactory(ScalarsConverterFactory.create()) .client(okHttpClient) //把 以前的 call 转化成 Observable,这是Retrofit与RxJava结合使用的关键 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build().create(ApiService.class); /** * retrofit的get请求 * @param url * @return */ public static Observableget(String url) { return apiService.get(url) .doOnNext(new Consumer () { @Override public void accept(@NonNull String s) throws Exception { Log.i("jiba","这是处理缓存本地操作==="+s); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } /** * retrofit的post请求 * @param url * @param map * @return */ public static Observable post(String url, Map map){ return apiService.post(url,map) .doOnNext(new Consumer () { @Override public void accept(@NonNull String s) throws Exception { Log.i("jiba","===这是个什么"); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }
前后对比一下,发现多了一个post的请求方式,这一点很重要,因为真实的项目中几乎全部都是post的请求方式,可以说关于get是绝种了,至于原因与为什么相信就不用我来解释了吧!post请求的好处,百度上搜一搜,那是一大片。。。
在这个类里面会发现用到了很关键的一个类,并且还是反射出来的,叫ApiService,既然有了,那我就去看看他是什么吧?
来,走起:
public interface ApiService { /** * retrofit的get请求 * @param url * @return */ @GET Observableget(@Url String url); /** * retrofit的post请求 * @return */ @FormUrlEncoded @POST Observable post(@Url String url, @FieldMap Map map); }
还是一样通过比对,才能有进步,发现多了一个@POST 注解的请求,关于retrofit中post请求我也做过很多功课,就在我博客中提到的就已经有三种了,我也一一给大家做过一些介绍,这里我就只给出地址,有兴趣的自己研究吧!retrofit上传头像post方式,retrofit的上传头像+携带参数,这两个地址是我以前用来上传头像的,但万变不离其中,post的请求是不会变的,变的只是我们的使用方式
接下来就是具体的使用方式(我采用的是mvp的代码架构,并且对mvp抽取了基类):
先看model层:
public class MyModel { /** * model中的get请求方式 * @param url * @param myInterfaces */ public void getModContent(String url, final MyInterfaces myInterfaces){ RetrofitFacety.get(url) .subscribe(new BaseServer() { @Override public void onSuccess(String json) { myInterfaces.chenggong(json); } @Override public void onErroy(String ss) { myInterfaces.shibai(ss); } }); } /** * model中的post请求 * @param url * @param map * @param myInterfaces */ public void postModContent(String url, Mapmap, final MyInterfaces myInterfaces){ RetrofitFacety.post(url,map) .subscribe(new BaseServer() { @Override public void onSuccess(String json) { myInterfaces.chenggong(json); } @Override public void onErroy(String ss) { myInterfaces.shibai(ss); } }); } }
这个应该不用多解释的,如果有疑问的话,只能说明你对mvp还不熟悉,model层中涉及到一个类:BaseServer 这个类其实也是对retrofit的封装,只不过他是对结果的封装,看代码:
/** * Created by mypc on 2018/1/6. * * 父类的公有方法的抽取,,抽象的思想 */ public abstract class BaseServer implements Observer{ public abstract void onSuccess(String json); public abstract void onErroy(String ss); @Override public void onError(Throwable e) { onErroy("请求失败"); Log.i("jiba","===="+e); } @Override public void onNext(String json) { try { // Log.i("jiba","onNext==="+json); onSuccess(json); }catch (Exception e1) { } } @Override public void onSubscribe(@NonNull Disposable d) { } @Override public void onComplete() { } }
这个类,采用的是java中抽象的思想!
言归正传,回到我们的使用当中来,present层:
public class MyPresenter<V> { private final MyModel myModel; public MyPresenter() { myModel = new MyModel(); } //将泛型v与p进行绑定 V view; public void attch(V view){ this.view=view; } /** * present中的get请求 * @param url * @param myInterfaces */ public void getPreContent(String url, final MyInterfaces myInterfaces){ myModel.getModContent(url, new MyInterfaces() { @Override public void chenggong(String json) { myInterfaces.chenggong(json); } @Override public void shibai(String ss) { myInterfaces.shibai(ss); } }); } /** * present中的post请求 * @param url * @param map * @param myInterfaces */ public void postPreContent(String url, Map关于present层,有疑惑可能就是他里面的泛型了,对于泛型,我没什么好说的,属于java基础的东西(其实我也不太会,但我多少了解一点),这里提供一个学习泛型的网址给大家: java泛型详解map, final MyInterfaces myInterfaces){ myModel.postModContent(url, map, new MyInterfaces() { @Override public void chenggong(String json) { myInterfaces.chenggong(json); } @Override public void shibai(String ss) { myInterfaces.shibai(ss); } }); } //将泛型v与p进行解绑 public void setonDestroy(){ try{ this.view=null; }catch (Exception e){ } } }
有了P层,M层肯定还需要一个V层的,所以关于V层的基类我也给大家粘出来了:
public abstract class BaseMVPActivity<V,P extends MyPresenter<V>> extends AppCompatActivity { public P myPresenter; public abstract P initPresenter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_mvp); myPresenter= initPresenter(); if(Build.VERSION.SDK_INT>=23){ String[] mPermissionList = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.CALL_PHONE,Manifest.permission.READ_LOGS,Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.SET_DEBUG_APP,Manifest.permission.SYSTEM_ALERT_WINDOW,Manifest.permission.GET_ACCOUNTS,Manifest.permission.WRITE_APN_SETTINGS}; ActivityCompat.requestPermissions(this,mPermissionList,123); } } @Override protected void onResume() { super.onResume(); myPresenter.attch((V) this); } @Override protected void onDestroy() { super.onDestroy(); myPresenter.setonDestroy(); } //封装吐司 public Toast toast; public void setToast(String ss){ if(toast==null){ toast=Toast.makeText(getApplicationContext(),ss,Toast.LENGTH_SHORT); } toast.setText(ss); toast.show(); } }
在BaseMvpActivity中,我也还做了另外一件事,就是Toast的封装,用这个吐司,完全不用担心内存泄漏的问题,可以说很完美了,当然了,在这个基类中我们还可以做上埋点(监测用户行为),也可以做上6.0的权限适配,这个6.0权限适配结合上埋点,两者配合使用,那简直就是一大杀招啊!!具体实现思路后期会给大家补上!
最后就到咱们最重要的环节了,MainActivity的按钮点击事件:
public class MainActivity extends BaseMVPActivity> implements View.OnClickListener { String iphone = "188****3955"; // 手机号换成自己的 String type = "register"; // 发送验证码类型:register -- 注册 、 login -- 验证码登陆 、 forget -- 忘记密码 private Button but1; private EditText yanzhengma; private Button but2; private TextView tishi; private Button but3; private TextView shuju; private PublicKey publicKey; // 将公司给的密钥通过base64转成公钥对象 @Override public MyPresenter initPresenter() { if (myPresenter == null) myPresenter = new MyPresenter<>(); return myPresenter; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); //RSA公钥加密对象 publicKey = RSAUtil.keyStrToPublicKey1(APIs.PUBLIC_KEY_STR); } private void initView() { but1 = (Button) findViewById(R.id.but1); but1.setOnClickListener(this); yanzhengma = (EditText) findViewById(R.id.yanzhengma); yanzhengma.setOnClickListener(this); but2 = (Button) findViewById(R.id.but2); but2.setOnClickListener(this); tishi = (TextView) findViewById(R.id.tishi); tishi.setOnClickListener(this); but3 = (Button) findViewById(R.id.but3); but3.setOnClickListener(this); shuju = (TextView) findViewById(R.id.shuju); shuju.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.but1: getYanZhengMa(); break; case R.id.but2: submit(); break; case R.id.but3: myPresenter.getPreContent(APIs.SHANGPIN, new MyInterfaces() { @Override public void chenggong(String json) { shuju.setText(json); } @Override public void shibai(String ss) { LogUtil.e("jiba","MainActivity 获取商品数据 有误==="+ss); } }); break; } } /** * iphone string Y 要注册的手机号 type string Y 发送验证码类型:register -- 注册 、 login -- 验证码登陆 、 forget -- 忘记密码 */ private void getYanZhengMa() { // 对数据参数进行公钥加密 iphone 参数需要加密 String publicEncryptedResult = RSAUtil.encryptDataByPublicKey(iphone.getBytes(), publicKey); LogUtil.i("jiba", "=====" + publicEncryptedResult); HashMap map = new HashMap<>(); map.put("iphone",publicEncryptedResult); map.put("type",type); myPresenter.postPreContent(APIs.YANZHENGMA, map, new MyInterfaces() { @Override public void chenggong(String json) { Gson gson = new Gson(); YaZhengMaBean yaZhengMaBean = gson.fromJson(json, YaZhengMaBean.class); if(yaZhengMaBean.getCode()==1){ setToast(yaZhengMaBean.getMsg()); }else{ setToast(yaZhengMaBean.getMsg()); } } @Override public void shibai(String ss) { LogUtil.e("jiba","MainActivity 获取验证码有误==="+ss); } }); } /** * encryptData array->json Y 注册所对应的信息(手机号、密码等) act string Y 判断所对应的类型:register-- 注册 code string->json Y 验证码 tel string->json Y 手机号 password string->json Y 密码 */ private void submit() { String yanzhengmaString = yanzhengma.getText().toString().trim(); if (TextUtils.isEmpty(yanzhengmaString)) { Toast.makeText(this, "验证码不能为空", Toast.LENGTH_SHORT).show(); return ; } HashMap map = new HashMap<>(); map.put("tel",iphone); map.put("password","w123456"); map.put("code",yanzhengmaString); JSONObject jsonObject = new JSONObject(map); String json = jsonObject.toString(); // 后台接口规则 参数必须转成json,并需要加密 // 将之前集合中的数据清空,从而复用map集合,这样可以达到内存优化的作用 map.clear(); // 对数据参数进行公钥加密 String encryptData = RSAUtil.encryptDataByPublicKey(json.getBytes(), publicKey); map.put("encryptData",encryptData); map.put("act",type); myPresenter.postPreContent(APIs.ZHUCE, map, new MyInterfaces() { @Override public void chenggong(String json) { Gson gson = new Gson(); YaZhengMaBean yaZhengMaBean = gson.fromJson(json, YaZhengMaBean.class); if(yaZhengMaBean.getCode()==1){ tishi.setText(yaZhengMaBean.getMsg()); }else{ tishi.setText(yaZhengMaBean.getMsg()); } } @Override public void shibai(String ss) { LogUtil.e("jiba","MainActivity 注册有误==="+ss); } }); } }
到这儿,我们的封装使用就结束了,希望能对大家有点帮助吧!!
差点忘记给大家附上APIs的接口了:
public class APIs { // 公司公钥 public static final String PUBLIC_KEY_STR = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDF3wFJC/f/5qHR30BzLpcxdRtq\n" + "ahoXu31tbGwg97iRhVfXxLqFp2OZqdJjkYNqCuuIzRlYSJqXURkXD3UxrbiwGAMi\n" + "FqPbXW/JEXgWqsqf59tRAWhJ5Ycy0Ln03lyCnVbAtdhzoqe+vfrNTg0R/D3vMMcS\n" + "O76rXvvK1zxF19vy9wIDAQAB\n"; // 公司域名 public static final String debug="http://www.jingchengcaidian.com/"; // 获取验证码 public static final String YANZHENGMA= debug + "app/message/getIphone"; // 注册账号 http://www.jingchengcaidian.com/app/login/getUserInfo public static final String ZHUCE= debug + "app/login/getUserInfo"; // 获取商品数据 public static final String SHANGPIN="https://www.zhaoapi.cn/product/getProducts?pscid=2&page=1&source=android"; }