
okhttp介绍 OkHttp是一个非常优秀的网络请求框架,已被谷歌加入到Android的源码中。目前比较流行的Retrofit也是默认使用OkHttp的。所以OkHttp的源码是一个不容错过的学习资源,学习源码之前,务必熟练使用这个框架,否则就是跟自己过不去。 okhttp优点 支持HTTP2/SPDY黑科技 socket自动选择最好路线,并支持自动重连 拥有自动维护的socket连接池,减少握手次数 拥有队列线程池,轻松写并发 拥有Interceptors轻松处理请求与响应(比如透明GZIP压- 缩,LOGGING) 实现基于Headers的缓存策略 至于为什么有这么多优点,各位看官老爷在下面的源码解析中慢慢体会吧! okhttp简单用法 既然是网络框架,那么先来看看它的post和get请求吧。总的来说,分为三步: 实例化一个OkHttpClient 对象; 构造Request请求体; 发请求,同步调用okHttpClient.newCall(request).execute();异步调用 okHttpClient.newCall(request).enqueue(new Callback())。 get(异步)请求 OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(URL).build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { String dataStr=response.body().string(); Log.e("info",dataStr); } }); get(同步)请求 OkHttpClient okHttpClient = new OkHttpClient(); Response response = okHttpClient.newCall(request).execute(); String dataStr=response.body().string(); post(异步)请求 OkHttpClient okHttpClient = new OkHttpClient(); RequestBody requestBody = new FormBody .Builder() .add("name", "张士超") .add("password", "123456") .build(); Request request = new Request .Builder() .url(URL) .post(requestBody) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { String dataStr=response.body().string(); Log.e("info",dataStr); } }); post(同步)请求 OkHttpClient okHttpClient = new OkHttpClient(); RequestBody requestBody = new FormBody .Builder() .add("name", "张士超") .add("password", "123456") .build(); Request request = new Request .Builder() .url(URL) .post(requestBody) .build(); try { Response response = okHttpClient.newCall(request).execute(); String dataStr=response.body().string(); } catch (IOException e) { e.printStackTrace(); } 总结:post与get请求区别在于需要构造一个RequestBody对象,同步和异步区别在于执行的是execute()还是enqueue(new Callback()),这也很好理解,异步需要回调接口反馈的请求数据对吧。 okhttp的源码深入 好啦,步入正题,看看我们的okhttp同学是怎么样完成一个个请求的! 首先我们看看okhttp源码涉及到的几个类的源码: Request请求类: public final class Request { final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Object tag; private volatile CacheControl cacheControl; // Lazily initialized. 这里面比较重要的是url、method、header和RequestBody,分别代表request请求的请求URL、请求方法、请求头和请求体。 Response响应类 public final class Response implements Closeable { final Request request; final Protocol protocol; final int code; final String message; final @Nullable Handshake handshake; final Headers headers; final @Nullable ResponseBody body; final @Nullable Response networkResponse; final @Nullable Response cacheResponse; final @Nullable Response priorResponse; final long sentRequestAtMillis; final long receivedResponseAtMillis; 这里面比较重要的是code响应码、message响应信息、headers响应头和body响应体。 Okhttp类 final Dispatcher dispatcher; final @Nullable Proxy proxy; final List<Protocol> protocols; final List<ConnectionSpec> connectionSpecs; final List<Interceptor> interceptors; final List<Interceptor> networkInterceptors; final EventListener.Factory eventListenerFactory; final ProxySelector proxySelector; final CookieJar cookieJar; final @Nullable Cache cache; final @Nullable InternalCache internalCache; final SocketFactory socketFactory; final @Nullable SSLSocketFactory sslSocketFactory; final @Nullable CertificateChainCleaner certificateChainCleaner; final HostnameVerifier hostnameVerifier; final CertificatePinner certificatePinner; final Authenticator proxyAuthenticator; final Authenticator authenticator; final ConnectionPool connectionPool; final Dns dns; final boolean followSslRedirects; final boolean followRedirects; final boolean retryOnConnectionFailure; final int connectTimeout; final int readTimeout; final int writeTimeout; final int pingInterval; Okhttp包含的东西很多,这里我们需要重点关注的是dispatcher调度器,interceptors自定义应用拦截器和networkInterceptors自定义网络拦截器。 ok,比较重要的三个类介绍完了,接下来我们看一看同步和异步请求的源码解析,先放一张图(盗的别人的图hhhh),让看官老爷门大致上有一个印象,后面比较好理解。 整个请求过程 整个过程大致说一下吧,首先不管啥请求okHttpClient.newCall(request)这玩意实际上返回一个RealCall类,然后同步请求调用execute(),异步调用enqueue()之后给调度器,其实同步和异步都给了调度器,只是异步调用了调度器的execute()进行请求调度处理,下面给一张图就了解啦: 调度器 处理完之后通过一系列花里胡哨的拦截器之后返回一个response响应,就获得数据啦! 请求过程源码解析 以下面代码为例,看看源码都干了啥: OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(URL).build(); okHttpClient.newCall(request).enqueue(new Callback()/execute() 前面两句话就不说了,就是OkHttpClient和Request 对象的实例化,我们看看okHttpClient.newCall(request)这里都干了些啥: @Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false ); } 进去newRealCall,看看: static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { RealCall call = new RealCall(client, originalRequest, forWebSocket); call.eventListener = client.eventListenerFactory().create(call); return call; } 总之就是工厂模式将request封装成一个RealCall对象,接着看okHttpClient.newCall(request).execute(),同步请求; @Override public Response execute() throws IOException { synchronized (this) {//1 if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); try { client.dispatcher().executed(this);//2 Response result = getResponseWithInterceptorChain();//3 if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { eventListener.callFailed(this, e); throw e; } finally { client.dispatcher().finished(this);//3 } } 比较重要的是上面三处注释: 首先看这个请求有没有被执行,每个请求只能被执行一次; 若没有被执行,则将这个请求任务给调度器dispatcher,简单看看这个调度器做了些什么: synchronized void executed(RealCall call) { runningSyncCalls.add(call); } 将这个请求加入到runningSyncCalls队列中,后面详细说说这个调度器。 经过一系列花里胡哨的拦截器得到响应,这具体后再说怎么做的。 执行完之后调度器dispatcher结束这个请求任务,看看具体怎么做的: void finished(RealCall call) { finished(runningSyncCalls, call, false); } 将请求任务从runningSyncCalls队列中移除。 这样整个同步请求流程就实现了,看下面这个图会清晰很多: 同步请求任务.png 调度器 上面可以知道,请求处理都交给调度器了,那我们来看看这个调度器是个什么鬼: public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; private @Nullable Runnable idleCallback; private @Nullable ExecutorService executorService; private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); 解释一下这几个参数啥子意思,第一个maxRequests最大请求数,表示最大并发数 ;maxRequestsPerHost 单个域名最大请求数 默认为 5 个。所以极端情况下,才会开启 64 个线程。这个场景非常罕见;readyAsyncCalls 表示异步请求的等待队列,runningAsyncCalls 正在执行的异步请求队列,AsyncCall队列;runningSyncCalls 正在执行的同步请求队列,因为是同步的,这也好理解,所以不需要等待队列。executorService这个就是调度器的线程池,java线程池我们都知道,我们看看这里的线程池怎么定义的: public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; } 可以知道,调度器的核心线程为0,非核心线程无数个,每个空线程的回收时间为60s。 异步请求 异步请求以下面这个为例,看看源码都怎么做的: OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(URL).build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { String dataStr=response.body().string(); Log.e("info",dataStr); } }); 与同步请求的差距主要在于调用的是enqueue()函数,点进去看看与execute的区别 @Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); client.dispatcher().enqueue(new AsyncCall(responseCallback)); } 先判断有没有执行过,如果没有,这执行调度器dispatcher的enqueue()函数,点进去看看吧: synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } } 这里就与我们之前讲的调度器队列有关系了,如果正在执行的异步队列runningAsyncCalls小于最大并发数,而且单域名的正在执行异步队列小于最大单域名最大请求数,则将这个请求放到正在执行的异步请求队列,并调用调度器的execute()方法,否则就放入等待异步队列。 调度器线程池总结 调度线程池Disptcher实现了高并发,低阻塞的实现 采用Deque作为缓存,先进先出的顺序执行 任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能 拦截器 主要是getResponseWithInterceptorChain方法 Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); } 1)在配置 OkHttpClient 时设置的 interceptors; 2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor; 3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor; 4)负责读取缓存直接返回、更新缓存的 CacheInterceptor; 5)负责和服务器建立连接的 ConnectInterceptor; 6)配置 OkHttpClient 时设置的 networkInterceptors; 7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。 OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。给个图加强理解: 拦截器.png 不多说了,结合图和源码理解吧,创作不易,给个小心心吧!
aidl与binder机制 为什么需要binder 没有用到binder之前,我们每个app生活在分配给自己的虚拟机和内存空间中,这样保证了app应用的安全,到很多时候我们需要用到跨进程通信(IPC),这时binder就为此而生。ActivityManagerService、WinderManagerService等系统服务的背后都是Binder。 binder跨进程通信原理 image 如上图,binder要想运作,必须有四个角色合作: 客户端:获取服务端的binder驱动引用。并调用它的transact方法即可向服务端发送消息。 服务端:指Binder实现类所在的进程,该对象一旦创建,内部则会启动一个隐藏线程,会接收客户端发送的数据,然后执行Binder对象中的onTransact()函数。 Binder驱动:当服务端Binder对象被创建时,会在Binder驱动中创建一个mRemote对象。 Service Manager:作用相当于DNS,就想平时我们通过网址,然后DNS帮助我们找到对应的IP地址一样,我们在Binder服务端创建的Binder,会注册到Service Manager,同理,当客户端需要该Binder的时候,也会去Service Manager查找。 运作流程: 服务端创建对应Binder实例对象,然后开启隐藏Binder线程,接收来自客户端的请求,同时,将自身的Binder注册到Service Manager,在Binder驱动创建mRemote对象。 客户端想和服务端通信,通过Service Manager查找到服务端的Binder,然后Binder驱动将对应的mRemote对象返回 至此,整个通信连接建立完毕 加强理解,先丢几张图: image 首先客户端开启服务,与服务端连接,然后实例化ServiceConnection对象,得到服务端的IBinder实例化类(若是同一进程,则返回的是服务端的Binder类,若不是同一进程,则返回服务端的代理类,这个问题后面会提到),然后调用该IBinder实例化类里的方法,实际上是利用该Ibinder的transact()方法向服务端发送数据,然后服务端调用onTransact返回数据给客户端,这样就完成了一次服务端与客户端的通信, aidl小栗子 下面这根据aidl小例子对上面的进行验证 第一步:写aidl接口文件 interface FoodInterface { //自带的,用于说明aidl支持的数据类型 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); //自己添加的 int getFoodPrice(String foodName); } 第二步:make project一下工程,然后可以在该目录下看到生成的java接口文件,名称与aidl文件名一致。 生成的java接口文件 第三步:完善服务端,新建服务,重写onBinder方法,新建Binder内部类,继承的是接口名.Stub抽象类,重写里面的抽象方法,该方法就是接口声明的方法。 完善服务端 第四步:完善客户端,启动服务,获取服务端的(代理)类,并调用服务端的方法,返回服务端的结果完成通信。 private ServiceConnection mServiceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { service1= FoodInterface.Stub.asInterface(service); Toast.makeText(MainActivity.this,"绑定成功!", Toast.LENGTH_SHORT).show(); } @Override public void onServiceDisconnected(ComponentName name) { service1=null; Toast.makeText(MainActivity.this,"断开服务!", Toast.LENGTH_SHORT).show(); } }; button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String foodStr = MainActivity.this.food.getText().toString().trim(); int price= 0; try { price = service1.getFoodPrice(foodStr); } catch (RemoteException e) { e.printStackTrace(); } priceView.setText(foodStr+"的价格为"+price+"元"); } }); 看效果 通信效果 接下来,我们来看一看系统帮我们生成的接口文件,下面是源码: public interface FoodInterface extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.wc.aidldemo.FoodInterface { private static final java.lang.String DESCRIPTOR = "com.example.wc.aidldemo.FoodInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.wc.aidldemo.FoodInterface interface, * generating a proxy if needed. */ public static com.example.wc.aidldemo.FoodInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.wc.aidldemo.FoodInterface))) { return ((com.example.wc.aidldemo.FoodInterface)iin); } return new com.example.wc.aidldemo.FoodInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0!=data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_getFoodPrice: { data.enforceInterface(descriptor); java.lang.String _arg0; _arg0 = data.readString(); int _result = this.getFoodPrice(_arg0); reply.writeNoException(); reply.writeInt(_result); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.example.wc.aidldemo.FoodInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean)?(1):(0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public int getFoodPrice(java.lang.String foodName) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(foodName); mRemote.transact(Stub.TRANSACTION_getFoodPrice, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getFoodPrice = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; public int getFoodPrice(java.lang.String foodName) throws android.os.RemoteException; } 不得不说,谷歌封装的确实好,将服务端和客户端代码封装在一个接口文件里了。看一下这个接口的结构: FoodInterface接口 { 抽象类Stub 继承Binder 实现FoodInterface{ 静态的asInterface方法{ 返回代理类(分两种情况:同进程,直接返回继承该Stub的Binder();不同进程,返回静态代理类) } 重写的onTransact方法 静态代理类Proxy 实现FoodInterface{ 实现getFoodPrice,主动调用transact方法 } } 声明getFoodPrice方法 } 上面已经写的很清楚了,总的过程就是服务端继承stub静态类,重写接口声明的方法,然后根据服务端启动服务,根据接口.stub.asInterface获取(代理)binder对象,然后binder调用声明的方法(实际上是调用transact向服务端发送数据)服务端调用对应的方法,利用onTransact反馈数据完成通信。 感谢各位看官老爷,创作不易,麻烦动动手指点个赞!! 参考文章:https://blog.csdn.net/cjh94520/article/details/71374872https://blog.csdn.net/u012702547/article/details/52748403
介绍 Jsoup是一个用来处理html文本的java库。它提供了非常方便的API,可以通过dom,css或者类似jquery的方法来提取和操作数据。 嗯,所以他到底是干嘛的呢? 当我们访问一个网站拿到它的html代码的时候,往往我们所需要的一些数据就已经包含在html里,Jsoup就是帮我们把这些我们想要的数据提取出来。还是不够清晰明了?没关系,我们一起来看一个demo。 demo 以解析本人首页为例https://www.jianshu.com/u/413ca7e5e66c,首先是拿到首页的html页面,并字符串的格式返回,这里使用okhttp框架,各位看官老爷也可以使用其他框架,只要能拿到html页面即可。 new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(URL).build(); try { Response response=okHttpClient.newCall(request).execute(); String htmlData=response.body().string(); resolve(htmlData); } catch (IOException e) { e.printStackTrace(); } } }).start(); 返回的html字符串如下 返回的html 然后利用Jsoup对得到的字符串进行解析,返回浏览器,按下F12,我们可以看到页面的html代码,找到对应的文章模块的html,在某个div下的id为list-container的ul、li列表: 文章模块的html 单看每一个li,可以看出文章的具体内容,这里只解析文章的标题,它在li里的class为title的a标签里: 需要解析的标题的位置 根据Jsoup对得到的html进行解析:、 private void resolve(String htmlData) { Document document = Jsoup.parse(htmlData); Elements elements = document.select("div#list-container ul li"); List<BlogModel> list=new ArrayList<>(); for(Element element:elements){ BlogModel blogModel = new BlogModel(); blogModel.setTitle(element.select("a.title").first().text()); list.add(blogModel); } for(BlogModel blogModel:list){ Log.e("info",blogModel.getTitle()); } } 下面是得到的数据日志: 解析结果 -附jsoup下载地址。
MVP优化的结构 mvp结构图.png view的结构 BaseView public interface BaseView { void showLoading(); void hideLoading(); } NewMvpView public interface NewMvpView extends BaseView { void setListItem(List<String> data); void showMessage(String message); } presenter结构 BasePresenter public abstract class BasePresenter<T> { public T mView; public void attach(T mView){ this.mView = mView; } public void dettach(){ mView = null; } } NewMvpPresenter public class NewMvpPresenter extends BasePresenter<NewMvpView> { private RequestBiz requestBiz; private Handler mHandler; public NewMvpPresenter() { requestBiz = new RequestBiziml(); mHandler = new Handler(Looper.getMainLooper()); } public void onResume(){ requestBiz.requestForData(new OnRequestListener() { @Override public void onSuccess(final List<String> data) { mHandler.post(new Runnable() { @Override public void run() { mView.hideLoading(); mView.setListItem(data); } }); } @Override public void onFailed() { mView.showMessage("请求失败"); } }); } public void onItemClick(int position){ mView.showMessage("点击了item"+position); } } activity结构 BaseMvpActivity public abstract class BaseMvpActivity<V,T extends BasePresenter<V>> extends AppCompatActivity { public T presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = initPresenter(); } @Override protected void onResume() { super.onResume(); presenter.attach((V)this); } @Override protected void onDestroy() { presenter.dettach(); super.onDestroy(); } public abstract T initPresenter(); } NewMvpActivity public class NewMvpActivity extends BaseMvpActivity<NewMvpView,NewMvpPresenter> implements NewMvpView,AdapterView.OnItemClickListener{ private ListView mvpListView; private ProgressBar pb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mvp); mvpListView = (ListView)findViewById(R.id.mvp_listview); mvpListView.setOnItemClickListener(this); pb = (ProgressBar) findViewById(R.id.mvp_loading); } @Override protected void onResume() { super.onResume(); presenter.onResume(); } @Override public NewMvpPresenter initPresenter() { return new NewMvpPresenter(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { presenter.onItemClick(position); } @Override public void setListItem(List<String> data) { ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,data); mvpListView.setAdapter(adapter); } @Override public void showMessage(String message) { Toast.makeText(this,message,Toast.LENGTH_SHORT).show(); } @Override public void showLoading() { pb.setVisibility(View.VISIBLE); } @Override public void hideLoading() { pb.setVisibility(View.GONE); } }
语法 正则.png 正则.png 正则.png 典型例子 ^\d+$ //匹配非负整数(正整数 + 0) ^[0-9][1-9][0-9]$ //匹配正整数 ^((-\d+)|(0+))$ //匹配非正整数(负整数 + 0) ^-[0-9][1-9][0-9]$ //匹配负整数 ^-?\d+$ //匹配整数 ^\d+(.\d+)?$ //匹配非负浮点数(正浮点数 + 0) ^(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9]))$ //匹配正浮点数 ^((-\d+(.\d+)?)|(0+(.0+)?))$ //匹配非正浮点数(负浮点数 + 0) ^(-(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9])))$ //匹配负浮点数 ^(-?\d+)(.\d+)?$ //匹配浮点数 ^[A-Za-z]+$ //匹配由26个英文字母组成的字符串 ^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串 ^[a-z]+$ //匹配由26个英文字母的小写组成的字符串 ^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串 ^\w+$ //匹配由数字、26个英文字母或者下划线组成的字符串 ^[\w-]+(.[\w-]+)*@[\w-]+(.[\w-]+)+$ //匹配email地址 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 匹配双字节字符(包括汉字在内):[^\x00-\xff] 匹配空行的正则表达式:\n[\s| ]*\r 匹配HTML标记的正则表达式:/<(.)>.</\1>|<(.*) />/ 匹配首尾空格的正则表达式:(^\s)|(\s$) 正则表达式用例 1、^\S+[a-z A-Z]$ 不能为空 不能有空格 只能是英文字母 2、\S{6,} 不能为空 六位以上 3、^\d+$ 不能有空格 只能是数字 4、(.*)(.jpg|.bmp)$ 只能是jpg和bmp格式 5、^\d{4}-\d{1,2}-\d{1,2}$ 只能是2004-10-22格式 6、^0$ 至少选一项 7、^0{2,}$ 至少选两项 8、^[\s|\S]{20,}$ 不能为空 二十字以上 9、^+?a-z0-9*@([a-z0-9]+(.|-))+[a-z]{2,6}$邮件 10、\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)([,;]\s\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)) 输入多个地址用逗号或空格分隔邮件 11、^(([0-9]+))?[0-9]{7,8}$电话号码7位或8位或前面有区号例如(022)87341628 12、^[a-z A-Z 0-9 *]+@[a-z A-Z 0-9 *]+(.[a-z A-Z 0-9 *]+)+(,[a-z A-Z 0-9 *]+@[a-z A-Z 0-9 *]+(.[a-z A-Z 0-9 ]+)+)$ 只能是字母、数字、下划线;必须有@和.同时格式要规范 邮件 13 ^\w+@\w+(.\w+)+(,\w+@\w+(.\w+)+)*$上面表达式也可以写成这样子,更精练。 14 ^\w+((-\w+)|(.\w+))@\w+((.|-)\w+).\w+$ [/size]