当前位置: 首页 > news >正文

单位做网站福建建设工程招投标信息网

单位做网站,福建建设工程招投标信息网,黄梅戏网页制作素材,三星手机网上商城介绍在组件化开发的时候#xff0c;组件之间是相互独立的没有依赖关系#xff0c;我们不能在使用显示调用来跳转页面了#xff0c;因为我们组件化的目的之一就是解决模块间的强依赖问题#xff0c;假如现在要从A业务组件跳转到业务B组件#xff0c;并且要携带参数跳转组件之间是相互独立的没有依赖关系我们不能在使用显示调用来跳转页面了因为我们组件化的目的之一就是解决模块间的强依赖问题假如现在要从A业务组件跳转到业务B组件并且要携带参数跳转这时候怎么办呢上学的时候在书上看到了一句很有意义的话任何软件工程遇到的问题都可以通过增加一个中间层来解决我们从这句话出发去思考组件之间是平行结构的它们之间相互没有交集要实现通信只有添加一个中间层将它们连接到一起这就是“路由”的概念由第一篇文章开始的组件化模型下的业务关系图可知路由就是起到一个转发的作用。路由就像一个桥梁一样让平行的河流(组件)之间可以通信和传递数据这样看起来好像他们之间又是强耦合的关系维护起来还是代价还有点大那么还有没有好点的办法呢那就是路由层使用接口和其他module之间建立弱耦合的关系。在组件化开发中实现跨组件跳转的库比较有名气的是阿里的ARouterARouter的功能很丰富这一节我们根据ARouter的源码理解自己实现一个简单版的ARouterARouter的功能太多了如过滤器拦截器等这里只是手写实现一部分核心功能-路由跳转。准备实现定义注解在router_annotation的module中定义注解Route.java/*** Target用于指定被此元注解标注的注解可以标出的程序元素**/Target(ElementType.TYPE)/*** RetentionPolicy.SOURCE 源码阶段 注解信息只会保留在源码中编译器在编译源码的时候会将其直接丢弃* RetentionPolicy.CLASS 编译阶段 javapoet使用 注解信息保留在class文件中VM不会持有其信息* RetentionPolicy.RUNTIME 运行阶段,注解信息保留在class文件中而且VM也会持有此注解信息 反射获得注解信息*/Retention(RetentionPolicy.CLASS)public interface Route {/*** 路由的路径标识一个路由节点*/String path();/*** 将路由节点进行分组可以实现按组动态加载*/String group() default ;}Route注解里有path和group这是仿照ARouter对路由进行分组。因为当项目变得越来越庞大的时候为了便于管理和减小首次加载路由表过于耗时的问题我们对所有的路由进行分组。定义注解处理器生成路由映射文件定义RouteProcessor实现AbstractProcessor类 这里使用google的 AutoService注册处理器使用JavaPoet实现源文件的编写。在router_compiler的build.gradle引入这两个库apply plugin: java-librarydependencies {implementation fileTree(dir: libs, include: [*.jar])annotationProcessor com.google.auto.service:auto-service:1.0-rc4compileOnly com.google.auto.service:auto-service:1.0-rc4implementation com.squareup:javapoet:1.11.1implementation project(:router_annotation)}// java控制台输出中文乱码tasks.withType(JavaCompile) {options.encoding UTF-8}sourceCompatibility 7targetCompatibility 7Processor 一般会重写父类的4个方法init初始化工作我们可以得到一些有用的工具例如 Filer,ElementUtils,TypeUtils.process最重要的方法所有的注解处理都是在此完成getSupportedAnnotationTypes返回我们所要处理的注解的一个集合getSupportedSourceVersion要支持的java版本AutoService(Processor.class)//处理器接受到的参数 代替 {link AbstractProcessor#getSupportedOptions()} 函数SupportedOptions(Constants.ARGUMENTS_NAME)/*** 指定使用的Java版本 替代 {link AbstractProcessor#getSupportedSourceVersion()} 函数* 声明我们注解支持的JDK的版本*/SupportedSourceVersion(SourceVersion.RELEASE_7)/*** 注册给哪些注解的 替代 {link AbstractProcessor#getSupportedAnnotationTypes()} 函数* 声明我们要处理哪一些注解 该方法返回字符串的集合表示该处理器用于处理哪些注解*/SupportedAnnotationTypes(Constants.ANN_TYPE_ROUTE)public class RouteProcessor extends AbstractProcessor {..../*** key是组名value是注解标记的element的元素数据集合* 使用Route的注解按照组名分表存储*/private Map groupMap new HashMap();/*** key:组名 value:实现IRouteGroup接口的className*/private Map rootMap new TreeMap();...}init方法中根据ProcessingEnvironment得到一些工具和参数Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);Messager ms processingEnvironment.getMessager();Log.init(ms);elementUtils processingEnvironment.getElementUtils();filerUtils processingEnvironment.getFiler();typeUtils processingEnvironment.getTypeUtils();Map options processingEnvironment.getOptions();if (!Utils.isEmpty(options)) {moduleName options.get(Constants.ARGUMENTS_NAME);}if (Utils.isBlank(moduleName)) {throw new RuntimeException(Not set Processor Parmaters.);}}扫描所有Route修饰的注解文件根据组名进行分组/*** 处理注解** param set 使用了支持注解的节点集合* param roundEnvironment 上下文* return true 表示后续处理器不会再处理*/Overridepublic boolean process(Set extends TypeElement set, RoundEnvironment roundEnvironment) {if (!Utils.isEmpty(set)) {Set extends Element elementsAnnotateds roundEnvironment.getElementsAnnotatedWith(Route.class);if (!Utils.isEmpty(elementsAnnotateds)) {try {parseRoute(elementsAnnotateds);} catch (Exception e) {Log.i(创建实现类异常---》e);}}}return true;}private void parseRoute(Set extends Element routeElements) throws Exception {// 通过elementUtils获得节点TypeElement activityTypeElement elementUtils.getTypeElement(Constants.ACTIVITY);//mirror 节点自描述TypeMirror activityTypeMirror activityTypeElement.asType();TypeElement IServiceTypeElement elementUtils.getTypeElement(Constants.ISERVICE);TypeMirror IServiceTypeMirror IServiceTypeElement.asType();Log.i(activityTypeMirror activityTypeMirror , IServiceTypeMirror IServiceTypeMirror);RouteMeta routeMeta;for (Element routeElement : routeElements) {Log.i(routeElementrouteElement);Route route routeElement.getAnnotation(Route.class);TypeMirror typeMirror routeElement.asType();if (typeUtils.isSubtype(typeMirror, activityTypeMirror)) {//activity节点routeMeta new RouteMeta(RouteMeta.Type.ACTIVITY, route, routeElement);}/* else if (typeUtils.isSubtype(typeMirror, IServiceTypeMirror)) {//IService节点routeMeta new RouteMeta(RouteMeta.Type.ISERVICE, route, routeElement);} **/else {throw new RuntimeException([Just Support Activity/IService Route] : routeElement);}//分组记录信息 groupMapcategories(routeMeta);}//生成类 需要实现的接口generatedGroup();generatedRoot();}每次检索到一个注解元素routeElement都要去groupMap 分类。private void categories(RouteMeta routeMeta) throws ClassNotFoundException {if (routeVerify(routeMeta)) {//1Log.i(group name routeMeta.getGroup() , path routeMeta.getPath());List routeMetas groupMap.get(routeMeta.getGroup());if (Utils.isEmpty(routeMetas)) {routeMetas new ArrayList();groupMap.put(routeMeta.getGroup(), routeMetas);}//2/* Class destination routeMeta.getDestination();if (destination null) {String className routeMeta.getElement().asType().toString();Log.i(nameclassName);destination Class.forName(className);routeMeta.setDestination(destination);}*/routeMetas.add(routeMeta);} else Log.e(group info error: routeMeta.getPath());}routeMeta合法后开始根据groupName进行分组归类。上边的注释2处我的本意是通过反射给routeMeta.setDestination设置值在后边生成文件的时候直接通过routeMeta.getsetDestination使用。但是这样做的时候会报错 ClassNotFoundException。一时半会也不知道原因晚上在看书的时候突然想起来这是在编译期完成的反射是在运行期使用所以会报ClassNotFoundException。注释1处的是验证routeMeta是否包含group包含group条件是,注解的时候声明group,或者path的路径必须是// 大于等于两个“/”private boolean routeVerify(RouteMeta meta) {String path meta.getPath();String group meta.getGroup();//路由地址必须是/ 开头if (Utils.isBlank(path) || !path.startsWith(/)) {return false;}String[] split path.split(/);if (split.length 3) {return false;}//如果没有分组就以第一个/后的节点为分组if (Utils.isBlank(group)) {String defaultGroup split[1];if (Utils.isBlank(defaultGroup)) {return false;}meta.setGroup(defaultGroup);}return true;}对RouteMeta进行分组整理后在parseRoute方法中根据组名生成IRouteGroup的实现类实现类中接受map参数将扫描的路由文件存储到map中。IRouteGroup接口的定义如下public interface IRouteGroup {/*** 扫描注解 搜集路由信息* param atlas key:路由路径 pathvalue:Route注解修饰的路由信息数据*/void loadInto(Map atlas);}该文件是在route_core的module中声明的。下边是通过Apt实现该接口的方法。/*** 生成IRouteGroup的实现类* throws Exception*/private void generatedGroup() throws Exception {//参数类型 MapParameterizedTypeName atlas ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ClassName.get(RouteMeta.class));//创建参数 Map atlasParameterSpec groupParamSpec ParameterSpec.builder(atlas, atlas).build();//遍历分组每一个分组创建一个类for (Map.Entry entry : groupMap.entrySet()) {//声明方法// Override// public void loadTo(Map atlas)MethodSpec.Builder loadIntoMethodOfGroupBuilder MethodSpec.methodBuilder(Constants.METHOD_LOAD_INTO).addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).returns(void.class).addParameter(groupParamSpec);//方法体 实现接口的抽象方法将分组后的路由信息插入atlas中//key 是pathvalue---RouteMetaString groupName entry.getKey();List routeMetas entry.getValue();for (RouteMeta routeMeta : routeMetas) {//atlas.put(path, RouteMeta.build(class, path, group)// RouteMeta build(Type type, Class destination, String path, String group)loadIntoMethodOfGroupBuilder.addStatement(atlas.put($S, $T.build($T.$L,$T.class, $S, $S)),routeMeta.getPath(),ClassName.get(RouteMeta.class),ClassName.get(RouteMeta.Type.class),routeMeta.getType(),ClassName.get((TypeElement) routeMeta.getElement()),routeMeta.getPath().toLowerCase(),groupName.toLowerCase());}TypeElement iRouteGroupTypeElement elementUtils.getTypeElement(Constants.ROUTE_GROUP);//创建Java文件String groupClassName Constants.NAME_OF_GROUP groupName;Log.i(groupClassNamegroupClassName);JavaFile.builder(Constants.PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(groupClassName).addModifiers(Modifier.PUBLIC).addSuperinterface(ClassName.get(iRouteGroupTypeElement)).addMethod(loadIntoMethodOfGroupBuilder.build()).build()).build().writeTo(filerUtils);Log.i(Generated RouteGroup: Constants.PACKAGE_OF_GENERATE_FILE . groupClassName);rootMap.put(groupName, groupClassName);}}在编写的时候这里需要格外小心出错了很难排查因为这是编译期不能进行debug所以只能借助Log在关键的地方加上日志。另外在编写的时候可以自己先在module2中创建一个实现该接口的类Router$$Group$$test然后对比着进行编码最后记得把Router$$Group$$test注释掉public class Router$$Group$$test implements IRouteGroup {private Map groupMap;Overridepublic void loadInto(Map atlas) {for (String groupName : groupMap.keySet()) {List routeMetas groupMap.get(groupName);for (RouteMeta routeMeta : routeMetas) {//RouteMeta build(Type type, Class destination, String path, String group)Class destination routeMeta.getDestination();if (destination null) {destination ClassName.get((Type) routeMeta.getElement());}//atlas.put(routeMeta.getPath(),RouteMeta.build(routeMeta.getType(),routeMeta.getDestination(),routeMeta.getPath(),routeMeta.getGroup()));}}}}回到parseRoute中下一步是generatedRoot()private void generatedRoot() throws Exception {TypeElement iRouteRootTypeElement elementUtils.getTypeElement(Constants.ROUTE_ROOT);TypeElement iRouteGroupTypeElement elementUtils.getTypeElement(Constants.ROUTE_GROUP);//Map routesParameterizedTypeName routes ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(iRouteGroupTypeElement))));//参数 Map routesParameterSpec rootParameterSpec ParameterSpec.builder(routes, routes).build();//函数 public void loadInto(Map routes routes)MethodSpec.Builder loadIntoMethodBuilder MethodSpec.methodBuilder(Constants.METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).returns(TypeName.VOID).addParameter(rootParameterSpec);//函数体for (String key : rootMap.keySet()) {loadIntoMethodBuilder.addStatement(routes.put($S, $T.class),key,ClassName.get(Constants.PACKAGE_OF_GENERATE_FILE, rootMap.get(key)));}String rootClassName Constants.NAME_OF_ROOT moduleName;JavaFile.builder(Constants.PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(rootClassName).addSuperinterface(ClassName.get(iRouteRootTypeElement)).addModifiers(Modifier.PUBLIC).addMethod(loadIntoMethodBuilder.build()).build()).build().writeTo(filerUtils);Log.i(Generated RouteRoot: Constants.PACKAGE_OF_GENERATE_FILE . rootClassName);}上边的moduleName是在init方法中初始化的Map options processingEnvironment.getOptions();if (!Utils.isEmpty(options)) {//Constants.ARGUMENTS_NAME moduleNamemoduleName options.get(Constants.ARGUMENTS_NAME);}processingEnvironment能够接收的参数需要在getSupportedOptions方法中定义也可以用注解的方式定义这里在定义RouteProcessor的时候使用SupportedOptions(Constants.ARGUMENTS_NAME)我们需要在依赖route_core库的 组件的build.gradle的添加这个参数javaCompileOptions{annotationProcessorOptions {arguments [moduleName : project.getName()]}}generatedRoot方法根据rootMap来创建一个IRouteRoot接口的实现类这个接口主要是记录每个组名对应Group类。同样在编码前也是先写一个实现类Router$$Root$$impl然后对比着进行编码。public class Router$$Root$$impl implements IRouteRoot {private Map map;Overridepublic void loadInto(Map routes) {for (String groupName : map.keySet()) {routes.put(groupName, map.get(groupName));}}}撰写完成后记得把这个类注释掉。注解和注解处理器我们都写完了我们写一个demo看看效果这里举一个setting的demo目录结构如下setting_module.png编译后生成的文件如下setting_build.pngpublic class Router$$Group$$business implements IRouteGroup {Overridepublic void loadInto(Map atlas) {atlas.put(/business/news, RouteMeta.build(RouteMeta.Type.ACTIVITY,BusinessNewsActivity.class, /business/news, business));atlas.put(/business/other, RouteMeta.build(RouteMeta.Type.ACTIVITY,BusinessOtherActivity.class, /business/other, business));}}public class Router$$Group$$personal implements IRouteGroup {Overridepublic void loadInto(Map atlas) {atlas.put(/personal/wallet, RouteMeta.build(RouteMeta.Type.ACTIVITY,MyWalletActivity.class, /personal/wallet, personal));atlas.put(/personal/userInfo, RouteMeta.build(RouteMeta.Type.ACTIVITY,UserInfoActivity.class, /personal/userinfo, personal));}}public class Router$$Root$$setting implements IRouteRoot {Overridepublic void loadInto(Map routes) {routes.put(business, Router$$Group$$business.class);routes.put(personal, Router$$Group$$personal.class);}}这里看到生成的类分别实现了IRouteRoot和IRouteGroup接口并且实现了loadInto()方法而loadInto方法通过传入一个特定类型的map就能把分组信息放入map里只要分组信息存入到特定的map里后我们就可以随意的从map里取路由地址对应的Activity.class做跳转使用。路由框架的初始化我们要实现一个路由框架就要考虑在合适的时机拿到这些映射文件中的信息以供上层业务做跳转使用。那么在什么时机去拿到这些映射文件中的信息呢首先我们需要在上层业务做路由跳转之前把这些路由映射关系拿到手但我们不能事先预知上层业务会在什么时候做跳转那么拿到这些路由关系最好的时机就是应用程序初始化的时候。另外如何去拿这些路由信息呢在上面已经介绍过IRouteRoot接口的所有实现文件里保存着各个module的分组文件(分组文件就是实现了IRouteGroup接口的类文件)那么只要拿到所有实现IRouteGroup接口的类的集合就可以根据path实现页面跳转了。public class MyApplication extends Application {Overridepublic void onCreate() {super.onCreate();Router.init(this);}}route_core module中的Router.javapublic static void init(Application application){context application;try {loadInfo();} catch (Exception e) {Log.e(TAG, 初始化失败!, e);e.printStackTrace();}}private static void loadInfo() throws PackageManager.NameNotFoundException, InterruptedException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//1.扫描apk中的所有dex文件找出使用注解生成的类Set routeMap ClassUtils.getFileNameByPackageName(context, Router.ROUTE_ROOT_PACKAGE);if (routeMap null) {return;}for (String className : routeMap) {if (className.startsWith(ROUTE_ROOT_PACKAGE.SDK_NAMESEPARATORSUFFIX_ROOT)) {// root中注册的分组信息将分组信息加入仓库中((IRouteRoot)(Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);}}}我们首先通过ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)得到apt生成的所有实现IRouteRoot接口的类文件集合通过上面的讲解我们知道拿到这些类文件便可以得到所有的路由地址和Activity映射关系。路由跳转实现经过前面的介绍我们已经能够在app启动的时候获得所有的路由信息接下来就可以实现跳转了。在app module的Mainctiviy中如下使用public void personalInfoJump(View view) {Router.getInstance().build(/personal/userInfo).navigation();}public void businessNewsJump(View view) {Router.getInstance().build(/business/news).navigation();}build的时候根据path路径得到一个postCard对象然后调用Postcard的navigation()方法完成跳转。Postcard的内容如下public class Postcard extends RouteMeta {private Bundle extras;private int flag -1;private Bundle optionCompat;private int enterAnim, exitAnim;...public Object navigation() {return navigation(null, null);}public Object navigation(Context context) {return navigation(context, null);}public Object navigation(Context context, NavigationCallback callback) {return Router.getInstance().navigation(context, this, -1, callback);}...}下面看一下Router 的navigation核心功能public Object navigation(Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback){prepareCard(postcard);if (callback ! null) {callback.onFound(postcard);}switch (postcard.getType()) {case ACTIVITY:final Context currentContext contextnull?Router.context:context;final Intent intent new Intent(currentContext, postcard.getDestination());intent.putExtras(postcard.getExtras());if (postcard.getFlag()!-1) {intent.setFlags(postcard.getFlag());}else if (!(currentContext instanceof Activity)){intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}new Handler(Looper.getMainLooper()).post(new Runnable() {Overridepublic void run() {if (requestCode0) {ActivityCompat.startActivityForResult((Activity) currentContext, intent,requestCode, postcard.getOptionCompat());}else {ActivityCompat.startActivity(currentContext, intent, postcard.getOptionCompat());}if ((postcard.getEnterAnim()!0||postcard.getExitAnim()!0) currentContext instanceof Activity) {((Activity)currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());}if (callback ! null) {callback.onArrival(postcard);}}});break;}return null;}prepareCard的实现如下private void prepareCard(Postcard card){RouteMeta routeMeta Warehouse.routes.get(card.getPath());if (routeMeta null) {Class extends IRouteGroup groupMeta Warehouse.groupsIndex.get(card.getGroup());if (groupMeta null) {throw new NoRouteFoundException(没找到对应路由: card.getGroup() card.getPath());}IRouteGroup iRouteGroup;try {iRouteGroup groupMeta.getConstructor().newInstance();} catch (Exception e) {throw new RuntimeException(路由分组映射表记录失败., e);}iRouteGroup.loadInto(Warehouse.routes);Warehouse.groupsIndex.remove(card.getGroup());prepareCard(card);}else {card.setDestination(routeMeta.getDestination());card.setType(routeMeta.getType());}}这段代码Warehouse.routes.get(card.getPath())通过path拿到对应的RouteMeta这个RouteMeta里面保存了activityClass等信息。继续往下看如果判断拿到的RouteMeta是空说明这个路由地址还没有加载到map里面(初始化时为了节省性能只会加载所有的分组信息而每个分组下的路由映射关系会使用懒加载在首次用到的时候去加载)只有在第一次用到当前路由地址的时候会去Warehouse.routes里面拿routeMeta如果拿到的是空会根据当前路由地址的group拿到对应的分组通过反射创建实例然后调用实例的loadInto方法把它里面保存的映射信息添加到Warehouse.routes里面并且再次调用prepareCard(card)这时再通过Warehouse.routes.get(card.getPath())就可以顺利拿到RouteMeta了。进入else{}里面调用了card.setDestination(routeMeta.getDestination())这个setDestination就是将RouteMeta里面保存的activityClass放入Postcard里面。prepareCard()方法调用完成后我们的postcard里面就保存了activityClass然后switch (postcard.getType()){}会判断postcard的type为ACTIVITY然后通过ActivityCompat.startActivity启动Activity。到这里路由跳转的实现已经讲解完毕了。结束语关于组件之间传递数据可以参考ARoute添加Extra 注解ExtraProcessor处理器以及IExtra接口。通过手写ARoute我们会学到注解注解处理器JavaPoet和组件化思路编写框架的思路等等。
http://www.sadfv.cn/news/5877/

相关文章:

  • 网站开发和安卓开发小程序商城首页设计
  • 网站是否被百度收录竞价推广哪家公司好
  • qq小程序开发导购网站的seo怎么做
  • 如何使用模板做网站鞍山钟点工招聘信息
  • 建站系统多少钱wordpress自动发布网站
  • 湖南网站制作哪家好顺德网站建设域名
  • 全网自媒体平台网站推广和优化教程
  • 网站综合营销方案设计自己可以做开奖网站吗
  • 广州做网站的哪家好源码库官网
  • 帮别人做钓鱼网站犯法吗天津优化网络公司的建议
  • 如何根据网址攻击网站阿里 网站备案核验单
  • 青田建设局网站上海网络维护服务公司
  • 电子商务网站策划书布局设计千万不要嫁给程序员
  • 保洁公司 网站模板威海网站建设吧
  • discuz网站同步秦皇岛软件开发培训
  • 手机百度屏蔽我网站关键词wordpress问题
  • 百度免费网站申请医疗器械公司排名
  • 网站文件权限设置遵义信息网
  • 合肥制作网站租服务器发布网站
  • 数字域名做网站金融网站模版
  • 农业信息网站建设业务外包的典型案例
  • 萍乡网站制作公司wordpress陶哲轩
  • 自己做信息网站十堰吧
  • 青岛当地的做公司网站的用数据库添加文章wordpress
  • 班级网站建设活动方案公众号申请网站
  • 新网站如何被网站收录做网站商业计划书范文
  • 网站tag作用做网站的入什么科目
  • 青海省高等级公路建设管理局网站南宁市网站开发
  • 申请免费建站0基础学设计该从何开始
  • 网盘搜索网站 怎么做深圳市注册公司需要什么资料