摘要:现在,我们使用了的话,那么框架内部就能帮我们封装了。每个中都有和这样的方法,没必要的。我们抽取出来,通过配置文件来把这两个方法替换掉,那么我们的程序就会更加优雅了。于是乎,就应运而生了。因此,学习的时候,不了解是没有任何关系的。
前言
这是Strtus的开山篇,主要是引入struts框架...为什么要引入struts,引入struts的好处是什么,以及对Struts2一个简单的入门....
为什么要引入struts?既然Servlet能够完成的事,我们为啥要用框架呢??
框架帮我们封装了很多常用的功能
把Web带过来的参数自动封装到JavaBean中[以前,我们刚开始学的时候是单个单个来获取参数的,后来我们又使用了BeanUtils写工具方法来帮我们封装]。现在,我们使用了Struts2的话,那么框架内部就能帮我们封装了。
更加灵活[不用把路径等信息写死在程序上],对于路径我们使用配置文件来进行管理,如果目录发生了变化,也不用一个一个去修改每个程序的路径。
每个Servlet中都有doGet和doPost这样的方法,没必要的。我们抽取出来,通过配置文件来把这两个方法替换掉,那么我们的程序就会更加优雅了。
于是乎,struts2就应运而生了。
自定义struts在正式讲解struts之前,我们来看一下,以我们现在的水平,能够怎么优化它。。
以用户的登陆注册案例来进行说明
传统的用户登陆注册dao
public class UserDao { public User login(User user) { if ("aaa".equals(user.getUsername()) && "123".equals(user.getPsd())) { System.out.println("登陆成功!"); return user; } else { System.out.println("登陆失败!"); return null; } } public void register(User user) { System.out.println("注册成功!" + user.getUsername()); } }
service
public class UserService { private UserDao userDao = new UserDao(); public User longin(User user) { return userDao.login(user); } public void register(User user) { userDao.register(user); } }
loginServlet
@javax.servlet.annotation.WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet") public class LoginServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //得到用户带过来的数据,封装到Bean对象中 String username = request.getParameter("username"); String psd = request.getParameter("psd"); User user = new User(); user.setPsd(psd); user.setUsername(username); try { //调用Service方法 UserService userService = new UserService(); userService.longin(user); //登陆成功跳转到首页 request.getRequestDispatcher("/index.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); //登陆失败,跳转到相关的提示页面 request.setAttribute("message","登陆失败了!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); } } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doPost(request, response); } }
registerServlet
@javax.servlet.annotation.WebServlet(name = "RegisterServlet",urlPatterns = "/RegisterServlet") public class RegisterServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //得到用户带过来的数据,封装到Bean对象中 String username = request.getParameter("username"); String psd = request.getParameter("psd"); User user = new User(); user.setPsd(psd); user.setUsername(username); try { //调用Service方法 UserService userService = new UserService(); userService.register(user); //注册成功跳转到登陆界面 request.getRequestDispatcher("/login.jsp").forward(request, response); //注册成功,我也可以跳转到首页 //request.getRequestDispatcher("/index.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); //注册失败,跳转到相关的提示页面 request.setAttribute("message","注册失败了!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); } } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doPost(request, response); } }
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>$Title$
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>$Title$
上面的代码已经经过了测试,是可以跑起来的。
①:跳转页面的路径是写死的。我在注册成功了以后,我可以跳转到首页上,也可以跳转到登陆的界面上。如果我要选择其中的一个,就必须修改源代码...
②:一个功能对应一个Servlet,太麻烦了...写了LoginServlet,还要写RegisterServlet....
新型的用户登陆注册我们会发现,无论什么Servlet上最终还是跳转到相对应的JSP页面的...也就是说,第一和第二步骤【封装数据、调用Service】我们可以封装起来...只要返回uri给Servlet跳转到JSP页面就好了。
LoginAction返回的uri分两种情况:
如果是转发,那么返回的是RequestDispatcher对象
如果是重定向,那么返回的是字符串
/** * Created by ozc on 2017/4/26. ** 一个Action对应一个Servlet,Action负责处理具体的请求 */ public class LoginAction { public Object login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { Object uri ; //得到用户带过来的数据,封装到Bean对象中 String username = request.getParameter("username"); String psd = request.getParameter("psd"); User user = new User(); user.setPsd(psd); user.setUsername(username); try { //调用Service方法 UserService userService = new UserService(); userService.longin(user); //登陆成功跳转到首页 request.getSession().setAttribute("user", user); //跳转到首页的时候需要重定向 //response.sendRedirect(request.getContextPath() + "/index.jsp"); //如果是重定向,那么返回的是字符串 uri = "/index.jsp"; return uri; } catch (Exception e) { e.printStackTrace(); //登陆失败,跳转到相关的提示页面 request.setAttribute("message","登陆失败了!!!"); //request.getRequestDispatcher("/message.jsp").forward(request, response); //如果是转发,那么返回的是RequestDispatcher对象 uri = request.getRequestDispatcher("/message.jsp"); return uri; } } }
LoginServlet就可以写成这样了:
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //得到LoginAction对象 LoginAction loginAction = new LoginAction(); Object uri = loginAction.login(request, response); //是重定向 if (uri instanceof String) { response.sendRedirect(request.getContextPath() + uri); } else { //是转发,强转成是RequestDispatcher对象 ((RequestDispatcher) uri).forward(request, response); } }RegisterAction
RegisterAction
/** * Created by ozc on 2017/4/26. * * 一个Action对应一个Servlet,Action负责处理具体的请求 */ public class RegisterAction { public Object register(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { Object uri ; //得到用户带过来的数据,封装到Bean对象中 String username = request.getParameter("username"); String psd = request.getParameter("psd"); User user = new User(); user.setPsd(psd); user.setUsername(username); try { //调用Service方法 UserService userService = new UserService(); userService.register(user); //登陆成功跳转到登陆页面 uri = request.getRequestDispatcher("/login.jsp"); return uri; } catch (Exception e) { e.printStackTrace(); //注册失败,跳转到相关的提示页面 request.setAttribute("message","注册失败了!!!"); //request.getRequestDispatcher("/message.jsp").forward(request, response); uri = request.getRequestDispatcher("/message.jsp"); return uri; } } }
RegisterServlet
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //得到RegisterAction RegisterAction registerAction = new RegisterAction(); Object uri = registerAction.register(request, response); //是重定向 if (uri instanceof String) { response.sendRedirect(request.getContextPath() + uri); } else { //是转发,强转成是RequestDispatcher对象 ((RequestDispatcher) uri).forward(request, response); } }思考
到目前为止,我们搞了两个Action类来封装Servlet的逻辑代码,我们再次看回Servlet的代码。
可以很清楚地发现:两个实现不同功能的Servlet仅仅是调用的Action不同....如果是仅仅调用的Action不同【通过反射来调用不同的Action】,那么我们应该想到使用一个Servlet来管理整个项目,也就是说:整个web项目只有一个核心的控制器
问题:
①:我们在之前是直接指明Servlet的映射路径了,现在要ActionServlet处理所有的请求,我们只要定一个规则:只要后缀为.action的,那么都交由核心控制器ActionServlet来控制....
②:现在全部的请求已经交由ActionServlet控制,那怎么知道调用的是哪个Action???我们可以通过请求的uri,比如:http://localhost:8080/login.action,其中login就代表的是调用LoginAction..也就是说login=LoginAction,我们可以通过properties文件来配置..
③:现在我们已经知道了调用的是哪个Action了,但是Action可能不仅仅只有一个方法,我们还要在调用的时候,指定的方法名是什么.这很简单,一般我们都是职责分工明确的,method=login....并且,调用的Action和具体的方法也是有关系的,不可能是孤立存在的。因此,我们的配置文件是不能使用properties的,需要使用XML
④:在调用方法的时候,是返回一个Object的uri的,uri的类型可能是String、也可以能是RequestDispatcher、并且返回的结果可能有几种情况的【可能跳转到首页,也可能跳转到登陆界面】
⑤:Action调用的方法和返回的uri也是是有关系的!.....不同的Action调用不同的方法,返回的uri也是不同的....
⑥:要跳转到哪个页面上,可以通过标识量来识别....比如:success表示成功执行,如果要重定向那么多加个type类型,如果不重定向就没有type类型..路径使用path来表示..因此,在具体的Action中,就不需要返回具体的uri,只要返回一个标识量即可
画一张图来梳理一下思路:
XML配置我们可以写出这样的XML配置,当ActionServlet初始化的时候,读取XML配置文件,就知道调用的是什么Action,Action中的什么方法,以及跳转到哪个页面上了。
/index.jsp /message.jsp /message.jsp /message.jsp
为了更好地管理这些信息,我们应该使用JavaBean来对它们封装
ActionMappingManager-------管理全部的Action
/** * Created by ozc on 2017/4/26. * * 该类管理着全部的Action * * 要管理全部的Action,就需要用一个容器来装载这些Action * * 选择Map集合是最合适的,可以通过key来得到Action,key就是中的name属性 * */ public class ActionMappingManager { private Map map = new HashMap<>(); //注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager public ActionMapping getActionMapping(String name) { return map.get(name); } }
ActionMapping----表示单个的Action
public class ActionMapping { //所有的results private Mapresults; //关键字name private String name; //要调用的Action路径 private String className; //Action中的方法 private String method; public Map getResults() { return results; } public void setResults(Map results) { this.results = results; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }
Results---表示的是结果视图
/** * Created by ozc on 2017/4/26. * * 该类表示的是结果视图 * * * */ public class Results { //方法返回的标识 private String name; //要跳转的方式 private String type; //要跳转的页面 private String page; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPage() { return page; } public void setPage(String page) { this.page = page; } }ActionMappingManager读取配置文件
在ActionMappingManager中,应该读取配置文件,然后把信息全部封装到里边去...
/** * Created by ozc on 2017/4/26. * * 该类管理着全部的Action * * 要管理全部的Action,就需要用一个容器来装载这些Action * * 选择Map集合是最合适的,可以通过key来得到Action,key就是ActionServlet中的name属性 * */ public class ActionMappingManager { private Map allAction ; public ActionMappingManager() { this.allAction = new HashMap<>(); //读取配置文件信息 init(); } public void init() { /********通过DOM4J读取配置文件信息*********/ try { //得到解析器 SAXReader saxReader = new SAXReader(); //读取在类目录下的mystruts.xml文件 InputStream stream = ActionMappingManager.class.getClassLoader().getResourceAsStream("mystruts.xml"); //得到代表XML文件的Document对象 Document document = saxReader.read(stream); //通过XPATH直接得到所有的Action节点 List list = document.selectNodes("//action"); //得到每个Action节点 for (int i = 0; i < list.size(); i++) { Element action = (Element) list.get(i); //把得到每个Action的节点信息封装到ActionMapping中 ActionMapping actionMapping = new ActionMapping(); String name = action.attributeValue("name"); String method = action.attributeValue("method"); String className = action.attributeValue("className"); actionMapping.setName(name); actionMapping.setMethod(method); actionMapping.setClassName(className); //得到action节点下的所有result节点 List results = action.elements("result"); //得到每一个result节点 for (int j = 0; j < results.size(); j++) { Element result = (Element) results.get(j); //把得到每个result节点的信息封装到Results中 Results results1 = new Results(); //得到节点的信息 String name1 = result.attributeValue("name"); String type = result.attributeValue("type"); String page = result.getText(); results1.setName(name1); results1.setType(type); results1.setPage(page); //把result节点添加到ActionMapping的集合中 actionMapping.getResults().put(name1, results1); } //最后把得到每个ActionMapping的信息添加到ActionMappingManager中 allAction.put(name, actionMapping); } } catch (DocumentException e) { new RuntimeException("初始化的时候出错了!“" + e); } } //注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager public ActionMapping getActionMapping(String name) { return allAction.get(name); } }
使用init()方法只加载创建一个ActionManagerMapping对象,并设置在Web容器启动了该Servlet就启动
/** * Created by ozc on 2017/4/26. * * * Web容器一启动的时候,该类就应该加载了,在web.xml文件中配置onloadStart */ public class ActionServlet extends HttpServlet { //该对象封装了所有的XML信息 ActionMappingManager actionMappingManager ; @Override public void init() throws ServletException { //让ActionMappingManager对象只有一个! actionMappingManager = new ActionMappingManager(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { //得到用户的uri String uri = request.getRequestURI(); //截取uri的关键部分-----截完应该是login uri = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf(".")); //通过uri得到配置文件中的action信息 ActionMapping actionMapping = actionMappingManager.getActionMapping(uri); //得到action的类名,方法名 String className = actionMapping.getClassName(); String method = actionMapping.getMethod(); //通过反射创建出Action的对象,调用对应的方法 Class t = Class.forName(className); Object o = t.newInstance(); //注意:这里的参数是接口的class,不是单纯的request的class,单纯的class是实现类 Method m = t.getMethod(method, HttpServletRequest.class, HttpServletResponse.class); //调用方法,得到标记 String returnFlag = (String) m.invoke(o, request, response); //通过标记得到result的具体信息 Results result = actionMapping.getResults().get(returnFlag); String type = result.getType(); String page = result.getPage(); //判断是重定向还是转发,为空就是转发,反则是重定向 if (type == null) { response.sendRedirect(page); } else { request.getRequestDispatcher(request.getContextPath() + page).forward(request, response); } } catch (Exception e) { e.printStackTrace(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
具体的Action的方法只要返回一个标识量即可,我们通过标识量来得到具体的跳转页面url和跳转的方法的。。。
效果: 自定义MyStruts总结:由于传统web的Controller模块存在弊端:
一些功能重复使用,代码过于重复了。
跳转的页面写死了。改变需求的时候需要更改源代码
本博文主要模拟Struts的开发流程
使用一个ActionServlet核心控制器来管理全部的Web请求,写XML配置文件,读取配置文件。
ActionMapping封装了Action的基本信息,在XML配置文件中就是读取Action的基本信息,封装到JavaBean上,最后使用ActionMapping类的集合统一管理起来。
当用户访问的时候,我们根据url也就是Action的名称反射出对应的类,来对其进行操作。
再根据XML文件的配置信息来确定跳转方法、跳转的url
我们现在学习的是Struts2,其实Struts1和Struts2在技术上是没有很大的关联的。 Struts2其实基于Web Work框架的,只不过它的推广没有Struts1好,因此就拿着Struts这个名气推出了Struts2框架。
因此,学习Struts2的时候,不了解Struts1是没有任何关系的。
在前面,已经说明了为什么要引入Struts框架,其实就是为了提高开发效率...
Struts2框架预先实现了一些功能:
请求数据自动封装
文件上传的功能
对国际化功能的简化
数据效验功能.......等等
Struts2开发步骤我们就直接来讲解Struts2的开发步骤是什么吧....在了解它的细节之前,先要把配置环境搭好!
引入jar文件完整的struts中的jar包有80多个,我们日常开发是不需要那么多个的。一般我们导入的jar包有8个:
commons-fileupload-1.2.2.jar 【文件上传相关包】
commons-io-2.0.1.jar【文件上传相关包】
struts2-core-2.3.4.1.jar 【struts2核心功能包】
xwork-core-2.3.4.1.jar 【Xwork核心包】
ognl-3.0.5.jar 【Ognl表达式功能支持表】
commons-lang3-3.1.jar 【struts对java.lang包的扩展】
freemarker-2.3.19.jar 【struts的标签模板库jar文件】
javassist-3.11.0.GA.jar 【struts对字节码的处理相关jar】
配置web.xml在web.xml中配置的过滤器,其实就是在为struts进行初始化工作
值得注意的是:如果该web.xml配置了多个fileter,那么struts的filter需要在最后面!
开发Actionstruts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*
开山篇我们已经说了,Servlet的业务代码,我们都使用Action来代替...Action类一般继承着ActionSupport
Action类也叫动作类,处理请求的类。
public class HelloAction extends ActionSupport { @Override public String execute() throws Exception { System.out.println("helloworld"); return "success"; } }
至于execute()方法是什么,我们先不要去管它,为啥要返回一个String,我们也不要去管它....只要记住开发步骤,并且,我们的Action类是要继承ActionSupport类的
配置struts.xml至于配置struts.xml,我们可以在文件中找到相对应的模版代码的...最终修改成下面这个样子就行了:
/index.jsp
看完上面的配置文件,是非常像我们开山篇写的struts框架的配置文件的....
效果:在地址栏中直接输入hello,就跳转到index.jsp页面了。并且,execute()中的语句被执行了...
Struts2执行流程我们来简单地了解一下Struts的执行流程,然后再慢慢对上面的开发步骤的部分进行讲解....
服务器启动下边我说的都是struts流程的重点:
加载web.xml文件
找到我们配置的filter中的StrutsPrepareAndExecuteFilter
StrutsPrepareAndExecuteFilter在里边执行init()方法
一直到Dispatcher dispatcher = init.initDispatcher(config);,初始化dispatcher
在初始化dispatcher的时候加载struts-default.xml和我们配置的struts.xml
下面用GIF图来看看它的执行过程:
细心的朋友可能会发现,我们在struts.xml的package节点下,extends了struts-default....那struts-default究竟是什么东西呢?
我们找到它的源码:
我们发现了一大堆的Bean,interceptor,result-type,interceptor-stack...下边我来讲解一下它们是干嘛用的...
bean指定了struts在运行的时候需要创建的对象类型
在运行struts的时候,可能需要创建一些对象,那么就通过Bean来指定
interceptor是struts定义的拦截器,一共有32个
前边已经说了,Struts为我们实现了一些功能,就是通过拦截器来实现的。
result-type是跳转结果的类型
Action业务方法中的返回值,我们发现几个实用的:redirect【重定向】、dispatcher【转发】、redirectAction【重定向到Action资源】、stream【文件下载的时候用】...跳转结果的类型也在这里定义了
interceptor-stack是拦截器的栈
拦截器有32个,我们可能会使用很多的拦截器,不可能一个一个来调用,于是提供了拦截器栈...其实可以简单看成文件夹和文件之间的关系
default-interceptor-ref是默认执行的拦截器栈
default-class-ref class是默认的执行Action类
还要补充的就是:默认的拦截器栈有18个拦截器....
拦截器和过滤器拦截器和过滤器都是拦截资源的
拦截器只拦截Action请求,是struts的概念...
过滤器拦截web的所有资源,是Servlet的概念...
小总结服务器启动的时候,其实就是加载了web.xml文件,然后调用init()方法去加载struts.xml和struts-default.xml之类的文件.....
注意:此时的拦截器是还没有被调用的。
访问阶段在服务器启动的阶段,仅仅是加载了各种的xml文件...那么当我们访问Action的时候,它的执行流程是怎么的呢?
首先,它会创建我们在struts.xml中配置的Action对象
接着,它会按照默认的顺序执行18个拦截器【也就是调用默认拦截器栈】
最后,它会执行Action的业务方法【也就是execute(),我们在struts.xml文件中配置了什么,就执行什么业务方法】
值得注意的是:每访问Action一次,它就会创建一个对象...它并不是和Servlet一样只有一个对象...因此它是线程安全的.
深入讲解struts.xml这是我们的struts.xml的内容,相信现在对它也不会太陌生了...
package/index.jsp
package其实就是包,那包用来干什么?包就是用来管理Action
通常来说,我们都是一个业务模版对应一个package
namename是包的名字,值得注意的是,包的名称是不能重复的。
extendsextends代表的是当前包继承着哪个包。在struts中,包一定要继承着struts-default
abstract在package中还有abstract这个属性,使用该属性时:表明当前包被其他的包继承...并且,在package下不能有action,否则会出错!
namespace在package中还有namespace这个属性---名称空间....它是作为路径的一部分的,默认是"/"
actoinaction:配置请求路径与Action类的映射关系
namename是请求路径的名字
classclass是处理action类的全名
methodmethod是调用的方法名称
resultresult代表的是Action中业务方法返回的值
namename是action处理返回的值
typetype是跳转的类型
文本值文本值是跳转的路径
细节前边已经说了,一个package应该对应一个业务模块..目的就是把职能细分出来...
struts为了让我们更好地管理xml文件,它还可以这样做:在不同的模块中用不同的xml文件进行描述...
最后在struts.xml文件中将其引入即可..
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68730.html
摘要:前言由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 前言 由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 由于更新比较频繁,因此隔一段时间才会更新目录导航哦~想要获取最新原创的技术文章欢迎关注我的公众号:Java3y Java3y文章目录导航 Java基础 泛型就这么简单 注解就这么简单 Druid数据库连接池...
摘要:要是使用到日历的话,我们想到使用这个日历类上面仅仅是我个人总结的要点,如果有错误的地方还请大家给我指正。 纳税服务系统总结 纳税服务系统是我第一个做得比较大的项目(不同于javaWeb小项目),该项目系统来源于传智Java32期,十天的视频课程(想要视频的同学关注我的公众号就可以直接获取了) 我跟着练习一步一步完成需求,才发觉原来Java是这样用来做网站的,Java有那么多的类库,页面...
摘要:性能会有所降低一点内容,刷新整个页面用户的操作页面会中断整个页面被刷新了就是能够做到局部刷新三对象是中最重要的一个对象。头信息已经接收,响应数据尚未接收。 一、什么是Ajax Ajax(Asynchronous JavaScript and XML) 异步JavaScript和XML Ajax实际上是下面这几种技术的融合: (1)XHTML和CSS的基于标准的表示技术 (2)DOM进...
摘要:甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。这就是的核心思想。统一配置,便于修改。带参数的构造函数创建对象首先,就要提供带参数的构造函数接下来,关键是怎么配置文件了。 前言 前面已经学习了Struts2和Hibernate框架了。接下来学习的是Spring框架...本博文主要是引入Spring框架... Spring介绍 Spring诞生: 创建Spring的...
摘要:前言上一次我们对的应用进行了一次全面的分析,这一次我们来聊聊。 showImg(https://segmentfault.com/img/remote/1460000020077803?w=1280&h=853); 前言 上一次我们对Paging的应用进行了一次全面的分析,这一次我们来聊聊WorkManager。 如果你对Paging还未了解,推荐阅读这篇文章: Paging在Recy...
阅读 1629·2019-08-30 15:54
阅读 2374·2019-08-30 15:52
阅读 2047·2019-08-29 15:33
阅读 3042·2019-08-28 17:56
阅读 3236·2019-08-26 13:54
阅读 1675·2019-08-26 12:16
阅读 2449·2019-08-26 11:51
阅读 1643·2019-08-26 10:26