首页 文章 精选 留言 我的

精选列表

搜索[PaaS框架],共10000篇文章
优秀的个人博客,低调大师

MVC框架,webx 流程解读

写在前面的话 写这篇文章的目的,主要还是站在一个新人的角度做一些沉淀,同样也为了方便后面的人快速熟悉集团web开发常用技术。这篇文章,将分析一个请求发到服务端,经tomcat容器、webx映射直到最后调用controller入口的过程,对java web基础知识结合webx做一个整体回顾。同时,也将简单分析集团web的基本分层方式,以及VO、DTO、DO等数据实例在各层所起的作用。 Web容器 理解web容器,也是理解我们程序的运行平台,同时也是了解servlet处理流程的基础所在。tomcat的主要结构如下图: Connector Connector是tomcat的连接器。Tomcat在监听80端口的时候,一个HTTP请求访问过来,实际上是通过在80端口用socket来输入HTTP报文。Connector通过socket读取报文文本并进行解析,然后将报文内容封装为request实体,并将响应结果利用response进行封装,新起一个线程,并交给container容器进行处理。 Container container是所有子容器的父接口。Engine主要负责对请求进行分发,而Host就是tomcat虚拟host功能的实体,Context就是我们一个应用服务的完整环境,每次一个请求对应的一个新的servlet,都是封装在Context中的。 Tomcat在处理Connector传来的request和response时,就是通过责任链模式,一层一层的去调用Engine、Host、Context并封装出一个servletWrapper来进行doService操作。 Servlet处理流程及WebX的调用 上面简单介绍了一下Tomcat的容器结构,接下来具体分析处理一个请求的过程。 tomcat容器执行过程 请求到达后,会新起一个线程并由Container进行处理。 Container中的Engine、Host、Context、Wrapper都继承了ValveBase抽象类并实现了其invoke方法。这也类似于webx的pipeline,对请求进行流水线处理。 首先是StandardEngineValve的invoke。engine通过对请求host域名的解析,映射到合适的host容器中去,然后调用host的invoke方法。 StandardEngine engine = (StandardEngine)this.getContainer(); Host host = (Host)engine.map(request, true); if(host == null) { ((HttpServletResponse)response.getResponse()).sendError(400, sm.getString("standardEngine.noHost", request.getRequest().getServerName())); } else { host.invoke(request, response); } 在host的invoke中,实际是调用了流水线的invoke方法: public void invoke(Request request, Response response) throws IOException, ServletException { this.pipeline.invoke(request, response); } 最终又调用到standardPipeline中的invokeNext: public void invokeNext(Request request, Response response) throws IOException, ServletException { Integer current = (Integer)this.state.get(); int subscript = current.intValue(); this.state.set(new Integer(subscript + 1)); if(subscript < this.valves.length) { this.valves[subscript].invoke(request, response, this); } else { if(subscript != this.valves.length || this.basic == null) { throw new ServletException(sm.getString("standardPipeline.noValve")); } this.basic.invoke(request, response, this); } } 可以看到流水线的设计模式是将engine、host、context等容器的实例放进一个数组中,并在运行时依次执行。 然后就到了StandardHostValve中,同样实行invoke方法: StandardHost host = (StandardHost)this.getContainer(); Context context = (Context)host.map(request, true); 逻辑也类似,就是找到对应的context,也就是我们的应用并调用invoke方法。 context的invoke主要做了两件事: 1、获取session并绑定 2、找到该url对应的wrapper,并调用wrapper的invoke方法。 这里先略过session的获取绑定过程,直接来到wrapper的调用中去。在servletWrapper中,我们可以看到几个熟悉的对象: StandardWrapper wrapper = (StandardWrapper)this.getContainer(); ServletRequest sreq = request.getRequest(); ServletResponse sres = response.getResponse(); Servlet servlet = null; HttpServletRequest hreq = null; servlet会通过wrapper进行分配: servlet = wrapper.allocate(); 从allocate方法中,我们可以看到servlet实际是通过类加载器记载的,并且当servlet加载完毕后,会调用初始化方法init。 classClass = classLoader.loadClass(actualClass); ... servlet = (Servlet)classClass.newInstance(); ... servlet.init(this.facade); 有了servlet以后,就开始出现另一个重要角色:ApplicationFilterChain。FilterChain会传入request和response并对servlet进行过滤: filterChain.doFilter(sreq, sres); 在doFilter中,会调用到internalDoFilter 方法。该方法对filters进行遍历调用: this.iterator = this.filters.iterator(); ... filter.doFilter(request, response, this); 可以看到,FilterChain 采用链式模式,通过多次调用chain.doFilter(request, response)方法会多次调用internalDoFilter方法,从而通过Iterator调用所有注册的Filter。 Webx执行流程 以一个简单的接口为例子,来看一看webX最终是如何调用到我们的execute方法的。 public class DemoScreen extends BmsBaseModule { public void execute(@Param('param') String param) { ... } } 首先,继续上一部分的Filter开始看。Filter是servlet的标准过滤器,而上部分最后提到的filters则封装了web.xml文件中关于Filter的配置: <filter> <filter-name>webx</filter-name> <filter-class>com.alibaba.citrus.webx.servlet.WebxFrameworkFilter</filter-class> <init-param> <param-name>excludes</param-name> <param-value>*/checkpreload.htm<!-- 需要被“排除”的URL路径,以逗号分隔,如/static, *.jpg。适合于映射静态页面、图片。 --></param-value> </init-param> <init-param> <param-name>passthru</param-name> <param-value><!-- 需要被“略过”的URL路径,以逗号分隔,如/myservlet, *.jsp。适用于映射servlet、filter。 对于passthru请求,webx的request-contexts服务、错误处理、开发模式等服务仍然可用。 --></param-value> </init-param> </filter> 上面是一个典型的webx配置,可以看到,设置的过滤器WebXFrameworkFilter会被ApplicationFilterChain执行。来看其中的doFilter方法: if (isExcluded(path)) { log.debug("Excluded request: {}", path); chain.doFilter(request, response); return; } 校验我们设置的excluded参数,如果需要排除,那么通过doFilter的internalDoFilter来链式调用下一个过滤器。 关键语句在此: getWebxComponents().getWebxRootController().service(request, response, chain); 调用webx RootController的service方法。service中首先对HttpServletRequest 和 HttpServletResponse 实例封装进RequestContext中。后续会调用 WebxRootControllerImpl 的 handleRequest 方法: 然后,会根据路径找到WebxComponent: WebxComponent component = getComponents().findMatchedComponent(path); 在findMatchedComponent中会依据url来进行匹配: for (WebxComponent component : this) { if (component == defaultComponent) { continue; } String componentPath = component.getComponentPath(); if (!path.startsWith(componentPath)) { continue; } // path刚好等于componentPath,或者path以componentPath/为前缀 if (path.length() == componentPath.length() || path.charAt(componentPath.length()) == '/') { matched = component; break; } } webx在Spring启动的时候,通过对文件目录的扫描,装配进符合规则的组件Component,比如xxx.module.screen下的类,来和url做匹配。找到对应的component后: served = component.getWebxController().service(requestContext); 然后就到了webx的pipeline处理流程: public boolean service(RequestContext requestContext) throws Exception { PipelineInvocationHandle handle = pipeline.newInvocation(); handle.invoke(); // 假如pipeline被中断,则视作请求未被处理。filter将转入chain中继续处理请求。 return !handle.isBroken(); } pipeline 同样是链式模式,会依次调用注册的valve。常用的valve可能包括url检查、登录检查、csrfToken校验等,同样pipeline中的一些条件语句比如Choose、Loop等同样也是一个valve,调用其invoke方法其实也是生成了一个新的pipeline并执行invoke。比如这样一个when-otherwise语句中就会有如下代码: if (!satisfied && otherwiseBlock != null) { otherwiseBlock.newInvocation(pipelineContext).invoke(); } 此外,比较重要的valve就是负责处理表单的PerformActionVavle、负责执行screen的PerformScreenValve。 因为screen在应用中经常是一个接口的入口,并最终调用到业务逻辑部分的代码。所以这里我们分析一下PerformScreenValve这个阀门是如何调用到我们的业务逻辑代码的。screenValve的invoke最终调用了performScreenModule函数。显示依据规则找到了我们的module,然后执行了execute方法: Module module = finder.getScreenModule(); ... module.execute(); 然后调用了DataBindingAdapter的execute方法: private final MethodInvoker executeMethod; ... executeMethod.invoke(moduleObject, log); 在MethodInvoker这个类里面,我们可以看到装配execute方法参数的过程。 首先,在扫描类中注解的时候,webX会为每一个execute方法中有@Param注解的参数生成一个DataResolver。MethodInvoker中会有如下代码获取参数,最终会调用FastMethod的invoke方法。 Object[] args = new Object[resolvers.length]; for (int i = 0; i < args.length; i++) { Object value; value = resolvers[i].resolve(); } ... fastMethod.invoke(moduleObject, args); 最后就是反射调用的过程了,我们的接口object即为moduleObject,而args即为带有@Param注解的参数。最终的整体调用流程如下图: 项目分层 关于web层、service层、manager层与DAO层 执行到我们发布的接口后,再来简单的说一下web项目常见分层手段。 在我们的项目中,常见的是依次调用web层,service层,manager,dao层。数据实体在VO、DTO、DO之间流转。 因为在执行某个业务的时候,可能需要查询多个表的数据并进行拼接。比如现在需要这样一条数据D,由A,B,C组成。如果仅仅提供一个方法查询A、B、C并拼接为D返回,那么下次如果又有一个需求为E(A+B)的数据,无疑又要重新写查询A和B的代码并拼接。 这样,我们把代码分层,使各部分职责独立。其中service负责拼接,manager负责查询,这样不同的service可以对manager进行复用。同时,由于DTO和DO不是完全对应,查询的时候往往要把DTO转换成DO查询条件去调用DAO,而DAO返回的结果DO又和web层所需要的数据格式不一致,所以常常要转换成DTO。 manager的作用,就是处理DTO到DO,然后DAO生成DO再到DTO的过程。如下就是一个manager: BmsPurchaseOrderQuery purchaseOrderQuery = new BmsPurchaseOrderQuery(); purchaseOrderQuery.setPurchaseCode(purchaseCode); List<BmsPurchaseOrderDO> orderDOList = bmsPurchaseOrderDAO.select(purchaseOrderQuery); Assert.notEmpty(orderDOList); return orderDOList.get(0); 假设查询了订单的数据。然后再来一个manger查询订单明细的数据: @Override public List<BmsPurchaseOrderTransOutInfoDO> getTranOutInfoListByCode(String subscribeCode) { //查询子单明细 BmsPurchaseOrderTransOutInfoDO transOutInfoQuery = new BmsPurchaseOrderTransOutInfoDO(); transOutInfoQuery.setSubscribeCode(subscribeCode); List<BmsPurchaseOrderTransOutInfoDO> transOutInfoDOList = bmsPurchaseOrderTransOutInfoDAO.select(transOutInfoQuery); Assert.notNull(transOutInfoDOList); return transOutInfoDOList; } 然后,在service对订单和明细进行拼接。 总而言之,分层如下: web层(也就是webX中的screen):处理表单,调用服务,返回结果 service层:业务逻辑,数据拼接 manager层:粒度比较细的查询 dao层:sql语句映射 分层可能会导致代码量增大,但是可以提高程序的可复用性,方便程序维护。 总结 至此为止,关于webx服务的请求处理的来龙去脉简单的分析了一遍。了解大致处理流程还是比较有必要的,这样当我们的程序出现异常,可以及时的定位问题。当打断点时,面对层层的调用栈也不会显得不知所措。当然,在这里也是对自己经常接触到的技术做一个简单的沉淀。

优秀的个人博客,低调大师

volley框架使用小Demo

MainActivity public class MainActivity extends AppCompatActivity { private static final String URL = "https://www.baidu.com/"; private RequestQueue mQueue; // volley的请求队列 @BindView(R.id.volley_get) Button btn; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mQueue = Volley.newRequestQueue(getApplicationContext()); ButterKnife.bind(this); } @OnClick({R.id.volley_get}) public void onClick(View v) { get(); } /** * 创建一个请求,这里我们做一个最简单的通过GET方式请求网页源码的操作。请求成功后打印结果。 */ private void get() { StringRequest request = new StringRequest(Request.Method.GET, URL, new Response.Listener<String>() { @Override public void onResponse(String arg0) { //Toast.makeText(getApplicationContext(), arg0, Toast.LENGTH_LONG).show(); textView = (TextView) findViewById(R.id.text); textView.setText(arg0.toString()); //Log.d("onResponse", arg0); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError arg0) { //Toast.makeText(getApplicationContext(), arg0.toString(), Toast.LENGTH_LONG).show(); textView = (TextView) findViewById(R.id.text); textView.setText(arg0.toString()); } }); mQueue.add(request); } } activity_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.an.volleytest.MainActivity"> <Button android:id="@+id/volley_get" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="get请求" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:id="@+id/text" android:layout_marginTop="25dp" android:layout_below="@+id/volley_get" android:layout_centerHorizontal="true" /> </RelativeLayout> 捕获.PNG 点击按钮 hello world 替换为请求网址的html文件

优秀的个人博客,低调大师

区块链开发知识框架

区块链学习更多的是去理解这种去中心化的思想和去中心化的价值所在,语言倒是其次的东西。 不过由于以太坊和Hyperledger Fabric 对go语言支持最好,所以go语言肯定是要学习的。 下面把我另外一个问题的答案copy过来,希望对你有所帮助。 背景介绍:本人从2015年开始研究区块链技术,也算是对区块链有一定的了解吧。 申明:预估看到本问题的知乎er应该都是想要入门区块链,那么本回答也主要是进行一个入门级别的介绍。只是入门级别介绍,不适合区块链从业人员提升学习。 ----------------------------------- 回答的目录如下: 一、区块链是什么? 二、从业务角度学习区块链技术? 三、从技术角度学习区块链技术? 四、学习资料推荐与共享? ------------------------------------- 一、区块链是什

优秀的个人博客,低调大师

Android Router路由框架使用

Router项目地址 1. 添加依赖 android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = ["moduleName": project.name] } } } } dependencies { implementation 'com.chenenyu.router:router:1.4.0-beta1' annotationProcessor 'com.chenenyu.router:compiler:1.4.0-beta1' } 2. Application中注册 public class App extends Application{ @Override public void onCreate() { super.onCreate(); Router.initialize(new Configuration.Builder() // 调试模式,开启后会打印log .setDebuggable(BuildConfig.DEBUG) // 注册模块名,每个Router使用的Module都要在这里注册 .registerModules("module1", "module2", "app") .build()); } } 3. 跳转URI /** * uri 可填写网页链接 * 1. http://www.baidu.com/ * 2. 动态添加路由中map集合中的键 */ Router.build(uri).callback(new RouteCallback() { @Override public void callback(RouteResult state, Uri uri, String message) { if (state == RouteResult.SUCCEED) { Toast.makeText(MainActivity.this, "succeed: " + uri.toString(), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "error: " + uri + ", " + message, Toast.LENGTH_SHORT).show(); } } }).go(this); 4. 动态添加路由 // 添加动态路由 Router.handleRouteTable(new RouteTable() { @Override public void handle(Map<String, Class<?>> map) { map.put("dynamic", DynamicActivity.class); } }); 5. 表过滤器与目标过滤器 Router.handleInterceptorTable(new InterceptorTable() { @Override public void handle(Map<String, Class<? extends RouteInterceptor>> map) { Log.e(TAG, "handle: " + map.toString()); } }); Router.handleTargetInterceptors(new TargetInterceptors() { @Override public void handle(Map<Class<?>, String[]> map) { Log.e(TAG, "handle: " + map.toString()); } }); 6. 跳转路由 Router.build("dynamic").go(this); 7. 注解注册 // 注解注册 @Route("result") public class ForResultActivity extends AppCompatActivity {} // 使用Bundle传递数据 Router.build("result").requestCode(0).with("extra", "Bundle From Module1Activity").go(this); 8. 自定义过滤器 // 1. 自定义过滤器 @Interceptor("SampleInterceptor") public class SampleInterceptor implements RouteInterceptor { @Override public boolean intercept(Context context, RouteRequest routeRequest) { Toast.makeText(context, String.format("Intercepted: {uri: %s, interceptor: %s}", routeRequest.getUri().toString(), SampleInterceptor.class.getName()), Toast.LENGTH_SHORT).show(); return true; } } // 2. 使用 @Route(value = "intercepted", interceptors = "SampleInterceptor") public class InterceptedActivity extends AppCompatActivity {} // 3. 添加过滤器 Router.build("test").addInterceptors("SampleInterceptor").go(this); // 4. 跳过过滤器Router.build("intercepted").skipInterceptors("SampleInterceptor").go(this); 9. 协议过滤 // 1. AndroidManifest.xml文件配置 <activity android:name=".SchemeFilterActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="filter" android:scheme="router" /> </intent-filter> </activity> // 2. 协议过滤 public class SchemeFilterActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scheme_filter); Uri uri = getIntent().getData(); if (null != uri) { Toast.makeText(this, "uri: " + uri.toString(), Toast.LENGTH_SHORT).show(); if (!uri.toString().startsWith("router://filter")) { Router.build(uri).go(this); } finish(); } } } // 3. 使用 Router.build("router://filter").go(this);

优秀的个人博客,低调大师

java安全框架shiro(一)

第一个简单的案例 ,通过读取.ini文件的方式模拟登陆, 1.通过Factory工厂的getInstance()方法来获取SecurityManager的实例,实例化Factory需要一个ini文件的路径作为参数; 2.将上面获取的实例set到 SecurityUtils这个类里,SecurityUtils类有一个get方法来获取Subject类 3.Subject这个类就是用来登陆和退出的,但是需要一个AuthenticationToken实例作为参数 上面是步骤,下面就来一步一步的介绍,最后会在最下面给出代码 第二步我就不说了,方法是静态的,直接类名调用就好了; 现在来到第三步,现在应该是得到subjiect的实例了,需要登录,但是需要一个token实例 现在token也有了,然后我们进行登录; 这里给出一个.ini文件的基本示例,把这个文件所在的路径填入第一步实例化工厂时的参数里即可 最后给出简单案例的代码 package com; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * 测试shiro第一个案例 * */ public class App { public static void main( String[] args ) { { /*实例化工厂*/ Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); /*获取实例*/ SecurityManager instance = factory.getInstance(); /*设置参数*/ SecurityUtils.setSecurityManager(instance); /*获取登录实例*/ Subject subject = SecurityUtils.getSubject(); /*实例化token,传入登录的用户和密码*/ AuthenticationToken token = new UsernamePasswordToken("admin1", "admin"); try { /*进行登录*/ subject.login(token); /*退出登录*/ subject.logout(); }catch (Exception e){ System.out.println("登录错误!!"); } } } } maven中添加的依赖 <dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> </dependencies>

优秀的个人博客,低调大师

Android--项目框架介绍

公司最近要开发一款Android平台下的应用,通讯软件,所以年后上班一直在研究Android方面的内容。 使用Eclipse开发的Android,对于一个新建的Android项目,自动生成的项目结构如下图: 自动生成的目录介绍: src目录:主要是存放编写的Java代码。 assets目录:资源目录。 res目录:存放图片、布局文件、字符串、菜单等文件。 bin目录:输出文件夹,如生成的APK文件。 project.properties:工程属性文件配置。 gen目录:系统自动生成的代码目录,存放R.java. 这个R.java文件是系统自动生成的文件,非常的重要。这个R.java默认有attr、drawable、layout、string等4个静态内部类,每个类对应一种资源。例如在工程中添加一幅图片,那么工程就会在此类的drawable内部类中添加一条数据,如果删除了此图片,工程则会自动删除此条数据。 在res资源文件夹下包含有以下的文件: Drawable存放工程图片的信息,默认png格式的图片。 Layout存放工程的布局文件以.xml结束。 Values文件夹下存放一个很重要的string.xml,此文件夹存放的是自定义的字符串和数值。 除了这个文件之外,还可以定义arrays.xml(用来定义数组)、color.xml(用来定义颜色和颜色字符串数值)、dimens.xml(用来定义尺寸数值)、styles.xml(用来定义样式)。这些文件只是为了区分资源的类型,一种开发规范,便于开发人员识别修改,其实可以全部定义在string.xml文件中,均可以自动生成到R.java,便于使用。 资源文件分别的格式: 数组: <string-array name="city"> <item >北京</item> <item >上海</item> </string-array> 颜色,使用二进制的RGB值表示: <color name="bgcolor">#FFCCFF</color> 尺寸: <dimen name="width">20dp</dimen> 样式: <sytle name="customText"> <item name="android:textSize">20sp</item> </sytle> 在安卓的activity中,均有一个全局的方法,getResource()来获取资源,具体资源的获取方法如下: 文件 取值方式 String.xml getResource().getString(resourceId)、getResource().getText(resourceId) Arrays.xml getResource().getStringArray(resourceId) colors.xml getResource().getDarwable(resourceId)、getResource().getColor(resourceId) Dimens.xml getResource().getDimension(resourceId) Styles.xml 不需要取值 AndroidManifest.xml文件介绍 AndroidManifest.xml是每个android程序中必须的文件。它位于整个项目的根目录,描述了package中暴露的组件(activities、services等等),他们各自的实现类,各种能被处理的数据和启动位置。能声明程序中的Activity、ContentProviders、ServersIntent Receivers、permissions和instrumentation(安全控制和测试)。 示例: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="bgxt.demo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".SimpleActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 其中manifest节点中的一些属性,介绍: Packgae:包,表示整个java应用程序的主要包名,而且是一个默认的程序名称。 Android:versionCode:表示该工程说生产的apk的版本号,1开始,逐渐递增。 Android:versionName:表示版本的一个名称。类似于:1.0、2.0. Android:installLoacation:自动寻找安装的地方,ROM或者SDCard,默认属性为自动。可选属性有auto:系统默认、internalOnly:仅仅只能安装在ROM上、preferExternal:会直接安装在sdcard上。 Application节点的一些属性介绍: Android:icon:表示应用程序的图标,Logo图片。 Android:label:表示应用的工程文字说明。 Activity节点的属性介绍: Android:name:表示整个应用程序的主程序的名称。 Intent-filter :意图过滤器,用来过滤用户的一些动作和操作。 本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/archive/2013/03/07/2949041.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册