
暂无个人介绍
推广:阿里云优惠券领取入口,请点击 快应用中调用分享,一次集成,享受多个平台参考官方资料:https://github.com/quickappcn/sample/blob/master/src/interface/system/share/index.ux <script> import share from '@system.share' import media from '@system.media' import file from '@system.file' import prompt from '@system.prompt' export default { private: { componentName: 'share', }, onInit() { this.$page.setTitleBar({text: 'Share'}) }, shareText () { share.share({ type: 'text/html', data: '分享内容传到这里', success: function () { console.info('share success') }, fail: function (erromsg, errocode) { prompt.showToast({ message: `${errocode}: ${erromsg}` }) } }) }, </script> <style>
推广:阿里云优惠券领取入口,请点击 快应用在调试时需要输出信息查看中间结果 类似android的toast 使用如下: import prompt from '@system.prompt' //导入包 export default { data: { key: '' }, onInit () { prompt.showToast({ message: 'hello' //输出结果 }) } }
错误提示:gradle-5.1.1-all.zip 下载失败,导致无法编译 1.复制连接到迅雷中进行下载:https://services.gradle.org/distributions/gradle-5.1.1-all.zip2.下载完成后。打开目录 /Users/mac用户名/.gradle/wrapper/dists/gradle-5.1.1-all 3.删除里面的临时文件,并把gradle-5.1.1-all.zip 拖入此目录。4.重启android studio 自动解压5.重启后提示版本不对6.打开sdk manager 选择28 点击确定(菜单-->Tools-->sdk manager) 先配置下ide的下载镜像,参考:https://www.cnblogs.com/pingxin/p/p00078.html我先的是:南阳理工学院镜像服务器地址:mirror.nyist.edu.cn 端口:80配置完镜像地址,再进入sdk管理界面选择对应要安装sdk. 开始安装sdk28经过漫长下载,重启android studio3.4 总结:安装完新版as3.4以后,要重新安装下新版NDK(有条件直接找下载地址通过迅雷下载),并绑定。ndk谷歌下载页面:https://developer.android.com/ndk/downloads/index.html?hl=zh-cn 设置代理:项目中gradle.properties中设置systemProp.http.proxyHost=127.0.0.1systemProp.http.proxyPort=1080
当fetch访问服务器返回数据触发success方法时,发现在success方法中无法使用this下的外部元素与涵数解决方法:const that = this const that = this //传递this到内部使用that代替 fetch.fetch({ url: urls, data: { id: 'LocalNews', ajax: 'json' }, responseType: 'json',// 支持返回类型是text、json、file和arraybuffer,默认返回字符串文本。 success: function (ret) { memList = memList.concat(ret.data.data) const list = memList.splice(0, that.size) that.productList = that.productList.concat(list)//在内部使用that来访问外部内容 } }
第一步增加点击事件 <div class="demo-page"> <input class="btn" type="button" value="跳转到详情页" onclick="routeDetail" /> </div> </template> 第二步实现点击事件 import router from '@system.router' export default { routeDetail () { router.push ({ uri: '/DemoDetail' //跳转到另一个页面在manifest.json中注册地址 }) } } </script>
在开发快应用布局时,要兼容各种大小屏幕的div的纵向排列,传统h5过度过来的同学会用到float:left但是在快应用中无法使这类写法生效 这时需要使用以上方向代替:display: flex; flex-direction: column; padding:20px; 此方法请写在需要纵向排列元素的父div中。 例图说明: 具体参考其它同学详细说明:发现问题:https://www.cnblogs.com/u-1596086/p/11322925.html分析问题并解决:http://www.yidianzixun.com/0KL4NPwf 关于flex布局间距与靠右布局,部分情况可以通过嵌套完成:flex各种情况:https://www.jianshu.com/p/8e6bed181333参考h5的情况:https://blog.csdn.net/hero82748274/article/details/81039651
错误提示:在开发快应用时有时会提示 websocket连接断开状态显示:手机与电脑ide连接不上,提示不在同一局域网内尝试:重新连接、检查网络、重启ide都没有效果解决:这时检查代码中是否存在代码错误导致编辑没通过,例如manifest.json中多一个逗号。修复语法后,重启ide后,ide与手机连调恢复。
快应用顶部导航栏隐藏方法,找到manifest.json-->"titleBar":false如下图:
一、问题: 重新安装MacOS 系统后,打开as(android studio 以下称as) 发现提示Cannot Run Git,如下图: 二、安装Git 点击Download进下载Git页面:https://git-scm.com/download 点击下载并安装。 打开终端:输入git命令,检测git是否安装成功。正常输出: 三、配置Git 点击Confiture 进入配置界面:(或as-->Preferences-->Version Control-->Git进入) 点击Test按钮报错:Cannot Run Git 四、安装xcode 后查询得知为xcode末安装,导致Git无法使用(很多工具都依赖xcode). 打开AppStore下载xcode. 重新打开as,提示如下: 解决方法:打开新安装的xcode 并通过协议并安装。安装完成后,再重启as 一切正常。
最近在windows服务器部署最新版mysql8.0时发现 navicat for mysql登陆不了。 根据错误提示 2059 - authentication plugin 'caching_sha2_password'” 搜索得知,原来是新版mysql8.0登陆验证改变导致的。使用命令登陆是正常的,所以需要使用命令登陆mysql后,把验证登陆修改回navicat for mysql支持的方式。 1.进入MySQL控制台2.use mysql;3.ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '替换您的密码'; 重新开打navicat for mysql 登陆成功。 参考:https://www.cnblogs.com/PatrickLiu/p/9626558.htmlhttps://blog.csdn.net/GRAY_KEY/article/details/80659916 未解决问题:怎么让navicat for mysql 支持mysql8.0的新验证方式呢。有兴趣的朋友可以研究下并追加评论。谢谢。
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。 博客搬家邀请码NWFZR
阿里云 推荐码 hut29f,适用于新手首次购买。
现在app上传到appStore的时候,项目中如果出现加密,状态栏是:缺少合规证明。 解决的方法是在Info。plist文件中添加:ITSAppUsesNonExemptEncryption 设置为NO 如下图:
错误如下: Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_RoutingHTTPServer", referenced from: 出现类似 Undefined symbols for architecture x86_64 错误,一般是缺少包的引用,请尝试xcode-->项目-->Build phases-->Link Binaray With Libraries-->选择“+”号-->在弹出的对话框中搜索缺少的包。引用即可。 注: 1.如果是第三方包,搜索不到的需要网上搜索并导入,或使用Pods管理 2.如果点击"+"导入后还有错误,请查看日志是否错误内容改变,有时导入的包也需要依赖其它包,这时要再按上面步骤继续查找缺少的包。
1.检测是否安装ftp 2.apt-get install 安装 (如果安装失败 执行apt-get install update<如果update失败 配置dns让自己的服务器 可以ping 通www.baidu.com>) 3.从用户表中把 root 注释掉或是删除掉 4.把cnf 中的读写权限打开
引用:http://blog.sina.com.cn/s/blog_48cf38890100go6x.html 1. 什么是回调函数 回调函数(callback Function),顾名思义,用于回调的函数。 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机。回调函数包含下面几个特性: 1、属于工作流的一个部分; 2、必须按照工作流指定的调用约定来申明(定义); 3、他的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能; 2. 回调机制 回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。 ======================================================= java回调机制: 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。 同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回 调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。 回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。 ======================================================== 用Java里的例子: package callbackexample; public interface ICallBack { //需要回调的方法 public void postExec(); } 另外的一个类: package callbackexample; public class FooBar { //组合聚合原则 private ICallBack callBack; public void setCallBack(ICallBack callBack) { this.callBack = callBack; doSth(); } public void doSth() { callBack.postExec(); } } 第二个类在测试类里面,是一个匿名类: package callbackexample; public class Test { public static void main(String[] args) { FooBar foo = new FooBar(); foo.setCallBack(new ICallBack() { public void postExec() { System.out.println("在Test类中实现但不能被Test的对象引用,而由FooBar对象调用"); } }); } } 上诉的代码: 1.两个类:匿名类和FooBar 2.匿名类实现接口ICallBack(在test测试的main方法中用匿名类的形式实现) 3.FooBar 拥有一个参数为ICallBack接口类型的函数setCallBack(ICallBack o) 4.匿名类运行时调用FooBar中setCallBack函数,以自身传入参数 5.FooBar已取得匿名类,就可以随时回调匿名类中所实现的ICallBack接口中的方法 ================================== 1。首先回调方法的概念与“构造方法”的概念是不一样的,它不是指java中某个具有特殊意义或用途的方法。2。称它为方法的“回调”更恰当一些,它是指方法的一种调用方式。任何一个被“回调”的方法,皆可称之为“回调方法” 3。方法的回调通常发生在“java接口”和“抽象类”的使用过程中。假设有接口名为 ICallBack 其中有方法名为postExec()有类Myclass 实现了该接口,也就是一定实现了postExec()这个方法。现在有另一个类FooBar它有个方法 setCallBack(ICallBack callBack) ,并且setCallBack方法调用了callBack的postExec()方法。如果现在,我们使用一个Myclass 的实例myClass,将它作为参数带入到setCallBack(ICallBack callBack)方法中,我们就说setCallBack(ICallBack callBack)方法回调了myClass的postExec()方法。 ==========================================================
引用:http://www.cnblogs.com/welenwho/archive/2012/05/10/2494984.html android想要获得系统权限有几种途径,一种就是你的程序固化的系统中,这种方式可以通过将apk放到rom中/system/app中然后刷机即可,或者是在你的程序root后通过root文件管理器将apk放到改目录下.第二种就是你的程序必须要有该平台的签名,那么怎样获得android下的系统签名呢. 首先需要有android的源码,编译出signapk.jar文件,signapk的源码在android源码目录的/build/tools/signapk下,首先需要cd到该目录下,然后通过javac signapk.java命令编译该源文件,得到SignApk$SignatureOutputStream.class和SignApk.class文件,然后手动创建 k/com/android/signapk目录,然后将两个class文件放到该目录下,执行jar cvfm signapk.jar SignApk.mf -C k\ .即可生成signapk.jar文件,得到android提供的签名程序(k\ .之间有空格). 然后是在android源码目录中的位置是/build/target/product/security下面的platform.pk8和platform.x509.pem两个文件(该目录下面还有两个testkey.pk8和testkey.x509.pem文件这两个是系统签名测试文件,第三方rom一般通过使用这个重新签名修改后的rom,上面提到的第一种方法修改完后需要使用该文件签名,不签在某些recovery下是可以通过的,但是还是建议签) 在获得signapk.jar文件和platform.pk8和platform.x509.pem文件后就可以对某个apk进行系统签名了 ,具体执行指令如下,假如需要对名为welen.apk(在此之前,welen.apk文件的生成时必须在AndroidManifest.xml文件中加入android:sharedUserId="android.uid.system",使其获得和系统一样的uid,在加入该属性后如果没有经过系统签名,该apk是无法安装的)的文件进行系统签名,则 java -jar signapk.jar platform.x509.pem platform.pk8 welen.apk welen_signed.apk 使用上面指令即可对welen.apk文件进行签名,签名后的文件拥有系统程序的权限,可执行静默安装卸载,系统关机重启,强制结束进程等操作,具体的关机重启的代码就不写了
引用:http://addiwang.blog.163.com/blog/static/118130772011221114230288/ Air的文件操做主要涉及两个类,FIle和FileStream。一般来说我们不会直接以文本形式保存文件,那样的内容不易于修改,所以这里我以 xml 文件为例。 Note: 下面所有的function可以直接复制到你的代码中运行 1.创建新文件。 public function createFileDemo():void{ var file:File = File.desktopDirectory.resolvePath("test.xml") //尝试从系统桌面获取test.xml文件。 var fileStream:FileStream = new FileStream(); // 创建FileStream 对象,用于读写文件 fileStream.open(file,FileMode.WRITE); //以WRITE方式打开file, 如果file中对应的文件不存在,创建新文件 //准备文件的内容 var content:XML = <root> <content> 我们的第一个文件创建完毕</content> </root> fileStream.writeUTFBytes(content.toXMLString()); //像文件中写入内容。 fileStream.close(); //完成写入,这时我们打开桌面的test.txt可以看到内容。 } 上面五句代码让我在我的桌面创建了名为test.xml的文件,并且写入了content。 这里有三个地方我们要注意 1. File.desktopDirectory — 因为AIR可以在Mac和Windows下运行,所以我们最好不要指定固定的地址,这里File.desktopDirectory的意思就是指向我系统 的桌面,因为我是mac,所以实际返回的是 /kevinluo/Desktop. 除此之外我们还可以得到如下面这些地址 File.documentsDirectory; //指向用户文档文件夹 File.applicationDirectory; //应用程序安装目录 File.getRootDirectories(); //文件系统根目录 等等,我就不一一列举了,具体的大家可以看File帮助中描述。 2.File.desktopDirectory.resolvePath("test.xml") — 获取桌面下的test.xml。这里我们还可以这样写 var file:File = File.desktopDirectory file = file.resolvePath("test.xml") 不过我建议还是直接写在一排,如果像这样分开写的话,当fileStream.open(file,FileMode.WRITE) 打开文件时,如果文件不存在就会报错而不会像上面那样创建新的文件。 3. fileStream.open(file,FileMode.WRITE) –FileMode.WRITE是打开文件的方法,打开文件的方法一共有如下四种 FileMode.READ // 只读方式打开文件 FileMode.WRITE // 写方式打开文件,文件的原有内容会被清除。文件不存在的话创建新文件 FileMode.APPEND //追加方式打开文件,写入的内容总是会添加到文件的末尾。文件不存在的话创建新文件 FileMode.UPDATE //直接打开文件,可以根据需要在指定位置插入数据。文件不存在的话创建新文件 这里我们以WRITE方式创建了新文件,并写入了“我们的第一个文件创建完毕”。 2. 读取已有的文件,修改内容,再更新文件。 publuc function modifyFileDemo():void{ var file2:File = File.desktopDirectory.resolvePath("test.xml"); //读取刚才创建的test.xml var fs:FileStream = new FileStream(); fs.open(file2,FileMode.READ); //以只读方式打开 var ct:XML = new XML(fs.readUTFBytes(fs.bytesAvailable)); //获取xml内容 ct.content = "我们的第一个文件修改完毕" //修改content节点下的内容 fs.open(file2,FileMode.WRITE); //重新以写方式打开文件,目的在于清除原有的内容 fs.writeUTFBytes(ct.toXMLString()) //写入修改过后的XML fs.close() } 这里我用了READ和WRITE的组合。在实际的使用中我们可以判断一个xml文件的大小来限制单个文件写入过大。 3.删除文件。 删除文件是最简单的。 public function deleteFIleDemo():void{ var file2:File = File.desktopDirectory.resolvePath("test.xml"); if(file2.exists){ //判断文件是否存在 file2.deleteFile() //删除文件 } } 如果使用file2.moveToTrash()则将文件移到垃圾箱 4.拷贝文件/移动文件 public function copyFileDemo():void{ var sourceFile:File = File.desktopDirectory.resolvePath("test.xml") //获取源文件 var newFile:File = File.desktopDirectory.resolvePath("test2.xml") //创建目标文件 sourceFile.copyTo(newFile,true) //执行拷贝,如果是移动的话sourceFile.moveTo(newFile,true) } 5. 异步与同步 AIR对文件的操作分为异步与同步两种方式。上面1–4介绍的是同步方式,其中的一些方法有另一个异步方式,看下面的列表 File.copyTo() — File.copyToAsync() File.moveTo() — File.moveToAsync() File.deleteDirectory() — File.deleteDirectoryAsync() File.deleteFile() — File.deleteFileAsync() File.getDirectoryListing() – File.getDirectoryListingAsync() File.moveToTrash() — File.moveToTrashAsync() FileStream.open — FileStream.openAsync() 使用异步方式时我们需要监听EVENT.COMPLETE和IOErrorEvent.IO_ERROR事件来获取file的操作结果。我拿上面的 拷贝做个例子 pirvate funnction copyFile(sourceFIlePath:String, targetFIlePath:String):void{ var sourceFile:File = File.desktopDirectory.resolvePath(sourceFIlePath) //获取源文件 var newFile:File = File.desktopDirectory.resolvePath(targetFIlePath) //创建目标文件 sourceFile.addEventListener(Event.COMPLETE,completeHandle) //监听文件操作complete事件 sourceFile.copyToAsync(newFile,true) ; //开始拷贝,异步方式 this. showLoadingBar() //打开loading图标 } private function completeHandle():void{ Alert.show("拷贝文件完成") this.closeLoadingBar() //关闭loading图标 } 异步方式的好处是我们可以从中实行一些别的操作,比如出现一个loading的提示之类。
引用:http://elf8848.iteye.com/blog/875830/ 资源下载: Spring_MVC_教程_快速入门_深入分析V1.1.pdf SpringMVC核心配置文件示例.rar 作者:赵磊 博客:http://elf8848.iteye.com 目录 一、前言二、spring mvc 核心类与接口三、spring mvc 核心流程图 四、spring mvc DispatcherServlet说明 五、spring mvc 父子上下文的说明 六、springMVC-mvc.xml 配置文件片段讲解 七、spring mvc 如何访问到静态的文件,如jpg,js,css 八、spring mvc 请求如何映射到具体的Action中的方法 九、 spring mvc 中的拦截器:十、 spring mvc 如何使用拦截器 十一、 spring mvc 如何实现全局的异常处理 十二、 spring mvc 如何把全局异常记录到日志中 十三、 如何给spring3 MVC中的Action做JUnit单元测试 十四、 spring mvc 转发与重定向 (带参数重定向) 十五、 spring mvc 处理ajax请求 十六、 spring mvc 关于写几个配置文件的说明 十七、 spring mvc 如何取得Spring管理的bean 十八、 spring mvc 多视图控制器 十九、 <mvc:annotation-driven /> 到底做了什么工作 二十、 本文中springMVC.xml配置文件是核心,这里给一个下载地址 说明:本作者是文章的原创作者,转载请注明出处:本文地址:http://elf8848.iteye.com/blog/875830 一、前言: 为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平。你的一个决定会影响团队未来的几年。要考虑方面太多: 1、简单易用,以提高开发效率。使小部分的精力在框架上,大部分的精力放在业务上。 2、性能优秀,这是一个最能吸引眼球的话题。 3、尽量使用大众的框架(避免使用小众的、私有的框架),新招聘来的开发人员有一些这方面技术积累,减低人员流动再适应的影响。 如果你还在为这件事件发愁,本文最适合你了。选择Spring MVC吧。 Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高。Spring 3.0更加完善,实现了对Struts 2的超越。现在越来越多的开发团队选择了Spring MVC。 Struts2也是非常优秀的MVC构架,优点非常多比如良好的结构,拦截器的思想,丰富的功能。但这里想说的是缺点,Struts2由于采用了值栈、OGNL表达式、struts2标签库等,会导致应用的性能下降,应避免使用这些功能。而Struts2的多层拦截器、多实例action性能都很好。可以参考我写的一篇关于Spring MVC与Struts2与Servlet比较的文章《Struts2、SpringMVC、Servlet(Jsp)性能对比 测试》 Spring3 MVC的优点: 1、Spring3 MVC使用简单,学习成本低。学习难度小于Struts2,Struts2用不上的多余功能太多。呵呵,当然这不是决定因素。 2、Spring3 MVC很容易就可以写出性能优秀的程序,Struts2要处处小心才可以写出性能优秀的程序(指MVC部分) 3、Spring3 MVC的灵活是你无法想像的,Spring框架的扩展性有口皆碑,Spring3 MVC当然也不会落后,不会因使用了MVC框架而感到有任何的限制。 Struts2的众多优点: 1、老牌的知名框架,从Struts1起积累了大量用户群体。技术文档丰富。 2、其它方面略... (呵呵,是不是不公平?) Spring的官方下载网址是:http://www.springsource.org/download (本文使用是的Spring 3.0.5版本) 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 二、核心类与接口: 先来了解一下,几个重要的接口与类。现在不知道他们是干什么的没关系,先混个脸熟,为以后认识他们打个基础。 DispatcherServlet -- 前置控制器 HandlerMapping接口 -- 处理请求的映射 HandlerMapping接口的实现类: SimpleUrlHandlerMapping 通过配置文件,把一个URL映射到Controller DefaultAnnotationHandlerMapping 通过注解,把一个URL映射到Controller类上 HandlerAdapter接口 -- 处理请求的映射 AnnotationMethodHandlerAdapter类,通过注解,把一个URL映射到Controller类的方法上 Controller接口 -- 控制器 由于我们使用了@Controller注解,添加了@Controller注解注解的类就可以担任控制器(Action)的职责, 所以我们并没有用到这个接口。 HandlerInterceptor 接口--拦截器 无图,我们自己实现这个接口,来完成拦截的器的工作。 ViewResolver接口的实现类 UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理 InternalResourceViewResolver类,比上面的类,加入了JSTL的支持 View接口 JstlView类 LocalResolver接口 HandlerExceptionResolver接口 --异常处理 SimpleMappingExceptionResolver实现类 ModelAndView类 无图。 三、核心流程图 本图是我个人画的,有不严谨的地方,大家对付看吧。总比没的看强。 转载请注明出处:本文地址:http://elf8848.iteye.com/blog/875830 四、DispatcherServlet说明 使用Spring MVC,配置DispatcherServlet是第一步。 DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet。 DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。 “某某规则”:是根据你使用了哪个HandlerMapping接口的实现类的不同而不同。 先来看第一个例子: Xml代码 <web-app> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> </web-app> <load-on-startup>1</load-on-startup>是启动顺序,让这个Servlet随Servletp容器一起启动。 <url-pattern>*.form</url-pattern> 会拦截*.form结尾的请求。 <servlet-name>example</servlet-name>这个Servlet的名字是example,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中,关于key,以后说明。 在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。 第二个例子: Xml代码 <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> 指明了配置文件的文件名,不使用默认配置文件名,而使用springMVC.xml配置文件。 其中<param-value>**.xml</param-value> 这里可以使用多种写法1、不写,使用默认值:/WEB-INF/<servlet-name>-servlet.xml2、<param-value>/WEB-INF/classes/springMVC.xml</param-value>3、<param-value>classpath*:springMVC-mvc.xml</param-value>4、多个值用逗号分隔 Servlet拦截匹配规则可以自已定义,拦截哪种URL合适? 当映射为@RequestMapping("/user/add")时,为例: 1、拦截*.do、*.htm, 例如:/user/add.do 这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。 2、拦截/,例如:/user/add 可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。 弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。想实现REST风格,事情就是麻烦一些。后面有解决办法还算简单。 3、拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。 转载请注明出处:本文地址:http://elf8848.iteye.com/blog/875830 五、父子上下文(WebApplicationContext) 如果你使用了listener监听器来加载配置,一般在Struts+Spring+Hibernate的项目中都是使用listener监听器的。如下 Java代码 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。 可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext); DispatcherServlet是一个Servlet,可以同时配置多个,每个 DispatcherServlet有一个自己的上下文对象(WebApplicationContext),称为子上下文(子容器),子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是DispatcherServlet.class.getName() + ".CONTEXT"。 可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request); 说明 :Spring 并没有限制我们,必须使用父子上下文。我们可以自己决定如何使用。 方案一,传统型: 父上下文容器中保存数据源、服务层、DAO层、事务的Bean。 子上下文容器中保存Mvc相关的Action的Bean. 事务控制在服务层。 由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。 当然,做为“传统型”方案,也没有必要这要做。 方案二,激进型: Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了-----没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。 本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。 由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。 总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。 Java--大项目能做好--按传统方式做,规规矩矩的做,好扩展,好维护。 Java--小项目能做快--按激进方式做,一周时间就可以出一个版本,先上线接受市场(用户)的反馈,再改进,再反馈,时间就是生命(成本)。 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 六、springMVC-mvc.xml 配置文件片段讲解 (未使用默认配置文件名) Xml代码 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <!-- 自动扫描的包名 --> <context:component-scan base-package="com.app,com.core,JUnit4" ></context:component-scan> <!-- 默认的注解映射的支持 --> <mvc:annotation-driven /> <!-- 视图解释类 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/><!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> </bean> <!-- 拦截器 --> <mvc:interceptors> <bean class="com.core.mvc.MyInteceptor" /> </mvc:interceptors> <!-- 对静态资源文件的访问 方案一 (二选一) --> <mvc:default-servlet-handler/> <!-- 对静态资源文件的访问 方案二 (二选一)--> <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/> <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/> </beans> <context:component-scan/> 扫描指定的包中的类上的注解,常用的注解有: @Controller 声明Action组件@Service 声明Service组件 @Service("myMovieLister") @Repository 声明Dao组件@Component 泛指组件, 当不好归类时. @RequestMapping("/menu") 请求映射@Resource 用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName") @Autowired 用于注入,(srping提供的) 默认按类型装配 @Transactional( rollbackFor={Exception.class}) 事务管理@ResponseBody@Scope("prototype") 设定bean的作用域 <mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。后面,我们处理响应ajax请求时,就使用到了对json的支持。后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean。 如何替换 <mvc:annotation-driven />?他到底做了什么工作,请看,最后面的 十九节 <mvc:annotation-driven /> 到底做了什么工作。 <mvc:interceptors/> 是一种简写形式。通过看前面的大图,知道,我们可以配置多个HandlerMapping。<mvc:interceptors/>会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。 <mvc:default-servlet-handler/> 使用默认的Servlet来响应静态文件。 <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> 匹配URL /images/** 的URL被当做静态资源,由Spring读出到内存中再响应http。 转载请注明出处:本文地址:http://elf8848.iteye.com/blog/875830 七、如何访问到静态的文件,如jpg,js,css? 如何你的DispatcherServlet拦截"*.do"这样的有后缀的URL,就不存在访问不到静态资源的问题。如果你的DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了。我们要解决这个问题。 目的:可以正常访问静态文件,不可以找不到静态文件报404。 方案一:激活Tomcat的defaultServlet来处理静态文件 Xml代码 <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> 要配置多个,每种文件配置一个 要写在DispatcherServlet的前面, 让 defaultServlet先拦截请求,这样请求就不会进入Spring了,我想性能是最好的吧。 Tomcat, Jetty, JBoss, and GlassFish 自带的默认Servlet的名字 -- "default"Google App Engine 自带的 默认Servlet的名字 -- "_ah_default"Resin 自带的 默认Servlet的名字 -- "resin-file"WebLogic 自带的 默认Servlet的名字 -- "FileServlet"WebSphere 自带的 默认Servlet的名字 -- "SimpleFileServlet" 方案二: 在spring3.0.4以后版本提供了mvc:resources , 使用方法: Xml代码 <!-- 对静态资源文件的访问 --> <mvc:resources mapping="/images/**" location="/images/" /> /images/**映射到ResourceHttpRequestHandler进行处理,location指定静态资源的位置.可以是web application根目录下、jar包里面,这样可以把静态资源压缩到jar包中。cache-period 可以使得静态资源进行web cache 如果出现下面的错误,可能是没有配置<mvc:annotation-driven />的原因。 报错WARNING: No mapping found for HTTP request with URI [/mvc/user/findUser/lisi/770] in DispatcherServlet with name 'springMVC' 使用<mvc:resources/>元素,把mapping的URI注册到SimpleUrlHandlerMapping的urlMap中,key为mapping的URI pattern值,而value为ResourceHttpRequestHandler,这样就巧妙的把对静态资源的访问由HandlerMapping转到ResourceHttpRequestHandler处理并返回,所以就支持classpath目录,jar包内静态资源的访问.另外需要注意的一点是,不要对SimpleUrlHandlerMapping设置defaultHandler.因为对static uri的defaultHandler就是ResourceHttpRequestHandler,否则无法处理static resources request. 方案三 ,使用<mvc:default-servlet-handler/> Xml代码 <mvc:default-servlet-handler/> 会把"/**" url,注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler处理并返回.DefaultServletHttpRequestHandler使用就是各个Servlet容器自己的默认Servlet. 补充说明:多个HandlerMapping的执行顺序问题: DefaultAnnotationHandlerMapping的order属性值是:0<mvc:resources/ >自动注册的 SimpleUrlHandlerMapping的order属性值是: 2147483646 <mvc:default-servlet-handler/>自动注册 的SimpleUrlHandlerMapping 的order属性值是: 2147483647 spring会先执行order值比较小的。当访问一个a.jpg图片文件时,先通过 DefaultAnnotationHandlerMapping 来找处理器,一定是找不到的,因为我们没有叫a.jpg的Action。然后再按order值升序找,由于最后一个 SimpleUrlHandlerMapping 是匹配 "/**"的,所以一定会匹配上,就可以响应图片。 访问一个图片,还要走层层匹配。不知性能如何? 最后再说明一下,方案二、方案三 在访问静态资源时,如果有匹配的(近似)总拦截器,就会走拦截器。如果你在拦截中实现权限检查,要注意过滤这些对静态文件的请求。 如何你的DispatcherServlet拦截 *.do这样的URL后缀,就不存上述问题了。还是有后缀方便。 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 八、请求如何映射到具体的Action中的方法?方案一:基于xml配置映射,可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射和拦截请求。配置方法略。 方案二:基于注解映射,可以使用DefaultAnnotationHandlerMapping。 Xml代码 <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> </bean> 但前面我们配置了<mvc:annotation-driven />,他会自动注册这个bean,就不须要我们显示的注册这个bean了。 如何替换 <mvc:annotation-driven />?他到底做了什么工作,请看,最后面的 十九节 <mvc:annotation-driven /> 到底做了什么工作。 以上都可以注入interceptors,实现权限控制等前置工作。我们使用第2种,基于注解来使用spring MVC 并在action类上使用:@Controller@RequestMapping("/user") 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 九、Spring中的拦截器:Spring为我们提供了:org.springframework.web.servlet.HandlerInterceptor接口, org.springframework.web.servlet.handler.HandlerInterceptorAdapter适配器,实现这个接口或继承此类,可以非常方便的实现自己的拦截器。 有以下三个方法: Action之前执行: public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler); 生成视图之前执行 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView); 最后执行,可用于释放资源 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 在preHandle中,可以进行编码、安全控制等处理; 在postHandle中,有机会修改ModelAndView; 在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 参数中的Object handler是下一个拦截器。转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十、如何使用拦截器?自定义一个拦截器,要实现HandlerInterceptor接口: Java代码 public class MyInteceptor implements HandlerInterceptor { 略。。。 } Spring MVC并没有总的拦截器,不能对所有的请求进行前后拦截。Spring MVC的拦截器,是属于HandlerMapping级别的,可以有多个HandlerMapping ,每个HandlerMapping可以有自己的拦截器。当一个请求按Order值从小到大,顺序执行HandlerMapping接口的实现类时,哪一个先有返回,那就可以结束了,后面的HandlerMapping就不走了,本道工序就完成了。就转到下一道工序了。拦截器会在什么时候执行呢? 一个请求交给一个HandlerMapping时,这个HandlerMapping先找有没有处理器来处理这个请求,如何找到了,就执行拦截器,执行完拦截后,交给目标处理器。如果没有找到处理器,那么这个拦截器就不会被执行。 在spring MVC的配置文件中配置有三种方法: 方案一,(近似)总拦截器,拦截所有url Java代码 <mvc:interceptors> <bean class="com.app.mvc.MyInteceptor" /> </mvc:interceptors> 为什么叫“近似”,前面说了,Spring没有总的拦截器。 <mvc:interceptors/>会为每一个HandlerMapping,注入一个拦截器。总有一个HandlerMapping是可以找到处理器的,最多也只找到一个处理器,所以这个拦截器总会被执行的。起到了总拦截器的作用。 如果是REST风格的URL,静态资源也会被拦截。 方案二, (近似) 总拦截器, 拦截匹配的URL。 Xml代码 <mvc:interceptors > <mvc:interceptor> <mvc:mapping path="/user/*" /> <!-- /user/* --> <bean class="com.mvc.MyInteceptor"></bean> </mvc:interceptor> </mvc:interceptors> 就是比 方案一多了一个URL匹配。 如果是REST风格的URL,静态资源也会被拦截。 方案三,HandlerMappint上的拦截器。 如果是REST风格的URL,静态资源就不会被拦截。因为我们精准的注入了拦截器。 Xml代码 <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <bean class="com.mvc.MyInteceptor"></bean> </list> </property> </bean> 如果使用了<mvc:annotation-driven />, 它会自动注册DefaultAnnotationHandlerMapping 与AnnotationMethodHandlerAdapter 这两个bean,所以就没有机会再给它注入interceptors属性,就无法指定拦截器。 当然我们可以通过人工配置上面的两个Bean,不使用 <mvc:annotation-driven />,就可以 给interceptors属性 注入拦截器了。 其实我也不建议使用 <mvc:annotation-driven />,而建议手动写详细的配置文件,来替代 <mvc:annotation-driven />,这就控制力就强了。 如何替换 <mvc:annotation-driven />?他到底做了什么工作,请看,最后面的 十九节 <mvc:annotation-driven /> 到底做了什么工作。 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十一、如何实现全局的异常处理? 在spring MVC的配置文件中: Xml代码 <!-- 总错误处理--> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView"> <value>/error/error</value> </property> <property name="defaultStatusCode"> <value>500</value> </property> <property name="warnLogCategory"> <value>org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</value> </property> </bean> 这里主要的类是SimpleMappingExceptionResolver类,和他的父类AbstractHandlerExceptionResolver类。 具体可以配置哪些属性,我是通过查看源码知道的。 你也可以实现HandlerExceptionResolver接口,写一个自己的异常处理程序。spring的扩展性是很好的。 通过SimpleMappingExceptionResolver我们可以将不同的异常映射到不同的jsp页面(通过exceptionMappings属性的配置)。 同时我们也可以为所有的异常指定一个默认的异常提示页面(通过defaultErrorView属性的配置),如果所抛出的异常在exceptionMappings中没有对应的映射,则Spring将用此默认配置显示异常信息。 注意这里配置的异常显示界面均仅包括主文件名,至于文件路径和后缀已经在viewResolver中指定。如/error/error表示/error/error.jsp 显示错误的jsp页面: Html代码 <%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%> <%@ page import="java.lang.Exception"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>错误页面</title> </head> <body> <h1>出错了</h1> <% Exception e = (Exception)request.getAttribute("exception"); out.print(e.getMessage()); %> </body> </html> 其中一句:request.getAttribute("exception"),key是exception,也是在SimpleMappingExceptionResolver类默认指定的,是可能通过配置文件修改这个值的,大家可以去看源码。 十二、如何把全局异常记录到日志中? 在前的配置中,其中有一个属性warnLogCategory,值是“SimpleMappingExceptionResolver类的全限定名”。我是在SimpleMappingExceptionResolver类父类AbstractHandlerExceptionResolver类中找到这个属性的。查看源码后得知:如果warnLogCategory不为空,spring就会使用apache的org.apache.commons.logging.Log日志工具,记录这个异常,级别是warn。 值:“org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”,是“SimpleMappingExceptionResolver类的全限定名”。这个值不是随便写的。 因为我在log4j的配置文件中还要加入log4j.logger.org.springframework.web.servlet.handler.SimpleMappingExceptionResolver=WARN,保证这个级别是warn的日志一定会被记录,即使log4j的根日志级别是ERROR。 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十三、如何给spring3 MVC中的Action做JUnit单元测试? 使用了spring3 MVC后,给action做单元测试变得很方便,我以前从来不给action写单元测试的,现在可以根据情况写一些了。 不用给每个Action都写单元测试吧,自己把握吧。 JUnitActionBase类是所有JUnit的测试类的父类 Java代码 package test; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.BeforeClass; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping; /** * 说明: JUnit测试action时使用的基类 * * @author 赵磊 * @version 创建时间:2011-2-2 下午10:27:03 */ public class JUnitActionBase { private static HandlerMapping handlerMapping; private static HandlerAdapter handlerAdapter; /** * 读取spring3 MVC配置文件 */ @BeforeClass public static void setUp() { if (handlerMapping == null) { String[] configs = { "file:src/springConfig/springMVC.xml" }; XmlWebApplicationContext context = new XmlWebApplicationContext(); context.setConfigLocations(configs); MockServletContext msc = new MockServletContext(); context.setServletContext(msc); context.refresh(); msc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context); handlerMapping = (HandlerMapping) context .getBean(DefaultAnnotationHandlerMapping.class); handlerAdapter = (HandlerAdapter) context.getBean(context.getBeanNamesForType(AnnotationMethodHandlerAdapter.class)[0]); } } /** * 执行request对象请求的action * * @param request * @param response * @return * @throws Exception */ public ModelAndView excuteAction(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerExecutionChain chain = handlerMapping.getHandler(request); final ModelAndView model = handlerAdapter.handle(request, response, chain.getHandler()); return model; } } 这是个JUnit测试类,我们可以new Request对象,来参与测试,太方便了。给request指定访问的URL,就可以请求目标Action了。 Java代码 package test.com.app.user; import org.junit.Assert; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.servlet.ModelAndView; import test.JUnitActionBase; /** * 说明: 测试OrderAction的例子 * * @author 赵磊 * @version 创建时间:2011-2-2 下午10:26:55 */ public class TestOrderAction extends JUnitActionBase { @Test public void testAdd() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); request.setServletPath("/order/add"); request.addParameter("id", "1002"); request.addParameter("date", "2010-12-30"); request.setMethod("POST"); // 执行URI对应的action final ModelAndView mav = this.excuteAction(request, response); // Assert logic Assert.assertEquals("order/add", mav.getViewName()); String msg=(String)request.getAttribute("msg"); System.out.println(msg); } } 需要说明一下 :由于当前最想版本的Spring(Test) 3.0.5还不支持@ContextConfiguration的注解式context file注入,所以还需要写个setUp处理下,否则类似于Tiles的加载过程会有错误,因为没有ServletContext。3.1的版本应该有更好的解决方案, 参见: https://jira.springsource.org/browse/SPR-5243 。 参考 :http://www.iteye.com/topic/828513 十四、转发与重定向 可以通过redirect/forward:url方式转到另一个Action进行连续的处理。 可以通过redirect:url 防止表单重复提交 。 写法如下: return "forward:/order/add"; return "redirect:/index.jsp"; 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 带参数重定向--RedirectAttributes 用户保存或修改后,为了防止用户刷新浏览器(F5)导致表单重复提交,一般在保存或修改操作之后会redirect到一个结果页面(不是forward),同时携带参数,如操作成功的提示信息。因为是Redirect,Request里的attribute不会传递过去。Spring在3.1才提供了这个能力--RedirectAttributes。 反复按F5,操作成功的提示信息也不会再次出来(总共只出现一次),效果很理想。 Java代码 public String save(@ModelAttribute("group") Group group, RedirectAttributes redirectAttributes) { accountManager.saveGroup(group); redirectAttributes.addFlashAttribute("message", "操作成功"); return "redirect:/account/group/"; } 十五、处理ajax请求 1、引入下面两个jar包,我用的是1.7.2,好像1.4.2版本以上都可以,下载地址:http://wiki.fasterxml.com/JacksonDownload jackson-core-asl-1.7.2.jar jackson-mapper-asl-1.7.2.jar 2、spring的配置文件中要有这一行,才能使用到spring内置支持的json转换。如果你手工把POJO转成json就可以不须要使用spring内置支持的json转换。 <mvc:annotation-driven /> 3、使用@ResponseBody注解 Java代码 /** * ajax测试 * http://127.0.0.1/mvc/order/ajax */ @RequestMapping("/ajax") @ResponseBody public Object ajax(HttpServletRequest request){ List<String> list=new ArrayList<String>(); list.add("电视"); nbsp; list.add("洗衣机"); list.add("冰箱"); list.add("电脑"); list.add("汽车"); list.add("空调"); list.add("自行车"); list.add("饮水机"); list.add("热水器"); return list; } 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十六、关于写几个配置文件的说明 我看到有的人把配置文件写两份:一个是原有的applicationContext.xml,这个文件从spring2.0-2.5时一直在使用。别一个是新加的spring MVC的配置文件。其实这两个文件是可以写成一个文件的,springMVC相关的配置,数据源,事务相关配置可以都写再一个配置文件中。本例子中只使用了一个spring配置文件叫“springMVC.xml”。就不要再多配置一个applicationContext.xml文件了。web.xml文件中也不要再配置org.springframework.web.context.ContextLoaderListener的listener了。写两个配置文件一般就会导致扫描两次,一定要精确控制扫描的包名,做到不重复扫描。写两个配置文件还出现事务不好使的现象,是当把@Transactional写有Action层时出现的。 是因为父子上下文的原因,请参看前的 第五节 父子上下文,里面有说明 。原因是父上下文不能访问子上下文。 十七、如何取得Spring管理的bean (请用第3种方法)1、servlet方式加载时,【web.xml】 Xml代码 <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> spring容器放在ServletContext中的key是org.springframework.web.servlet.FrameworkServlet.CONTEXT.springMVC注意后面的springMVC,是你的servlet-name配置的值,注意适时修改。 Java代码 ServletContext sc=略 WebApplicationContext attr = (WebApplicationContext)sc.getAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.springMVC"); 2、listener方式加载时:【web.xml】 Xml代码 <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 【jsp/servlet】可以这样取得 Java代码 ServletContext context = getServletContext(); WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context); 3、通用的方法来了,神器啊,前的 1、2两种方法并不通用,可以抛弃了。在配置文件中加入: Xml代码 <!-- 用于持有ApplicationContext,可以使用SpringContextHolder.getBean('xxxx')的静态方法得到spring bean对象 --> <bean class="com.xxxxx.SpringContextHolder" lazy-init="false" /> Java代码 import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext. * */ public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量. */ public void setApplicationContext(ApplicationContext applicationContext) { SpringContextHolder.applicationContext = applicationContext; // NOSONAR } /** * 取得存储在静态变量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { checkApplicationContext(); return applicationContext; } /** * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { checkApplicationContext(); return (T) applicationContext.getBean(name); } /** * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static <T> T getBean(Class<T> clazz) { checkApplicationContext(); return (T) applicationContext.getBeansOfType(clazz); } /** * 清除applicationContext静态变量. */ public static void cleanApplicationContext() { applicationContext = null; } private static void checkApplicationContext() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder"); } } } 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十八、多视图控制器 当有jsp,flt (模板)等多种页面生成展示方式时,spring默认使用的是“视图解析器链”。 真是一个链,所以性能不好,spring会在“视图解析器链”中顺序的查找,直到找到对应的 “视图解析器” 。jsp视图解析器一定要写在最后面,因为一旦调用jsp,就向浏览器发出数据了,Spring就没有机会再尝试下一个了。 所以自己写一个"多视图解析器",依靠扩展名来区分,可一次准确的选中一个 视图解析器,提高性能(会有多少提高呢?没测试过). 下面的例子支持jsp,flt (模板)两种页面生成展示方式,你中以自己添加,支持更多。 Xml代码 <!-- 多视图处理器 --> <bean class="com.xxx.core.web.MixedViewResolver"> <property name="resolvers"> <map> <entry key="jsp"> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> </bean> </entry> <entry key="ftl"> <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="cache" value="true"/> <property name="contentType" value="text/html;charset=UTF-8"></property> <!-- 宏命令的支持 --> <property name="exposeSpringMacroHelpers" value="true"/> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/> <property name="requestContextAttribute" value="rc"></property> </bean> </entry> </map> </property> </bean> <!-- freemarker config --> <bean id="freeMarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/ftl/" /> <property name="freemarkerSettings"> <props> <prop key="template_update_delay">5</prop> <prop key="default_encoding">UTF-8</prop> <prop key="locale">zh_CN</prop> </props> </property> </bean> Java代码 import java.util.Locale; import java.util.Map; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; /** * 说明: 多视图处理器 * * @author 赵磊 * @version 创建时间:2011-8-19 上午09:41:09 */ public class MixedViewResolver implements ViewResolver{ private Map<String,ViewResolver> resolvers; public void setResolvers(Map<String, ViewResolver> resolvers) { this.resolvers = resolvers; } public View resolveViewName(String viewName,Locale locale) throws Exception{ int n=viewName.lastIndexOf("."); if(n!=-1){ //取出扩展名 String suffix=viewName.substring(n+1); //取出对应的ViewResolver ViewResolver resolver=resolvers.get(suffix); if(resolver==null){ throw new RuntimeException("No ViewResolver for "+suffix); } return resolver.resolveViewName(viewName, locale); }else{ ViewResolver resolver=resolvers.get("jsp"); return resolver.resolveViewName(viewName, locale); } } } 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 十九、 <mvc:annotation-driven /> 到底做了什么工作 一句 <mvc:annotation-driven />实际做了以下工作:(不包括添加自己定义的拦截器) 我们了解这些之后,对Spring3 MVC的控制力就更强大了,想改哪就改哪里。 Xml代码 <!-- 注解请求映射 --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="logNDCInteceptor"/> <!-- 日志拦截器,这是你自定义的拦截器 --> <ref bean="myRequestHelperInteceptor"/> <!-- RequestHelper拦截器,这是你自定义的拦截器--> <ref bean="myPermissionsInteceptor"/> <!-- 权限拦截器,这是你自定义的拦截器--> <ref bean="myUserInfoInteceptor"/> <!-- 用户信息拦截器,这是你自定义的拦截器--> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="byteArray_hmc" /> <ref bean="string_hmc" /> <ref bean="resource_hmc" /> <ref bean="source_hmc" /> <ref bean="xmlAwareForm_hmc" /> <ref bean="jaxb2RootElement_hmc" /> <ref bean="jackson_hmc" /> </list> </property> </bean> <bean id="byteArray_hmc" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /><!-- 处理.. --> <bean id="string_hmc" class="org.springframework.http.converter.StringHttpMessageConverter" /><!-- 处理.. --> <bean id="resource_hmc" class="org.springframework.http.converter.ResourceHttpMessageConverter" /><!-- 处理.. --> <bean id="source_hmc" class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /><!-- 处理.. --> <bean id="xmlAwareForm_hmc" class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" /><!-- 处理.. --> <bean id="jaxb2RootElement_hmc" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /><!-- 处理.. --> <bean id="jackson_hmc" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /><!-- 处理json--> 转载请注明出处:原文地址:http://elf8848.iteye.com/blog/875830 二十、 本文中springMVC.xml配置文件是核心,这里给一个下载地址 要在http://www.iteye.com/网站有注册帐号才能下载(这不能怪我) Spring_MVC_教程_快速入门_深入分析V1.1.pdf SpringMVC核心配置文件示例.rar Spring_MVC_教程_快速入门_深入分析V1.1.pdf (706.2 KB) 下载次数: 16441 SpringMVC核心配置文件示例.rar (2.2 KB) 下载次数: 9545 查看图片附件
引用:http://blog.csdn.net/jarvischu/article/details/8115390 1. 程序 [cpp] view plaincopy /************************************************************************ * 名 称:Windows_Frist_Code.cpp * 功 能:Windows编程入门 * 描 述:包含WinMain函数、WNDCLASS、消息循环等多种内容 windows窗口程序的流程如下:【WinMain入口】-->创建和设计窗口类 -->注册窗口类-->创建、显示和更新窗口-->消息循环-->【窗口过程函数】 * 作 者:JarvisChu * 时 间:2012-10-24 * 修 订:1. 2012-10-26,Jarvis. 完善代码和注释。 ************************************************************************/ #include <windows.h> #include <stdio.h> #include "resource.h" //回调函数 LRESULT CALLBACK WinProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); //入口函数 WinMain int WINAPI WinMain(HINSTANCE hInstance, //当前应用程序的句柄 HINSTANCE hPrevInstance,//先前应用程序的句柄,总是NULL LPSTR lpCmdLine, //不包含程序名的命令行,可通过GetCommandLine获取 int nShowCmd //窗口显示方式 ) { //-------------------创建和设计窗口类---------------------------------------------------- WNDCLASS wndclass; wndclass.cbClsExtra =0; wndclass.cbWndExtra =0; wndclass.hbrBackground =(HBRUSH)GetStockObject(GRAY_BRUSH); wndclass.hCursor =LoadCursor(hInstance,MAKEINTRESOURCE(ID_MYCURSOR)); //LoadCursor(NULL,IDC_HELP);// wndclass.hIcon =LoadIcon(hInstance,MAKEINTRESOURCE(ID_MYICON)); //LoadIcon(NULL,IDI_APPLICATION);// wndclass.hInstance =hInstance; wndclass.lpfnWndProc =WinProc; wndclass.lpszClassName ="Jarvis"; wndclass.lpszMenuName =NULL; wndclass.style =CS_HREDRAW | CS_VREDRAW; //-------------------注册窗口类---------------------------------------------------- RegisterClass(&wndclass); //-------------------创建显示更新窗口---------------------------------------------------- HWND hwnd; hwnd=CreateWindow("Jarvis","Jarvis",WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_MAXIMIZE, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL); ShowWindow(hwnd,SW_SHOWNORMAL); UpdateWindow(hwnd); //-------------------消息循环---------------------------------------------------- MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } //窗口过程函数实现 LRESULT CALLBACK WinProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { case WM_LBUTTONDOWN: MessageBox(hwnd,"LeftButton Clicked!","Prompt",0); break; case WM_CLOSE: if(IDYES==MessageBox(hwnd,"Are you sure to quit?","Prompt",MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; } 2. WinMain函数解析 2.1. WinMain函数原型 [cpp] view plaincopy int WINAPI WinMain ( HINSTANCE hInstance, //当前应用程序的句柄 HINSTANCE hPrevInstance,//先先前应用程序的句柄,总是NULL LPSTR lpCmdLine, //不含程序名的命令行,通过GetCommandLine获取 int nShowCmd //窗口显示方式 ); 2.2. WinMain函数功能 WinMain是一个函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。WinMain函数应初始化应用程序,显示主窗口,进入一个消息接收-发送循环,这个循环是应用程序执行的其余部分的顶级控制结构。 2.3. 窗口显示方式 WinMain函数的nShowCmd参数指示了窗口的显示方式。显示方式可以是下表中的任何一种。 表格 1 窗口显示方式 SW_HIDE 隐藏窗口并且激活另外一个窗口 SW_RESTORE 激活并显示窗口。如果窗口已经最小化或最大化,系统将以恢复到原来的尺寸和位置显示窗口(与SW_SHOWNORMAL相同) SW_SHOW 激活一个窗口并以原来的尺寸和位置显示窗口 SW_SHOWMAXIMIZED 激活窗口并且将其最大化 SW_SHOWMINIMIZED 激活窗口并将其最小化(以图标显示) SW_SHOWMINNOACTIVE 将一个窗口显示为图标。激活窗口维持活动状态 SW_SHOWNA 以窗口的当前状态显示窗口。激活窗口保持活动状态 SW_SHOWNOACTIVATE: 以窗口的最近一次的尺寸和位置显示窗口。激活窗口维持激活状态 SW_SHOWNORMAL 激活并显示窗口。如果窗口最大化或最小化,系统将其恢复到原来的尺寸和位置(与SW_RESTORE相同) SW_MINIMIZE 最小化指定的窗口,并且激活在系统表中的顶层窗口 3. 创建和设计窗口类 3.1. WNDCLASS结构体定义 [cpp] view plaincopy typedef struct { UINT style; //窗口类型CS_HREDRAW|CS_VREDRAW WNDPROC lpfnWndProc; //窗口回调函数 int cbClsExtra; //指定紧随在 WNDCLASS 后分配的字节数,初始化为零 int cbWndExtra; //指定紧随在窗口实例之后分配的字节数,初始化为零。 HINSTANCE hInstance; //指示该窗口类的回调函数所在实例的句柄,不为NULL HICON hIcon; //窗口图标句柄,若为NULL,系统提供默认 HCURSOR hCursor; //光标资源句柄 HBRUSH hbrBackground; //背景画刷句柄 LPCTSTR lpszMenuName; //菜单资源名 LPCTSTR lpszClassName; //窗口对应的窗口类名 } WNDCLASS, *PWNDCLASS; // 3.2. 窗口类型 多种窗口类型可以使用 | 号叠加 表格 2 窗口类型 标识 描述 CS_BYTEALIGNCLIENT 在字节边界上(在x方向上)定位窗口的用户区域的位置 CS_BYTEALIGNWINDOW 在字节边界上(在x方向上)定位窗口的位置 CS_CLASSDC: 该窗口类的所有窗口实例都共享一个窗口类DC CS_DBLCLKS 允许向窗口发送双击鼠标键的消息 CS_GLOBALCLASS 当调用CreateWindow 或 CreateWindowEx 函数来创建窗口时允许它的hInstance参数和注册窗口类时传递给RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。 CS_HREDRAW 当水平长度改变或移动窗口时,重画整个窗口 CS_NOCLOSE 禁止系统菜单的关闭选项 CS_OWNDC 给予每个窗口实例它本身的DC。注意,尽管这样是很方便,但它必须慎重使用,因为每个DC大约要占800个字节的内存。 CS_PARENTDC 将子窗口的裁剪区域设置到父窗口的DC中去,这样子窗口便可以在父窗口上绘制自身。注意,这是子窗口还是从系统缓存中获取DC,而不是使用父窗口的DC。使用该风格可以提高系统性能。 CS_SAVEBITS 以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息。该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失。设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存。 CS_VREDRAW 当垂直长度改变或移动窗口时,重画整个窗口 3.3. 窗口图标 3.3.1. LoadIcon函数 [cpp] view plaincopy HICON LoadIcon( HINSTANCE Instance, //应用程序的实例句柄 LPCTSTR lpIconName //图标资源的字符串型ID ) ; 3.3.2. 加载系统预定义图标 [cpp] view plaincopy LoadIcon(NULL, IDI_APPLICATION); 表格 3 系统图标资源 IDI_APPLICATION Default application icon. IDI_ASTERISK Same as IDI_INFORMATION. IDI_ERROR Hand-shaped icon. IDI_EXCLAMATION Same as IDI_WARNING. IDI_HAND Same as IDI_ERROR. IDI_INFORMATION Asterisk icon. IDI_QUESTION Question mark icon. IDI_WARNING Exclamation point icon. IDI_WINLOGO Windows logo icon. Windows XP: Default application icon. IDI_SHIELD Security Shield icon. 3.3.3. 加载自定义图标 [1] 添加图标资源 File-->New-->Files-->Icon File (Name it “My_Icon.ico” on the right)-->Draw the Icon [2] 添加资源文件 File-->New-->Files-->Resource Script (Name it “My_Resource”) 打开resource.h 文件,添加语句“#define ID_MYICON 1024”(1024 可以随意) 用记事本打开My_Resource.rc文件,添加语句“ID_MYICON ICON My_Icon.ico” [3] 添加“resource.h”头文件的引用 在主程序的.cpp文件中,引用头文件“#include “resource.h”” [4] 使用自定义图标 [cpp] view plaincopy LoadIcon(hInstance,MAKEINTRESOURCE(ID_MYICON)); 3.4. 光标资源 光标资源的加载类似于图标资源 3.4.1. LoadCursor函数 [cpp] view plaincopy HCURSOR LoadCursor ( HINSTANCE Instance, //应用程序的实例句柄 LPCTSTR lpCursorName //光标资源的字符串型ID ) ; 3.4.2. 加载系统预定义光标 [cpp] view plaincopy LoadCursor(NULL,IDC_CROSS); IDC_APPSTARTING Standard arrow and small hourglass IDC_ARROW Standard arrow IDC_CROSS Crosshair IDC_HAND Windows 98/Me, Windows 2000/XP: Hand IDC_HELP Arrow and question mark IDC_IBEAM I-beam IDC_ICON Obsolete for applications marked version 4.0 or later. IDC_NO Slashed circle IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL. IDC_SIZEALL Four-pointed arrow pointing north, south, east, and west IDC_SIZENESW Double-pointed arrow pointing northeast and southwest IDC_SIZENS Double-pointed arrow pointing north and south IDC_SIZENWSE Double-pointed arrow pointing northwest and southeast IDC_SIZEWE Double-pointed arrow pointing west and east IDC_UPARROW Vertical arrow IDC_WAIT Hourglass 3.4.3. 加载自定义光标 具体步骤与图标相同(3.3.3) [1] resource.h [2] My_Resource.rc [3] main.cpp 3.5. 背景画刷 背景画刷就是用来设置窗口的背景。 hbrBackground是画刷的句柄,它必须是用于绘制背景的物理刷子的句柄,或者是一个颜色的值。 如果给出一个颜色的值,它必须是下面列出的标准系统颜色之一(系统将对所选颜色加1)。 3.5.1. 使用标准系统颜色 [cpp] view plaincopy hbrBackground=(HBRUSH)(COLOR_ACTIVEBORDER+1); 表格 4 标准系统颜色 名称 样式 名称 样式 COLOR_ACTIVEBORDER COLOR_ACTIVECAPTION COLOR_CAPTIONTEXT COLOR_WINDOWTEXT COLOR_BTNTEXT COLOR_MENUTEXT COLOR_APPWORKSPACE COLOR_HIGHLIGHTTEXT COLOR_BACKGROUND COLOR_GRAYTEXT COLOR_HIGHLIGHT COLOR_INACTIVEBORDER COLOR_INACTIVECAPTION COLOR_BTNFACE COLOR_SCROLLBAR COLOR_WINDOWFRAME COLOR_MENU COLOR_BTNSHADOW COLOR_WINDOW 3.5.2. 使用系统预定义的画刷 [cpp] view plaincopy hbrBackground =(HBRUSH)GetStockObject(BLACK_BRUSH); BRUSH 名称 BLACK_BRUSH 黑色画刷 DKGRAY_BRUSH 暗灰色画刷 DC_BRUSH (Win7中错误) GRAY_BRUSH 灰色画刷 HOLLOW_BRUSH 空心刷(相当于NULL_BRUSH) LTGRAY_BRUSH 浅灰色画刷 NULL_BRUSH 空心刷(即背景透明) WHITE_BRUSH 白色画刷 3.5.3. GetStockObject()函数 [cpp] view plaincopy HGDIOBJ GetStockObject(int fnObject); 该函数检索预定义的备用笔、刷子、字体或者调色板的句柄。 fnObject可以是: BLACK_BRUSH,WHITE_PEN,SYSTEM_FONT, DEFAULT_PALETTE 4. 注册窗口类 [cpp] view plaincopy ATOM RegisterClass(CONST WNDCLASS *lpWndClass); 窗口只有在其对应的窗口类注册之后,才能使用CreateWindow或CreateWindowEx创建。 5. 创建窗口 [cpp] view plaincopy HWND hwnd; hwnd = CreateWindow("Jarvis","Jarvis",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL); [cpp] view plaincopy HWND CreateWindow( LPCTSTR lpClassName, // 对应的窗口类的名称 LPCTSTR lpWindowName, //窗口名称 DWORD dwStyle, / /窗口类型/ int x, //初始水平位置 int y, //初始垂直位置 int nWidth, //窗口宽度 int nHeight, //窗口高度 HWND hWndParent, //父窗口句柄 HMENU hMenu, //菜单资源句柄 HINSTANCE hInstance, //实例句柄 LPVOID lpParam //传给WM_CREATE的值 ); Window_Style(可以组合) 表格 5 Window_Style Style 说明 WS_BORDER 创建一个单边框的窗口。 WS_CAPTION 创建一个有标题框的窗口(包括WS_BODER风格)。 WS_CHILD 创建一个子窗口。这个风格不能与WS_POPUP风格合用。 WS_CHLDWINDOW 与WS_CHILD相同。 WS_CLIPCHILDREN 当在父窗口内绘图时,排除子窗口区域。在创建父窗口时使用这个风格。 WS_CLIPSIBLINGS 排除子窗口之间的相对区域,也就是,当一个特定的窗口接收到WM_PAINT消息时,WS_CLIPSIBLINGS 风格将所有层叠窗口排除在绘图之外,只重绘指定的子窗口。如果未指定WS_CLIPSIBLINGS风格,并且子窗口是层叠的,则在重绘子窗口的客户区时,就会重绘邻近的子窗口。 WS_DISABLED 创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息。 WS_DLGFRAME 创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条。 WS_GROUP 指定一组控制的第一个控制。这个控制组由第一个控制和随后定义的控制组成,自第二个控制开始每个控制,具有WS_GROUP风格,每个组的第一个控制带有WS_TABSTOP风格,从而使用户可以在组间移动。用户随后可以使用光标在组内的控制间改变键盘焦点。 WS_HSCROLL 创建一个有水平滚动条的窗口。 WS_ICONIC 创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE风格相同。 WS_MAXIMIZE 创建一个初始状态为最大化状态的窗口。 WS_MAXIMIZEBOX 创建一个具有最大化按钮的窗口。该风格不能与WS_EX_CONTEXTHELP风格同时出现,同时必须指定WS_SYSMENU风格。 WS_OVERLAPPED 产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同。 WS_OVERLAPPEDWINDOW 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXIMIZEBOX风格的层叠窗口,与WS_TILEDWINDOW风格相同。 WS_POPUP 创建一个弹出式窗口。该风格不能与WS_CHLD风格同时使用。 WS_POPUPWINDOW 创建一个具有WS_BORDER,WS_POPUP,WS_SYSMENU风格的窗口,WS_CAPTION和WS_POPUPWINDOW必须同时设定才能使窗口某单可见。 WS_SIZEBOX 创建一个可调边框的窗口,与WS_THICKFRAME风格相同。 WS_SYSMENU 创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格。 WS_TABSTOP 创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制。 WS_THICKFRAME 创建一个具有可调边框的窗口,与WS_SIZEBOX风格相同。 WS_TILED 产生一个层叠的窗口。一个层叠的窗口有一个标题和一个边框。与WS_OVERLAPPED风格相同。 WS_TILEDWINDOW 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU, WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXMIZEBOX风格的层叠窗口。与WS_OVERLAPPEDWINDOW风格相同。 WS_VISIBLE 创建一个初始状态为可见的窗口。 WS_VSCROLL 创建一个有垂直滚动条的窗口。 6. 显示窗口 [cpp] view plaincopy BOOL ShowWindow(HWND hWnd, //窗口句柄 int nCmdShow //显示方式 ); 显示方式可以是下表中的任何一种。 表格 6 窗口显示方式 窗口显示方式 说明 SW_HIDE 隐藏窗口并激活其他窗口。 SW_MAXIMIZE 最大化指定的窗口。 SW_MINIMIZE 最小化指定的窗口并且激活在Z序中的下一个顶层窗口。 SW_RESTORE 激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。 SW_SHOW 在窗口原来的位置以原来的尺寸激活和显示窗口。 SW_SHOWDEFAULT 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的。 SW_SHOWMAXIMIZED 激活窗口并将其最大化。 SW_SHOWMINIMIZED 激活窗口并将其最小化。 SW_SHOWMINNOACTIVE 窗口最小化,激活窗口仍然维持激活状态。 SW_SHOWNA 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。 SW_SHOWNOACTIVATE 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。 SW_SHOWNORMAL 激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。 7. 更新窗口 如果窗口更新的区域不为空,UpdateWindow函数通过发送一个WM_PAINT消息来更新指定窗口的客户区。函数绕过应用程序的消息队列,直接发送WM_PAINT消息给指定窗口的窗口过程,如果更新区域为空,则不发送消息。 8. 消息循环 [cpp] view plaincopy MSG msg; while (GetMessage(&msg,NULL,0,0)) //从消息队列中取得一条消息 { TranslateMessage(&msg); //将虚拟键消息转化成字符消息 DispatchMessage(&msg); //将消息发送给相应的窗口过程函数 }// 8.1. MSG 结构体 MSG结构体包含一条WindowMessage的全部信息 [cpp] view plaincopy typedef struct tagMSG { //msg HWND hwnd; //接受消息循环的窗口句柄 UINT message; //消息类型 WPARAM wParam; //附加信息 LPARAM lParam; //附加信息 DWORD time; //投递到消息队列的时间 POINT pt; //鼠标的位置 }MSG; // 8.2. 自定义MSG 参见博文:MFC程序最小化到系统托盘区的一个简单实例 9. 窗口过程 窗口过程函数是一个应用程序定义的函数,用来处理发送到窗口的消息。WNDPROC类型定义了一个指向该回调函数的指针。WindowProc是用于应用程序定义函数的占位符,也就是说,程序员自己更改WindowProc这个名称,但是参数类型不变。 一个Windows 程序可以包含多个窗口过程。一个窗口过程总是与调用RegisterClass注册的特定窗口类相关联。程序通常不直接调用窗口过程。窗口过程通常由 Windows 本身调用。通过调用 SendMessage 函数,程序能够直接调用它自己的窗口过程。 9.1. 函数声明 [cpp] view plaincopy LRESULT CALLBACK WindowProc( HWND hwnd, //指向窗口的句柄 UINT uMsg, //指定消息类型 WPARAM wParam, //指定其余的、消息特定的信息 LPARAM lParam //指定其余的、消息特定的信息 ); 9.2. 函数功能 该函数主要是用来处理发送给窗口的各类MSG消息。 窗口过程在处理消息时,必须返回0。 窗口过程不予处理的所有消息应该被传给名为DefWindowProc() 函数。 从 DefWindowProc 返回的值必须由窗口过程返回。 本文部分内容来自MSDN和网络。 转载请注明地址: JarvisChu的专栏
引用:http://fish2700.blog.163.com/blog/static/130713192009103035723281/ 使用Method反射调用函数时,我们通常会遇到以下几种情况: public void test(){ System.out.println(&quot;函数参数:0&quot;); } public void test(String str){ System.out.println("函数参数:1----------" + str); } public void test(String str1, String str2){ System.out.println("函数参数:2----------" + str1 +" " + str2); } public void test(Object...objs){ System.out.print("函数参数:" + objs.length + "----------------"); for(Object o : objs ){ System.out.print(o.toString() + " "); } } 而当我们使用Class.getMethod,则需要传递所调用函数的参数类型。查看Class.getMethod 的API可知,需要传递的类型被表示为一个可变参数。 我们知道,传递可变参数时,非序列参数会被编译成编列,即变成一个Object[]类型的数组,但是本身为序列的则会直接被转型Object[]数组。 那么,前三种情况按照要求传递,则传递给getMethod的参数会被转变为一个一维的参数列表的Object数组。第四种情况,其函数本身便要求传递一个可变参数,即一个Object[]类型的参数。如果我们按照正常方法传递,则此Object[]类型的参数会被直接转型使用,而我们最终传给函数的应该是一个二维的Ojbect数组,即Object[][]类型。getMethod方法的匹配过程是指寻找参数长度与Object数组的长度相等,且每个参数类型与Object数组每个数组项相同的方法。 所以,再这种情况下,我们应当对每四种情况下将要传递的参数进行一次包装,将其包装成一个二维的Object数组。方法如下: Object[] obj = new Object[1]; String[] strs = new String[]{"xiao","she", "qing"}; obj[0] = strs ; 此时的obj则是我们将要传给Class.getMethod的参数,而strs则是我们要传递给调用函数test(Object...objs)的参数。这里的obj长度为1是因为可变参数在没有参数传递之前的检查时的长度为1,被视为一元参数。 由于Spring使用的是Java代理,所以,在Spring中会经常遇到类似的问题。 具体代码如下: package test; import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method; public class MethodDemo { public void test(){ System.out.println("函数参数:0"); } public void test(String str){ System.out.println("函数参数:1----------" + str); } public void test(String str1, String str2){ System.out.println("函数参数:2----------" + str1 + " " + str2); } public void test(Object...objs){ System.out.print("函数参数:" + objs.length + "----------------"); for(Object o : objs ){ System.out.print(o.toString() + " "); } } /** * @param args */ /** * @param args */ public static void main(String[] args) { //testMethod(); printMethodType(); } public static void printMethodType(){ Method[] methods = MethodDemo.class.getMethods(); Class[] cs; for(Method m : methods){ System.out.println("----------------" + m.getName() + "----------------"); cs = m.getParameterTypes(); System.out.println(cs.length); for(Class c : cs){ System.out.println(c.toString()); } } } public static void testMethod(){ MethodDemo demo = new MethodDemo(); Method method; try { method = MethodDemo.class.getMethod("test", null); method.invoke(demo, null); System.out.println("--------------------------------------"); String content = "xiao"; method = MethodDemo.class.getMethod("test", String.class); method.invoke(demo, content); System.out.println("--------------------------------------"); String str1 = "xiao"; String str2 = "qing"; method = MethodDemo.class.getMethod("test", String.class, String.class); method.invoke(demo, str1, str2); System.out.println("--------------------------------------"); Object[] obj = new Object[1]; obj[0] = new String[]{"xiao", "she", "qing"}; //obj[1] = new String[]{"xiao", "qing"}; method = MethodDemo.class.getMethod("test", Object[].class); method.invoke(demo, obj); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
引用:http://jojol-zhou.iteye.com/blog/729254 Android 命令行手动编译打包过程图 【详细步骤】: 1使用aapt生成R.java类文件: 例: F:\explorer\android-sdk-windows2.2\tools>f:\explorer\android-sdk-windows2.2\platforms\android-3\tools\aapt.exe package -f -m -J F:\explorer\AndroidByread\gen -S res -I f:\explorer\android-sdk-windows2.2\platforms\android-3\android.jar -M AndroidManifest.xml 其中 -f -m -J F:\explorer\AndroidByread\gen 代表按覆盖的形式在gen目录下生成带包路径的R.java,-S res指定资源文件 ,-I f:\explorer\android-sdk-windows2.2\platforms\android-3\android.jar 指定使用的android类,-M AndroidManifest.xml指定程序的配置文件 aapt Usage: 2使用android SDK提供的aidl.exe把.aidl转成.java文件: usage: aidl OPTIONS INPUT [OUTPUT] aidl --preprocess OUTPUT INPUT... OPTIONS: -I<DIR> search path for import statements. -d<FILE> generate dependency file. -p<FILE> file created by --preprocess to import. -o<FOLDER> base output folder for generated files. -b fail when trying to compile a parcelable. INPUT: An aidl interface file. OUTPUT: The generated interface files. 3第三步 编译.java类文件生成class文件: 例:F:\explorer\AndroidByread>javac -encoding GB18030 -target 1.5 -bootclasspath F:\explorer\android-sdk-windows2.2\platforms\android-3\android.jar -d bin src\com\byread\reader\*.java gen\com\byread\reader\R.java 4使用android SDK提供的dx.bat命令行脚本生成classes.dex文件: 例: F:\explorer\AndroidByread>F:\explorer\android-sdk-windows2.2\platforms\android-3\tools\dx.bat --dex --output=F:\explorer\AndroidByread\bin\classes.dex f:\explorer\AndroidByread\bin\classes 其中classes.dex为生成的目标文件,f:\explorer\AndroidByread\bin\classes为class文件所在目录 5使用Android SDK提供的aapt.exe生成资源包文件(包括res、assets、androidmanifest.xml等): F:\explorer\AndroidByread>F:\explorer\android-sdk-windows2.2\platforms\android-3\tools\aapt.exe package -f -M AndroidManifest.xml -S res -A assets -I F:\explorer\android-sdk-windows2.2\platforms\android-3\android.jar -F bin\byreadreader 将AndroidManifest.xml,res和assets文件夹中的资源文件打包生成byreadreader,用法参见1 6第六步 生成未签名的apk安装文件: apkbuilder ${output.apk.file} -u -z ${packagedresource.file} -f ${dex.file} -rf ${source.dir} -rj ${libraries.dir} 例: F:\explorer\AndroidByread>f:\explorer\android-sdk-windows2.2\tools\apkbuilder.bat f:\explorer\byreadreader.apk –v -u -z f:\explorer\AndroidByread\bin\byreadreader -f f:\explorer\AndroidByread\bin\class.dex -rf F:\explorer\AndroidByread\src 其中f:\explorer\byreadreader.apk为生成的apk ,-z f:\explorer\AndroidByread\bin\byreadreader为资源包,f:\explorer\AndroidByread\bin\class.dex为类文件包 7使用jdk的jarsigner对未签名的包进行apk签名: use jarsigner jarsigner -keystore ${keystore} -storepass ${keystore.password} -keypass ${keypass} -signedjar ${signed.apkfile} ${unsigned.apkfile} ${keyalias} 例如: F:\explorer\android-sdk-windows2.2\tools>jarsigner –keystore f:\explorer\eclipse3.5\bbyread.keystore -storepass byread002 -keypass byread002 -signedjar f:\explorer\byread.apk f:\explorer\byreadreader.apk byread 其中–keystore f:\explorer\eclipse3.5\bbyread.keystore 为密钥文件 -storepass byread002为密钥文件密码 byread 为密钥别名 -keypass byread002为密钥别名密码,-signedjar f:\explorer\byread.apk为签名后生成的apk文件 f:\explorer\byreadreader.apk为未签名的文件。 参考:http://asantoso.wordpress.com/2009/09/15/how-to-build-android-application-package-apk-from-the-command-line-using-the-sdk-tools-continuously-integrated-using-cruisecontrol/
引用:http://www.cnblogs.com/2050/archive/2012/08/29/2662932.html SWFUpload是一个flash和js相结合而成的文件上传插件,其功能非常强大。以前在项目中用过几次,但它的配置参数太多了,用过后就忘记怎么用了,到以后要用时又得到官网上看它的文档,真是太烦了。所以索性就把它的用法记录下来,也方便英语拙计的同学查看,利人利己,一劳永逸。 SWFUpload的特点: 1、用flash进行上传,页面无刷新,且可自定义Flash按钮的样式; 2、可以在浏览器端就对要上传的文件进行限制; 3、允许一次上传多个文件,但会有一个上传队列,队列里文件的上传是逐个进行的,服务器端接收文件时跟普通的表单上传文件是一样的; 4、提供了丰富的事件接口供开发者使用; SWFUpload的文件上传流程是这样的: 1、引入相应的js文件 2、实例化SWFUpload对象,传入一个配置参数对象进行各方面的配置。 3、点击SWFUpload提供的Flash按钮,弹出文件选取窗口选择要上传的文件; 4、文件选取完成后符合规定的文件会被添加到上传的队列里; 5、调用startUpload方法让队列里文件开始上传; 6、文件上传过程中会触发相应的事件,开发者利用这些事件来更新ui、处理错误、发出提示等等; SWFUpload包括三部分的内容:SWFUpload.js、swfupload.swf、初始化配置参数及各种事件处理函数。所以首先在页面引入SWFUpload.js ? 1 <script src='SWFUpload.js'></script> 然后实例化一个SWFUpload对象: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 var swfu; window.onload = function () { var settings_object = {//定义参数配置对象 upload_url : "http://www.swfupload.org/upload.php", flash_url : "http://www.swfupload.org/swfupload.swf", file_post_name : "Filedata", post_params : { "post_param_name_1" : "post_param_value_1", "post_param_name_2" : "post_param_value_2", "post_param_name_n" : "post_param_value_n" }, use_query_string : false, requeue_on_error : false, http_success : [201, 202], assume_success_timeout : 0, file_types : "*.jpg;*.gif", file_types_description: "Web Image Files", file_size_limit : "1024", file_upload_limit : 10, file_queue_limit : 2, debug : false, prevent_swf_caching : false, preserve_relative_urls : false, button_placeholder_id : "element_id", button_image_url : "http://www.swfupload.org/button_sprite.png", button_width : 61, button_height : 22, button_text : "<b>Click</b> <span class="redText">here</span>", button_text_style : ".redText { color: #FF0000; }", button_text_left_padding : 3, button_text_top_padding : 2, button_action : SWFUpload.BUTTON_ACTION.SELECT_FILES, button_disabled : false, button_cursor : SWFUpload.CURSOR.HAND, button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT, swfupload_loaded_handler : swfupload_loaded_function, file_dialog_start_handler : file_dialog_start_function, file_queued_handler : file_queued_function, file_queue_error_handler : file_queue_error_function, file_dialog_complete_handler : file_dialog_complete_function, upload_start_handler : upload_start_function, upload_progress_handler : upload_progress_function, upload_error_handler : upload_error_function, upload_success_handler : upload_success_function, upload_complete_handler : upload_complete_function, debug_handler : debug_function, }; swfu = new SWFUpload(settings_object);//实例化一个SWFUpload,传入参数配置对象 }; /*定义各种事件监听函数*/ function swfupload_loaded_function(){} function file_dialog_start_function(){} ...等等 我们看到要实现一个swfupload上传功能很简单,就是实例化一个swfupload对象。但繁琐的地方就在于实例化实要用到的参数配置对象,以及各种事件的发生时机以和提供的参数。所以重点来了。下面几个表格对开发中要用到的东西列举了出来,虽然已经蛮多了,但并不是swfupload的全部,我列出来的只是常用的。要查看完整的文档,请到swfupload官网上查询。 一、配置参数对象中的常用属性及说明 属性 类型 默认值 描述 upload_url String 处理上传文件的服务器端页面的url地址,可以是绝对地址,也可以是相对地址,当为相对地址时相对的是当前代码所在的文档地址 preserve_relative_urls Boolean false 如果为false则SWFUpload会把swfupload.swf用到的相对地址转换为绝对地址,以达到更好的兼容性 file_post_name String Filedata 相当于用普通的文件域上传文件时的name属性,服务器端接收页面通过该名称来获取上传的文件 post_params Object(直接量) 一个对象直接量,里面的键/值对会随着每一个文件一起上传,文件上传要附加一些信息时很有用 use_query_string Boolean false 为false时,post_params属性定义的参数会以post方式上传;为true时,则会以get方式上传(即参数会以查询字符串的形式附加到url后面) file_types String 该属性指定了允许上传的文件类型,当有多个类型时使用分号隔开,比如:*.jpg;*.png ,允许所有类型时请使用 *.* file_types_description String 指定在文件选取窗口中显示的文件类型描述,起一个提示和说明的作用吧 file_size_limit String 指定要上传的文件的最大体积,可以带单位,合法的单位有:B、KB、MB、GB,如果省略了单位,则默认为KB。该属性为0时,表示不限制文件的大小。 file_upload_limit Number 指定最多能上传多少个文件,当上传成功的文件数量达到了这个最大值后,就不能再上传文件了,也不能往上传队列里添加文件了。把该属性设为0时表示不限制文件的上传数量。 file_queue_limit Number 指定文件上传队列里最多能同时存放多少个文件。当超过了这个数目后只有当队列里有文件上传成功、上传出错或被取消上传后,等同数量的其他文件才可以被添加进来。当file_upload_limit的数值或者剩余的能上传的文件数量小于file_queue_limit时,则取那个更小的值 flash_url String swfupload.swf文件的绝对或相对地址,相对地址是指相对于当前的页面地址。实例化swfupload后,就不能再改变该属性的值了。 prevent_swf_caching Boolean 为true时会加一个随机数在swfupload.swf地址的后面,以阻止flash影片被缓存,这是为了防止某些版本的IE浏览器在读取缓存的falsh影片时出现的bug button_placeholder_id String 指定一个dom元素的id,该dom元素在swfupload实例化后会被Flash按钮代替,这个dom元素相当于一个占位符 button_placeholder DOMElement 指定一个dom元素,该dom元素在swfupload实例化后会被Flash按钮代替,这个dom元素相当于一个占位符。当button_placeholder_id与button_placeholder都存在时,以button_placeholder_id为优先 button_image_url String 指定Flash按钮的背景图片,相对地址或绝对地址都可以。该地址会受到preserve_relative_urls属性的影响,遵从与upload_url一样的规则。该背景图片必须是一个sprite图片,从上到下包含了Flash按钮的正常、鼠标悬停、按下、禁用这四种状态。因此该图片的高度应该是Flash按钮高度的四倍 button_width Number 指定Flash按钮的宽度 button_height Number 指定Flash按钮的高度,应该为button_image_url所指定的按钮背景图片高度的1/4 button_text String 指定Flash按钮上的文字,也可以是html代码 button_text_style String Flash按钮上的文字的样式,使用方法见示例 button_text_top_padding Number 指定Flash按钮顶部的内边距,可使用负值 button_text_left_padding Number 指定Flash按钮左边的内边距,可使用负值 button_disabled Boolean false 为true时Flash按钮将变为禁用状态,点击也不会触发任何行为 button_cursor 指定鼠标悬停在Flash按钮上时的光标样式,可用值为SWFUpload.CURSOR里定义的常量 button_window_mode 指定Flash按钮的WMODE属性,可用值为SWFUpload.WINDOW_MODE里定义的常量 file_dialog_start_handler Function fileDialogStart事件侦听函数 file_queued_handler Function fileQueued事件侦听函数 file_queue_error_handler Function fileQueueError事件侦听函数 file_dialog_complete_handler Function fileDialogComplete事件侦听函数 upload_start_handler Function uploadStart事件侦听函数 upload_progress_handler Function uploadProgress事件侦听函数 upload_error_handler Function uploadError事件侦听函数 upload_success_handler Function uploadSuccess事件侦听函数 upload_complete_handler Function uploadComplete事件侦听函数 二、各种事件说明 要实现与用户的交互,靠的就是在这些事件上做文章了 fileDialogStart ( ) 在文件选取窗口将要弹出时触发 fileQueued ( file object ) 当一个文件被添加到上传队列时会触发此事件,提供的唯一参数为包含该文件信息的file object对象 fileQueueError ( file object, error code, message ) 当文件添加到上传队列失败时触发此事件,失败的原因可能是文件大小超过了你允许的数值、文件是空的或者文件队列已经满员了等。该事件提供了三个参数。第一个参数是当前出现问题的文件对象,第二个参数是具体的错误代码,可以参照SWFUpload.QUEUE_ERROR中定义的常量 fileDialogComplete ( number of files selected, number of files queued, total number of files in the queued ) 当文件选取完毕且选取的文件经过处理后(指添加到上传队列),会立即触发该事件。可以在该事件中调用this.startUpload()方法来实现文件的自动上传参数number of files selected指本次在文件选取框里选取的文件数量参数number of files queued指本次被添加到上传队列的文件数量参数total number of files in the queued指当前上传队列里共有多少个文件(包括了本次添加进去的文件) uploadStart ( file object ) 当文件即将上传时会触发该事件,该事件给了你在文件上传前的最后一次机会来验证文件信息、增加要随之上传的附加信息或做其他工作。可以通过返回false来取消本次文件的上传参数file object为当前要上传的文件的信息对象 uploadProgress ( file object, bytes complete, total bytes ) 该事件会在文件的上传过程中反复触发,可以利用该事件来实现上传进度条参数file object为文件信息对象参数bytes complete为当前已上传的字节数参数total bytes为文件总的字节数 uploadError ( file object, error code, message ) 文件上传被中断或是文件没有成功上传时会触发该事件。停止、取消文件上传或是在uploadStart事件中返回false都会引发这个事件,但是如果某个文件被取消了但仍然还在队列中则不会触发该事件参数file object为文件信息对象参数error code为错误代码,具体的可参照SWFUpload.UPLOAD_ERROR中定义的常量 uploadSuccess ( file object, server data, received response ) 当一个文件上传成功后会触发该事件参数file object为文件信息对象参数server data为服务器端输出的数据 uploadComplete( file object ) 当一次文件上传的流程完成时(不管是成功的还是不成功的)会触发该事件,该事件表明本次上传已经完成,上传队列里的下一个文件可以开始上传了。该事件发生后队列中下一个文件的上传将会开始 三、swfupload实例的方法 方法中大多数是动态改变参数配置对象的方法 destroy ( ) 当不需要再使用SWFUpload了的时候,可以使用该方法来销毁它的实例和dom元素 startUpload( file_id ) 开始上传队列中指定的文件参数file_id代表要上传的文件的id,如果未填写这个参数,则会上传队列中第一个文件 cancelUpload ( file_id, trigger_error_event ) 取消文件的上传参数file_id为要取消的文件的id,如果该参数为undefined或者未填写,则会取消队列里的第一个文件参数trigger_error_event接受一个布尔值,当为false时取消文件不会触发uploadError事件,默认为true stopUpload ( ) 终止当前正在上传的文件,会触发uploadError事件。如果当前没有文件在上传,则该方法什么都不会做 getStats ( ) 获取队列的stats object setStats ( stats_object ) 修改队列的stats_object,传入修改过的stats_object作为参数 getFile ( file_id|index ) 根据文件id或文件索引来获取一个File Object,当使用文件id时只能获得队列里的文件,当使用文件索引时所有文件(包括队列内和队列外)都可获得 addPostParam ( name, value) 往配置对象中post_params指定的附加信息对象中增加键/值对 removePostParam ( name) 移除置配置对象中的post_params包含的某一个键/值对,参数name为要移除的值的键名 addFileParam ( file_id, name, value) 为某个特定文件增加随之一起上传的附加信息。注意,只有在该指定的文件上传时,附加的信息才会一起上传。而配置对象中post_param设置的附加信息在任一文件上传时都会与之一起发送。参数file_id为要指定的文件id,参数name和value分别为附加信息的名称和值 removeFileParam ( file_id, name) 移除通过addFileParam方法增加的附加信息,两个参数相信就不用我多讲了吧 setUploadURL ( url) 动态设置配置对象中upload_url的值 setPostParams ( param_object) 动态设置配置对象中post_params属性的值,新的值会覆盖旧的值。参数param_object必须为一个对象直接量,且里面的属性和值都只能为字符串 setFileTypes ( types, description) 动态设置配置对象中file_types 和 file_types_description属性的值。两个参数都不能省略 setFileSizeLimit ( file_size_limit) 动态设置配置对象中file_size_limit属性的值 setFileUploadLimit ( file_upload_limit) 动态设置配置对象中file_upload_limit属性的值 setFileQueueLimit ( file_queue_limit) 动态设置配置对象中file_queue_limit属性的值 setFilePostName ( file_post_name) 动态设置配置对象中file_post_name属性的值 setUseQueryString ( use_query_string) 动态设置配置对象中use_query_string属性的值 setButtonImageURL ( url) 动态设置配置对象中button_image_url属性的值 setButtonDimensions ( width, height) 动态设置Flash按钮的宽度和高度,两个参数分别为宽度和高度的值,类型为数字,且不能带单位 setButtonText ( text) 动态设置配置对象中button_text属性的值 setButtonTextStyle ( css_style_text) 动态设置配置对象中button_text_style属性的值 setButtonTextPadding ( left, top ) 动态设置Flash按钮的左边内边距和顶部内边距 setButtonDisabled ( isDisabled ) 动态对Flash按钮进行禁用和不禁用的操作,参数为一个布尔值 setButtonCursor ( buttonCursor ) 动态设置配置对象中button_cursor的值 四、文件信息对象 File Object 在事件监听函数中,经常要用到文件信息对象来获取文件的信息以供下一步的操作 属性 类型 描述 id String SWFUpload定义的文件id,用来控制文件的上传 index Number 文件的索引,用在getFile(i)方法中 name String 文件的原始名称,不包括路径 type String 文件类型 creationdate Date 文件的创建日期 modificationdate Date 文件的最后修改日期 filestatus Number 当前文件的状态,详细的请参照SWFUpload.FILE_STATUS中定义的常量 五、队列状态对象 Stats Object 用来获取当前队列的状况 属性 类型 描述 in_progress Number 得到的值为1或0,表明当前队列是否有文件正在上传中 files_queued Number 目前上传队列中的文件数量 successful_uploads Number 已成功上传(指触发了uploadSuccess事件)的文件数量 upload_errors Number 上传失败的文件数量(包括被取消上传的文件) upload_cancelled Number 被取消上传的文件数量 queue_errors Number 触发了fileQueueError事件的文件数量 六、一些常量 定义的一些常量,便于理解 常量名 描述 SWFUpload.instances 该常量是一个对象,代表一个页面上所有的SWFUpload实例的引用的集合,用SWFUpload实例的movieName属性进行索引 SWFUpload.movieCount 页面上存在的SWFUpload实例的数量 SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED 用户选取的文件超过了允许的数量 SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT 文件的体积超过了允许的大小 SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE 文件是空的 SWFUpload.QUEUE_ERROR.INVALID_FILETYPE 不允许的文件类型 SWFUpload.UPLOAD_ERROR.HTTP_ERROR 服务器返回的状态码不是200 SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL 没有设置 upload_url SWFUpload.UPLOAD_ERROR.IO_ERROR 读取或传输文件时发生错误 SWFUpload.UPLOAD_ERROR.SECURITY_ERROR 上传受到了安全方面的限制 SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED 上传的文件数量超过了允许的最大值 SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED 上传出现错误 SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND 给startUpload()方法传入的文件id不存在 SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED uploadStart()方法中返回了false SWFUpload.UPLOAD_ERROR.FILE_CANCELLED 上传被取消了 SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED 上传被终止了 SWFUpload.FILE_STATUS.QUEUED 文件正在队列中等待上传 SWFUpload.FILE_STATUS.IN_PROGRESS 文件正在上传 SWFUpload.FILE_STATUS.ERROR 文件在添加到队列或是上传的时候出现了错误 SWFUpload.FILE_STATUS.COMPLETE 文件已上传成功 SWFUpload.FILE_STATUS. 文件被取消上传 SWFUpload.CURSOR.ARROW 鼠标以箭头显示 SWFUpload.CURSOR.HAND 鼠标以手形显示 SWFUpload.WINDOW_MODE.WINDOW Flash按钮会显示在页面的所有dom元素上面 SWFUpload.WINDOW_MODE.OPAQUE 允许其他dom元素覆盖住Flash按钮 SWFUpload.WINDOW_MODE.TRANSPARENT 允许Flash按钮透明显示 内容太多了,感觉有点乱了,如果还不清楚怎么使用,建议看下官网的使用流程,明白怎么使用后再来看这些属性、事件、方法、常量什么的吧。
引用:http://www.cnblogs.com/devinzhang/archive/2011/12/20/2294686.html http://blog.sina.com.cn/s/blog_7bee201901013nkk.html Android工程的编译过程 现在很多人想对Android工程的编译和打包进行自动化,比如建立每日构建系统、自动生成发布文件等等。这些都需要我们对Android工程的编译和打包有一个深入的理解,至少要知道它的每一步都做了什么,需要什么环境和工具,输入和输出是什么。那么我们就来挖掘一下Android的编译过程中的细节。 首先,我们假定你的系统(什么系统都行,不限于Linux还是Windows系统,当然,我在这里默认使用Linux系统来举例子,但在 Windows中几乎没有什么差别)已经安装了JDK和Android SDK。再假定你的Android SDK的路径是ANDROID_SDK_HOME,你想要编译的Android OS版本是ANDROID_OS_VERSION(比如android-1.6、android-8、android-10等)。 我们重点关心的是(1)这个过程的输入是什么?(2)这个过程的输出是什么?(3)这个过程使用了什么工具?至于使用什么参数,可以自己去看对应命令的帮助文件,或者在网上搜索,这不是本文的重点。 步骤中提到的工具如下表: 名称 功能介绍 在操作系统中的路径 aapt Android资源打包工具 ${ANDROID_SDK_HOME}/platform-tools/appt aidl Android接口描述语言转化为.java文件的工具 ${ANDROID_SDK_HOME}/platform-tools/aidl javac Java Compiler ${JDK_HOME}/javac或/usr/bin/javac dex 转化.class文件为Davik VM能识别的.dex文件 ${ANDROID_SDK_HOME}/platform-tools/dx apkbuilder 生成apk包 ${ANDROID_SDK_HOME}/tools/opkbuilder jarsigner .jar文件的签名工具 ${JDK_HOME}/jarsigner或/usr/bin/jarsigner zipalign 字节码对齐工具 ${ANDROID_SDK_HOME}/tools/zipalign 第一步:打包资源文件,生成R.java文件【输入】Resource文件(就是工程中res中的文件)、Assets文件(相当于另外一种资源,这种资源Android系统并不像对res中的文件那样优化它)、AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、Android基础类库(Android.jar文件)【输出】打包好的资源(一般在Android工程的bin目录可以看到一个叫resources.ap_的文件就是它了)、R.java文件(在gen目录中,大家应该很熟悉了)【工具】aapt工具,它的路径在${ANDROID_SDK_HOME}/platform-tools/aapt(如果你使用的是Windows系统,按惯例路径应该这样写:%ANDROID_SDK_HOME%\platform-tools\aapt.exe,下同)。 第二步:处理AIDL文件,生成对应的.java文件(当然,有很多工程没有用到AIDL,那这个过程就可以省了)【输入】源码文件、aidl文件、framework.aidl文件【输出】对应的.java文件【工具】aidl工具 第三步:编译Java文件,生成对应的.class文件【输入】源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)【输出】.class文件【工具】javac工具 第四步:把.class文件转化成Davik VM支持的.dex文件【输入】 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)【输出】.dex文件【工具】javac工具 第五步:打包生成未签名的.apk文件【输入】打包后的资源文件、打包后类文件(.dex文件)、libs文件(包括.so文件,当然很多工程都没有这样的文件,如果你不使用C/C++开发的话)【输出】未签名的.apk文件【工具】apkbuilder工具 第六步:对未签名.apk文件进行签名【输入】未签名的.apk文件【输出】签名的.apk文件【工具】jarsigner 第七步:对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的)【输入】签名后的.apk文件【输出】对齐后的.apk文件【工具】zipalign工具 知道了这些细节之后,我们就可以实现很多我们想实现东西了,比如:自动化,我们可以使用某种脚本,像Windows下的批处理,linux下的Bash,Java下的Ant,Python、Perl这样的脚本语言,甚至直接用Java、.net这们的强类型语言也是可以的。如果你真正弄懂了上面的步骤,了解了编译打包过程的本质,你完全可以以你想要的任何方式实现它的自动化,这才是真正的“举一反三,以不变应万变”。再比如,对Android SDK的精简,大家知道,现在Android SDK动辙几百兆,我们完全可以应用上面的知识,只保留必要的工具,把SDK精简到10M以下。当然,还可以做很多事情,前提是你真正弄懂了它。
引用:http://jingyan.baidu.com/article/a65957f4b12b8724e77f9b5a.html Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5 支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。目前最新版本是7.0。 工具/原料 Tomcat Jre 步骤/方法 1 Tomcat的使用之前必须得Jre,至于是怎么装的,这个晚上的教程就和街上的白菜一样多,再次就不多说了,另外本文所说的tomcat是免安装版的。 2 当安装完成tomcat后,可以在tomcat的bin目录点击startup.bat启动tomcat服务,这是在浏览器中输入 ,如果能够看到tomcat的首页tomcat便已经安装正确,便可以使用了。 3 4 但是这样启动的话有一个不好的地方就是任务栏会一直存在着一个dos的窗口,这个窗口便是tomcat的控制台,实时的输出tomcat的运行情况,便于查找一些存在者的错误;但更多的时候是不需要这个dos的窗口的而且需要每次都开启这个服务,很是麻烦。 5 6 现在开始为tomcat做到开机并后台运行,做到简洁方便。 7 首先打开tomcat的bin目录找到service.bat 8 9 开始->运行->cmd 10 11 将service.bat左键点击拖入到dos窗口,当然也可以把该地址写进去,然后按Enter,如图.. 如果按了之后出来的不是这样,那便是安装tomcat的那个路径错了,请检查。 12 13 成功之后在dos窗口键入 service.bat install Tomcat 输完然后按Enter键,出现如下窗口,便成功了。 14 15 这一便在服务中添加了一个tomcat的服务,只需要将此服务开启即可,若要开机启动就将服务设成是自动的。 开启服务 16 17 18 好了支持tomcat的开机后台启动已近做好了,来做个页面测试下吧。 19 20 21 22 这时任务栏也没有了tomcat启动的dos窗口了 23 解决问题~~~~
引用:http://blog.chinaunix.net/uid-26863299-id-3578280.html package com.cn.fanjg.rtti; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @function: * @date : 2013-4-10 下午03:28:53 */ @SuppressWarnings("rawtypes") public class AssignValueForAttributeUtil { /** 正则表达式 用于匹配属性的第一个字母 {@value [a-zA-Z]} **/ private static final String REGEX = "[a-zA-Z]"; public static void setAttrributeValue(Object obj,String attribute,Object value) { String method_name = convertToMethodName(attribute,obj.getClass(),true); Method[] methods = obj.getClass().getMethods(); for (Method method : methods) { /** * 因为这里只是调用bean中属性的set方法,属性名称不能重复 * 所以set方法也不会重复,所以就直接用方法名称去锁定一个方法 * (注:在java中,锁定一个方法的条件是方法名及参数) * **/ if(method.getName().equals(method_name)) { Class[] parameterC = method.getParameterTypes(); try { /**如果是基本数据类型时(如int、float、double、byte、char、boolean) * 需要先将Object转换成相应的封装类之后再转换成对应的基本数据类型 * 否则会报 ClassCastException**/ if(parameterC[0] == int.class) { method.invoke(obj,((Integer)value).intValue()); break; }else if(parameterC[0] == float.class){ method.invoke(obj, ((Float)value).floatValue()); break; }else if(parameterC[0] == double.class) { method.invoke(obj, ((Double)value).doubleValue()); break; }else if(parameterC[0] == byte.class) { method.invoke(obj, ((Byte)value).byteValue()); break; }else if(parameterC[0] == char.class) { method.invoke(obj, ((Character)value).charValue()); break; }else if(parameterC[0] == boolean.class) { method.invoke(obj, ((Boolean)value).booleanValue()); break; }else { method.invoke(obj,parameterC[0].cast(value)); break; } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } } } private static String convertToMethodName(String attribute,Class objClass,boolean isSet) { /** 通过正则表达式来匹配第一个字符 **/ Pattern p = Pattern.compile(REGEX); Matcher m = p.matcher(attribute); StringBuilder sb = new StringBuilder(); /** 如果是set方法名称 **/ if(isSet) { sb.append("set"); }else{ /** get方法名称 **/ try { Field attributeField = objClass.getDeclaredField(attribute); /** 如果类型为boolean **/ if(attributeField.getType() == boolean.class||attributeField.getType() == Boolean.class) { sb.append("is"); }else { sb.append("get"); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } /** 针对以下划线开头的属性 **/ if(attribute.charAt(0)!='_' && m.find()) { sb.append(m.replaceFirst(m.group().toUpperCase())); }else{ sb.append(attribute); } return sb.toString(); } public static Object getAttrributeValue(Object obj,String attribute) { String methodName = convertToMethodName(attribute, obj.getClass(), false); Object value = null; try { /** 由于get方法没有参数且唯一,所以直接通过方法名称锁定方法 **/ Method methods = obj.getClass().getDeclaredMethod(methodName); if(methods != null) { value = methods.invoke(obj); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return value; } }
引用:http://www.blogjava.net/jxlazzw/articles/389452.html 现有每多个javabean,但是每个bean中都有不同的属性,并且都是通过get和set方法来修改和获取值。如果调试一步一步去猜内部结构,想用一个方法可以获取不同对像中各个属性的值,怎么办呢?可以利用JAVA的反射机制。 1.此为小引 //运用反射机制遍历单个对像中每个属性值 public static void Reflect_Object(Object o,String classPath){ try { Class userClass = Class.forName(classPath);//加载类 Method[] methods = userClass.getDeclaredMethods();//获得类的方法集合 //遍历方法集合 for(int i =0 ;i<methods.length;i++){ //获取所有getXX()的返回值 //methods[i].getName()方法返回方法名 if(methods[i].getName().startsWith("get")){ Object object = methods[i].invoke(o, null); System.out.println(" "+methods[i].getName()+"="+object); } } System.out.println("===="); } catch (Exception e) { e.printStackTrace(); } } 2.若遍历的bean有父类,则方法1则取不到父类中各个属性的值,故本类为扩充,不仅可以取到bean中每个属性的值,连父类的属性值也可以一并获得。 @SuppressWarnings("unchecked") public static void Reflect_Object(Object o,String classPath){ try { Class _class = Class.forName(classPath);// 加载类// Method[] methods = _class.getDeclaredMethods();// 获得类的方法集合 recursive(o,_class); // 遍历方法集合// for (int i = 0; i < methods.length; i++) {// // 获取所有getXX()的返回值// if (methods[i].getName().startsWith("get")) {// 方法返回方法名// methods[i].setAccessible(true);//允许private被访问(以避免private getXX())// Object object = methods[i].invoke(o, null);// System.out.println(" " + methods[i].getName() + "=" + object);// }// } // 遍历父类方法集合// if (_class.getGenericSuperclass() != null) {// Class superClass = _class.getSuperclass();// 父类// Method[] superMethods = superClass.getDeclaredMethods();//父类方法// for (int j = 0; j < superMethods.length; j++) {// // 获取所有getXX()的返回值 Method method :// if (superMethods[j].getName().startsWith("get")) {// 方法返回方法名// methods[j].setAccessible(true);//允许private被访问// Object object = superMethods[j].invoke(o, null);// System.out.println(" " + superMethods[j].getName()+ "=" + object);// }// }// Field[] fields = superClass.getDeclaredFields();//父类变量// for(Field field : fields) {// System.out.println(field);// }// } System.out.println("===="); } catch (Exception e) { e.printStackTrace(); } } //递归遍历类及父类的属性值 @SuppressWarnings("unchecked") public static Class recursive(Object o,Class _class){ if(_class==null) return null; else{ Method[] methods = _class.getDeclaredMethods();// 获得类的方法集合 // 遍历方法集合 for (int i = 0; i < methods.length; i++) { // 获取所有getXX()的返回值 if (methods[i].getName().startsWith("get")) {// 方法返回方法名 methods[i].setAccessible(true);//允许private被访问(以避免private getXX()) Object object; try { object = methods[i].invoke(o, null); System.out.println(" " + methods[i].getName() + "=" + object); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } System.out.println("===="+_class.getName()); return recursive(o,_class.getSuperclass()); } }
引用:http://104zz.iteye.com/blog/1694762 第一:我们先看下质量压缩方法: Java代码 private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中 options -= 10;//每次都减少10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片 return bitmap; } 第二:图片按比例大小压缩方法(根据路径获取图片并压缩): Java代码 private Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;//这里设置高度为800f float ww = 480f;//这里设置宽度为480f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); return compressImage(bitmap);//压缩好比例大小后再进行质量压缩 } 第三:图片按比例大小压缩方法(根据Bitmap图片压缩): Java代码 private Bitmap comp(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;//这里设置高度为800f float ww = 480f;//这里设置宽度为480f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); return compressImage(bitmap);//压缩好比例大小后再进行质量压缩 }
引用:http://xiaohuafyle.iteye.com/blog/1607258 通过反射创建新的类示例,有两种方式: Class.newInstance() Constructor.newInstance() 以下对两种调用方式给以比较说明: Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数; Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。 Class.newInstance() 抛出所有由被调用构造函数抛出的异常。 Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的; Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。 Class A(被调用的示例): Java代码 public class A { private A() { System.out.println("A's constructor is called."); } private A(int a, int b) { System.out.println("a:" + a + " b:" + b); } } Class B(调用者): Java代码 public class B { public static void main(String[] args) { B b=new B(); out.println("通过Class.NewInstance()调用私有构造函数:"); b.newInstanceByClassNewInstance(); out.println("通过Constructor.newInstance()调用私有构造函数:"); b.newInstanceByConstructorNewInstance(); } /*通过Class.NewInstance()创建新的类示例*/ private void newInstanceByClassNewInstance(){ try {/*当前包名为reflect,必须使用全路径*/ A a=(A)Class.forName("reflect.A").newInstance(); } catch (Exception e) { out.println("通过Class.NewInstance()调用私有构造函数【失败】"); } } /*通过Constructor.newInstance()创建新的类示例*/ private void newInstanceByConstructorNewInstance(){ try {/*可以使用相对路径,同一个包中可以不用带包路径*/ Class c=Class.forName("A"); /*以下调用无参的、私有构造函数*/ Constructor c0=c.getDeclaredConstructor(); c0.setAccessible(true); A a0=(A)c0.newInstance(); /*以下调用带参的、私有构造函数*/ Constructor c1=c.getDeclaredConstructor(new Class[]{int.class,int.class}); c1.setAccessible(true); A a1=(A)c1.newInstance(new Object[]{5,6}); } catch (Exception e) { e.printStackTrace(); } } } 输入结果如下: 通过Class.NewInstance()调用私有构造函数: 通过Class.NewInstance()调用私有构造函数【失败】 通过Constructor.newInstance()调用私有构造函数: A's constructor is called. a:5 b:6 说明方法newInstanceByClassNewInstance调用失败,而方法newInstanceByConstructorNewInstance则调用成功。 如果被调用的类的构造函数为默认的构造函数,采用Class.newInstance()则是比较好的选择, 一句代码就OK;如果是老百姓调用被调用的类带参构造函数、私有构造函数, 就需要采用Constractor.newInstance(),两种情况视使用情况而定。 不过Java Totorial中推荐采用Constractor.newInstance()。
引用:http://abc20899.iteye.com/blog/1096620 1。获取资源的输入流 资源文件 sample.txt 位于 $PROJECT_HOME/assets/ 目录下,可以在 Activity 中通过 Context.getAssets().open(“sample.txt”) 方法获取输入流。 注意:如果资源文件是文本文件则需要考虑文件的编码和换行符。建议使用UTF-8和Unix换行符。 2. WebView 加载assets目录下的html文件 资源文件 sample.html 位于 $PROJECT_HOME/assets/ 目录下,可以通过以下代码 WebView.loadUrl(“file:///android_asset/sample.html”); 加载html文件。 Android 系统为每个新设计的程序提供了/assets目录,这个目录保存的文件可以打包在程序里。/res 和/assets的不同点是,android不为/assets下的文件生成ID。如果使用/assets下的文件,需要指定文件的路径和文件名。下面这个例子,显示如何访问/assets下的内容。 在文件中/assets 中建立/image子目录,将/res/drawable下的icon.png子目录拷贝到该目录中。在/assets子目录中建立readme.txt文件,文件中输入文本“hello,world!!!”。 布局文件:main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <EditText android:id="@+id/firstId" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <EditText android:id="@+id/secondId" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> 程序文件: package com.cn.getassets; import android.app.Activity; import android.os.Bundle; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import android.widget.EditText; public class GetAssets extends Activity { private EditText firstField; private EditText secondField; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Log.d("show main.xml","ok"); setContentView(R.layout.main); Log.d("show main.xml","ok"); AssetManager assetManager = getAssets(); String[] files = null; try { files = assetManager.list("image"); } catch (IOException e) { Log.e("tag", e.getMessage()); } firstField = (EditText) findViewById(R.id.firstId); firstField.setText(Integer.toString(files.length)+"file.File name is"+ files[0]); InputStream inputStream = null; try { inputStream = assetManager.open("readme.txt"); } catch (IOException e) { Log.e("tag", e.getMessage()); } String s = readTextFile(inputStream); secondField = (EditText) findViewById(R.id.secondId); secondField.setText(s); } private String readTextFile(InputStream inputStream) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte buf[] = new byte[1024]; int len; try { while ((len = inputStream.read(buf)) != -1) { outputStream.write(buf, 0, len); } outputStream.close(); inputStream.close(); } catch (IOException e) { } return outputStream.toString(); } }
引用:http://blog.csdn.net/ahwr24/article/details/7255379 try {//得到类对象Class c = Class.forName("完整类名");Object yourObj = c.newInstance();//得到方法Method methlist[] = cls.getDeclaredMethods();for (int i = 0; i < methlist.length; i++) {Method m = methlist[i];}//获取到方法对象,假设方法的参数是一个int,method名为setAgeMethod sAge = c.getMethod("setAge", new Class[] {int.class});//获得参数ObjectObject[] arguments = new Object[] { new Integer(37)};//执行方法sAge.invoke(yourObj , arguments);} catch (Exception e) {}
引用:http://andrewyu.blog.51cto.com/1604432/544659 Tomcat7跟以前的版本一样,默认的发布程序是/usr/local/tomcat/webapps/ROOT下面,即我们必须将测试文件放在此目录下才能进行测试,如何更改ROOT路径呢?我们可以修改vim /usr/local/tomcat/webapps/conf/server.xml,我的文件如下:<Host name="localhost" appBase="/data/htdocs/www" unpackWARs="true" autoDeploy="true"></Host></Engine>在<host></host>标签之间添加上:<Context path="" docBase="example" debug="0" reloadable="true" />注:path是说明虚拟目录的名字,如果你要只输入ip地址就显示主页,则该键值留为空;docBase是虚拟目录的路径,它默认的是$tomcat/webapps/ROOT目录,现在我在webapps目录下建了一个example目录,让该目录作为我的默认目录。debug和reloadable一般都分别设置成0和true,重启tomcat应该就可以生效了。 本文出自 “抚琴煮酒” 博客,请务必保留此出处http://andrewyu.blog.51cto.com/1604432/544659
引用:http://www.oschina.net/p/glances glances 是一款用于 Linux、BSD 的开源命令行系统监视工具,它使用 Python 语言开发,能够监视 CPU、负载、内存、磁盘 I/O、网络流量、文件系统、系统温度等信息。 glances 可以为 Unix 和 Linux 性能专家提供监视和分析性能数据的功能,其中包括: CPU 使用率 内存使用情况 内核统计信息和运行队列信息 磁盘 I/O 速度、传输和读/写比率 文件系统中的可用空间 磁盘适配器 网络 I/O 速度、传输和读/写比率 页面空间和页面速度 消耗资源最多的进程 计算机信息和系统资源 glances 工具可以在用户的终端上实时显示重要的系统信息,并动态地对其进行更新。这个高效的工具可以工作于任何终端屏幕。另外它并不会消耗大量的 CPU 资源,通常低于百分之二。glances 在屏幕上对数据进行显示,并且每隔两秒钟对其进行更新。您也可以自己将这个时间间隔更改为更长或更短的数值。glances 工具还可以将相同的数据捕获到一个文件,便于以后对报告进行分析和绘制图形。输出文件可以是电子表格的格式 (.csv) 或者 html 格式。
hadoop集群管理工具? puppet python 把其中一张表拆分为几个小表,目的是把一张表和一张表的关联转换为多张小表和一张表的关联,最后合并 Hive中有分区的概念 可以将大的表按照日期或者地域之类的进行分区 这样在在连接的时候 中间结果能小很多 spring-data-hadoop
引用:http://blog.csdn.net/mengxiangbaidu/article/details/7020484 1.安装JDK, 2.安装, [php] view plaincopy apt-get install tomcat7 3.tomcat7的几个目录 [php] view plaincopy /usr/share/tomcat7 /var/lib/tomcat7/ /etc/tomcat7 [php] view plaincopy root@coder-671T-M:/usr/share/tomcat7# tree . ├── bin │ ├── bootstrap.jar │ ├── catalina.sh │ ├── catalina-tasks.xml │ ├── configtest.sh │ ├── digest.sh │ ├── setclasspath.sh │ ├── shutdown.sh │ ├── startup.sh │ ├── tomcat-juli.jar -> ../../java/tomcat-juli.jar │ ├── tool-wrapper.sh │ └── version.sh ├── conf -> /etc/tomcat7 ├── defaults.md5sum ├── defaults.template ├── lib │ ├── annotations-api.jar -> ../../java/tomcat-annotations-api-7.0.21.jar │ ├── catalina-ant.jar -> ../../java/catalina-ant-7.0.21.jar │ ├── catalina-ha.jar -> ../../java/tomcat-catalina-ha-7.0.21.jar │ ├── catalina.jar -> ../../java/tomcat-catalina-7.0.21.jar │ ├── catalina-tribes.jar -> ../../java/catalina-tribes-7.0.21.jar │ ├── commons-dbcp.jar -> ../../java/commons-dbcp.jar │ ├── commons-pool.jar -> ../../java/commons-pool.jar │ ├── el-api.jar -> ../../java/tomcat-el-api-2.2.jar │ ├── jasper-el.jar -> ../../java/tomcat-jasper-el-7.0.21.jar │ ├── jasper.jar -> ../../java/tomcat-jasper-7.0.21.jar │ ├── jsp-api.jar -> ../../java/tomcat-jsp-api-2.2.jar │ ├── servlet-api.jar -> ../../java/tomcat-servlet-api-3.0.jar │ ├── tomcat-api.jar -> ../../java/tomcat-api-7.0.21.jar │ ├── tomcat-coyote.jar -> ../../java/tomcat-coyote-7.0.21.jar │ ├── tomcat-i18n-es.jar -> ../../java/tomcat-i18n-es-7.0.21.jar │ ├── tomcat-i18n-fr.jar -> ../../java/tomcat-i18n-fr-7.0.21.jar │ ├── tomcat-i18n-ja.jar -> ../../java/tomcat-i18n-ja-7.0.21.jar │ └── tomcat-util.jar -> ../../java/tomcat-util-7.0.21.jar ├── logs -> /var/lib/tomcat7 └── webapps 5 directories, 31 files root@coder-671T-M:/var/lib/tomcat7# tree . ├── catalina.2011-11-28.log ├── catalina.out ├── common │ └── classes ├── conf -> /etc/tomcat7 ├── localhost.2011-11-28.log ├── localhost_access_log.2011-11-28.txt ├── logs -> ../../log/tomcat7 ├── server │ └── classes ├── shared │ └── classes ├── webapps │ ├── index.html │ └── ROOT │ ├── index.html │ └── META-INF │ └── context.xml └── work -> ../../cache/tomcat7 12 directories, 7 files [php] view plaincopy root@coder-671T-M:/etc/tomcat7# tree . ├── Catalina │ └── localhost ├── catalina.properties ├── context.xml ├── logging.properties ├── policy.d │ ├── 01system.policy │ ├── 02debian.policy │ ├── 03catalina.policy │ ├── 04webapps.policy │ └── 50local.policy ├── server.xml ├── tomcat-users.xml └── web.xml 3 directories, 11 files 4. tomcat7的几个命令 [php] view plaincopy root@coder-671T-M:/var/lib/tomcat7# /etc/init.d/tomcat7 start * Starting Tomcat servlet engine tomcat7 [ OK ] root@coder-671T-M:/var/lib/tomcat7# /etc/init.d/tomcat7 restart * Stopping Tomcat servlet engine tomcat7 [ OK ] * Starting Tomcat servlet engine tomcat7 [ OK ] root@coder-671T-M:/var/lib/tomcat7# /etc/init.d/tomcat7 stop * Stopping Tomcat servlet engine tomcat7 [ OK ] root@coder-671T-M:/var/lib/tomcat7# /etc/init.d/tomcat7 start * Starting Tomcat servlet engine tomcat7 [ OK ] root@coder-671T-M:/var/lib/tomcat7# /usr/share/tomcat7# /usr/share/tomcat7/bin/shutdown.sh /usr/share/tomcat7# /usr/share/tomcat7/bin/startup.sh 5.tomcat的网站目录,默认是 [php] view plaincopy root@coder-671T-M:/usr/share/tomcat7-root# ll 总用量 20 drwxr-xr-x 3 root root 4096 2011-11-28 15:35 ./ drwxr-xr-x 409 root root 12288 2011-11-28 15:35 ../ drwxr-xr-x 3 root root 4096 2011-11-28 15:35 default_root/ root@coder-671T-M:/usr/share/tomcat7-root# tree . └── default_root ├── index.html └── META-INF └── context.xml 2 directories, 2 files root@coder-671T-M:/usr/share/tomcat7-root# 而不是webapps 6.修改tomcat的访问端口为80 (1)更改/etc/tomcat7/server.xml中的 <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" redirectPort="8443" /> (2)从ubuntu10.04起,默认是关闭1024一下的端口.需要手工打开,可以到编辑/etc/default/tomcat7中的最后一行AUTHBIND=yes来实现 默认是#AUTHBIND=no 具体操作步骤如下: -、sudo vi /etc/tomcat7/server.xml,修改为port="80" -、sudo vi /etc/default/tomcat7 ,修改为AUTHBIND=yes -、sudo /etc/init.d/tomcat7 restart,进行重新启动 -、netstat -ant,查看80端口是否起来
引用:http://blog.csdn.net/nairuohe/article/details/6175243 /etc/tomcat6 - 全局配置 /usr/share/tomcat6/ - 程序主目录 /usr/share/tomcat6/conf/Catalina/localhost/ - 本机部署的 Catalina 配置 /var/lib/tomcat6/ - 工作主目录 /var/lib/tomcat6/webapps - (应用文件实际存放于此) /var/lib/tomcat6/work - 动态工作目录(动态编译的 .jsp 存放于此)
http://dev.10086.cn/cmdn/wiki/index.php?doc-view-4075.html http://dev.10086.cn/cmdn/wiki/index.php?doc-view-6204.html http://blog.csdn.net/yaoyeyzq/article/details/6399351
引用:http://www.kunli.info/2011/08/21/android-native-code-study-note/ JNI,全称Java Native Interface,是用于让运行在JVM中的Java代码和运行在JVM外的Native代码(主要是C或者C++)沟通的桥梁。代码编写者即可以使用JNI从Java的程序中调用Native代码,又可以从Native程序中调用Java代码。这样,编程人员可以将低阶的代码逻辑包装到高阶的程序框架中,获得高性能高效率的同时保证了代码框架的高抽象性。 在Android中,仅有以下类库是允许在JNI中使用的: libc (C library) headers libm (math library) headers JNI interface headers libz (Zlib compression) headers liblog (Android logging) header OpenGL ES 1.1 (3D graphics library) headers (since 1.6) A Minimal set of headers for C++ support JNI本身仅仅是一个把两者融合的工具,作为编程者需要做的,就是在Java代码和Native代码中按照固定的格式告诉JNI如何调用对方。在Android中,有两种方式可以调用JNI,一种是Google release的专门针对Android Native开发的工具包,叫做NDK。去Android网站上下载该工具包后,就可以通过阅读里面的文档来setup一个新的包含Native代码的工程,创建自己的Android.mk文件,编译等等;另一种是完整的源码编译环境 ,也就是通过git从官方网站获取完全的Android源代码平台。这个平台中提供有基于make的编译系统。更多细节请参考这里。不管选择以上两种方法的哪一个,都必须编写自己的Android.mk文件,有关该文件的编写请参考相关文档。 下面通过一个简单的使用例子来讲解JNI。Android给C和C++提供的是两套不同的Native API,本文仅以C++举例说明。假设这么一个需求,Java代码需要打印一个字符串,而该字符串需要Native代码计算生成。对应的JNI流程是这样的: 1. 在准备打印字符串的Android类中,添加两段代码。 第一段是: private native String getPrintStr(); 这一行代码的目的是告诉JNI,这个Java文件中有这么一个函数,该函数是在Native代码中执行的,Native代码会返回一个字符串供Java代码来输出。 第二段是: try {System.loadLibrary(“LIBNAME” } catch (UnsatisfiedLinkError ule) {Log.e(TAG, “Could not load native library”);} 这两行代码是告诉JNI,你需要找的所有Native函数都在libLIBNAME.so这个动态库中。注意JNI会自动补全lib和so给LIBNAME,你只需要提供LIBNAME给loadLibrary就行了。在最后执行的时候,JNI会先找到这个动态库,然后找里面的OnLoad函数,具体注册流程由OnLoad函数接管。 关于如何确定这个LIBNAME,和如何定义OnLoad函数,下面就会讲。 2. 上面的第一步是告诉JNI,java代码需要和Native代码交互,同时把在哪里找,找什么都通知了。接下来的事情就由Native端接管。如果把上面的getPrintString函数申明比作原型,那么本地代码中的具体函数定义就应该和该原型匹配,JNI才能知道具体在哪里执行代码。具体来说,应该有一个对应的Native函数,有和Java中定义的函数同样的参数列表以及返回值。另外,还需要有某种机制让JNI将两者相互映射,方便参数和返回值的传递。在老版的JNI中,这是通过丑陋的命名匹配实现的,比如说在Java中定义的函数名是getPrintStr, 该函数属于package java.come.android.xxx,那么中对应Native代码中的函数名就应该是Java_com_android_xxx_getPrintStr。这样给开发人员带来了很多不便。可以用javah命令来生成对应Java code中定义函数的Native code版本header文件,从中得知传统的匹配方法是如何做的。具体过程如下: 通过SDK的方式编译Java代码。 找到Eclipse的工程目录,进入bin目录下。这里是编译出的java文件所对应的class文件所在。 假设包括Native函数调用的java文件属于com.android.xxx package,名字叫test.java,那么在bin下执行javah -jni com.android.xxx.test 执行完后,可以看到一个新生成的header文件,名字为com_android_xxx_test.h。打开后会发现已经有一个函数申明,函数名为java_com_android_xxx_test_getPrintStr。这个名字就包括了该函数所对应Java版本所在的包,文件以及名称。这就是JNI传统的确定名字的方法。 值得注意的是,header文件中不仅包含了基于函数名的映射信息,还包含了另一个重要信息,就是signature。一个函数的signature是一个字符串,描述了这个函数的参数和返回值。其中”()” 中的字符表示参数,后面的则代表返回值。例如”()V” 就表示void Func(); “(II)V” 表示 void Func(int, int); 数组则以”["开始,用两个字符表示。 具体的每一个字符的对应关系如下: 字符 Java类型 C类型 V void void I jint int Z jboolean boolean J jlong long D jdouble double F jfloat float B jbyte byte C jchar char S jshort short 上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject。 一个例外是String类,其对应的类为jstring。举例: Ljava/lang/String; String jstring Ljava/net/Socket; Socket jobject 如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z" 这个signature非常重要,是下面要介绍的新版命名匹配方法的关键点之一。所以,即使传统的命名匹配已经不再使用,javah这一步操作还是必须的,因为可以从中得到Java代码中需要Native执行的函数的签名,以供后面使用。 3. 在新版(版本号大于1.4)的JNI中,Android提供了另一个机制来解决命名匹配问题,那就是JNI_OnLoad。正如前面所述,每一次JNI执行Native代码,都是通过调用JNI_OnLoad实现的。下面的代码是针对本例的OnLoad代码: /* Returns the JNI version on success, -1 on failure. jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed"); goto bail; } assert(env != NULL); if (!register_Test(env)) { LOGE("ERROR: Test native registration failed"); goto bail; } /* success -- return valid version number */ result = JNI_VERSION_1_4; bail: return result; } 分析这个函数。首先,OnLoad通过GetEnv函数获取JNI的环境对象,然后通过register_Test来注册Native函数。register_Test的实现如下: int register_Test(JNIEnv *env) { const char* const ClassPathName = "com/android/xxx/test"; return registerNativeMethods(env, ClassPathName, TestMethods, sizeof(TestMethods) / sizeof(TestMethods[0])); } 在这里,ClassPathName是Java类的全名,包括package的全名。只是用 “/” 代替 ”.” 。然后我们把类名以及TestMethods这个参数一同送到registerNativeMethods这个函数中注册。这个函数是基于JNI_OnLoad的命名匹配方式的重点。 在JNI中,代码编写者通过函数signature名和映射表的配合,来告诉JNI_OnLoad,你要找的函数在Native代码中是如何定义的(signature),以及在哪定义的(映射表)。关于signature的生成和含义,在上面已经介绍。而映射表,是Android使用的一种用于映射Java和C/C++函数的数组,这个数组的类型是JNINativeMethod,定义为: typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; 其中,第一个变量是Java代码中的函数名称。第二个变量是该函数对应的Native signature。第三个变量是该函数对应的Native函数的函数指针。例如,在上面register_Test的函数实现中,传给registerNativeMethods的参数TestMethods就是映射表,定义如下: static JNINativeMethod TestMethods[] = { {“getPrintStr”, “()Ljava/lang/String”, (void*)test_getPrintStr} }; 其中getPrintStr是在Java代码中定义的函数的名称,()Ljava/lang/String是签名,因为该函数无参数传入,并返回一个String。test_getPrintStr则是我们即将在Native code中定义的函数名称。该映射表和前面定义的类名ClassPathName一起传入registerNativeMethods: static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* Methods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { LOGE(“Native registration unable to find class ‘%s’”, className); return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { LOGE(“RegisterNatives failed for ‘%s’”, className); return JNI_FALSE; } return JNI_TRUE; } 在这里,先load目标类,然后注册Native函数,然后返回状态。 可以看出,通过映射表方式,Java code中的函数名不须再和Native code中的函数名呆板对应。只需要将函数注册进映射表中,Native code的函数编写就有了很大的灵活性。虽说和前一种传统的匹配方法比,这种方式并没有效率上的改进,因为两者本质上都是从JNI load开始做函数映射。但是这一种register的方法极大降低了两边的耦合性,所以实际使用中会受欢迎得多。比如说,由于映射表是一个<名称,函数指针>对照表,在程序执行时,可多次调用registerNativeMethods()函数来更换本地函数指针,而达到弹性抽换本地函数的目的。 4. 接下来本应介绍test_getPrintStr。但在此之前,简单介绍Android.mk,也就是编译NDK所需要的Makefile,从而完成JNI信息链的讲解。Android.mk可以基于模版修改,里面重要的变量包括: LOCAL_C_INCLUDES:包含的头文件。这里需要包含JNI的头文件。 LOCAL_SRC_FILES: 包含的源文件。 LOCAL_MODULE:当前模块的名称,也就是第一步中我们提到的LIBNAME。注意这个需要加上lib前缀,但不需要加.so后缀,也就是说应该是libLIBNAME。 LOCAL_SHARED_LIBRARIES:当前模块需要依赖的共享库。 LOCAL_PRELINK_MODULE:该模块是否被启动就加载。该项设置依具体程序的特性而定。 5. 至此,JNI作为桥梁所需要的所有信息均已就绪。JNI知道在调用Java代码中的getPrintStr函数时,需要执行Native代码。于是通过System.loadLibrary所加载的libLIBNAME.so找到OnLoad入口。在OnLoad中,JNI发现了函数映射表,发现getPrintStr对应的Native函数是test_getPrintStr。于是JNI将参数(如果有的话)传递给test_getPrintStr并执行,再将返回值(如果有的话)传回Java中的getPrintStr。 6. 用于最后测试的test_getPrintStr函数实现如下: const jstring testStr = env->NewStringUTF(“hello, world”); return testStr; 然后在Java代码中打印出返回的字符串即可。这个网页详细介绍了env可以调用的所有方法。 7. 关于测试时使用Log。调用JNI进行Native Code的开发有两种环境,完整源码环境以及NDK。两种环境对应的Log输出方式也并不相同,差异则主要体现在需要包含的头文件中。如果是在完整源码编译环境下,只要include <utils/Log.h>头文件(位于Android-src/system/core/include/cutils),就可以使用对应的LOGI、LOGD等方法了,当然LOG_TAG,LOG_NDEBUG等宏值需要自定义。如果是在NDK环境下编译,则需要include <android/log.h>头文件(位于ndk/android-ndk-r4/platforms/android-8/arch-arm/usr/include/android/),另外自己定义宏映射,例如: #include <android/log.h> #ifndef LOG_TAG #define LOG_TAG “MY_LOG_TAG” #endif #define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) #define LOGI(…) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGW(…) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) #define LOGE(…) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define LOGF(…) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) 另外,在Android.mk文件中对类库的应用在两种环境下也不相同。如果是NDK环境下,需要包括 LOCAL_LDLIBS := -llog 而在完整源码环境下,则需要包括 LOCAL_SHARED_LIBRARIES := libutils libcutils 8. 如果希望知道如何在Native中访问Java类的私有域和方法,请参考这篇文章 Published 2011-08-21 Filed in 杂项 and tagged android, jni, NDK
引用:http://www.cnblogs.com/ychellboy/archive/2013/02/22/2922683.html 使用NDK在android上做开发是一件“痛并快乐着”的差事,之所以“快乐”是因为可以将一些原有的C/C++库直接移植到android上,而不需要用java再开发一套功能相同的库。然而这同时也是一件“痛苦”的事件,因为android本身是裁减过的linux,好些system call不能使用,另外由于没有采用glibc(用的是Bionic libc,原因见wiki),好些函数所在的头文件位置也有变化,这都给移植工作带来困难。更为坑爹的是一些函数在头文件里能找到定义在具体库里确没有实现(比如:pthread_mutex_timedlock)。 android native开发在编译链接阶段会遇到上述“惨痛”经历,但更为痛苦的是好不容易变成可执行文件,一运行就crash没有任何信息。遇到这种情况,在排除了代码有低级错误的情况后,最终只能想办法做debug。(本文余下篇幅在不特殊注明的情况下都是指使用NDK在android上做native code的开发)。 在android上NDK开发的程序进行查错主要有两种方法: (1)使用log进行查错:在程序源代码上加log,根据log信息来排查错误。这种方式应该是最为常用的,因为其普适性很高。不过作为在VxWorks上移植过网络库的苦逼,深知用log排错的效率是多么的低,特别是在排查底层库时。而遇到多线程的程序,log排错是多么的无力。 (2)使用ndk-gdb调试程序:用过gdb的都知道它多么的强大,但是想要使用ndk-gdb需要做很多的配置,还会碰到很多坑,因此想真正使用起来也不是件容易的事(毕竟是开源项目,和VxWorks这种高富帅是没法比的)。 本文主要介绍如何配置使用ndk-gdb进行debug,所使用android-ndk-r8d/samples/hello-jni作为入口调用一个static library。 —— Here we go! 一、开发环境 1. ubuntu 12.04 x86_64 2. eclipse 3.7(只是为了方便启动android模拟器) 3. android NDK r8d 4. android SDK 2.2 ~ 4.2 5. ant (打包程序使用) 在windows环境下可以配置cygwin来实现ndk-gdb,本人在windows上使用相同方法也达到了效果,对cygwin的配置这里不再讨论,有疑问可以找google老师。 二、准备阶段 1. 下载linux平台的NDK,并解压到相应目录。这里需要注意的是:虽然google网站上写着NDK for Linux 32/64-bit(x86),但是ndk中的一些工具(比如NDK自带的awk,make,sed)在64bit的ubuntu上并不能直接运行,因为这些工具是32bit的程序,需要32bit的运行时库。解决方法是:sudo apt-get install libc6-i386, sudo apt-get install lib32asound2 lib32z1 lib32stdc++6 lib32bz2-1.0 安装这些常用的32位库。如果是CentOS则需要:yum install libgcc.i686 yum install glibc-static.i686 yum install glibc-devel.i686 2. 下载SDK,在下载页面的”DOWNLOAD FOR OTHER PLATFORMS“ –>“ADT Bundle”找到对应的版本下载,并解压到相应的目录。这时SDK下的platforms会有最新版本的android,下载历史版本的android就要使用tools下的工具:./android list sdk 根据列举出来的编号执行如下: ./android update sdk –t 1 –u 则更新编号是1的包。使用android update把所需要的历史版本都下载下来。 3. 根据实际情况在~/.profile(或~/.bash_profile)中设置如下环境变量,设置完毕后执行source ~/.profile使之生效: # ---- NDK ---- NDK_ROOT=~/mysoftware/NDK/android-ndk-r8d PATH=$PATH:$NDK_ROOT export NDK_ROOT # ---- android-SDK ---- ANDROID_SDK_ROOT=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk PATH=$PATH:$ANDROID_SDK_ROOT export ANDROID_SDK_ROOT # ---- adb ---- ADB_PATH=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/platform-tools PATH=$PATH:$ADB_PATH # ---- tools/android ---- PATH=$PATH:~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/tools export PATH 三、修改hello-jni 由于项目使用c++编程,做这个实验的时候就将jni/hello-jni.c 改为hello-jni.cpp,代码如下: 1: #include <string.h> 2: #include <jni.h> 3: #include <unistd.h> 4: #include "shared/thread.h" 5: 6: /* This is a trivial JNI example where we use a native method 7: * to return a new VM String. See the corresponding Java source 8: * file located at: 9: * 10: * apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java 11: */ 12: 13: using namespace shared; 14: 15: extern "C" { 16: 17: void* StartThread(void* obj) 18: { 19: return NULL; 20: } 21: 22: jstring 23: Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, 24: jobject thiz ) 25: { 26: volatile int bGo = 0; 27: while(!bGo) { 28: sleep(1); 29: } 30: 31: Thread mythread(&StartThread, NULL); 32: mythread.Start(); 33: 34: return env->NewStringUTF("Hello from JNI !"); 35: //return (*env)->NewStringUTF(env, "Hello from JNI !"); 36: } 37: 38: } 注意需要用extern “C”{ } 把Java_com_example_hellojni_HelloJni_stringFromJNI函数包起来,while (!bGo)是为了方便调试,因为ndk-gdb会先把程序run起来后再attach上去,这里需要一个while让程序等一会。上述代码中的Thread类是在libshared.a的静态库中,因此需要修改hello-jni目录下的jni/Android.mk文件。如下: 1: LOCAL_PATH := $(call my-dir) 2: 3: include $(CLEAR_VARS) 4: LOCAL_MODULE := shared 5: LOCAL_SRC_FILES := ../shared/obj/local/armeabi/libshared.a 6: LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/shared 7: include $(PREBUILT_STATIC_LIBRARY) 8: 9: include $(CLEAR_VARS) 10: LOCAL_MODULE := hello-jni 11: LOCAL_SRC_FILES := hello-jni.cpp 12: LOCAL_STATIC_LIBRARIES := shared 13: LOCAL_C_INCLUDES := $(LOCAL_PATH)/../ 14: 15: include $(BUILD_SHARED_LIBRARY) 红色部分为添加或修改项,编译前需要在环境变量C_INCLUDE_PATH中加入jni.h的路径,比如: 1: C_INCLUDE_PATH=$C_INCLUDE_PATH:~/mysoftware/NDK/android-ndk-r8d/platforms/android-8/arch-arm/usr/include 2: 3: export C_INCLUDE_PATH PS:libshared.a在build时需要加NDK_DEBUG=1的参数,即:ndk-build NDK_DEBUG=1,这么编译才能带上debug信息。 四、万事俱备 1. shell进入ndk/samples/目录,运行android update project --path hello-jni,生成build.xml用于apk打包。(也可以在hello-jni目录里运行:android update project -t 1 -p . --subprojects) 2. 进入ndk/samples/hello-jni,修改AndroidManifest.xml文件 1: <application android:label="@string/app_name" 2: android:debuggable="true"> 3. 运行ndk-build 4. 运行ant debug 5. 启动android的模拟器(可以从eclipse启动) 6. 运行adb install –r bin/HelloJni-debug.apk 7. 运行ndk-gdb –start 开始debug,后续和使用gdb一样 8. 需要图形化界面进行debug,可以参考[2] 几点重要说明: 1. ndk-gdb用的是client/server形式对目标机器进行debug, gdb 调试器 与 gdbserver 的关系,就是 gdb 与 stub的关系,如下图所示[3] : 2. ndk-gdb最坑爹的是:gdb和gdbserver的版本必须是匹配的才能debug: 每一个模拟器在system/bin下都有gdbserver,这些gdbserver是和模拟器本身的android版本有关的,而下载的NDK的ndk-gdb一般都是最新的gdb,因此gdb和gdbserver的版本常常匹配不了。这时需要把对应版本的gdbserver push到emulator上,然后指定./gdbserver,必须指定“./”因为在linux下默认优先查找system目录。 References: [1] 使用eclipse/ndk-gdb对java/native code联合调试 [2] Eclipse+CDT+GDB调试android NDK程序 [3] ndk-gdb对java/native code联合调试 [4] 使用eclipse/ndk-gdb对java/native code联合调试 [5] 把hello-jni的.c后缀改成.cpp后出错 分类: Android开发
引用:http://blog.sina.com.cn/s/blog_4a0a39c30100auh9.html Android是基于Linux的操作系统,处理器是ARM的,所以要在Linux或Windows等x86系统上编译Android能运行的程序,你需要一个交叉编译器。 在Linux下面,你可以自已编译一个交叉编译环境,但Windows下面,就比较复杂(也可以在cygwin中编译一个),但你可以选择下载一个现成的交叉编译环境: http://www.codesourcery.com/gnu_toolchains/arm/download.html Windows: http://www.codesourcery.com/gnu_toolchains/arm/portal/package3400/public/arm-none-linux-gnueabi/arm-2008q3-41-arm-none-linux-gnueabi.exe Linux: http://www.codesourcery.com/gnu_toolchains/arm/portal/package3399/public/arm-none-linux-gnueabi/arm-2008q3-41-arm-none-linux-gnueabi.bin 安装好了之后,将 CodeSourcery编译器的bin目录 (我的是D:\Program Files\CodeSourcery\Sourcery G++ Lite\bin) 加入你的PATH环境变量中,就可以开始你的Android Native C开发之旅了,写好一个简单的C程序: #include <stdlib.h> int main(int argc, char** argv) { printf("hello android!\nI'm %s!\nI like android very much!!!\n", "Martin Foo"); return 0;} 另存成hello.c,进入命令行模式,确保交叉编译器的bin目录,及Android SDK的tools目录在你的系统环境变量的path里面,用如下命令编译: arm-none-linux-gnueabi-gcc -static hello.c -o hello 注意,一定要加上static参数,否则编译好的可能会在Android上不能运行。 启动Android模拟器,用如下命令将文件push到Android模拟器上: adb shell mkdir /dev/sampleadb push hello /dev/sample/hello adb shell chmod 777 /dev/sample/hello 先创建 /dev/sample目录,再将编译好的hello上传上去,最后将hello改成可执行的。 再进入命令行模式,进入Android的shell环境: adb shell
引用:http://www.cnblogs.com/lyeo/archive/2012/05/11/2496261.html 直接上代码: var data = { name: 'my name', description: 'short description' } $.ajaxFileUpload({ url: '/File/Upload', secureuri: false, data: data, fileElementId: 'fileToUpload', dataType: 'json', success: function (data) { alert(data.msg); }, error: function (data) { alert("error"); } }); 传递一个data包,如果要把所有的input都传递就: var data ={}; $("#"+ formId +" :input").each(function(){data[this.name]=this.value}); 后台代码:直接 Request["参数名"]就能取值 引用自:http://stackoverflow.com/questions/4797523/ajaxfileupload-have-more-params-than-file
引用:http://my.eoe.cn/blue_rain/archive/240.html 当图片过大,或图片数量较多时使用BitmapFactory解码图片会出java.lang.OutOfMemoryError: bitmap size exceeds VM budget,要想正常使用则需分配更少的内存,具体的解决办法是修改采样值BitmapFactory.Options.inSampleSize,例如: 1 2 3 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 4; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 如何设置恰当的inSampleSize 设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。 1 2 3 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。 查看Android源码,Android提供了一种动态计算的方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels); int roundedSize; if (initialSize <= 8 ) { roundedSize = 1; while (roundedSize < initialSize) { roundedSize <<= 1; } } else { roundedSize = (initialSize + 7) / 8 * 8; } return roundedSize; } private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { // return the larger one when there is no overlapping zone. return lowerBound; } if ((maxNumOfPixels == -1) && (minSideLength == -1)) { return 1; } else if (minSideLength == -1) { return lowerBound; } else { return upperBound; } } 使用该算法,就可动态计算出图片的inSampleSize。 1 2 3 4 5 6 7 8 9 10 11 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(imageFile, opts); opts.inSampleSize = computeSampleSize(opts, -1, 128*128); opts.inJustDecodeBounds = false; try { Bitmap bmp = BitmapFactory.decodeFile(imageFile, opts); imageView.setImageBitmap(bmp); } catch (OutOfMemoryError err) { } 此办法只是获取的图片的缩略图,可能会造成失真!
引用:http://my.eoe.cn/blue_rain/archive/340.html 对于目前的状况来说,移动终端的网络状况没有PC网络状况那么理想。在一个Android应用中,如果需要接收来自服务器的大容量数据,那么就不得不考虑客户的流量问题。本文根据笔者的一个项目实战经验出发,解决大容量数据的交互问题,解决数据大小会根据实际情况动态切换问题(服务器动态选择是否要压缩数据,客户端动态解析数据是否是被压缩的),还有数据交互的编码问题。 解决数据过大的问题,最直观的方法就是压缩数据。服务器将需要传递的数据先进行压缩,再发送给Android客户端,Android客户端接收到压缩的数据,对其解压,得到压缩前的数据。 如果规定Android客户端和服务器的交互数据必须是经过某种压缩算法后的数据,那么这种“规定”失去了视具体情况而定的灵活性。笔者拟将Http协议进行封装,将动态的选择传输的数据是否要经过压缩,客户端也能动态的识别,整理并获得服务器想要发送的数据。Android客户端向服务器请求某个方面的数据,这个数据也许是经过压缩后传递比较合适,又也许是将原生数据传递比较合适。也就是说,笔者想要设计一种协议,这种协议适用于传输数据的数据量会动态的切换,也许它会是一个小数据,也许它又会是一个数据量庞大的大数据(大数据需要经过压缩)。 可能说的比较抽象,那么我用实际情况解释一下。 我项目中的一个实际情况是这样的:这个项目是做一个Android基金客户端,Android客户端向服务器请求某一个基金的历史走势信息,由于我的Android客户端实现了本地缓存,这让传递数据的大小浮动非常大。如果本地缓存的历史走势信息的最新日期是5月5日,服务器的历史走势信息的最新日期是5月7日,那么服务器就像发送5月6日和5月7日这两天的走势信息,这个数据很小,不需要压缩(我使用的压缩算法,对于数据量过小的数据压缩并不理想,数据量过小的数据压缩后的数据会比压缩前的数据大)。然而,Android客户端也可能对于某个基金没有任何的缓存信息,那么服务器将发送的数据将是过去三四年间的历史走势信息,这个数据会有点大,就需要进行压缩后传递。那么客户端对于同一个请求得到的数据,如何判断它是压缩后的数据还是未曾压缩的数据呢? 笔者使用的解决方案是把传递数据的第一个字节作为标识字节,将标识这个数据是否被压缩了。也能标识传递数据的编码问题。Android对于接收到的数据(字节数组),先判断第一个字节的数据,就能根据它所代表的数据格式和编码信息进行相应的操作。说了那么多,也许不如看实际的代码理解的快。首先是压缩算法,这里笔者用到的是jdk自带的zip压缩算法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 package com.chenjun.utils.compress; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class Compress { private static final int BUFFER_LENGTH = 400; //压缩字节最小长度,小于这个长度的字节数组不适合压缩,压缩完会更大 public static final int BYTE_MIN_LENGTH = 50; //字节数组是否压缩标志位 public static final byte FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY = 0; public static final byte FLAG_GBK_STRING_COMPRESSED_BYTEARRAY = 1; public static final byte FLAG_UTF8_STRING_COMPRESSED_BYTEARRAY = 2; public static final byte FLAG_NO_UPDATE_INFO = 3; /** * 数据压缩 * * @param is * @param os * @throws Exception */ public static void compress(InputStream is, OutputStream os) throws Exception { GZIPOutputStream gos = new GZIPOutputStream(os); int count; byte data[] = new byte[BUFFER_LENGTH]; while ((count = is.read(data, 0, BUFFER_LENGTH)) != -1) { gos.write(data, 0, count); } gos.finish(); gos.flush(); gos.close(); } /** * 数据解压缩 * * @param is * @param os * @throws Exception */ public static void decompress(InputStream is, OutputStream os) throws Exception { GZIPInputStream gis = new GZIPInputStream(is); int count; byte data[] = new byte[BUFFER_LENGTH]; while ((count = gis.read(data, 0, BUFFER_LENGTH)) != -1) { os.write(data, 0, count); } gis.close(); } /** * 数据压缩 * * @param data * @return * @throws Exception */ public static byte[] byteCompress(byte[] data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 压缩 compress(bais, baos); byte[] output = baos.toByteArray(); baos.flush(); baos.close(); bais.close(); return output; } /** * 数据解压缩 * * @param data * @return * @throws Exception */ public static byte[] byteDecompress(byte[] data) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 解压缩 decompress(bais, baos); data = baos.toByteArray(); baos.flush(); baos.close(); bais.close(); return data; } } 这里供外部调用的方法是byteCompress()和byteDecompress(),都将接收一个byte数组,byteCompress是数据压缩方法,将返回压缩后的数组数据,byteDecompress是数据解压方法,将返回解压后的byte数组数据。FLAG_GBK_STRING_COMPRESSED_BYTEARRAY表示服务器传递的数据是GBK编码的字符串经过压缩后的字节数组。其它的常量也能根据其名字来理解。(这里多说一句,最好将编码方式和是否压缩的标识位分开,比如将标识字节的前四个位定义成标识编码方式的位,将后面四个位标识为是否压缩或者其它信息的标识位,通过位的与或者或方式来判断标识位。笔者这里偷懒了,直接就这么写了。) 下面是处理传递数据的方法(判断是否要压缩)。我这里用要的是Struts 1框架,在Action里组织数据,并作相应的处理(压缩或者不压缩),并发送。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { JjjzForm jjjzForm = (JjjzForm) form; //基金净值历史走势信息 ArrayList<Jjjz> jjjzs = null; //得到基金净值历史走势的方法省略了 Gson gson = new Gson(); String jsonStr = gson.toJson(jjjzs, jjjzs.getClass()); byte[] resultOriginalByte = jsonStr.getBytes(); //组织最后返回数据的缓冲字节数组 ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream(); OutputStream os = null; try { os = response.getOutputStream(); //如果要返回的结果字节数组小于50位,不将压缩 if(resultOriginalByte.length < Compress.BYTE_MIN_LENGTH){ byte flagByte = Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY; resultBuffer.write(flagByte); resultBuffer.write(resultOriginalByte); } else{ byte flagByte = Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY; resultBuffer.write(flagByte); resultBuffer.write(Compress.byteCompress(resultOriginalByte)); } resultBuffer.flush(); resultBuffer.close(); //将最后组织后的字节数组发送给客户端 os.write(resultBuffer.toByteArray()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } 这里我预发送的数据是一个Json格式的字符串(GBK编码),将判断这个字符串的长度(判断是否适合压缩)。如果适合压缩,就将缓冲字节数组(ByteArrayOutputStream resultBuffer)的第一个字节填充FLAG_GBK_STRING_COMPRESSED_BYTEARRAY,再将Json字符串的字节数组压缩,并存入数据缓冲字节数组,最后向输出流写入缓冲字节数组,关闭流。如果不适合压缩,将发送的数据的第一个字节填充为FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY,再将Json字符串的字节数组直接存入数据缓冲字节数组,写入输出流,关闭流。 最后就是Android客户端的解析了,将上述的Compress压缩辅助类拷贝到Android项目中就行。下面是Http请求后得到的字节数组数据做解析工作。(Android客户端如何使用Http向服务器请求数据请参考我前面的一篇博客)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 byte[] receivedByte = EntityUtils.toByteArray(httpResponse.getEntity()); String result = null; //判断接收到的字节数组是否是压缩过的 if (receivedByte[0] == Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY) { result = new String(receivedByte, 1, receivedByte.length - 1, EXCHANGE_ENCODING); } else if (receivedByte[0] == Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY) { byte[] compressedByte = new byte[receivedByte.length - 1]; for (int i = 0; i < compressedByte.length; i++) { compressedByte[i] = receivedByte[i + 1]; } byte[] resultByte = Compress.byteDecompress(compressedByte); result = new String(resultByte, EXCHANGE_ENCODING); } 这里最后得到的result就是服务器实际要发送的内容。 缺陷反思:任何设计都是有缺陷的。我这样做已经将Http协议做了进一层封装。Http的数据部分的第一个字节并不是实际数据,而是标识字节。这样,降低了这个接口的可重用性。统一发送Json字符串的Action能被网页(Ajax)或者其他客户端使用,经过封装压缩之后,只有能识别这个封装(就是能进行解析)的客户端能使用这个接口。网页(Ajax)就不能解析,那么这个Action就不能被Ajax使用。 具体开发过程中要视具体情况而定,如果数据量小的话我还是建议使用标准的Http协议,也就是说直接发送字符串,不做任何的压缩和封装。如果数据量实在过于大的话,建议使用我上述的方法。 有博友问,对于Android应用来说,什么样的数据才算是大数据。我想这个大数据的界限并不是固定的,并不是说10k以上,或者100k以上就算是大数据,这个界限是由许多方面的利弊来衡量的。首先我要说,我设计的这个协议是适用于大数据和小数据动态切换的情况。对于大小数据界限的划定,交给开发人员去衡量利弊。这个衡量标准我想应该包括以下几部分内容: 第一,压缩算法的有效临界点。只有要压缩的数据大于这个点,压缩后的数据才会更小,反之,压缩后的数据会更加的大。我使用的zip算法这个点应该是50字节左右,因此,在我应用中,将大数据定义成50字节以上的数据。 第二:压缩和解压的开销。服务器要压缩数据,客户端要解压数据,这个都是需要CPU开销的,特别是服务器,如果请求量大的话,需要为每一个响应数据进行压缩,势必降低服务器的性能。我们可以设想这样的一种情况,原生数据只有50字节,压缩完会有40字节,那么我们就要思考是否有必要来消耗CPU来为我们这区区的10个字节来压缩呢? 综上,虽然这个协议适合大小数据动态切换的数据传输,但是合理的选择大数据和小数据的分割点(定义多少大的数据要压缩,定义多少以下的数据不需要压缩)是需要好好权衡的。
引用:http://my.eoe.cn/blue_rain/archive/477.html 首先,看下效果: 首先,看下xml文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#c9c9c9" > <RelativeLayout android:id="@+id/relate_level3" android:layout_width="280dp" android:layout_height="140dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level3" > <ImageButton android:id="@+id/c1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="6dip" android:layout_marginLeft="12dip" android:background="@drawable/channel1" /> <ImageButton android:id="@+id/c2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/c1" android:layout_marginBottom="12dip" android:layout_marginLeft="28dip" android:background="@drawable/channel2" /> <ImageButton android:id="@+id/c3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/c2" android:layout_marginBottom="8dip" android:layout_marginLeft="6dip" android:layout_toRightOf="@+id/c2" android:background="@drawable/channel3" /> <ImageButton android:id="@+id/c4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="6dip" android:background="@drawable/channel4" /> <ImageButton android:id="@+id/c5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/c6" android:layout_marginBottom="8dip" android:layout_marginRight="6dip" android:layout_toLeftOf="@+id/c6" android:background="@drawable/channel5" /> <ImageButton android:id="@+id/c6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/c7" android:layout_alignParentRight="true" android:layout_marginBottom="12dip" android:layout_marginRight="28dip" android:background="@drawable/channel6" /> <ImageButton android:id="@+id/c7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="6dip" android:layout_marginRight="12dip" android:background="@drawable/channel7" /> </RelativeLayout> <RelativeLayout android:id="@+id/relate_level2" android:layout_width="180dp" android:layout_height="90dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level2" > <ImageButton android:id="@+id/menu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="6dip" android:background="@drawable/icon_menu" /> <ImageButton android:id="@+id/search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_margin="10dip" android:background="@drawable/icon_search" /> <ImageButton android:id="@+id/myyouku" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_margin="10dip" android:background="@drawable/icon_myyouku" /> </RelativeLayout> <RelativeLayout android:id="@+id/relate_level1" android:layout_width="100dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level1" > <ImageButton android:id="@+id/home" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="10dp" android:background="@drawable/icon_home" /> </RelativeLayout> </RelativeLayout> 大家看到主要有三个RalativeLayout,就是大家看到的三层,但是关于图片的倾斜 是怎样实现的呢?实际上是个假象,图片是正放的,里面图像是倾斜的。如下图:这样大概能明白,下面就是开始动画效果了,先看下主Activity: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class TestYoukuActivity extends Activity { /** Called when the activity is first created. */ private boolean areLevel2Showing = true, areLevel3Showing = true; private RelativeLayout relate_level2, relate_level3; private ImageButton home, menu; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findViews(); setListener(); } private void findViews() { relate_level2 = (RelativeLayout) findViewById(R.id.relate_level2); relate_level3 = (RelativeLayout) findViewById(R.id.relate_level3); home = (ImageButton) findViewById(R.id.home); menu = (ImageButton) findViewById(R.id.menu); } private void setListener() { // 给大按钮设置点击事件 home.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!areLevel2Showing) { MyAnimation.startAnimationsIn(relate_level2, 500); } else { if (areLevel3Showing) { MyAnimation.startAnimationsOut(relate_level2, 500, 500); MyAnimation.startAnimationsOut(relate_level3, 500, 0); areLevel3Showing = !areLevel3Showing; } else { MyAnimation.startAnimationsOut(relate_level2, 500, 0); } } areLevel2Showing = !areLevel2Showing; } }); menu.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!areLevel3Showing) { MyAnimation.startAnimationsIn(relate_level3, 500); } else { MyAnimation.startAnimationsOut(relate_level3, 500, 0); } areLevel3Showing = !areLevel3Showing; } }); } } 应该注意到了: 1 MyAnimation.startAnimationsIn(relate_level2, 500); 看一下这个静态方法的实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void startAnimationsIn(ViewGroup viewgroup, int durationMillis) { viewgroup.setVisibility(0); for (int i = 0; i < viewgroup.getChildCount(); i++) { viewgroup.getChildAt(i).setVisibility(0); viewgroup.getChildAt(i).setClickable(true); viewgroup.getChildAt(i).setFocusable(true); } Animation animation; animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 1.0f); animation.setFillAfter(true); animation.setDuration(durationMillis); viewgroup.startAnimation(animation); } RotateAnimation是画面转移旋转动画效果,看一下它的构造方法:RotateAnimation(Context context, AttributeSet attrs)Constructor used when a RotateAnimation is loaded from a resource. RotateAnimation(float fromDegrees, float toDegrees)Constructor to use when building a RotateAnimation from code. RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)Constructor to use when building a RotateAnimation from code RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)Constructor to use when building a RotateAnimation from code在这里使用的是第四个构造方法: fromDegrees:旋转的开始角度。 toDegrees:旋转的结束角度。 pivotXType:X轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。 pivotXValue:X坐标的伸缩值。 pivotYType:Y轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。 pivotYValue:Y坐标的伸缩值。 关于角度问题: 1 2 3 4 5 6 当角度为负数——表示逆时针旋转 当角度为正数——表示顺时针旋转 (负数from——to正数:顺时针旋转) (负数from——to负数:逆时针旋转) (正数from——to正数:顺时针旋转) (正数from——to负数:逆时针旋转) 关于pivotXValue:这一点的X坐标的对象被旋转,在指定的绝对数字0是左边边缘。如果pivotXType数是绝对的这个值可以是一个绝对,另外也可以是百分比(在1.0为100%)。50%是x中点,100%为右边缘。 同理,pivotYValue:这一点的Y坐标的对象被旋转,在指定的绝对数字0是顶部边缘。如果pivotYType数是绝对的这个值可以是一个绝对,另外也可以是百分比(在1.0为100%)。50%是Y中点,100%为下边缘。 然后再看下调用的其他的方法: setFillAfter: If fillAfter is true, the transformation that this animation performed will persist when it is finished. Defaults to false if not set. Note that this applies when using an AnimationSet to chain animations. The transformation is not applied before the AnimationSet itself starts.如果fillAfter为真,transformation 动画将一直运行直到它完成。默认设置为假。注意:这适用于当使用一个AnimationSet连锁动画。transformation 是不适用AnimationSet本身之前开始。 setDuration:设置动画时间。 再看一下退出: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // 图标的动画(出动画) public static void startAnimationsOut(final ViewGroup viewgroup, int durationMillis, int startOffset) { Animation animation; animation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 1.0f); animation.setFillAfter(true); animation.setDuration(durationMillis); animation.setStartOffset(startOffset); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation arg0) {} @Override public void onAnimationRepeat(Animation arg0) {} @Override public void onAnimationEnd(Animation arg0) { viewgroup.setVisibility(8); for (int i = 0; i < viewgroup.getChildCount(); i++) { viewgroup.getChildAt(i).setVisibility(8); viewgroup.getChildAt(i).setClickable(false); viewgroup.getChildAt(i).setFocusable(false); } } }); viewgroup.startAnimation(animation); } 有一个animation.setStartOffset(startOffset);是设置animation多长时间以后执行。 源码下载地址:http://dl.vmall.com/c0hkh1m6kw
引用:http://my.eoe.cn/blue_rain/archive/3631.html 1、一些概念 模式的定义: 每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。 什么是设计模式? 设计模式是在某种特别的情况下,针对某种问题的某种典型、通用的解决方法。 我们是需要适当了解并学习一些设计模式,在程序开发过程中,总是会涉及到一些框架设计,模块设计之类的东西,如果能很好理解并运行设计模式,你所设计的模块或框架将会要稳定得多,因为这些设计模式它们都是通用的解决方案,是经过实践经验了的。 比如说,在程序里,可能会有通知模块,A模块的数据发生变化,B模块需要得到通知,对于这样的需要,你可能会想到用"广播","消息"或者"回调"的方式来解决,的确,刚才我所说的那三种也能解决,但是,这三种都是存在一些缺点,比如说广播,用Intent来传输数据很困难,对于"消息",无法很好的跟踪,对于"回调",有可能你A与B模块根本不可相互访问。此时,如果你会用观察者模式的问题,这种问题可以很轻松解决。 当然,这里是需要具体问题具体分析的,我主要的意思就是说,要适当利用模式,我们不能为了用模式而去用模式,我们是要用模式来解决我们实际的问题。 概念完整性 关于概念完整性,在《人月神话》一书在有大量的阐述,这里,我把我的理解写出来,与大家分享。 1)概念完整性是系统设计中最重要的考虑因素。当你的系统规模越大,这一点体现得越明显。 2)为了获取概念的完整性,设计必须由一个人或者具有共识的小型团队来完成。这一点很好理解,关于设计,可以让所有的人参与,但是决定权在少数人手里,如果大家都想参与设计,这是根本没有办法保正系统设计是统一完整的。 3)要获得概念上的完整性,就必须有人控制这些概念,类似于贵族的专制统治。这里,对于团队中的项目经理或架构师必须对项目有绝对的权威,不然,这个项目里面的就无法统一号令。 4)概念完整性表现有: 1 2 3 4 5 - 开发过程中,需求、设计、编码的一致性 - 整个程序具有统一的风格,比如对话框样式,按钮风格,色调等UI元素 - 整个程序具体统一的结构,比如不同模块访问网络,它们的调用方式一致,例如异步访问都用回调方式通知结果,相同的功能应该提取成共通模块。 - 开发人员能很好的执行需求人员和设计人员的意图。 - 有完整的文档,需求文档,设计文档,测试文档,处理流程的文档等。 如何保持概念完整性 1 2 3 4 5 - 在制度上给予保证,产品的负责人必须建立技术上的绝对权威 - 技术负责人员(SE,SL)必须严格执行项目的需求,设计,必须深入到编码细节 - 在不同阶段,保持与所有人员的持续沟通,鼓励开发人员提意见。 - 让开发人员参与设计,但不决定设计 - 通过持续的反馈和沟通来实现模块重用 2、设计之前应该做什么 2.1 共通类的设计 2.1.1 Widget设计 TextViewEditTextButtonTitle barTool bar...### 为什么要提供这些共通控件? ### 统一字体大小,如App字体不随系统字体变化而变化统一UI式样,如Button, EditText具有相同的背景等复用代码 2.1.2 Adapter Items 根据式样,提取需要在AdapterView中显示的Item简单的复合布局自绘制,从而提高滑动性能- ListView中放Gallery时,提高上下滑动性能- 尽量优化绘制### 数据驱动 ### Adapter Items提供核心的方法- setData(Object data)- getData();Adapter#getView实现更加简单- 实现简单- 不会因为UI变化而变化下面代码示例了Adapter#getView()方法的实现,它返回BookView,BookView提供方法来接收数据,至于BookView的显示,则根据设置的数据来显示,这就是数据驱动UI。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public View getView(int position, View convertView, ViewGroup parent) { if (null == convertView) { convertView = new BookView(getContext()); convertView.setLayoutParameter(new AbsListView.LayoutParameter(150, 150)); } Book book = m_bookList.get(position); BookView bookView = (BookView)convertView; bookView.setBook(book); return convertView; } 2.1.3 Dialog 扩展于Dialog类提供Dialog关闭的事件Dialog的高度随内容的变化而变化可以设置按钮的文字,可见性,字体等方法设置按钮点击事件的listener要考虑对话框的三个属性:Title, Content area, Action buttons 2.1.4 Utility -LogDateFormatBitmapNotificationShared PreferenceEnvironmentDevice... 2.2 Task管理 线程只是一种机制,保证我们要完成的任务不运行在UI线程(也就是说不阻塞UI),完成的任务才是我们关注的核心,因此,我们可以通过设计,把线程封装,让使用者根本感觉不到是线程,他只用关心他要做的事情就行了。这里,我们可以设计一种"异步链式调用"的框架,把线程进行了封装。使用都只需要这样用: 1 2 3 4 5 new TaskManager() .next(task1) .next(task2) .next(task3). .execute(); 这里,task1, task2, task3是顺序执行的,举个例子:我们要访问网络,取得一个图片,使用这个TaskManager我们需要3个task, task1:显示一个ProgressDialog。 task2:访问网络,创建bitmap。 task3:关闭对话框,显示bitmap。 这一点,可以参考CoreLib工程中的task.TaskManager类。 关于TaskManager,有以下几点需要注意: -封装了线程让调用者只关注自己的业务处理保证顺序链式地执行某一个任务上一个任务的输出,作为下一个任务的输入能暂停、恢复任何一个任务 2.3 缓存设计 -把内存占用量大的对象存放在缓存中,如bitmap利用了LruCache类来实现利用了AsyncTask类来加载bitmap不用再手动释放bitmap内存,该操作有风险不用再关心AbsListView的scroll状态关于缓存的更多详细细节,请参考[ 请参考CoreLib工程中的cache包 ]。 这样做,有什么好处, 不用再手动释放bitmap内在,该操作有风险,因为该bitmap是否有View引用,如果当一个View在试图绘制一个已经回收的bitmap,这里会抛出异常。 2.4 线程管理 无消息循环的线程: 1 2 3 4 5 6 new Thread(null, new Runnable() { public void run() { // Do you works. } }, "Thread_name_xxx").start(); 什么情况下使用这种线程: -做完一件事情就结束,这件事发生频率不高,比如从SD card中读取图片数据不需要复用线程 在使用线程,最好给线程加上名字,这样利用高度与跟踪。 有消息循环的线程: 这样的线程拥有消息循环,当消息队列中没有消息时,这个线程会被挂起。我们要做一件事情时,只需要给它发送一个消息就行了。 这种情况通常是为了复用线程,不用频繁创建线程,比如音乐播放器程序,专门启动一个有消息循环的线程来获得音乐的专辑图片。 我们通常还要创建一个与这个线程的消息循环(Looper)相关联的Handler,由它来处理消息,注意,这做的事情是运行在后台线程的。 3,程序框架如何设计 Android程序的结构 UI层数据展示与管理用户交互绘制Adapter业务逻辑层持久化数据(内存中,相当于全局数据)数据加式(数据层的数据有时候需要进行加工成UI层需要的数据)数据变化的通知机制数据层数据访问(DB,文件,网络等)缓存(图片,文件等)配置文件(shared perference) 下面,我试着画了一个Android程序的结构,如果有不好的地方,欢迎指正。 4,一些基本原则 下面列出一些通常的原则,我们应当在开发过程中遵循,欢迎补充与指正。 4.1 提供initialize()方法 在Activity.onCreate()或者View的构造方法中调用,在以后看代码时,人们通常首先会去找initialize()这样的方法。 4.2 封装点击事件 把View的点击事件,提成方法,这样在listener处只是一个方法调用者,一般的事件封装为:onXXXClick(View v)。 4.3 设计一个BaseActivity类 让所有的Activity都继承自BaseActivity类,这样,我们可以做很多有用的事情 -定义共通属性显示共通对话框(Progress dialog)取得top activity可以手动管理启动的activity 4.4 设计Application类 存全局数据,比top activity, application context。 4.5 异常处理 -报告功能是处理异常的精髓在finally块中执行清理操作不要用try-catch-finally来判断业务逻辑考虑设计自定义的异常类 4.6 标注的使用 -重写的方法一定要加@Override不使用的方法,不要删除,可以标记为@Deprecated,这个做法在维护型的项目中特别有用。 4.7 注册与反注册 -局部广播各种listenerService等 4.8 封装Bitmap操作 我们应当把Bitmap操作封装起来,比如从文件加载,保存,网络下载,动态计算sample size等。有了封装后,我们可以对其集中优化。 4.9 绘制处理 一定要注意绘制方面的东西,不要在onDraw()/onTouchEvent()中创建新对象。
引用:http://my.eoe.cn/blue_rain/archive/552.html 有的时候我们生成库文件,需要在里面加入一些UI,并提供出一些xml的资源,那如何在其他项目中使用呢? 我们只需要在需要生成库文件的代码中不要直接是用R. ,而是使用自己写的方法获取。 下面上代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 import java.lang.reflect.Field; import android.content.Context; import android.util.Log; public class ResUtil { private static final String TAG = ResUtil.class.getName(); private static ResUtil instance; private Context context; private static Class id= null; private static Class drawable = null; private static Class layout = null; private static Class anim = null; private static Class style = null; private static Class string = null; private static Class array = null; private ResUtil(Context paramContext) { this.context = paramContext.getApplicationContext(); try { drawable = Class.forName(this.context.getPackageName() + ".R$drawable"); } catch (ClassNotFoundException localClassNotFoundException1) { Log.i(TAG, localClassNotFoundException1.getMessage()); } try { layout = Class.forName(this.context.getPackageName() + ".R$layout"); } catch (ClassNotFoundException localClassNotFoundException2) { Log.i(TAG, localClassNotFoundException2.getMessage()); } try { id = Class.forName(this.context.getPackageName() + ".R$id"); } catch (ClassNotFoundException localClassNotFoundException3) { Log.i(TAG, localClassNotFoundException3.getMessage()); } try { anim = Class.forName(this.context.getPackageName() + ".R$anim"); } catch (ClassNotFoundException localClassNotFoundException4) { Log.i(TAG, localClassNotFoundException4.getMessage()); } try { style = Class.forName(this.context.getPackageName() + ".R$style"); } catch (ClassNotFoundException localClassNotFoundException5) { Log.d(TAG, localClassNotFoundException5.getMessage()); } try { string = Class.forName(this.context.getPackageName() + ".R$string"); } catch (ClassNotFoundException localClassNotFoundException6) { Log.d(TAG, localClassNotFoundException6.getMessage()); } try { array = Class.forName(this.context.getPackageName() + ".R$array"); } catch (ClassNotFoundException localClassNotFoundException7) { Log.d(TAG, localClassNotFoundException7.getMessage()); } } public static ResUtil getResofR(Context paramContext) { if (instance == null) instance = new ResUtil(paramContext); return instance; } public int getAnim(String paramString) { return getResofR(anim, paramString); } public int getId(String paramString) { return getResofR(id, paramString); } public int getDrawable(String paramString) { return getResofR(drawable, paramString); } public int getLayout(String paramString) { return getResofR(layout, paramString); } public int getStyle(String paramString) { return getResofR(style, paramString); } public int getString(String paramString) { return getResofR(string, paramString); } public int getArray(String paramString) { return getResofR(array, paramString); } private int getResofR(Class<?> paramClass, String paramString) { if (paramClass == null) { Log.d(TAG, "getRes(null," + paramString + ")"); throw new IllegalArgumentException("ResClass is not initialized."); } try { Field localField = paramClass.getField(paramString); int k = localField.getInt(paramString); return k; } catch (Exception localException) { Log.d(TAG, "getRes(" + paramClass.getName() + ", " + paramString + ")"); Log.d(TAG, "Error getting resource. Make sure you have copied all resources (res/) from SDK to your project. "); Log.d(TAG, localException.getMessage()); } return -1; } } 使用方法:在要生成库文件的Java文件中,使用 1 2 View view = getLayoutInflater().inflate(ResUtil.getResofR(this).getLayout("activity_main"), null); set
引用:http://bbs.csdn.net/topics/340061850 先 apt-get update 再执行安装