amp@java

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  99 随笔 :: 0 文章 :: 228 评论 :: 0 Trackbacks

2007年3月19日 #

当年是从CSDN博客迁过来的,因为那里很不稳定,那时候这里很火的;
前段时间发现,Blogjava的登录页面居然没有验证码了,图片显示错误,无法登录,预感这个地方要凉,只好到处找替代,但没找到好用的博客,github是一个选项,但似乎不是很适合做博客。
现在终于又可以登录了,不过首页居然只剩一篇文章,我以为数据都没了,登录发现还是有的,不过太让人不放心了。
posted @ 2018-08-18 22:14 amp@java 阅读(133) | 评论 (0)编辑 收藏

 昨天下午开始,之前用得好好的USB鼠标,突然不能用了,找到个PS/2鼠标,却发现主板没有鼠标的PS/2口,只有键盘的PS/2口,真是奇葩,幸好在用的键盘是PS/2口的,费了九牛二虎之力,用键盘操作,Win+R,compmgmt.msc进入计算机管理,上下箭头移到设备管理器,发现右边的USB控制器前全是黄色叹号,尝试卸载再安装,却怎么也装不上,重新启动发现鼠标在启动Windows前还是亮灯的,但到了显示Windows徽标的时候灯就灭了,应该不是硬件问题,而是驱动问题。
为了进一步证实硬件没问题,找了个安装系统的U盘,插进去启动,能够正常使用鼠标,于是目标就聚焦在找回驱动上。
开始折腾:
1、首先是找官方驱动啊,我这电脑是老机,AMD7系列主板,找了半天,这个主板驱动并没有包含USB控制器,因为USB控制器都是Windows自带的驱动,下载了一个南桥驱动,安装后并没有效果;
2、Windows自带的驱动原来都是放在C:\Windows\System32\DriverStore\FileRepository下,USB控制器相关的驱动,就在usbport.inf_amd64_xxxxxxxxxx文件夹里,xxxxxxxxx是一串16进制数字,悲催的是,安装这个驱动时要不提示找不到指定文件,要不说第三方INF没有签名;
3、自己折腾搞不定,找个软件吧,第一个想到的是驱动之家官方的驱动精灵,下载下来发现是个全家桶啊,什么腾讯管家,金山毒霸,浏览器首页修改一应俱全,而且没有鼠标点击,用TAB键根本移动不到取消框,只好默认全部安装了,装完启动,检测,提示系统自带驱动缺失,于是回车修复,但每次修复完,重新检测还是那样,而且没有提示USB控制器驱动安装有问题,有些功能用键盘无法操作,不知道是不是还有哪里可以操作一下,于是又搜了一下,如何用键盘代替鼠标,居然真的找到了!
4、按WIN键,输入设置,回车,打开设置主页:

移到“轻松使用”,进去后左边选择“鼠标”,在右边启用“使用数字小键盘在屏幕上移动鼠标”(按空格键开关),最好把三个开关都打开,如果没有启用CTRL键加速功能,鼠标移动非常慢:

好了,现在可以用小键盘移动鼠标了;
5、继续回到驱动精灵,再次修复,还是不行啊,这个东西除了带来一堆垃圾,什么作用都没有!于是把它带来的垃圾以及它自己卸载了。
6、似乎360也有一个驱动大师,于是就下载了一个,这个倒是很纯洁,但是功能太弱,完全没发现问题;
7、刚才搜索“安装驱动 找不到指定的文件”时,发现一个论坛提到了这个,是驱动人生的论坛,好像还有解决方案,但要注册才能下载,难道驱动人生可以解决?于是就下载了一个驱动人生,安装的时候还是附带全家桶,不过现在可以用键盘移动鼠标取消了,只安装了驱动人生自己,跟刚才两个软件不同的是,它提示USB外设驱动没有安装,于是点修复,结果反反复复出现等待光标,就是无法完成;
8、之前又搜索到,驱动安装的日志在C:\Windows\INF\setupapi.dev.log文件里,于是打开这个文件,发现Driver package failed signature verification. Error = 0xE000022F,驱动程序签名有问题,所以不能安装成功;
9、Windows10有个高级启动选项是禁用强制驱动签名,如何进入高级启动选项呢?以前是按F8,现在不行了,要在设置里面,更新和安全,恢复,高级启动,立即重启,然后设置疑难解答,高级启动,再重启,就可以进入高级启动菜单,按7进入禁止强制驱动签名模式,重启后再用驱动人生修复,果然成功了;
10、打开驱动人生下载目录,DTLFolder\DriversDownLoad,发现它下载了USB驱动目录是USB_10.0.10240.16384_WHQL_107049,里面文件如下:

除了第一个xml文件是程序自己用的外,其他都是USB驱动用到的文件,点右键发现那几个sys文件,除了usbohci.sys和usbuhci.sys外,其他都有数字签名,而usbuhci.sys我这里没用到,问题就出在usbohci.sys上:


11、难道是驱动人生替换了未签名的文件?图谋不轨?为了验证一下,我又下载了一个Windows10安装光盘(版本是当前使用的1703版):
cn_windows_10_multiple_editions_version_1703_updated_march_2017_x64_dvd_10194190.iso
12、怎么提取安装光盘中的内置驱动?找了一下,原来Windows的安装盘从VISTA起,不再使用XP以前的I386目录和Drivers.cab文件存放驱动,而是打包在一个Install.wim镜像文件中,要找到驱动文件,必须用工具提取,这个工具就是Imagex.exe,微软自己做的命令行工具,但是我的电脑上没有,于是下载了一个64位的,放在C盘根目录,通过如下命令即可提取:
c:\IMAGEX_x64 /mount f:\sources\install.wim 5 i:\1703
其中F盘是在iso文件上点右键,打开方式选“Windows资源管理器”打开后虚拟出来的盘符,其实就相当于系统自带的虚拟光驱,I盘是硬盘,用来存放挂载的镜像文件,5是选择挂载哪一个版本的Windows(多合一版),如果不知道要挂哪个,把这个数字改成100,会显示xml文件内容,并提示找不到这个索引号,从xml文件内容就能找到各版本的信息,然后再重新挂载正确的即可。这个挂载其实是个解压缩过程,时间很长,提取完之后就跟安装好了Windows一样,目录都列好了。
今天又发现另一个图形化的工具,Dism++,比这个操作更简单。Dism是PowerShell内置的命令,也是与镜像有关,也能挂载提取,但用了一下似乎提示权限有问题,Dism++是国内开源爱好者自己开发的图形化工具,与Dism没有关系。
13、好了,原版的Windows已经准备好,进入Windows\System32\DriverStore\FileRepository目录,搜索usbohci.sys,在usbport.inf_amd64_8e5f608c0111283d目录下,点右键一看,也是没签名的:
这不是坑爹吗?你自己带的东西都没签名,然后又不给用!!!!
14、有点怀疑是Windows自己更新的时候修改了一些策略,导致之前可以用的不能用了,为了再次验证,又继续下载了两个版本的Windows10安装光盘,分别是早期的1607和最新的1709,找到usbohci.sys,如下所示:
从左到右依次为1607,1703,1709,均未签名,基本可以判断是Windows自己抽风了。
15、昨天晚上搞到12点多,搞定鼠标后没有重启测试,今天早上开机,果然发现鼠标又不能用了,因为我没有选择禁用强制签名选项来启动,系统发现那个没签名的驱动,就把它停了,尝试卸载,结果再装也装不上,于是只好又设置高级启动,重新禁止强制签名,进入系统,装上驱动,恰好这时Windows又在后台偷偷摸摸地更新,不知道更新了啥,让我重启。
16、重启之后,奇迹出现,刚才明明提示没有签名强制安装的驱动,现在居然正常启动也没问题了,而且查看驱动详情的时候出现了矛盾的一幕:
外面显示数字签名者:未经数字签名,里面的sys文件又显示数字签名者是Microsoft Windows,然而进入C:\Windows\System32\drivers目录,找到usbohci.sys,点右键,却发现并没有数字签名:
好吧,你开心就好,反正不要再禁我的鼠标就行……
感谢这次蛋疼的折腾之旅,让我知道了驱动程序来自哪里,安装日志在哪里,哪个软件坑爹又没用,怎么玩安装盘,怎么用键盘操作鼠标,怎么进入高级启动界面……
我为什么要知道这些??????????????为微软的疏忽买单啊!!!!!
啥都不说了,它又提示我重启了,不知道又有什么奇迹会发生……
posted @ 2017-11-19 12:43 amp@java 阅读(1104) | 评论 (0)编辑 收藏

 每次换手机,把旧手机的数据迁移到新手机就是个很麻烦的事情,幸好最近华为的“手机克隆”APP越来越强大,居然能够把微信的聊天记录包括图片原封不动地迁移到新手机上,以前用微信自带的聊天记录转移功能只能转移文字信息,图片视频全部丢失,不知道现在的怎么样。手机克隆还能把SD卡的内容也转移过来,基本满足了需要。
但是要把手机上的东西传到电脑就没那么简单了,现在已经没有了以前的大容量存储模式,只能选择MTP模式,这种模式其实不是一个完整的文件系统,有很多限制,所以一些传统的软件读取不到,例如FastCopy是用不了的,用Windows自带的文件管理器来复制,开始计算时间就要等很久,中间出了个错就前功尽弃;还有通过手机上的APP访问电脑共享的方式,在手机上复制也可以,但是同样会莫名其妙卡死,FTP同理,折腾了好久,还是觉得自己动手比较好。
MTP协议在维基百科里解释得比较清楚:https://en.wikipedia.org/wiki/Media_Transfer_Protocol ,简单点说就是:
1、不是以块设备的形式访问,跟U盘不同;
2、只能单线程访问,不能同时进行多个操作,只能一个接一个;
3、控制权在设备上,对外展示的内容由设备决定;
4、默认不能直接对文件进行部分修改,只能复制过来修改完再复制回去,但Android对协议做了扩展,能够修改部分文件内容;
5、在Linux上有些软件能够把它挂载为文件系统,这样其他软件就能像访问普通文件系统一样访问了,但是Windows下似乎没有。

不过有人开发了一个在Windows下通过JNI实现的Java库jmtp,项目托管在Google Code,被墙了,但是GitHub有人fork了一个,可以下载下来,我下载的是https://github.com/reindahl/jmtp
里面包含了C++的代码和Java的代码,以及两个已经编译好的dll文件,分别用于Win32和Win64,把其中一个dll文件放在工程目录下,再把Java源代码加入工程中即可使用,文档比较简陋,但是看test目录下的MtpTest.java,基本可以摸到如何使用了,这个协议比较简单,其实没什么功能,我要的只是把文件复制到电脑上。
根据MtpTest.java,稍微修改一下,做个递归复制即可把手机上的所有文件复制到电脑上:
package test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.rmi.server.SocketSecurityException;
import java.util.ArrayList;


import jmtp.PortableDevice;
import jmtp.PortableDeviceFolderObject;
import jmtp.PortableDeviceManager;
import jmtp.PortableDeviceObject;
import jmtp.PortableDeviceStorageObject;


public class TestApp {

   

    
public static void main(String[] args) {
        
// TODO Auto-generated method stub
        
        ArrayList
<PortableDeviceStorageObject> devices = new ArrayList<>();

        PortableDeviceManager manager 
= new PortableDeviceManager();

        
for (PortableDevice device : manager) {
            System.out.println(device);
            device.open();
            
            
// Iterate over deviceObjects
            for (PortableDeviceObject object : device.getRootObjects()) {
                String storageName
=object.getName();
                System.out.println(storageName);

                
// If the object is a storage object
                if (object instanceof PortableDeviceStorageObject) {
                    PortableDeviceStorageObject storage 
= (PortableDeviceStorageObject) object;
                    System.out.println(storage.getChildObjects().length);
                    
for (PortableDeviceObject child : storage.getChildObjects()) {
                            copyall(child,
"E:\\手机备份\\"+object.getName());
                    }
                }
            }

            device.close();
            System.out.println(size);
        }

  
  
    }
    
    
public static void copyall(PortableDeviceObject obj,String path) {

        if(obj instanceof PortableDeviceFolderObject) {
          

            String objName=obj.getName();
            
if(objName.contains(":")) {
                objName
=objName.replace(':''');
            }
            String newPath 
= path+"\\"+objName;
            System.out.println(
"创建文件夹:"+newPath);
            
            File file = new File(newPath);
            if(!file.exists()) {
                file.mkdirs();
            }
            for(PortableDeviceObject subObj:((PortableDeviceFolderObject) obj).getChildObjects()) {             
                copyall(subObj,newPath);
            }
        }
        
else {      
            
if(obj.getName().contains(":"))
                
return;
            System.out.println(
"开始复制文件到:"+path+"\\"+obj.getName());
            File file 
= new File(path);
            obj.copy(file.toPath());                     
            System.out.println(
"文件复制完成!");
        }
    }

}
其中发现有点问题:
1、Android设备文件名里是可以包含冒号(:)的,但Windows是不可以的,所以复制到这些文件的时候会有问题,于是遇到目录名这样就把它改为中文的冒号(:),但是遇到文件名这样就不行了,因为这个库的copy函数只需要指定目标目录,不需要指定目标文件名,所以这些文件只能放弃;
2、Android手机的MTP协议是由“媒体存储”这个系统APP控制的,有时候手机上可以看到的文件,通过MTP访问却怎么也看不到,重启手机也不行,应该就是这个APP没有更新数据,需要把它的系统数据清除掉,等它重建完重新访问就可以看到了,不过这个重建时间非常长,可以查看它数据占用的空间,刚清除之后会发现它占用的空间会不断增长,到了不增长的时候就是重建完了,就可以正常访问了;
3、这个库有时候还有点bug,有一次发现它读取到的文件和文件夹都没有了最后一个.后面的部分,所以总是卡住,重新插拔一下手机数据线又没问题了;
4、为了避免复制了半天结果发现不完整,又要重来,最好在复制前先统计一下文件大小,看看跟手机上看到的占用存储空间是不是一致,对于MTP设备上的文件,可以通过getSize函数得到大小,把上面复制操作改为大小累加即可,速度比复制快一些,不过由于小文件太多,也不会快很多。

把手机里的文件复制到电脑后,通过一些简单的分析,发现有很多其实是垃圾来的,也可以为手机空间清理提供参考,因为在电脑上分析起来比在手机上方便一些。例如一些视频APP的缓存,居然超过1G,占用了宝贵的内部存储空间,之前一直都没发现,通过电脑里的按文件大小搜索才发现。
posted @ 2017-11-17 14:54 amp@java 阅读(2117) | 评论 (0)编辑 收藏

09款老速腾,不支持USB和AUX,要听歌除了CD以外,就是刻录在CD上的MP3了,以前不知道用什么软件刻录了一张碟,能够完美地显示中文文件名和ID3信息,前几天用ImgBurn刻了一张,发现中文是乱码,开始以为是ID3信息显示乱码,于是下载了一个Mp3Tag,把所有ID3信息都清除,结果显示文件名依然乱码,重新写入ID3信息,发现ID3可以正常显示,但文件名还是乱码。
于是就把原来那张可以正常显示中文的碟拿来研究一下,发现它的ID3标签只是ID3v1,而后来重新写入的ID3是ID3v2.3,两个都可以正常显示中文,说明ID3信息是正常的,文件名乱码不是这个问题。
但是不知道用什么软件来显示光盘的文件系统,只能一次次摸索。
幸好有一张CD-RW可以反复尝试。
ImgBurn默认是使用ISO9660+UDF,而ISO 9660则使用最老的ISO 9660文件系统,也就是1988版本,文件名默认是不支持中文的,不知道是不是这个原因,于是就把文件系统改为ISO 9660+Joliet,如下图:

并且把ISO 9660标准改为1999:

可能是因为字符编码那里改为了ASCII,所以就好了。
后来又尝试只使用UDF文件系统,结果认不出碟。



所以,目前能够使用中文的环境其实就是:
ImgBurn使用ISO 9660 1999标准;
ID3使用v1或v2.3都可以。

posted @ 2017-02-10 09:17 amp@java 阅读(334) | 评论 (0)编辑 收藏

最近新部署了一个信息系统,厂家居然没有升级方案,所有数据都要重新输入,包括用户、角色等都要重新配置,真是操蛋。要是一个个录入简直是日狗了,这些用户在其他信息系统早已存在,但是每个都复制粘贴提交一遍也不是办法,于是就想用程序自动完成这些操作。步骤如下:
1、从其他信息系统的数据库导出用户信息,也可以直接从其他信息系统的界面把所有用户信息复制下来放在一个文本文件里,反正就是准备好数据源;
2、在需要录入用户信息的系统中,用人工操作的方式登录系统,并录入一个用户,同时用Wireshark抓包,查看整个过程要提交一些什么样的表单数据;
3、在程序中用httpclient提交同样的数据,完成登录,并从第1步的数据源中读取用户信息,然后循环提交录入用户所需的数据,完成用户的录入;
4、新系统没有默认的用户角色,是需要一个个修改的,是根据用户的ID来确定当前修改的用户,并且提交一个角色ID来进行设定,因此需要首先获取用户的ID,然后根据该ID来提交角色ID,而用户ID是通过用户列表页面获取到的,因此还需要通过正则表达式来获取所有用户的ID,然后循环提交角色ID,完成角色设定。

花了不少时间才搞定,有几个地方需要注意:
1、如果表单数据不包含中文,直接把表单的Name和Value加在HttpPost的URL的?后面即可,不需要专门建立NameValuePair,如下所示:
HttpPost httppost = new HttpPost("http://1.1.1.1/test/adduser?userid=abc&username=efg");
httpclient.execute(httppost);

但是,如果表单数据包含中文,例如姓名,用这种方式提交的表单数据会出现乱码,即使通过URLEncoder进行编码后再发也不行,必须建立NameValuePair,再加到HttpPost的Entity里面,如下所示:
HttpPost httppost = new HttpPost("http://1.1.1.1/test/adduser?userid=abc");
List
<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(
new BasicNameValuePair("username","张三"));
httppost.setEntity(
new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
httpclient.execute(httppost);

2、用于网页内容查找的正则表达式的使用方式一般为:
Pattern p = Pattern.compile(".*?abc(whattoget)123.*?");
Matcher m = p.matcher(line);
if(m.matches()){
     String whattoget 
= m.group(1);
}
".*?abc(whattoget)123.*?"就是一个正则表达式,如果用于匹配一行的时候,由于要查找的内容是在行中间,所以前后需要加上.*?,表示前后可以是任意字符,也可以什么都没有,而表达式中间的(whattoget)就表示一个group,编号为1,编号为0的group是整个匹配的字符串,找到之后提取group(1)即可得到想要的内容。
测试正则表达式是一项很麻烦的工作,不过有个很好的软件可以完成此工作:RegexBuddy,支持各种语言的正则表达式的调试。
学习正则表达式的宝典是《Mastering Regular Expression》。
posted @ 2016-01-12 15:54 amp@java 阅读(3556) | 评论 (1)编辑 收藏

今天遇到一个非常奇怪的问题,有台装XP的电脑,插上USB键盘没反应,还以为是键盘坏了,又找来两个不同型号的键盘,依然不行,又以为是USB接口坏了,结果在电脑启动的时候又可以按F2进入BIOS,这样就只有一个原因,Windows的驱动没装上了。
幸好这电脑还有传统的PS/2口,而且插上就能识别,否则连Windows都进不去,因为按Ctrl+Alt+Del没反应。
进去之后提示安装USB键盘驱动,到最后一步提示安装失败,原因是拒绝访问。
上网搜了一下,安装驱动拒绝访问的其中一个原因是注册表有个键的权限设置有问题,改过来即可,但是我打开注册表,连那个键都没找到,不是这个原因。
不过从这个解决方案中也知道了驱动安装的日志是在Windows目录下的setupapi.log文件里面,于是打开那个文件,发现每次安装都有两个拒绝访问的错误,但并没有说是注册表拒绝访问,在拒绝访问之前,还提到一个叫MlCoInst.dll的文件没有签名。
上网搜MlCoInst.dll,没有找到任何结果,在System32目录下找到它,看属性,果然没有签名,是个三无文件,不知道为什么每次安装驱动都要调用它。
日志里面还提到了“共同安装程序”,似乎和CoInst有点关联,于是又查了一下,原来安装驱动的时候可以通过调用“共同安装程序”来实现某些目的,例如修改驱动程序的签名状态,欺骗操作系统,这样就可以只安装一次驱动即可,不用每次插入都安装一次。
于是尝试把MlCoInst.dll删除,提示删除失败。
在注册表里面搜索MlCoInst.dll,把所有找到的键值都删除,再次插拔键盘,顺利安装完毕,删除MlCoInst.dll,也成功了,果然是它的问题。

这个应该是某个USB设备的驱动引进来的,而且修改了usb.inf,每次安装任何USB设备都要调用它,但它可能与Windows的签名机制有冲突,所以导致安装失败,真是坑爹!
posted @ 2015-04-02 17:04 amp@java 阅读(3486) | 评论 (0)编辑 收藏

做GUI程序的时候,通常有个后台工作线程在努力工作,但是中间又需要一些暂停,而关闭程序的时候,必须立即结束那个线程,退出程序,也有的时候需要停止后台线程,但不关闭程序。例如,做一个目录监控程序,发现目录中有文件的时候,执行一定的操作,执行完之后没有文件了,就要暂停一下,过几秒或几分钟再次检测,这时候就要对线程进行暂停操作,如果在暂停的时候,用户要关闭程序,就必须马上停止线程,如果用户需要暂停检测,按下某个按钮后,需要让线程马上停止,但再次按下某个按钮,线程又必须马上开始。

以前我都是通过检测停止标记和用Thread.sleep(time)来完成的,后台线程的每次循环都要检查停止标记,如果发现停止标记已设定,就不再循环,退出线程,在线程内部,如果需要暂停,就执行Thread.sleep(time)。通过把线程的setDaemon(true)方法,还可以让线程作为后台线程,当图形界面关闭后,线程也自动退出。

但是,这种方式有个问题,如果我需要在图形界面上点击按钮来停止线程,但并不退出程序,而点击按钮的时候线程正处于sleep状态,就对它没有任何办法,只能让它醒过来再操作,如果sleep的时间比较长,例如1分钟,那么点击按钮之后,用户最多要等1分钟才能把线程停下来。当然,Thread对象有个interrupt方法,但是已经被标记为过期,一般不建议使用了。感谢评论中watchzerg的提醒,Thread的interrupt()并没有标记为过期,可以按照他的说法来操作,更为简单。

怎么让线程能暂停,又能随时叫醒呢?原来Java里最原始的对象Object就自带此功能。

每个Object都有wait(time)和notify()方法,前者就是让拥有该Obejct的线程处于暂停状态,后者则让线程马上唤醒,通过这两个方法,就能够满足上述的所有要求。

首先,建立一个同步对象:
Object syncObj = new Object();

然后在线程中需要暂停的地方,调用该对象的wait(time)方法:
synchronized (syncObj) {
       syncObj.wait(60*1000);
}

在图形界面的按钮监听事件中,对该对象执行notify()方法:
        button_1.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {

                thread.setStop(true);
                synchronized (syncObj) {
                        syncObj.notify();
                 }
                //为了等待线程退出,还可以加上以下语句:
                thread.join();

            }
        });

posted @ 2015-03-10 16:52 amp@java 阅读(7654) | 评论 (2)编辑 收藏

Eclipse有个功能就是把整个项目打包成一个可执行的Jar文件,里面包含了所有项目引用了的库,如果电脑上安装了JRE,直接双击就可以启动,看起来很方便,如下图所示:

可以选择把所有类库打包进去,也就是Jar里面还包含一堆Jar:

还可以生成ant脚本:


生成的jar文件,可以直接通过java -jar xx.jar启动,简单快捷。


然而,这样导出来的可执行jar,启动速度却非常慢,这跟包的大小有关。有个项目导出来的包有40MB,结果启动需要近一分钟,在性能差的电脑上,甚至要几分钟,就是你执行完命令后,没有任何界面显示,但是查看任务管理器发现java进程的CPU占用率在浮动,说明正在努力启动,过了一段时间之后界面突然显示出来,简直让人崩溃。可是在Eclipse里面运行,却是一点就开。

我开始尝试缩小导出的包。但是Eclipse的导出对话框并没有提供需要打包哪些库的选项,都是默认把所有库都打包进去,但是它可以生成ant脚本,可以通过编辑ant脚本的方式来减少不必要的库。经过多次尝试,终于把40MB的包缩成了17MB,启动速度快了一些,但是依然需要半分钟以上。

后来觉得,能不能不打包直接运行呢?于是把导出来的包用压缩软件解压,再把里面包含的jar包继续解压,最后得到一堆没有压缩的class文件,再通过指定classpath的方式,直接运行程序入口class,发现启动速度和在Eclipse里面一样,一点就开。

打包的好处是部署简单,只需要一个文件,但带来的缺点实在不能忍,打散的方式部署起来稍微难一点,但是启动速度够快,对普通用户来说,这个才是最重要的。

当然,还有一种方式是像Eclipse那样,启动时显示一个图片,底下一个进度条显示启动进度,不过这样也加大了工作量,而且每次都要等那进度条,实际上也很烦。
posted @ 2015-03-10 15:07 amp@java 阅读(5103) | 评论 (0)编辑 收藏

     摘要: JMF太老了,各种问题得不到解决,Oracle也没再升级过,如果能找到新东西,最好能把它扔掉。最近OpenCV比较火,还有人用Java封装了OpenCV,成立了JavaCV项目,通过改造VideoInput这个基于C语言的项目,能够用Java来调用摄像头,JMF可以扔掉了。如果想测试,非常简单,把那些编译好的jar文件放入Build Path即可,如果是在Windows X86环境下,则只需要把带...  阅读全文
posted @ 2015-02-15 11:41 amp@java 阅读(10382) | 评论 (7)编辑 收藏

其实我觉得用java结合SQL来做同样的事情更简单,但是EXCEL的好处是不用编程,虽然里面有好多陷阱,但是掌握之后能够熟练运用还是比每次都编个程序更简单。VLOOKUP函数的使用比较复杂,搞了好久都不明白,终于找到了下面这篇文章,解决了很多问题。

以下是转载文章,原来的出处不知道是哪里,应该是来自

http://www.excelpx.com
但具体的地址不详。



VLOOKUP函数是Excel中几个最重函数之一,为了方便大家学习,兰色幻想特针对VLOOKUP函数的使用和扩展应用,进行一次全面综合的说明。本文为入门部分

     一、入门级

      VLOOKUP是一个查找函数,给定一个查找的目标,它就能从指定的查找区域中查找返回想要查找到的值。它的基本语法为:

      VLOOKUP(查找目标查找范围返回值的列数精确OR模糊查找)

下面以一个实例来介绍一下这四个参数的使用

     例1:如下图所示,要求根据表二中的姓名,查找姓名所对应的年龄。  

   公式:B13 =VLOOKUP(A13,$B$2:$D$8,3,0)  

   参数说明:

       1 查找目标:就是你指定的查找的内容或单元格引用。本例中表二A列的姓名就是查找目标。我们要根据表二的“姓名”在表一中A列进行查找。

        公式:B13 =VLOOKUP(A13,$B$2:$D$8,3,0)   

       2 查找范围(VLOOKUP(A13,$B$2:$D$8,3,0) : 指定了查找目标,如果没有说从哪里查找,EXCEL肯定会很为难。所以下一步我们就要指定从哪个范围中进行查找。VLOOKUP的这第二个参数可以从一个 单元格区域中查找,也可以从一个常量数组或内存数组中查找。本例中要从表一中进行查找,那么范围我们要怎么指定呢?这里也是极易出错的地方。大家一定要注 意,给定的第二个参数查找范围要符合以下条件才不会出错:

        A 查找目标一定要在该区域的第一列。本例中查找表二的姓名,那么姓名所对应的表一的姓名列,那么表一的姓名列(列)一定要是查找区域的第一列。象本例中,给定的区域要从第二列开始,即$B$2:$D$8,而不能是$A$2:$D$8。因为查找的“姓名”不在$A$2:$D$8区域的第一列。

        B 该区域中一定要包含要返回值所在的列,本例中要返回的值是年龄。年龄列(表一的D列)一定要包括在这个范围内,即:$B$2:$D$8,如果写成$B$2:$C$8就是错的。

       3 返回值的列数(B13 =VLOOKUP(A13,$B$2:$D$8,3,0))。这是VLOOKUP第3个参数。它是一个整数值。它怎么得来的呢。它是“返回值”在第二个参数给定的区域中的列数。本例中我们要返回的是“年龄”,它是第二个参数查找范围$B$2:$D$8的第3列。这里一定要注意,列数不是在工作表中的列数(不是第4列),而是在查找范围区域的第几列。如果本例中要是查找姓名所对应的性别,第3个参数的值应该设置为多少呢。答案是2。因为性别在$B$2:$D$8的第2列中。

       4 精确OR模糊查找(VLOOKUP(A13,$B$2:$D$8,3,0)  ),最后一个参数是决定函数精确和模糊查找的关键。精确即完全一样,模糊即包含的意思。第4个参数如果指定值是0或FALSE就表示精确查找,而值为1 或TRUE时则表示模糊。这里兰色提醒大家切记切记,在使用VLOOKUP时千万不要把这个参数给漏掉了,如果缺少这个参数默为值为模糊查找,我们就无法精确查找到结果了。  

      好了,关于VLOOKUP函数的入门级应用就说到这里,VLOOKUP函数可不只是这么简单的查找,我们讲的还只是1/10的用法。其他的没法在一篇文章中说明。敬请期待“VLOOKUP的使用方法-进阶篇”吧。

 

上一讲咱们学习了VLOOKUP的基本用法和示例,本讲将介绍VLOOKUP在使用中的一些小技巧。

Excel函数速成教程全系列(包括VLOOKUP函数,IF函数,offset函数,sumif函数等66个函数)预计6月初全部录制完成,现已在淘宝开始预订(8折优惠),地址:http://item.taobao.com/item.htm?id=17500884347

一、VLOOKUP多行查找时复制公式的问题

    VLOOKUP函数的第三个参数是查找返回值所在的列数,如果我们需要查找返回多列时,这个列数值需要一个个的更改,比如返回第2列的,参数设置为2,如 果需要返回第3列的,就需要把值改为3。。。如果有十几列会很麻烦的。那么能不能让第3个参数自动变呢?向后复制时自动变为2,3,4,5。。。   

    在EXCEL中有一个函数COLUMN,它可以返回指定单元格的列数,比如

         =COLUMNS(A1) 返回值1

         =COLUMNS(B1) 返回值2

   而单元格引用复制时会自动发生变化,即A1随公式向右复制时会变成B1,C1,D1。。这样我们用COLUMN函数就可以转换成数字1,2,3,4。。。 

    例:下例中需要同时查找性别,年龄,身高,体重。

   

     公式:=VLOOKUP($A13,$B$2:$F$8,COLUMN(B1),0)

  公式说明:这里就是使用COLUMN(B1)转化成可以自动递增的数字。

二、VLOOKUP查找出现错误值的问题。

    1、如何避免出现错误值。

     EXCEL2003 在VLOOKUP查找不到,就#N/A的错误值,我们可以利用错误处理函数把错误值转换成0或空值。

      即:=IF(ISERROR(VLOOKUP(参数略)),"",VLOOKUP(参数略)

     EXCEL2007,EXCEL2010中提供了一个新函数IFERROR,处理起来比EXCEL2003简单多了。

     IFERROR(VLOOKUP(),"") 

    2、VLOOKUP函数查找时出现错误值的几个原因

      A、实在是没有所要查找到的值

      B、查找的字符串或被查找的字符中含有空格或看不见的空字符,验证方法是用=号对比一下,如果结果是FALSE,就表示两个单元格看上去相同,其实结果不同。

      C、参数设置错误。VLOOKUP的最后一个参数没有设置成1或者是没有设置掉。第二个参数数据源区域,查找的值不是区域的第一列,或者需要反回的字段不在区域里,参数设置在入门讲里已注明,请参阅。

     D、数值格式不同,如果查找值是文本,被查找的是数字类型,就会查找不到。解决方法是把查找的转换成文本或数值,转换方法如下:

     文本转换成数值:*1或--或/1

     数值转抱成文本:&""  

     VLOOKUP函数的初级篇就说到这里了,咱们下一讲将介绍VLOOKUP的模糊查找有、反向查找等。

 

 在学习了VLOOKUP的入门和初级篇后,本文将带将大家学习VLOOKUP的进阶篇:VLOOKUP的模糊查找。

    一、字符的模糊查找    

        在A列我们知道如何查找型号为“AAA”的产品所对应的B列价格,即:

    =VLOOKUP(C1,A:B,2,0)

       如果我们需要查找包含“AAA”的产品名称怎么表示呢?如下图表中所示。

     公式=VLOOKUP("*"&A10&"*",A2:B6,2,0)  

    公式说明:VLOOKUP的第一个参数允许使用通配符“*”来表示包含的意思,把*放在字符的两边,即"*" & 字符 & "*"。

   二、数字的区间查找

      数字的区间查找即给定多个区间,指定一个数就可以查找出它在哪个区间并返回这个区间所对应的值。

    在VLOOKUP入门中我们提示VLOOKUP的第4个参数,如果为0或FALSE是精确查找,如果是1或TRUE或省略则为模糊查找,那么实现区间查找正是第4个参数的模糊查找应用。

    首先我们需要了解一下VLOOKUP函数模糊查找的两个重要规则:

    1、引用的数字区域一定要从小到大排序。杂乱的数字是无法准确查找到的。如下面A列符合模糊查找的前题,B列则不符合 

    

    2、模糊查找的原理是给一定个数,它会找到和它最接近,但比它小的那个数。详见下图说明。

    

   最后看一个实例: 

    例:如下图所示,要求根据上面的提成比率表,在提成表计算表中计算每个销售额的提成比率和提成额。

    

   公式:=VLOOKUP(A11,$A$3:$B$7,2)

   公式说明:

    1、上述公式省略了VLOOKUP最后一个参数,相当于把第四个参数设置成1或TRUE。这表示VLOOKUP要进行数字的区间查找。

    2、图中公式中在查找5000时返回比率表0所对应的比率1%,原因是0和10000与5000最接近,但VLOOKUP只选比查找值小的那一个,所以公式会返回0所对应的比率1%。

 

 前言:前面我们分别学习了VLOOKUP函数的入门、初级和进阶篇。今天我们学习VLOOKUP函数的高级应用部分-VLOOKUP函数的数组应用。(本文由兰色幻想原创,转载请注明转自excel精英培训

 一、VLOOKUP的反向查找。

    一般情况下,VLOOKUP函数只能从左向右查找。但如果需要从右向右查找,则需要把区域进行“乾坤大挪移”,把列的位置用数组互换一下。

    例1:要求在如下图所示表中的姓名反查工号。

     

    公式:=VLOOKUP(A9,IF({1,0},B2:B5,A2:A5),2,0)

    公式剖析:

        1、这里其实不是VLOOKUP可以实现从右至右的查找,而是利用IF函数的数组效应把两列换位重新组合后,再按正常的从左至右查找。

        2、IF({1,0},B2:B5,A2:A5)这是本公式中最重要的组成部分。在EXCEL函数中使用数组时(前提时该函数的参数支持数组),返回的结 果也会是一个数组。这里1和0不是实际意义上的数字,而是1相关于TRUE,0相当于FALSE,当为1时,它会返回IF的第二个参数(B列),为0时返 回第二个参数(A列)。根据数组运算返回数组,所以使用IF后的结果返回一个数组(非单元格区域):{"张一","A001";"赵 三","A002";"杨五","A003";"孙二","A004"}

 二、VLOOKUP函数的多条件查找。

      VLOOKUP函数需要借用数组才能实现多条件查找。

     例2:要求根据部门和姓名查找C列的加班时间。

     分析:我们可以延用例1的思路,我们的努力方向不是让VLOOKUP本身实现多条件查找,而是想办法重构一个数组。多个条件我们可以用&连接在一起,同样两列我们也可以连接成一列数据,然后用IF函数进行组合。

    公式:{=VLOOKUP(A9&B9,IF({1,0},A2:A5&B2:B5,C2:C5),2,0)}

    公式剖析:

       1、A9&B9 把两个条件连接在一起。把他们做为一个整体进行查找。

       2、A2:A5&B2:B5,和条件连接相对应,把部分和姓名列也连接在一起,作为一个待查找的整体。

       3、IF({1,0},A2:A5&B2:B5,C2:C5) 用IF({1,0}把连接后的两列与C列数据合并成一个两列的内存数组。按F9后可以查看的结果为:

       {"销售张一",1;"销售赵三",5;"人事杨五",3;"销售赵三",6}

       4、完成了数组的重构后,接下来就是VLOOKUP的基本查找功能了,另外公式中含有多个数据与多个数据运算(A2:A5&B2:B5),,所以必须以数组形式输入,即按ctrl+shift后按ENTER结束输入。

     三、VLOOKUP函数的批量查找。

     VLOOKUP一般情况下只能查找一个,那么多项该怎么查找呢?

     例3 要求把如图表中所有张一的消费金额全列出来

     分析:经过前面的学习,我们也有这样一个思路,我们在实现复杂的查找时,努力的方向是怎么重构一个查找内容和查找的区域。要想实现多项查找,我们可以对查找的内容进行编号,第一个出现的是后面连接1,第二个出现的连接2。。。

     公式:{=VLOOKUP(B$9&ROW(A1),IF({1,0},$B$2:$B$6&COUNTIF(INDIRECT("b2:b"&ROW($2:$6)),B$9),$C$2:$C$6),2,)}

     公式剖析:

        1、B$9&ROW(A1) 连接序号,公式向下复制时会变成B$9连接1,2,3

        2、给所有的张一进行编号。要想生成编号,就需要生成一个不断扩充的区域(INDIRECT("b2:b"&ROW($2:$6)),然后在这个逐行扩充的区域内统计“张一”的个数,在连接上$B$2:$B$6后就可以对所有的张一进行编号了。

       3、IF({1,0}把编号后的B列和C组重构成一个两列数组

     通过以上的讲解,我们需要知道,VLOOKUP函数的基本用法是固定的,要实现高级查找,就需要借助其他函数来重构查找内容和查找数组。

     至此VLOOKUP函数从入门到高级的四篇VLOOKUP函数使用教程全部结束了,VLOOKUP函数在数组运算中还有着其他应用,但只是配角了,所以本系列不再介绍。由于笔者水平有限,不免有错漏之处,请大家多多指点。

posted @ 2014-12-16 09:10 amp@java 阅读(500) | 评论 (0)编辑 收藏

JMF(Java Media Framework)是Java平台使用摄像头、麦克风等媒体设备的应用程序框架,但到了2.1.1e就不再更新,在Windows 7 X64上还能正常运行,只是安装的界面让你感觉回到了Windows98的时代。


不过年代久远的东西,虽然还能用,但可能会遇到一些奇怪的问题,折腾了两个月,发现了两个比较大的问题:

第一个是在Windows 7 x64上提示摄像头初始化失败的问题。这个问题很奇怪,电脑刚开机的时候可以顺利找到一次摄像头并正常操作,但是第二次就会提示摄像头初始化失败。有人提出的解决方法是安装一个叫ManyCamera的程序,这个程序可以把一个摄像头供多个程序同时使用,其实就等于中间加了一层转换,效果会有点差别,免费版还会加上水印,要求比较高的人可能会不爽,但是目前找不到其他办法。

第二个是在程序中找不到摄像头,不光找不到摄像头,其他媒体设备通通找不到,使用以下语句:
vector = CaptureDeviceManager.getDeviceList ( null )
按照文档说明是返回所有媒体设备,但每次vector都是null。
在Eclipse中运行又能正常,导出成jar之后运行就会找不到摄像头。
原因在于找不到jmf.properities文件,该文件包含所有检测到的媒体设备的信息,最简单的解决方法就是把JMF安装目录下lib子目录中的jmf.properties文件复制到最后运行的jar所在的目录,不过如果摄像头改过的话,重新检测后要把新的文件复制到jar目录,因为检测到的媒体设备信息都会存放在JMF安装目录里面的jmf.properties文件里。

StackOverflow里面有个人对这个问题解释得比较清楚:
http://stackoverflow.com/questions/8768142/java-capturedevicemanagergetdevicelist-is-empty


虽然问题解决了,但是还是不明白为何在Eclipse中可以正常运行,导出jar后运行却找不到摄像头,即使把JMF所有jar和lib目录都加入系统的CLASSPATH环境变量里还是不行。


另外,JMF安装程序会自动把它的jar和lib目录加入系统的%CLASSPATH%环境变量,但是如果你卸载了再重新安装到其他目录,并不会改变%CLASSPATH%的值,需要手动修改。不过这个环境变量似乎没啥用处。
最好不要把JMF安装到默认的Program Files目录,可能会运行不了,安装到短目录会比较保险,它似乎还是只认Dos时代的8.3目录结构,但偏偏又默认安装到Program Files里面。
posted @ 2014-07-01 10:33 amp@java 阅读(7246) | 评论 (0)编辑 收藏

其实我也搞不懂Windows的域,反正能用就行了。
但是最近有一台客户端的时间改不了,总是提示特权级不够,按理说应该是组策略限制了,但是我把那台计算机从包含该组策略的OU中移出来,还是不行,这就奇怪了,难道组策略不是指针对OU里面的成员的吗?百思不得其解啊,最后只能在BIOS里面把时间改了。
今天发现我自己的电脑设置不了屏幕保护,也是组策略限制了,然后把我的用户和计算机都移出了组策略应用的OU,结果发现还是设置不了,这下肯定是组策略的应用上有问题了。
搜索了半天发现有个叫rsop.msc的管理工具,可以看到某用户在某计算机上应用的组策略,结果发现我还是应用之前的组策略,但是计算机配置和用户配置前面都有个红叉,右键-属性-错误信息里面显示:
Title
由于下面列出的错误,组策略结构 失败。

系统找不到指定的路径。

注意: 由于 GP Core 失败,其它组策略组件没有一个处理了它们的策略。因此,其它组件的状态信息不可用。
好像是有某个组策略找不到,所以不能应用的意思。
然后想起来在域名下面有个“新建组策略”,但是没有做过任何配置,应该是有人手贱点了一下新建按钮加进去的,于是把它删除,还是不行。
重启了一下客户端,居然好了,时间也可以改了,屏幕保护也可以改了,一切都按计划进行。


莫名其妙~~
posted @ 2014-06-23 16:11 amp@java 阅读(1309) | 评论 (0)编辑 收藏

现在的手机摄像头动辄几百万上千万像素,如果电脑需要用到摄像头又没有的话,不妨用手机的摄像头代替。

我是在做一个电脑二维码识别器的时候,因为原来的摄像头太差,从而想到用一台淘汰的Android手机来代替。

这类应用应该不少,我首先找到的是一个叫DraoidCam的应用,装好之后发现免费版没法调整分辨率,于是放弃之。

然后又找到了一个国内做的免费软件,叫魅色,非常简单,支持USB和WiFi连接方式,如果是USB连接的话,打开USB调试模式之后,运行电脑的客户端,手机上就自动装上了App并且自动运行,可以调整分辨率,不过最高只有640*480,帧率不到10,不过已经能够满足我的需求了。

然后就可以像普通PC摄像头一样使用了,在JMF里面也能找到,于是就可以被Java调用了。经测试,效果比原来的PC摄像头好多了。

软件主页:http://www.libfetion.org/meise/
posted @ 2014-06-05 15:35 amp@java 阅读(1695) | 评论 (4)编辑 收藏

     摘要: TabActivity在API 13(Android 3.2)被标记为过期,需要使用Fragment来实现,Fragment是Android 3.0引入的一个概念,主要就是为了适应各种不同的屏幕大小(手机、平板电脑)。Android 4.1发布时,google还发布了一个Android Support v4的包,用于Android 1.6以上的系统兼容新的特性,其中包括Fragment。为了在低于...  阅读全文
posted @ 2012-12-27 19:07 amp@java 阅读(15608) | 评论 (0)编辑 收藏

SQL Server 2000的导入导出功能还是不错的,支持各种各样的数据库,但是却有好多奇怪的bug,不能直接操作,几乎每一步都要上网搜索,最后搞定了,一定要记下来:
1、在同一台电脑上装好SQL Server 2000的客户端和Oracle 10g的客户端,并分别设置好到源数据库(SQL Server 2000数据库)和目标数据库(Oracle 数据库)的连接,两个数据库都有图形界面的企业管理器,很容易设置好;
2、在控制面板-管理工具-数据源里添加一个DSN,驱动程序选择类似“Oracle in OraClient10g_home1”的,确定之后输入Data Source Name(随意),Description(随意),TNS Service Name(在企业管理器里设置好的连接名),User ID(用户名),然后按Test Connection测试是否连接成功,成功之后点OK;
3、在SQL Server 2000的企业管理器里,在任意一个表上点右键,所有任务,导出数据,在目的里选择“Oracle in OraClient10g_home1”,用户/系统DSN里面就会出现刚才设置好的DSN名字,选中,然后输入用户名密码,点两次下一步就会出现选择源表和视图对话框
4、这里要注意的是,勾上源中的某个表,在目的里面就会出现"用户名"."表名"的默认选项,如果你刚才使用的Oracle用户名是小写的话,这里也会是小写,一定要改成大写,否则会提示该用户名不存在

目的也可以使用Microsoft OLE DB Provider for Oracle,在属性里面设置服务器名称为Oracle的TNS名称,用户名和密码输入Oracle用户名和密码,测试连接通过即可,后面的步骤都一样。

如果出现以下错误:

OLE DB 提供程序 'MSDAORA' 报错。

[OLE/DB provider returned message: 未找到 Oracle 客户端和网络组件。这些组件是由 Oracle 公司提供的,是 Oracle 8i (或更高) 客户软件安装的一部分。

 

在安装这些组件之前,将无法使用此提供程序。]

OLE DB 错误跟踪[OLE/DB Provider 'MSDAORA' IDBInitialize::Initialize returned 0x80004005:  


就要修改注册表,有人已经作出了详细的修改说明,在这里可以看到:
http://www.cnblogs.com/autumn/articles/splinkedserver.html

我把那个表也贴在这里:
Oracle Client  Microsoft Windows NT、
Oracle Microsoft Windows 95、
Client Windows 98 和 Windows 98 SE
Microsoft Windows 2000/XP/2003
7.x [HKEY_LOCAL_MACHINE\SOFTWARE
\Microsoft\TransactionServer\Local Computer\My Computer]
"OracleXaLib"="xa73.dll"
"OracleSqlLib"="SQLLib18.dll"
"OracleOciLib"="ociw32.dll

 

[HKEY_LOCAL_MACHINE\SOFTWARE
Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="xa73.dll"
 "OracleSqlLib"="SQLLib18.dll"
 "OracleOciLib"="ociw32.dll"
 
8.0 [HKEY_LOCAL_MACHINE\SOFTWARE
\Microsoft\Transaction Server
\Local Computer\My Computer]
"OracleXaLib"="xa80.dll"
"OracleSqlLib"="sqllib80.dll"
"OracleOciLib"="oci.dll"
 
[HKEY_LOCAL_MACHINE\SOFTWARE
 \Microsoft\MSDTC\MTxOCI]
 "OracleXaLib"="xa80.dll"
 "OracleSqlLib"="sqllib80.dll"
"OracleOciLib"="oci.dll"
8.1 [HKEY_LOCAL_MACHINE\SOFTWARE
\Microsoft\Transaction Server
\Local Computer\My Computer]
"OracleXaLib"="oraclient8.dll"
"OracleSqlLib"="orasql8.dll"
"OracleOciLib"="oci.dll"
 
[HKEY_LOCAL_MACHINE\SOFTWARE
 \Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="oraclient8.dll"
"OracleSqlLib"="orasql8.dll"
"OracleOciLib"="oci.dll"
 
9.0 [HKEY_LOCAL_MACHINE\SOFTWARE
\Microsoft\Transaction Server
\Local Computer\My Computer]
"OracleXaLib"="oraclient9.dll"
"OracleSqlLib"="orasql9.dll"
"OracleOciLib"="oci.dll"
[HKEY_LOCAL_MACHINE\SOFTWARE
 \Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="oraclient9.dll"
"OracleSqlLib"="orasql9.dll"
"OracleOciLib"="oci.dll"
 
10.0 [HKEY_LOCAL_MACHINE\SOFTWARE
\Microsoft\Transaction Server
\Local Computer\My Computer]
"OracleXaLib"="oraclient10.dll"
"OracleSqlLib"="orasql10.dll"
"OracleOciLib"="oci.dll"
[HKEY_LOCAL_MACHINE\SOFTWARE
 \Microsoft\MSDTC\MTxOCI]
"OracleXaLib"="oraclient10.dll"
"OracleSqlLib"="orasql10.dll"
"OracleOciLib"="oci.dll"
 
posted @ 2012-04-28 10:22 amp@java 阅读(2554) | 评论 (0)编辑 收藏

当读写二进制文件,或者要把非标准长度的整数与标准长度的整数互相转换时,就要用到大量的位操作,虽然看起来很简单,实际上里面却有很多细节很容易出错。

首先,Java有些标准跟C/C++是不同的:

1、Java采用高字节在前的方式读写数据,例如要把一个4字节的int数值写入文件时,它是按照从高字节到低字节的顺序写入的,读取的时候也是这样读出来。
而C/C++则采用平台相关的方式,在Windows平台采用低字节在前的方式,在Linux/Unix平台则采用高字节在前的方式。
如果Java要读取C/C++创建的二进制文件,就要注意这个问题,最好先搞清楚原来的文件是采用哪种方式创建的。网络通信也要注意。

2、Java没有无符号数,无论byte,short,int,long都是有符号整数,而C/C++有个unsigned关键字可以设置一个数值为无符号数。

3、Java的整数基本数据类型就是byte,short,int,long这几个,长度分别为1,2,4,8字节,C/C++可以用typedef定义各种数据类型。

第二,Java是采用补码来存放整数的。
有时候觉得补码的定义有些奇怪,实际上可以这样理解:

把一个整数从0一直往上加1,加到溢出就变成了负数的最小值,然后再继续加1,最后又能回到0,实际上就是一个轮回。
例如一个byte类型的整数,一共有8位,能表示256个数值,采用补码的话数值范围就是-128~127,表示方法如下:
0        0000 0000
1        0000 0001
.
.
126    0111 1110
127    0111 1111
-128   1000 0000
-127   1000 0001
.
.
-1       1111 1111
0         0000 0000

第三、不同长度的整数转换。
如果是从较短的数转成较长的数,很简单,如果是正数就在高字节补0,如果是负数就在高字节补1。
例如byte的127转为short的127:
byte:0111 1111
short:0000 0000 0111 0111
byte的-127转为short的-127
byte:1000 0001
short:1111 1111 1000 0001
如果是从较长的数转成较短的数,实际上就是把高位都截断了,所以转出来的数值可能完全不是一回事了。
例如short的256转为byte:
short:0000 0001 0000 0000
byte: 0000 0000
把256变成了0
short的-255转成byte:
short:1111 1111 0000 0001
byte:0000 0001
把-255变成了1

第四、位运算操作符及它们的优先级
Java的位运算操作符包括:~非,|按位或,&按位与,^按位异或,<<左移,>>右移,>>>右移左侧补0
各种运算符的优先级如下表所示:
优先级
运算符
结合性
1
() [] .
从左到右
2
! +(正) -(负) ~ ++ --
从右向左
3
* / %
从左向右
4
+(加) -(减)
从左向右
5
<< >> >>>
从左向右
6
< <= > >= instanceof
从左向右
7
== !=
从左向右
8
&(按位与)
从左向右
9
^
从左向右
10
|
从左向右
11
&&
从左向右
12
||
从左向右
13
?:
从右向左
14
= += -= *= /= %= &= |= ^= ~= <<= >>= >>>=
从右向左
根据该表可以看到,位运算操作符的优先级各有不同,分别为:
1、~
2、>> << >>>
3、&
4、^
5、|
另外需要特别注意的是,除了~,其他位运算操作的优先级都低于加减,所以要记得以下语句是返回32而不是7!
1<<2+3
还有就是&、^、|的优先级都是低于逻辑操作符的,因此下面的语句会编译出错,幸好Java不像C那样对所有大于1的值都认为是真,否则下面的语句也能编译通过,但可能与你的意图不太一样,可能调试半天才发现。
if(3&1>0)
如果记不清楚,还是按照你的意图加上括号最保险。

第五、字节数组与整数之间的转换
为了把一个整数存入文件,或者从文件中读取一个整数,需要经常在字节数组和整数之间转换,这个过程要用到大量的位运算。
首先需要记住的是,在参与所有运算前,Java都会把byte、short类型的值都转换成int,然后再对转换后的int进行操作。例如下面的语句会编译出错:
byte a=10,b=20,c;
c=a+b;

因为a和b在相加前都被转成了int,最后得到的结果是个int类型的值,如果要赋给byte类型的c,必须显式地进行类型转换,即把第二句改为:
c=(byte)(a+b)

这一点很关键,因为对于一个最高位为1的byte类型的整数(负数),在运算之前它会被强制转换成int类型,根据上面所说的第三点,其实就是往前面的三个高字节补上1,这样一来,它在参与位运算的过程中,就不仅仅是它本身的8个bit参与了,实际上连前3个字节的24个bit(均为1)也参与了。例如有一个整数i=1082163328,它的二进制表示为:
01000000 10000000 10000000 10000000
分为4个字节存储,除了第一个字节是正数外,其余3个字节均为负数。假如用a代表最高字节的值,用b代表其他三个字节的值,如果按照通常的理解,你可能会这样得到i的值:
i=(a<<24)+(b<<16)+(b<<8)+b

如果a和b都是正数,上面的等式是成立的,但是在这个例子里,却是错的,因为上式中的a和b都已经被强制转换成了int类型再参加运算,实际上
a=00000000 00000000 00000000 01000000
b=11111111 11111111 11111111 10000000
i=01000000 00000000 00000000 00000000+11111111 10000000 00000000 00000000+11111111 11111111 10000000 00000000+11111111 11111111 11111111 10000000
最后得到的结果是1065320320,不是原来的值了。
为了不让byte在强制转换成int的过程加入了我们不想要的高位1,我们需要把它跟0xff进行与操作,i的值应该这样运算:
= ( ( a& 0xff ) << 24 ) +( ( b & 0xff ) << 16 ) + ( ( b & 0xff ) << 8 ) + ( b & 0xff )

注意,因为&和<<的优先级都低于+,所以上面的括号是不能少的。不过由于跟0xff与操作之后,其余24位都变成了0,因此可以把+改为|操作,因为任何值与0进行或操作都得到本身:
= ( a & 0xff ) << 24 | ( b & 0xff ) << 16 | ( b & 0xff ) << 8 | ( b & 0xff )

由于<<的优先级高于|,所以省了一些括号。最高字节可以不与0xff进行与操作,因为它转换成int后左边增加的3个字节都在左移24位时被去掉了:
= a << 24 | ( b & 0xff ) << 16 | ( b & 0xff ) << 8 | ( b & 0xff )


把int转为字节数组的时候比较简单,直接右移截断即可:
byte[] b = new byte[4];
b[0= (byte) (i >> 24);
b[1= (byte) (i >> 16);
b[2= (byte) (i >> 8);
b[3= (byte) i;


第六、非标准长度整数的存储和读取
假如有两个变量,他们的值可以用12个bit来表示,如果我们用16bit的short类型来表示一个变量,那么两个变量就需要4个字节,而实际上它们只需要3个字节就能表示出来,如果存储空间比较有限,写入文件时可以把它们存放在3个字节里面,但是读写过程就需要进行转换。
在内存里,它们都是标准的数据类型:
short a,b;

写入文件时,我们用第一个字节和第二个字节的前半部分来表示a,把第二个字节的后半部分和第三个字节来表示b,即:
1:xxxx xxxx
2:xxxx yyyy
3:yyyy yyyy
x和y都表示一个bit,分别用来存放a和b。写入时先把a和b转为字节数组:
byte[] out = new byte[3];
out[
0= (byte) ( a >> 4 );//把a的高8位放在第一个字节
out[1= (byte) ( a << 4 );//先把a左移四位,在右边补上4个0,第二个字节的高4位就是a的低4位了,第二个字节的高4位已经生成,低4位还是0
out[1|= (byte) ( b >> 8 & 0x0f );//b右移8位,并与0x0f进行与操作,实际上就只保留了b的高4位,并且是在字节的低4位上,跟第二步得到的字节进行或操作,就生成了第二个字节
out[2= (byte) b;//把b的高4位截断就得到了低8位
然后再把这个字节数组写入文件,就可以用3个字节表示两个整数了。
读取:
=(short)( (out[0& 0xff<< 4 | ( out[1& 0xf0 )>>4);
= (short)((out[1& 0x0f<< 8 | ( out[2& 0xff));
posted @ 2012-04-08 16:56 amp@java 阅读(1799) | 评论 (2)编辑 收藏

AdMob是往手机应用程序里添加广告的最流行的方式,Android程序基本都是靠这个赚钱。看文档似乎很简单,但是操作起来却不是那么回事,今天搞了一上午才弄明白怎么正确添加,网上搜索到的资料都不适合最新的SDK。

按照AdMob的官方文档,很简单,只要把AdMob的开发包jar放到Build Path的Libraries里面就行了,这样做编译是没问题的,但是一运行就会出错,提示
java.lang.NoClassDefFoundError:com.google.ads.AdView
这是因为Google最近更新了ADT到17.0,改变了项目依赖的检测方式,官方的说明在这里,不过看得不是很懂。有人用图形的方式标了出来,容易理解一些,看这里。如果不求甚解,就把刚才放到Build Path里面的jar移除,直接在项目目录下建立一个libs目录,然后把那个jar文件放进去就行了。

AdMob SDK也更新到了4.3.1,网上搜索到的在XML文件里面设置AdView属性的方法也已过时,现在不需要建立attrs.xml文件,直接增加一个
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
就可以在AdView标签里面设置ads开头的属性了,关于xml设置AdView属性的官方文档隐藏得比较深,没有在目录中列出来,只能在其他文档里面的链接里进去,在这里
另外,按照官方说明,要在AndroidManifest.xml里面添加一个Activity的声明:
    <activity android:name="com.google.ads.AdActivity"
              android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
后面那个属性android:configChanges的最后两个值screenSize|smallestScreenSize是在Android 3.2后面才增加的,这两个值必须填上去,否则运行的时候会报错。如果你使用的SDK是3.2以下的,识别不了这两个值,编译也不会报错。因此你的项目必须使用Android 3.2以上的SDK,也就是项目根目录下的project.properties文件里面的属性target的值必须是android-13以上,例如
target=android-15
但是可以在AndroidManifest.xml里面设置android:minSdkVersion和android:targetSdkVersion为比13低的值,这样就不需要安装Android 3.2以上的手机或平板来运行这个程序,实际上AdMob支持Android 1.5以上的系统,只是开发需要更高版本的SDK而已。


这次折腾让我真正体会到Android更新得实在太快了,连官方的文档都来不及更新,开发人员只能自己摸索总结,幸好有强大的搜索工具,只要你遇到的问题不是第一个,就会有人找到解决方案。



posted @ 2012-03-25 19:23 amp@java 阅读(3680) | 评论 (7)编辑 收藏

SWT里TableEditor的作用是可以在表格里面显示一些控件,例如列表、按钮等,有时候是根据表格的内容在控件上显示不同内容的,如果更新了表格内容,就要同时更新控件,但是表格内容可以通过Table控件的removeAll()来清除,而表格中的控件则无法用这个方法清除,你调用Table的removeAll()方法,往表格里填入新内容后,控件还是上次的控件,但是你一操作那些控件就会出异常,提示那些控件已经disposed。

解决方法是显式地调用控件及TableEditor的dispose()方法,在你建立TableEditor的时候,把它的引用保存起来,把里面的控件的引用也保存起来,到整个表格需要的清除的时候,通过引用先把控件dispose掉,再把TableEditor也dispose掉,这样整个表格的内容就真正清除了。

例如有一个表格名为table,里面的每一行都有3列,第一列是文本,第二列是Combo,第三列是Button,绘制表格的时候是这样的:

TableItem ti = new TableItem(table,SWT.NONE);
ti.setText(
0,"some string");
te 
= new TableEditor(table);
Combo combo 
= new Combo(table,SWT.NONE);
controls.add(combo);
te.setEditor(combo,ti,
1);
Button button 
= new Button(table,SWT.NONE);
controls.add(button);
te.setEditor(button,ti,
2);

其中te和controls都是成员变量,te的类型是TableEditor,controls的类型是ArrayList<Control>。
当整个table要清除内容时,可以这样:
//删除控件
for(Control control:controls){
control.dispose();
}
//删除TableEditor
te.dispose();
//删除文本
table.removeAll();
posted @ 2012-03-09 11:09 amp@java 阅读(3803) | 评论 (0)编辑 收藏

mars课程里关于Socket通信那一课说那些程序只能在真机上运行,模拟器模拟不了,实际上是可以的。
Android模拟器是通过一个类似路由器的虚拟网络层与电脑相连,可以看作模拟器是处于“内网”当中,每个模拟器都有自己的虚拟路由器,而且虚拟路由器的地址总是10.0.2.1,在模拟器看来,电脑的地址是10.0.2.2,模拟器自己的地址是10.0.2.15,无论你启动多少个模拟器,对于模拟器来说都是这样的地址,模拟器之间不能直接通信。
启动模拟器的时候,电脑会给模拟器分配两个端口,通过这两个端口,电脑就能操作模拟器。第一个启动的模拟器的端口是5554和5555,第二个是5556和5557,以此类推,最多可以同时启动32个模拟器。第一个端口(偶数端口)可以接受telnet连接,对模拟器进行设置,第二个端口(奇数端口)则接受adb连接,可以用来调试。第一个端口可以在模拟器窗口的标题栏看到,如下图所示:

5554表示端口号,t表示模拟器名称。
实际上,这些端口也是电脑监听的端口,在电脑上通过netstat可以看到本机正在监听这些端口,因此通过telnet localhost 5554就能连上第一台模拟器,连上之后通过help命令可以查看操作帮助。
为了实现电脑和模拟器上的android程序进行socket通信,需要把程序开启的端口通过端口映射设置到电脑上,这跟家里的路由器端口映射概念是一样的。telnet到模拟器之后,通过
redir add tcp:1234:1234
就能把模拟器上的1234端口映射到电脑上,第一个表示电脑端口,第二个表示模拟器程序要使用端口,这两个数字可以相同也可以不同,要映射udp端口就把tcp改为udp即可
redir add udp:1234:1234
这样一来,当模拟器的程序打开1234端口时,在电脑上也打开了对应的端口,只要通过电脑连接127.0.0.1的对应端口,就连上了模拟器的程序端口,就可以通过电脑上的client向模拟器的server发送数据,不需要通过真机运行。
如果要让第一个模拟器向第二个模拟器发送数据,也可以把第二个模拟器的端口映射到电脑上,然后在第一个模拟器程序中向10.0.2.2的对应端口发送数据即可。


模拟器还有一个很有意思的功能,每个模拟器默认的电话号码就是它的第一个端口号,例如开了两个模拟器,第一个拨打5556,第二个就会显示5554来电,还能接通,发短信也可以,这样就能模拟电话和短信功能。

详细的信息可以看Dev Guide的模拟器部分:http://developer.android.com/guide/developing/devices/emulator.html
posted @ 2012-02-24 10:37 amp@java 阅读(2399) | 评论 (0)编辑 收藏

前天是情人节,虽然结婚好多年了,但是老婆一直都喜欢惊喜的浪漫,可惜我却是个木讷的呆子,做不出那些轰动的事情。那天下午马上就要下班回家了,突然在微博上看到有人谈到geek的情人节礼物,虽然我不是geek,但是最近在学Android,老婆的手机也是Android系统的,何不专门做个程序给她?

想法可嘉,但是动起手来却不是那么回事。学了那么多天,真正派上用场的还没学到。时间只剩下不到一个小时了,我会的只是在屏幕上显示几个大字:XXX,我爱你!

后来想想似乎太单调,如果能够加上点背景音乐可能好点,但是还没学会怎么使用,上网搜了一下,幸好很简单,用MediaPlayer就可以了,几条语句就能搞定。音乐文件怎么来呢?通过网络在线播放是最简单的,于是就到百度MP3搜了一下“情人节快乐”,我记得有一首歌里面一直在喊“情人节快乐”的,结果最后发现那首歌名字叫《没有情人的情人节》……

算了,管它呢,有老婆就行了,没有情人照样过情人节,打开发现链接居然是百度的,以前百度不是说它只负责搜索,不负责存放吗,怎么现在的MP3都放在百度的服务器了?把链接复制下来,在模拟器上运行还是挺好的,因为它用的是电脑的宽带,呵呵。不过过了一会提示下载失败,把那个地址往浏览器一贴,果然打不开了,原来百度这种下载链接是有有效期的,只能让你试听一下,然后下载,不是长期有效的。这就麻烦了,到时候装在手机上没声音岂不是很糗?

再搜索一下,还好,可以把MP3文件放在assets里面,发布程序的时候把它包含在apk里面就行了,不用联网了。

现在可以在显示大字的同时播放《没有情人的情人节》了。看了一下效果,还是有点怪异,手机的状态栏和程序的标题跟黑色的背景,红色的大字似乎不太搭配,于是继续搜索全屏代码,哈哈,两句搞定,这样炫多了。

不过一直看着那几个字没任何反应,似乎太单调了,于是就想让它们不断变色,或者动一下也好,但是无论怎么弄都搞不定,下班时间到了,要去接老婆了,就这样吧。

之前都是在模拟器上运行,或者接个USB在手机上运行,还不知道怎么打包成apk呢,这下居然没搜索到,可能太简单了,大家都没说。于是在项目上点右键,果然看到导出apk的菜单,我选择了unsigned方式导出,在手机上居然安装不了。再搜索一下,哦,原来是要导出成signed apk才能装的,但是我没有证书,怎么signed呢?没想到ADT还可以生成证书,一下就搞定了,这比Symbian那种签名简单多了,但是可能也是导致Android恶意软件泛滥的原因之一。

去接老婆的时候通过蓝牙把apk发到她手机上,装上,运行,从她表情上看出,我的努力没有白费。不过最后还是发现了个严重的bug,那首歌还没放完,另外一个声音已经重新开始了,形成了“二重唱”的效果,而且程序退出之后还在唱,赶工造成的悲剧啊。

下面是代码:
public class LoveActivity extends Activity {
    TextView text;
    
/** Called when the activity is first created. */
    @Override
    
public void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
        WindowManager.LayoutParams.FLAG_FULLSCREEN);  
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        setContentView(R.layout.main); 
        MediaPlayer mp 
= new MediaPlayer();
        
try{
            AssetManager assetManager 
= getAssets();
            AssetFileDescriptor afd 
= assetManager.openFd("a.mp3");
            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            mp.prepare();
            mp.start();
        }
        
catch (Exception e) {
            e.printStackTrace();
        }
    }
}
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:id="@+id/textView1" 
    android:text
="@string/loveyou" 
    android:layout_width
="match_parent" 
    android:layout_height
="match_parent" 
    android:gravity
="center" 
    android:textColor
="#ff0000"
    android:textStyle
="bold"
    android:textSize
="50dip"
    
/>
    
   
</LinearLayout>
strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    
<string name="hello">Hello World, LoveActivity!</string>
    
<string name="app_name">Love</string>
    
<string name="loveyou">XXX\n我爱你</string>
</resources>
再把那首mp3放到assets里,改名为a.mp3即可。




希望明年能做出个更好的。
posted @ 2012-02-16 11:06 amp@java 阅读(2267) | 评论 (2)编辑 收藏

昨天在调试一段Android程序的时候发现总是出现NullPointerException,是来自一句System.out.println(),但是把里面的内容分拆了几次都找不到哪里有null,最后发现居然是因为最终输出的字符串是null!

一直以来,在JavaSE里,如果字符串本身是null,System.out.println()打印该字符串,会在终端输出“null”,而在Android里却是直接抛出NullPointerException,整个程序都会被终止。

大家可以测试一下下面的语句在两种环境下的运行结果:
1 String s = null;
2 System.out.println(s);
posted @ 2012-02-13 14:49 amp@java 阅读(1368) | 评论 (1)编辑 收藏

这是mars课程里面关于handler和线程的一个例子:
package mars.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

public class HandlerTest extends Activity {
    Handler handler 
= new Handler();
    
/** Called when the activity is first created. */
    @Override
    
public void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        System.out.println(
"1");
        handler.post(r);
        System.out.println(
"2");
        setContentView(R.layout.main);
        System.out.println(
"activity---->"+Thread.currentThread().getId());
        System.out.println(
"activity name--->"+Thread.currentThread().getName());
    }
    
    Runnable r 
= new Runnable() {
        
        @Override
        
public void run() {
            
// TODO Auto-generated method stub
            System.out.println("handler---->"+Thread.currentThread().getId());
            System.out.println(
"handlername---->"+Thread.currentThread().getName());
            
try {
                Thread.sleep(
10000);
            } 
catch (InterruptedException e) {
                
// TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(
"3");
        }
    };
}

根据mars的解释,handler所在的线程跟Activity的线程是同一个线程,所以在
handler.post(r);
语句后,执行的是Runnable里面的run函数,这个函数没有在新开的线程中执行,只是简单地调用了run函数,所以这个app在模拟器运行时要过10秒才会显示界面,因为run函数里面睡眠了10秒,等它返回后才执行setContentView函数设置界面元素。
根据实际运行结果,的确是过了10秒才能显示界面。

但是奇怪的是System.out语句似乎没有受到影响,下面是日志:
log
02-09 11:12:43.553: INFO/System.out(591): 1
02-09 11:12:43.553: INFO/System.out(591): 2
02-09 11:12:43.674: INFO/System.out(591): activity---->1
02-09 11:12:43.674: INFO/System.out(591): activity name--->main
02-09 11:12:43.713: INFO/System.out(591): handler---->1
02-09 11:12:43.713: INFO/System.out(591): handlername---->main
02-09 11:12:53.775: INFO/System.out(591): 3

从日志可以看出,除了run函数里面睡眠后才执行的打印函数推迟了10秒才执行之外,其他都是没有受到任何延时,顺序执行的,在
handler.post(r);
语句前后的打印函数都被按顺序执行了,唯独
setContentView(R.layout.main);
需要在run函数返回后才执行,这是什么道理?难道打印函数的优先级更高,不会堵塞?如果是这样的话为什么在run函数里面还是要等睡眠结束才执行呢?
posted @ 2012-02-09 19:25 amp@java 阅读(1827) | 评论 (6)编辑 收藏

很想搞点Android的小程序玩玩,但是却万事开头难,不知道从哪里开始,看了官方的文档,结果一天都看不了几段,还是英文的,比较痛苦。找了些电子书,也是看过目录就不知道放哪里了。这种编程的入门没有强大的决心真的很难静下心来学习。
前几天又心血来潮,搜索了一下android开发的网站,发现了一些视频,看起来还挺有意思,连续看了几个,慢慢地也摸着一点门路了,以前没有试过用这种方法学习,都是看书,现在发现看视频似乎更高效,起码眼睛没那么累,有人在念,很多时候只要用耳朵听就行了,而且所有软件的界面、步骤都很清楚,这些东西通过书是没法表现出来的。
现在学会了让一个Activity显示出来,放上点控件,处理一下点击,页面布局等,算是入门了,但很多东西还要继续看,那些视频还有好多。
入门系列视频地址:
http://www.marsdroid.org/
还有一个深入浅出系列:
http://www.eoeandroid.com/thread-109361-1-1.html
由于没有入门,所以还在看第一个系列,第二个系列只看了两个。
到现在发现android界面的构建很像web界面,android是通过layout来排布控件,而web是通过css,android可以通过java来操纵控件,而web则是通过javascript操纵控件,回调函数则跟JavaSE一样。



不过有时候很容易忘记了一个步骤导致程序运行出错,一般来说,要让一个Activity成功运行,需要做以下步骤:
1、有一个继承与Activity的类
2、有一个layout文件,里面是各种控件的布局,在Activity里面应该用setContentView加载这个layout
3、有一个strings文件,里面是界面的各种字符串,用于国际化
4、在AndroidManifest里面加入这个Activity
第4步很容易忘记。
posted @ 2012-02-08 14:55 amp@java 阅读(251) | 评论 (0)编辑 收藏

前段时间听了一家公司介绍分布式存储产品,号称性能超过传统的磁盘阵列+小型机,但价格却比这种组合低,而且具有维护简单、数据安全等优点,其核心就是把一些PC服务器通过网络连接起来,把数据分散存储在这些服务器上,查找的时候把任务分配到这些服务器上,让它们分别完成各自的小任务,最后再汇合出结果,核心就是:每个节点都是一个数据存储单元和运算单元的结合,这些节点性能要求不高,可以是各种各样的配置,只要在上面运行集群要求的软件即可,一台挂了不要紧,换上去一台就能自动恢复,增加一台就能提高性能,减少一台也不会有很大影响,只是性能稍微下降,每份数据都有多个备份,能够平衡分布在各服务器上。这种架构的缺点是,只要用上了这套东西,所有的软件就必须从这家公司购买,因为整个集群的控制和访问接口都是他们提供的。
该公司也坦言这其实就是google的服务器应用技术,实际上就是google提出来的云计算。

今天看了一些文章介绍,发现上面介绍的东西实际上已经通过Apache的一个项目Hadoop实现,不知道那家公司是不是就是简单包装了一下这个项目。

Hadoop包含两个主要的模块,分别是HDFS分布式文件系统和MapReduce集群计算机制。刚刚发布了1.0.0版本。

主页: http://hadoop.apache.org/

blogjava有位高手已经翻译了HDFS的架构说明:http://www.blogjava.net/killme2008/archive/2008/06/05/206043.html,但对应的是上一个版本0.20的,里面说到了很多没有实现的东西,不知道是否已经在1.0版本实现,目前官方网站上该文章的版本还是0.20的。

这里有三篇文章介绍Hadoop在单机环境、多机环境下的安装和配置,还有应用程序的开发:http://www.ibm.com/developerworks/cn/linux/l-hadoop-1/index.html

看起来还是挺有意思的,以前就曾经有过想法,把单位淘汰下来的那一大堆台式电脑组成一个存储或者计算的小集群,也许能够代替一两台PC服务器,现在看来可以试试,以后的趋势就是云计算了,先自己搞点小云看看效果如何。
posted @ 2012-01-19 14:47 amp@java 阅读(218) | 评论 (0)编辑 收藏

这个不仅仅是IE6的错,而是所有IE的错!!
IE有一个特别随意的功能,就是能够通过Element的name来操作一个Element,有些人觉得这个功能很方便,实际它不仅不符合标准,还会导致IE自己变得莫名其妙。
假如你的Form有一个提交按钮的name="submit",那么,很遗憾,你这个form无论如何都不能通过Javascript来提交,只要你一调用了这个form的submit()方法,IE就会告诉你它不支持这个方法!这不是搞笑吗?哪个浏览器会不支持form的submit()方法?虽然IE不标准,也不至于这样吧?对不起,就是这样。

因为当你调用form的方法submit(),它首先想到的是你的提交按钮!
theForm.submit()
这个语句在IE看来并不是调用theForm的submit方法,而是调用了名字为submit的提交按钮!如果你在某个元素对象后面加个括号,会有什么效果?当然就是出错。在IE的独特视角下,上面这个语句与下面这个语句的效果一样:
(theForm.submit)();
前一个括号代表了名称为submit的按钮对象,后面那个括号就无法理解了,所以IE告诉你不支持这个方法。

IE整个家族都是如此的丑陋!

posted @ 2010-12-09 15:02 amp@java 阅读(279) | 评论 (0)编辑 收藏

一直都看到很多人说,IE6是Web开发人员的恶梦,以前我单位的内部网络,所有电脑装的都是IE6,我只针对IE6开发,似乎没什么感觉,反正在我的电脑上看到什么样的,其他电脑上看到也是一样的。
后来因为某公司给我们做的一个系统运行速度实在太慢,我把自己的电脑更新到IE8,发现速度快了很多,但是有些东西不兼容,我对那些不兼容的功能使用得比较少,就不管了。
终于,我自己又要开发一个小项目,用了一个网上下载的CSS模板,发现里面很多针对IE的注释,从IE 5到IE 8都有,反正我电脑上看着效果不错,于是就用了。
辛苦了好几天,我那项目基本完工了,想在IE6上看看效果,Shit,本来显示在页面右方主要区域的表格掉在了左边导航栏的下面,表格里面一些DIV的下划线莫名其妙地不见了,这些东西在我的电脑上显示得好好的,也没用到什么特别的东西,怎么差别会这么大呢?
一个个问题排查:
表格问题,在IE6上表现得有些诡异,显示完之后表格是在下面的,但是鼠标晃过导航栏里面的链接,表格又会自动跳到右方区域,真见鬼。改了一下布局的padding,一个表格好了。另外一个却还是掉下去,仔细研究了一下,把表格的宽度减少10px,好了。
下划线问题,我用的是:
border-bottom:1px solid grey
结果IE6不会显示出这条下划线。搜索一下才知道这是IE6的bug,它理解不了这么长的句子!必须写成这样:
border-bottom-width:1px;
border-bottom-style:solid;
border-bottom-color:grey;
还有比这更傻逼的吗?有!
它显示出的线是黑色的,不是grey的!而且根据DIV里面内容的颜色不同,它还会变色!如果里面是红色的字,那么它下面的线也是红色,如果是绿色的字,下面的线也是绿色,如果没有指定颜色,它就是黑色,非常智能,但就是不显示你要的颜色!
后来知道原来它根本不知道grey的意思,必须指定颜色代码才能解决。


这就是IE6,没想到微软这么大一个公司还出了这么一个垃圾,而且出了那么多年,至今还占据中国浏览器市场的半壁江山!



posted @ 2010-12-08 20:51 amp@java 阅读(535) | 评论 (4)编辑 收藏

有一台WIN2003的服务器,在单位的局域网内,与互联网是物理隔离的,最近上面运行的一个WEB服务器经常出错,查看日志发现是因为数据库不能连接,因为系统的所有端口都已经被占用完。使用netstat -abn查看发现svchost.exe开启了大量状态为SYN_SENT的连接,目标端口都是445,但是连接的IP各种各样的都有,由于机器不能建立Internet连接,所以状态都是SYN_SENT,重启一下这些连接都没有了,但是过一会又会迅速建立起来,很快就把系统的所有端口都占用了。
根据端口找出哪个服务真不容易,通过netstat -abn只能查到是svchost.exe,最多还能得到一个PID,确定是哪个svchost.exe,然后通过tasklist /svc可以查到那个svchost对应了哪些服务,但是一看,很多服务都是使用那个svchost,包括Server,Workstation等等,根本不知道是哪个产生的连接。
找了半天发现有人和我一样:http://www.petri.co.il/forums/showthread.php?t=36427,讨论了半天最后也找到了解决方法:http://www.symantec.com/security_response/writeup.jsp?docid=2009-011316-0247-99,原来是W32.Downadup这个病毒惹的祸,下载了专杀工具回来查了一下,果然找到了两个被感染的文件,一个是jpg文件,在IE的缓存里,一个是dll文件,在system32里。
根据dll文件名在注册表里查到了它注册的服务,原来又是之前处理过的那种,服务名随机、服务描述为空、启动类型为自动、状态为未启动、dll名随机,但是我记得这台服务器已经打过补丁,也没有出现svchost错误,所以就忽略了服务的检查,没想到这种东西还有不同的症状。
这个病毒利用的是KB958644的漏洞,到微软下载了补丁回来,一看才发现原来那台服务器以前已经装过这个补丁。补丁的作用也许就是只能防止再出问题,但不能解决已有的问题,所以那台服务器虽然装了补丁,但是可能已经被感染了,于是就没治好。

posted @ 2010-06-30 11:33 amp@java 阅读(11712) | 评论 (1)编辑 收藏

把APE、FLAC等无损压缩音频文件刻录成CD的软件有很多,搜索一把,出现频率最多的是用NERO+插件,但是现在的NERO体积实在庞大,我尝试下载一个NERO 10,大小不过200多M,结果安装的时候解压到临时文件夹,我的C盘1G多的空间都不够它解压,根本就安装不了,为了这么一点事情用这么大一个软件实在没必要,仔细搜索一下,刻录的软件其实有很多,都能自动地把APE刻录到CD上,方法远比NERO简单。
第一个软件是Burrrn
Burrrn是个专门干这事的软件,它支持的文件包括ape、flac、mp3、ogg等,实际上也是通过插件来完成编码解码工作的,不过这些插件都是内置在程序包里,不用再逐个下载。它的界面非常简单,使用也非常简单,基本一打开就会使用。
第二个软件是ImgBurn
这个软件比较强大,不但能够刻录APE,还可以把光盘提取成镜像、把文件或文件夹生成镜像、把文件或文件夹刻录到光盘上、把镜像刻录到光盘上,基本上NERO常用的功能都有了,软件只有几MB,十分环保。我下载这个软件的时候只是想使用它的生成镜像功能,因为下载下来一些蓝光原盘需要制作成ISO文件才能用POWERDVD播放。后来发现有人说这个软件还可以刻录APE,不过也需要使用相关的解码器,只是这些解码器在安装常用的播放器的时候一般都已经安装过了,例如我以前装过终极解码,其中包含APE解码器,就不需要另外安装了。
使用的时候只需要选择刻录镜像到光盘功能,选中与APE相关的cue文件,然后就可以分轨刻录了。

这两个软件的体积都很小,而且都是免费软件,不但环保还合法。

第一次使用Burrrn刻录APE的时候,用KMPlayer播放,发现直接播放APE的效果跟播放刻录出来的CD效果不一样,CD的效果动态范围明显不如直接播放APE,我以为是刻录软件的问题,后来使用ImgBurn刻录,也是一样的效果。仔细对比之后发现KMP播放的时候是使用不同的滤镜,可能是这个原因导致效果不同,但是播放APE和播放CD又不可能使用相同的滤镜,所以到底是什么原因也很难说得清楚,不过CD机上不认APE,也只能这样了。


posted @ 2010-06-22 15:30 amp@java 阅读(577) | 评论 (0)编辑 收藏

1、服务命名。Oracle的服务命名就跟计算机的名称一样。一般来说,在局域网里面,计算机名称是与IP一一对应的,通信的时候需要使用IP,所以就有了DNS,把计算机名翻译成IP。同样的道理,在Oracle体系里,服务命名对应计算机名,服务名(SID)+IP+端口+协议(TCP/IP)就对应了计算机的IP,Oracle体系里的DNS其实就是一个配置文件,把服务命名翻译成可以用于通信的服务名(SID)+IP+端口+协议(TCP/IP)。有趣的是,与计算机里的ping命令一样,Oracle里面也有个TNSPing命令,通过“TNSPing 服务命名”,就会得到像ping命令相似的结果,验证该命名对应的数据库是否有效。

2、主机身份验证。Oracle的一些维护工作往往需要输入主机身份验证信息,但是默认情况下,即使输入了正确的用户名和密码,系统也会提示“用户口令错误”,让人十分困惑。其实真实的原因是,你输入的用户必须在“作为批处理作业登录”里面,否则就会出现上述错误。操作方法如下:运行-gpedit.msc-计算机配置-Windows设置-安全设置-本地策略-用户权利指派-作为批处理作业登录-添加用户或组...-加入要登录的用户。
执行数据库迁移操作的时候,可能需要在源数据库主机和目标数据库主机都进行上述操作才能成功。

更多问题待续
posted @ 2009-11-12 10:23 amp@java 阅读(266) | 评论 (0)编辑 收藏

9月10号,CHDBits开放了注册权限,赶紧注册了一个帐户,这是我第一次登录PT站。每个文件都有几个甚至几十个种子在等待下载者,真是“僧多粥少”。下载速度果然很猛,下了几个文件,基本上可以达到ADSL的上限,甚至遇到了前所未有的极速,不过持续时间较短,但是平均速度都接近上限。
然而,ADSL小水管终究不是玩PT的料,严格的分享率要求和ADSL龟速的上传带宽使得我两天之后就变成了冰人,如果20天之内再不提高分享率,我的帐户将会被封禁。仔细考虑了一下,我还是“过把瘾就死”算了,因为以我这8:1的上传和下载带宽,要长期达到要求基本上是不可能的事情,如果要我的分享率达到1:1,那我4M的下载带宽就等于缩水成和上传带宽一样,只有512K了,我装这4M的宽带还有什么意义?
其实在PT站外下载,速度也是不错的,无论是emule还是BT都能达到上限的80%以上,当然,是在种子刚刚放出的一段时间,只要热度一过,速度马上下降,这是与PT没法比的。
还是等到哪一天光纤到户再玩PT吧,现在的任务就是趁着20天没过,赶紧“过瘾”,嘿嘿!
posted @ 2009-09-14 19:44 amp@java 阅读(281) | 评论 (2)编辑 收藏

VISTA和WIN7都有一种OEM激活法,该激活方式需要以下三个条件:
1、BIOS中有相应厂商的SLIC,VISTA要求SLIC 2.0,WIN7则要求SLIC 2.1。一般来说,品牌机的BIOS里面本来都带有SLIC,不过有的没打开(例如DELL的OPTIPLEX系列),需要用一些小工具打开(例如Asset);有的版本是2.0的,只能用来激活VISTA,如果要激活WIN7,需要更新SLIC,但更新官方的BIOS并不会更新SLIC,因此还需要找到专门的修改过的BIOS文件来更新。
2、安装的VISTA或者WIN7导入了相应的OEM厂商的证书,如果安装的系统是OEM版的,安装完之后证书就在里面了;如果安装的是普通版本的,还需要手动导入,命令为:
slmgr.vbs -ilc <证书路径>
一般每个厂商都只有一个证书,而且VISTA和WIN7的证书是相同的,也就是说相同的证书可同时用于VISTA或者WIN7。
3、所安装的VISTA或WIN7的key,与证书不同,key是每个版本的Windows一个,HomeBasic,HomePremium,Business,Ultimate等版本分别有各自的key,VISTA和WIN7的key也是不同的。导入key的方式是运行以下命令:
slmgr.vbs -ipk <key>
如果安装的时候输入了相应的key就不用这一步了。

同时具备了上述条件的VISTA和WIN7就被激活了,这些条件不需要按顺序来准备,只要具备即可,也就是说系统安装后再刷BIOS,或者系统安装前刷BIOS都是可以的。
 关于BIOS的信息,可浏览: 
http://www.bios.net.cn

关于Windows的信息,可浏览:
http://www.pcbeta.com
posted @ 2009-08-17 21:22 amp@java 阅读(346) | 评论 (0)编辑 收藏

jQuery UI里面只有一个DatePicker,只能选择日期,不能选择时间,有人做了一个可以选择时间的DateTimePicker,在这里(http://razum.si/jQuery-calendar/TimeCalendar.html)可以看到,把jquery.js,jquery-calendar.js,jquery-calendar.css下回来之后就可以用了。
但是有几个Bug需要自己修改:
1、当输入框里面的时间是0点时,控件显示不完整,这是因为有个函数有bug,如下所示:

     /*  Ensure numbers are not treated as octal.  */
    trimNumber: 
function (value) {
        
if  (value  ==  '')
            
return  '';
        
while  (value.charAt( 0 ==  ' 0 ) {
            value 
=  value.substring( 1 );
        }
        
return  value;
    },

         while  (value.charAt( 0 ==  ' 0 ) {
            value 
=  value.substring( 1 );
        }

这一句,如果是0点的话,最终会出错,因为它的长度最后是1,不能执行substring(1),改成下面就好了:

     /*  Ensure numbers are not treated as octal.  */
    trimNumber: 
function (value) {
        
if  (value  ==  '')
            
return  '';
        
while  (value.charAt( 0 ==  ' 0 &&  value.length >1 ) {
            value 
=  value.substring( 1 );
        }
        
return  value;
    },

2、作者是在jQuery 1.1.2版本下实现的,现在最新版本是1.3.2,这个控件在1.3.2下会出现异常,不能选择日期,这是因为有几个选择器有问题:
 1         $('.calendar_daysRow td[a]').hover( // highlight current day
 2             function() {
 3                 $(this).addClass('calendar_daysCellOver');
 4             }, function() {
 5                 $(this).removeClass('calendar_daysCellOver');
 6         });
 7         $('.calendar_daysRow td[a]').click(function() { // select day
 8             popUpCal.selectedDay = $("a",this).html();
 9             popUpCal.selectDate();
10         });
上面的$('.calendar_daysRow td[a]')在jQuery 1.3.2中不能使用,$("a",this)也是有问题的,同时,在FireFox中,<a>的不能设置背景颜色,所以hover函数不起作用,把它设在<td>也能达到相同的效果,改成以下代码即可:
 1         //$('.calendar_daysRow td a').hover( // highlight current day
 2         $('.calendar_daysRow td').hover( // highlight current day
 3             function() {
 4                 $(this).addClass('calendar_daysCellOver');
 5             }, function() {
 6                 $(this).removeClass('calendar_daysCellOver');
 7         });
 8         //$('.calendar_daysRow td[a]').click(function() { // select day
 9         $('.calendar_daysRow td a').click(function() { // select day
10             //alert("click");
11             //popUpCal.selectedDay = $("a",this).html();
12             popUpCal.selectedDay = $(this).html();
13             popUpCal.selectDate();
14         });

经过修改之后在IE7和FireFox3都能在jQuery 1.3.2环境下正常运行。
posted @ 2009-05-22 19:37 amp@java 阅读(41524) | 评论 (19)编辑 收藏

初次使用jQuery,发现真是个好东西,把很多东西都简化了,循环基本上都去掉了,可以说是开创了JS的一种新模式,虽然开始觉得有点不习惯,但是很容易学习也很容易看懂。一个神奇的$原来有那么多的功能,很强大,呵呵。
最让人欣慰的是它屏蔽了所有浏览器的差别,可以在各种浏览器上用相同的代码实现相同的效果。

posted @ 2009-05-21 18:42 amp@java 阅读(252) | 评论 (0)编辑 收藏

二维条形码比普通的条形码能保存更多的信息,已经应用到很多领域里面。例如手机电影票,就是一个嵌在彩信里面的二维码图像。南航也推出了网上办理登机牌业务,办理完成之后往手机发送一条包含二维码的彩信,到了机场就可以通过自助设备扫描二维码,打印登机牌。
然而,专业的二维码扫描设备价格十分昂贵,最便宜的都在1000元以上,到淘宝上搜搜就知道了。借助Java和一个开源的库,我们却可以通过普通的网络摄像头实现相同的效果,成本只需要几十块。
Open Source QR Code Library是一个开源的QR Code(二维条形码的一种)生成和读取的库,官方网站为:http://qrcode.sourceforge.jp/,里面包含了生成和读取QR Code的所有代码,其中有个jmfexample就能实现通过摄像头读取QR Code,经过本人尝试,几十块的普通摄像头效果已经不错了,一次读取几百字节都没问题。
使用这个库的步骤如下:
1、到其官方网站下载回来;
2、到sun的网站下载JMF包并安装;
3、插上摄像头,打开我的电脑,查看是否出现“USB视频设备”,然后打开,看摄像头工作是否正常
4、运行JMF里面的JMF Registry程序,点击“Detect Capture Devices”,查找视频设备,查找到之后会在左边的列表里出现“vfw:Microsoft WDM Image Capture (Win32):0”类似的设备,点击就会在右边出现其详细信息,我的摄像头是这样的:
Name = vfw:Microsoft WDM Image Capture (Win32):0

Locator = vfw://0

Output Formats---->

0. javax.media.format.YUVFormat
  YUV Video Format: Size = java.awt.Dimension[width=640,height=480] MaxDataLength = 614400 DataType = class [B yuvType = 32 StrideY = 1280 StrideUV = 1280 OffsetY = 0 OffsetU = 1 OffsetV = 3

1. javax.media.format.YUVFormat
  YUV Video Format: Size = java.awt.Dimension[width=160,height=120] MaxDataLength = 38400 DataType = class [B yuvType = 32 StrideY = 320 StrideUV = 320 OffsetY = 0 OffsetU = 1 OffsetV = 3

2. javax.media.format.YUVFormat
  YUV Video Format: Size = java.awt.Dimension[width=176,height=144] MaxDataLength = 50688 DataType = class [B yuvType = 32 StrideY = 352 StrideUV = 352 OffsetY = 0 OffsetU = 1 OffsetV = 3

3. javax.media.format.YUVFormat
  YUV Video Format: Size = java.awt.Dimension[width=320,height=240] MaxDataLength = 153600 DataType = class [B yuvType = 32 StrideY = 640 StrideUV = 640 OffsetY = 0 OffsetU = 1 OffsetV = 3

4. javax.media.format.YUVFormat
  YUV Video Format: Size = java.awt.Dimension[width=352,height=288] MaxDataLength = 202752 DataType = class [B yuvType = 32 StrideY = 704 StrideUV = 704 OffsetY = 0 OffsetU = 1 OffsetV = 3

注意,其中Output Formats都是javax.media.format.YUVFormat,而QR Code Library里默认的设备不是这种格式的,所以需要对源码作一定的修改。

5、用Eclipse打开QR Code Library的源码,作出一些适当的修改:
如果摄像头是上面所说的只支持YUV格式,则需要修改jp.sourceforge.qrcode.example.jmf.camDataSource.java,把setMainSource函数里的
VideoFormat vidformat = new VideoFormat(VideoFormat.RGB);
修改为
VideoFormat vidformat = new VideoFormat(VideoFormat.YUV);
否则永远也找不到摄像头。

6、把JMF包里的jmf.jar放到Classpath里
7、执行jp.sourceforge.qrcode.example.jmf.jmfexample,搞定

当然,由于源码开放的,只要符合许可,你想怎么改都行,可以把它嵌入到某个应用程序里面,这个程序就具有了扫描QR Code的功能了。

posted @ 2009-04-02 16:22 amp@java 阅读(5903) | 评论 (13)编辑 收藏

今天早上回到单位发现好几部服务器都出现了与svchost.exe有关的错误,有一台svchost.exe进程占用CPU达到100%,慢如蜗牛。普通PC机上则出现网络时断时续,重启后能打开网页,但很快就所有网页打不开,某些网络程序运行时则出现缓冲区已满等错误。更新病毒库查毒后发现整个局域网的所有电脑都出现Hack.Exploit.Win32.MS08-067.k病毒,杀毒软件显示svchost.exe里的病毒已清除,但是重启又出现。
搜索得知这是MS08-067漏洞导致,于是下载补丁,打上,但是病毒并没有清除。无奈只能求助高人,在高人指点下找到了病毒位置并清之,过程如下:
1、运行,输入services.msc,打开服务管理器,按照“描述”排序,在“描述”栏为空的那几项服务中查找一个名字很奇怪、由几个随机字符组成、没有任何意义的服务,它的状态一般是停止,但是启动模式是自动,这里要把它改成已禁用;
2、重启电脑;
3、运行,输入regedit,打开注册表编辑器,输入那个奇怪服务的名字进行查找,找到以那个名字命名的键值,在其下面有个名为Parameters的子键,该子键内有个ServiceDll的字符串值,就是病毒文件所在,找到那个病毒文件并删之;
4、到微软网站下载MS08-067补丁,打上,搞定!

这是我第一次碰到这种迅速感染整个局域网的病毒,似乎跟当年的冲击波有得比,搞起来烦得要命,几十台机啊……
posted @ 2009-01-08 23:05 amp@java 阅读(5088) | 评论 (2)编辑 收藏

现在的手机大多具有蓝牙功能,手机上的JavaME程序也能访问蓝牙端口,蓝牙的协议有多种,但最简单的可能就是虚拟串口(rfcomm)协议了,在该协议中,蓝牙端口被虚拟成一个串口,只要获取其InputStream和OutputStream后,就可以进行读写操作了,与socket差不多。 待续…
posted @ 2008-11-27 19:16 amp@java 阅读(861) | 评论 (0)编辑 收藏

为了充分利用晚上的带宽,我搞了一部专门的破机用于BT/EMULE下载,上班时间为了不影响大家上网,必须停止下载任务。有的下载工具本身带有计划任务功能,但是有些只能控制速度,不能控制连接,例如eMule,虽然可以停止下载,但是连接还是很多的,脆弱的ADSL路由/Modem经不起大量的连接,很容易就死翘翘了。这种情况下,最痛快的解决办法是拔线,但是每天插拔也不是办法,软一点的办法是禁用网卡,但是每次手动操作也不是办法。更直接的办法就是计划任务自动启用/禁用网卡了。要实现这种目的必须要有能够自动运行的工具,devcon就是一个命令行工具,能够在命令行中实现“设备管理器”的功能,我这里只要“启用/禁用”功能即可。
devcon的下载地址:
http://support.microsoft.com/kb/311272/zh-cn
微软出品,权威产品。

使用的时候还是有点小问题,搜索了一下找不到答案,只有自己解决了:
1、禁用设备的命令是:
devcon disable 设备ID
这个设备ID怎么得到呢?通过设备管理器,在设备上点击“属性”,在“详细信息”标签里面有个下拉列表,选择“设备范例Id”,下面显示出来的就是设备ID,
怎么把这个ID复制出来呢?右键是没办法的,但是直接CTRL+C就OK了。
2、设备ID不能直接输入,否则会把ID开头相同的一大堆设备都操作一遍,最后还会出几个错误提示;加上双引号也不行,会提示没有设备被禁用/启用;必须这样写:@"设备ID",所以最后的命令格式是:
devcon disable @"设备ID"
不知道是什么道理。

在计划任务里面添加两个任务:“启用网卡”,“禁用网卡”,分别用devcon enable,devcon disable命令即可,例如早上8点半禁用,晚上23:30启用(有部分好筒子晚上加班)。这样一来,就可以24小时开着那几个下载程序也不会有影响了,在网卡禁用的时候,无论它们怎么尝试连接,都跳不出如来佛的掌心。

posted @ 2008-08-28 17:19 amp@java 阅读(3249) | 评论 (3)编辑 收藏

说来惭愧,虽然工作学习都离不开计算机,但直到两个星期以前,我还没有为自己买过一台新电脑,倒是给人家装了快10台。近段时间终于下定决心买台电脑了。我不玩游戏,买电脑只是为了上网,看片,写点小程序。现在流行高清电影,上了一下相关论坛,发现要流畅“软解”H264编码的1080P电影,至少需要4核的CPU,Intel的Q6600盒装1500左右,AMD 羿龙9550 1100左右,前者的性能好于后者,但是Intel的集成主板又贵又烂,AMD 的780G芯片组是目前最强的整合芯片组,价格便宜量又足,于是就把架构定在AMD上。技嘉的GA-MA78GM-S2H 是论坛上讨论最多的主板,应该也是用的人最多的780G主板之一,凭借技嘉优秀的做工,充足的用料,丰富的接口,赢得了广泛的好评,但是这个是小板,由于面积的局限,南北桥芯片发热比较厉害。后来技嘉推出了标准ATX板型的GA-MA78G-DS3H ,采用了全固态电容(小板是供电部分全固态),减少了一个ESATA接口,增加了一个1394接口,增加了三条PCI-E插槽,散热效果应该比小板好,于是就定下了这块主板。由于高清电影动辄10G以上,所以买了个希捷500G 7200.11硬盘,以后降价的话再加一个做RAID 0就爽了。内存选用kingston 1G * 2 DDR2-800组成双通道。光驱是明基DW2000 DVD刻录机。这些都很快定下来了,倒是机箱和电源费了不少脑筋。看了不少评论和测评,最后才定下了航嘉的“时光之门”机箱,看中的就是它的用料和4个前置USB+一个1394口。电源要选主动PFC,效率更高,同时也要额定400W以上,因为那个CPU的TDP就有95W,挑了半天,定在TT的KK500A,额定功率400W,主动PFC,14CM大风扇。显示器22寸液晶就可以了,我对它的要求不高,因为看片的话也是通过HDMI接到液晶电视上看,初步决定三星2243BW。
这一切准备好之后,找了个时间杀到电脑城。报价如下:
CPU     Phenom X4 9550 盒装 1120
主板 技嘉GA-MA78G-DS3H  680
内存 Kingston DDR2-800 1G*2 140*2=280
硬盘 希捷7200.11 500GB 585
光驱 BenQ DW2000 DVD刻录机 225
机箱 航嘉时光之门H301 265
电源 TT KK500P (KK500A的升级版) 470
显示器 三星 2243BW 1850
键盘鼠标 杂牌套装 30
合计 5505

挑好之后我让JS备货,然后就回去了。过了没多久,JS打电话来说AMD的那个CPU没货,问我要不要9750,我说算了,等有货再装吧,那个CPU的TDP 125W,太恐怖。结果过了两天,还是没货,我打电话问了本市的其他电脑店,也是没货,说是奥运影响了物流(不得不说奥运真是劳民伤财,个人观点,不要上纲上线)。JS出了个主意说让我先用双核的4800+,有货的时候再换成9550。开始的时候我不同意,考虑到淘宝自己买,问了一下真的有卖的,还比JS那里便宜20块,但是又怕出现问题不知道找谁,虽然盒装全国联保,但是我这个小地方好像没有AMD的代理。最后还是同意了JS的办法,再奔电脑城,把装了4800+的主机扛了回来。显示器我问过三星的代理,只要1790,JS说他卖不了这个价,于是就不要显示器,键盘鼠标看了一下,也很烂,不要。回来打开电脑,我靠,居然一点声音都没有,真的,从来没见过这么安静的电脑,如果不是灯亮着,跟没开一个样。可能归功于电源那14cm的风扇,只要很低的转速就可以产生大风量。上淘宝买了根3米长的HDMI线,接到家里的32寸液晶上,很郁闷地发现不能点对点,那个电视的物理分辨率是1366*768,但却不支持这个输入分辨率。没办法,只好用1280*720输入,看文字比较模糊,但也没多大影响。看电影那叫一个爽,比电脑上爽多了。
前天去电脑城买了一套双飞燕的防水鼠标键盘套装KB-827D,65块。昨天和一个同事一起想去买三星的那款显示器,无意中却看到了一款AOC的2280V,参数和三星2243BW基本一样,但是价格便宜不少,只要1540,还带DVI线,3年质保,比三星的D-SUB线+1年质保要好。于是就要了一台,店家包无点,开箱检查了一下,两台都是完美屏,拿回来了。
这个过程中,最郁闷的事情是弄那几个显示接口了。780G的主板一般都带有D-SUB、DVI-D、HDMI三种接口,但是DVI和HDMI接口不能同时使用。技嘉的这块主板就是这样,而且还是自动选择HDMI,也就是插上DVI和HDMI之后只有HDMI有东西。AOC的那两个接口也是自动选择,也就是两个接口都插上的话,就会自动选择DVI(本来可以手动选择的,但是如果两个都插上的话好像手动选择无效)。
首先碰到的问题是声音。按照主板的说明书,如果需要通过HDMI口输出声音的话,必须在控制面板->声音和音频设备->音频里面在声音播放默认设备下选择Realtek HDA HDMI Out。但是我把电脑搬回单位折腾的时候,发现这里并没有出现Realtek HDA HDMI Out这个选项,只有Realtek HD Audio output!在设备管理器里却能发现ATI HDMI Audio这个设备。搜索了半天,好像有人遇到过这个问题,但是没有答案;有人说在BIOS里面开启,但我发现默认已经开启了;打电话给技嘉客服,那个人叫我先接上电视试试。那时候还没有买HDMI线,试不了,于是就不管它了。结果HDMI线到了之后,一接上电视,马上就出声了,晕啊,原来必须插上HDMI线之后才能选择那个Realtek HDA HDMI Out!!!!
昨天把显示器买回来之后也碰到问题了。我把DVI口和D-SUB口和HDMI口都接上了,启动电脑,显示器自动选择DVI输入,自检界面->Windows滚动条都没问题,但是滚动条过去之后,显示器提示没信号,黑屏了,怎么按都没反应,选择信源也不行。只能关机重启,然后按F8选择VGA模式,进去之后分辨率是640*480,没问题,但是只要一调整分辨率,无论是调成800*600还是1680*1050,都一样的没信号,黑屏。把D-SUB线和DVI线分别拔下,还是不行。想到ATI控制中心里面看看,结果偏偏在这个时候ATI控制中心启动不了!!重装驱动,还是不行。下载最新的8.7的催化剂驱动,一装完重启就蓝屏。只要又装回8.6版。搞了三四个小时,弄到最后,把HDMI线拔了下来,只接一个DVI到显示器,居然一切正常了,启动成功,分辨率1680*1050,完美。仔细一想,才明白原来这块主板只支持DVI和HDMI其中之一输出,而且接上HDMI后默认就输出HDMI了,虽然我那电视一直没开,但是线插上之后就不再输出DVI了。把DVI拔了之后,由于之前只有电视作为显示器,没有配置双显示器输出,所以D-SUB也不会有信号,所以无论拔DVI还是拔D-SUB都没信号,只有拔HDMI才行。这时候我又尝试安装中文版的8.6 ATI控制中心,装完之后还是启动不了,尝试一下Restart Runtime,我靠,居然就可以启动了!!神奇!!!进去把显示器作为主显示,电视作为扩展。重启之后显示器和电视机都能显示了,而且显示器是1680*1050,电视是1280*720,调整了一下电视的过扫描,使它满屏,然后用KMPlayer放电影,把窗口拖到电视那边,全屏,好了,可以一边在书房上网一边在客厅放电影了(我在书房和客厅之间的墙上打了个洞)!!有点遗憾的是,显示器只能用D-SUB输入,浪费了那根DVI线。不过这个可以通过独立显卡来解决,以后再说吧。
780G主板的确厉害,集成的显卡可以轻松硬解1080P的视频,CPU占用不到10%,但是有时候会出现绿格子,还有一些奇怪的现象。所以可能的话我还是尽量使用软解。不过到目前为止,4核CPU还没到货,这个4800+软解VC1有时候都会卡,更别说H264了。而且AMD那个原装风扇实在太烂了,看片的时候60多度,甚至死机,必须把盖子打开,拿个台扇往里吹才能把一部片子看完。后来花10块买了个12cm的机箱风扇,装上之后似乎也没什么效果。看来只能换CPU散热器了,初步决定买个九州风神的β400+,100左右,4根热管的,对付那个9550应该可以吧。不过要等CPU到了再买。突然发现,DIY就是个烧钱的过程,我多年的积蓄已经花得差不多了……
到目前投入的钱:
主机3625+显示器1540+机箱风扇10+HDMI线190+鼠标键盘65=5430
预计还要投入的散热器,突破5500了!!
这就是我的第一台自用新电脑。

2008年9月6日更新:
截至目前,已在淘宝上购入如下附件:
1、九州风神β400+散热器一个,连邮费102块。买回来装上才发现噪音太大了,不得已只好DIY了一下,把风扇拆掉,装上原装散热器的风扇。
2、为了在客厅控制电脑,买了个无线鼠标,太科的,可能是山寨货,连邮费82,刚开始的时候直接在客厅就可以穿墙控制书房的电脑了,不过第二次就不行了,可能电力不足吧,现在只能把鼠标拿到墙边才能有,已经买了一条5m的USB延长线,明天送货。
posted @ 2008-07-27 21:35 amp@java 阅读(469) | 评论 (2)编辑 收藏

     摘要: Nokia MMS Library是一个开放源代码的Java MMS开发包,里面包含了源码、文档、示例程序等,很容易就能学会。下面是用这个包发送彩信的方法: 1、电脑通过GPRS/EDGE MODEM或手机连接移动梦网,注意,必须是移动梦网(CMWAP),不是互联网(CMNET),如果手动拨号,一般是拨*99***1#或*99***2#,拨通之后打开命令行窗口,telnet 10.0.0.172...  阅读全文
posted @ 2008-06-17 14:35 amp@java 阅读(3444) | 评论 (11)编辑 收藏

这几天要在单位部署一个软件包,是msi形式的,据说可以通过“域推送”的形式安装,搜索了一下,发现需要在 组策略->软件设置->软件安装 里面添加放在共享目录里的软件包。但是我试过无论在“计算机配置”里面还是在“用户配置”里面添加那个软件包,无论是“发布”还是“指派”,都没办法自动安装,最多只是在“添加删除程序”里面的“添加新程序”出现那个软件包,还需要手动安装。
域控制器上按照部门划分了多个组织单位(OU),并把用户分配到各自的组织单位中,组策略就是在这些组织单位上面应用的。
昨天晚上看了一下“Windows2000资源大全”,里面有讲到这种软件安装方法。原来是要把计算机也划分到组织单位中,然后在那个组织单位的组策略的“计算机配置”里面设置那个软件包,当计算机启动后,进入“应用计算机设置”阶段,还没出现登录界面时,就会自动安装。之前对组策略的理解有问题,以为组策略只应用于用户,实际上是“用户配置”应用于用户,“计算机配置”应用于计算机,要对让每台计算机自动安装,而不是在用户登录之后安装,就必须在“计算机配置”里面设置,要把这个设置应用于所有计算机,就要把那些计算机划分到某个组织单位,然后对这个组织单位应用组策略。

之前尝试过用另外一种方法实现软件的自动安装,这种方法不仅仅适用于msi,还适用于所有安装文件。
利用Windows的WMI服务,可以对远程用户的注册表进行操作,在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce里面添加一个值为安装文件路径的字符串,就会在计算机启动并登录后执行该安装文件,执行完毕后会把这个键值删除,也就是只执行一次。
利用PsTools,可以对远程计算机进行重启操作,当计算机重启后,用户登录时,就会执行安装过程。如果不需要倒计时重启,还可以通过WMI服务进行重启操作,这样就可以完全通过Script来实现,并且能够记录操作的成功与否。
通过以下代码可以连接远程计算机:
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMIService = objSwbemLocator.ConnectServer(strComputer,"root\default",strUser,strPassword,"MS_409","ntlmdomain:")
其中strComputer是远程计算机名,strUser是域管理员帐号,strPassword是域管理员密码
得到objWMIService之后,就可以像对本机一样操作WMI,例如获取注册表操作类StdRegProv:
Set objStdRegProv = objWMIService.Get("StdRegProv")
关于远程注册表操作可以查看MSDN里的StdRegProv类说明。
通过Win32_OperatingSystem类可以实现对远程计算机的关机、重启等操作。
WMI是个强大的工具,在局域网中可以实现很多方便的管理操作。但是需要通过VBScript来实现,而VBScript的异常处理十分弱智;使用JScript的话,很多WMI类的操作又十分繁琐。
posted @ 2008-06-14 16:12 amp@java 阅读(1077) | 评论 (0)编辑 收藏

    JSF只能采用POST的方法进行FORM提交,同一页面要显示不同的内容,只有通过POST来改变参数或者通过SESSION传递变量。有时候有大量相似的页面,这些页面只有很少一部分不同(例如不同用户、不同角色、不同页码等),通过跟在URL后面的query string本来可以很容易地实现,但在JSF里却不行,只要一提交,马上就把?后面那串东西丢掉。假如有一个页面,根据URL后面的id参数来显示FORM的内容,提交的时候如果验证出错,那个FORM的内容就丢失了,显示一个莫名其妙的页面,因为这时候那个id参数已经没有了。还有常用的分页操作,本来在URL后面加上个页码就可以了,但是JSF的分页控件却只能通过POST来翻页,一刷新就出来个“重试”“取消”的对话框,让人烦恼。如果页面内容根据不同用户角色有细微不同,就得每个角色建立一个页面,每个页面对应一个managed bean,这些页面的内容基本相同,bean的内容也基本相同,却要分别建立,一点重用的机会都没有。今天做的一个东西就遇到了这种问题:
一个简单的审批流程,只有3个角色,申请者、审批者、执行者,整个流程是这样的:申请者填单->审批者查看并审批->执行者执行并填入结果->申请者查看。这样,每种角色都有两种列表:未回复和已回复,这两种列表在三个角色里面都是相似的,但有细微区别:
申请者 审批者 执行者
未回复列表 自己已发出但未被执行者执行的列表 申请者已提交但自己未审批的列表 审批已通过但自己未执行的列表
已回复列表 自己已发出且执行者已执行的列表 申请者已提交且自己已审批的列表 审批已通过且自己已执行的列表

这些列表的不同之处在于:已登录用户的角色(可以从session中得到,但一个用户可能有多种角色,他可能以不同的角色查看列表),列表的当前状态(对每种角色都有两种状态),列表的页码。由于每个列表都有可能有大量数据,所以必须用到分页。通过JSF的DataTable空间和DataScroller控件可以简单地实现分页,然而,这种分页导致URL后面的参数无效了,这样一来,这六种列表就必须通过6个页面来显示了,通过页面的名称来区分当前用户的角色、所查看的列表的状态,每个页面通过POST来决定页码。本来一个页面+一个bean+3个参数就可以决定,现在需要6个页面+6个bean来完成了。

list.jsp?type=0&role=0&page=1->type0_role0.jsp
list.jsp?type=0&role=1&page=1->type0_role1.jsp
list.jsp?type=0&role=2&page=1->type0_role2.jsp
list.jsp?type=1&role=0&page=1->type1_role0.jsp
list.jsp?type=1&role=1&page=1->type1_role1.jsp
list.jsp?type=1&role=2&page=1->type1_role2.jsp

假如有n种状态m种角色那岂不是要n*m个页面+n*m个bean?

有点想放弃JSF这个鸡肋了。

希望有高手给条生路走走!
posted @ 2008-06-04 19:48 amp@java 阅读(2339) | 评论 (11)编辑 收藏

刚刚才知道,原来Java的线程是不能重启的,也就是说,当线程的run()方法执行到最后一行,退出之后,这个线程就结束了,不能再通过start()方法重启启动这个线程,只能重新构造一个线程对象,再调用其start()方法来启动,但这个对象和原来那个对象已经不同了。
为了实现某个线程对象的“重启”功能,可以在它的run()方法的最外层加上一个循环语句:
1 public void run(){
2   while(!stop){
3     //do something
4   }
5 }

这样,只要stop不为true,run()方法就不会结束,不断地“重启”。
如果run()方法里面还有一个循环,那么就要两个停止标志:
1 public void run(){
2   while(!stop){
3     //do something
4     while(!stop && !restart){
5       //do other thing
6     }
7   }
8 }
9 
这样,如果需要“重启”线程,只要把restart设为true即可,如果要退出线程,只要把stop设为true即可。
posted @ 2008-04-22 10:56 amp@java 阅读(4161) | 评论 (0)编辑 收藏

之前用过一个带CheckBox的Tree控件,叫dhtmlXTree,支持xml数据,功能很好很强大,但是有个问题怎么也解决不了,不知道什么原因,就是第一次显示的时候能够正常显示树形列表,但第二次进入相同页面就会显示错误,所有图片都看不见,只看到其中的文字,而且这时候整个浏览器像死了一样,点击上面的任何链接都没反应,必须关闭重新打开才有效,或者等上十几分钟才反应过来,不知道是不是一直在后台运行某个Javascript脚本。虽然这个控件的源代码可以看到,但是一条注释都没有,都不知道哪个部分出了问题。

在google上搜索替代方案时,看到了BlueShoes的树形控件,具有多种形态,包括CheckBox,RadioButton等,每种形态都有例子,而且有详细的规范的文档(phpdocument,与JavaDoc类似),关键是代码里面也有详细的注释,可以看到每个部分的实现原理。与dhtmlXTree使用XML存储数据不同的是,这个树形控件使用多维数组来存储数据,似乎可读性和效率稍差,但出错的几率也少了,不需要用到浏览器的XML处理。与前者一样也支持IE5。

关于BlueShoes的树形控件的详细的情况可以看这里:http://www.blueshoes.org/en/javascript/tree/

那个网站上还有很多其他控件,也是很优秀的,虽然它的后台是php框架,但客户端控件同样可以用于JSP。
posted @ 2008-02-26 11:28 amp@java 阅读(440) | 评论 (0)编辑 收藏

浏览器的缓存有时候显得很讨厌,明明已经更新了内容,就是不显示新的,只要URL不变,浏览器就不会去检查服务器是否已经更新,而是用缓存里的东西,起码在IE里是这样。
使用AJAX经常需要动态更新某一过程的状态,例如短信发送的状态,如果相隔几秒查询一次,URL不变的话,显示出来的状态永远不会改变,因为浏览器第一次获取了内容之后就不再更新了。
为了解决这个问题,最简单的办法就是在URL后面加上一个不断改变的查询字符串(query string),例如:
request.jsp?q=q&date=(new Date()).getTime()
红色部分就是不断改变的查询字符串。

感谢http://wangcheng.javaeye.com/blog/135887的提示

http://batmanwl.blog.sohu.com/71841783.html这里还可以看到多种解决方案
posted @ 2008-02-26 10:33 amp@java 阅读(800) | 评论 (1)编辑 收藏

通过JavaME的WMA可选包发送短信时,手机会提示是否允许程序发送该短信,虽然不太影响应用,但每次都要按一下允许也很不爽。从网上看到有人说对jar包签名后就不会出现这种情况了,但是签名的证书要向两个大公司买,因为手机里面一般只内置了那两个大公司的根证书,而且手机不允许安装新的根证书,这不分明是圈钱吗?不知道当初制定这个标准的家伙收了那两个公司多少黑钱!虽然这样会使很多病毒不能运行,但是那两个公司又不是上帝,给钱就可以买到证书了,他们又没什么力量也不会去研究购买证书的人要干什么,如果有恶意的人只要花点钱,同样可以造出带签名的病毒来。
但是办法总是有的,有人另辟蹊径,通过非常手段给手机安装一个自己制作的根证书。详细过程可以看这里:http://browndrf.blogspot.com/,原理就是利用了NOKIA某些型号的手机可以通过web下载证书并安装的漏洞。我实验了一下,整个过程其实不用那么复杂,不需要用到NOKIA的签名工具,直接用SUN WTK的签名工具就可以了,也不需要通过网络安装程序,跟平时安装没签名的程序一样。
然而,签了名的程序同样需要确认才能发送短信!而且比没签名的程序更麻烦,还要在MIDLet-Permissions里面填上一大堆许可,不签名的软件这个项根本不用填。唯一的好处是安装的时候不会提示程序不受信任,而且不能用无签名的同名程序覆盖。但是这种安装过程的问题并不需要多大关注,因为安装只是很少的时间,关键是使用。
后来在这里:http://blog.csdn.net/zhengyun_ustc/archive/2006/04/07/654226.aspx又发现一篇文章,说明能够安装根证书的不过是个别机型的漏洞,是不符合MIDP 2.0安全标准的,算了,这个问题还是没法解决,死心!
JavaME真的有点像鸡肋,它最大的优点是跨平台,但各种手机的支持程度又各不相同,一点点小的差异就搞得人晕头转向。而且各种各样的限制使得它最多就弄点小游戏玩玩,要连接网络还得一堆的许可,程序大了还容易莫名其妙地崩溃。真是食之无味,弃之可惜。
可是,我还是要搞……
posted @ 2008-02-01 18:41 amp@java 阅读(312) | 评论 (0)编辑 收藏

Bouncy Castle(http://www.bouncycastle.org/)是一个庞大的加密类库,支持Java和C#,其中Java部分发布的源代码压缩包就有24M左右,支持JDK 1.0~5.0,支持J2ME(现在叫JavaME)。
但是,这个类库使用起来并不那么方便,它并没有与普通Java类库那样以Jar形式发布立即可用的二进制类库,而只发布源代码,源代码包含了多份,包括JDK1.0,JDK1.1,JDK1.2,JDK1.3,JDK1.4,JDK1.5,J2ME等,每种环境都有少量文件不一样,有多个用于Ant的build xml,分别用于各种不同的JDK,但是每个xml定义了多个target,不知道哪个target才能build出可用的类库来,选中了其中某个target有可能会出现错误,build不下去。文档太简单,基本没讲到怎么生成可用的类库,网上的相关文档也很少。我尝试了两天才终于把第一个MIDP加密测试程序运行成功。
我的应用只是实现Java ME的加密,所以这里讲的只是针对JavaME的,JavaSE应该会简单一点。

打开下载下来的压缩包(crypto-138.zip),会发现里面有一堆文件夹和一堆文件,其中一个文件夹叫j2me,于是第一反应就是打开它看看里面是什么,发现里面只有少量的源代码文件,看来这只是适用于j2me的一部分源代码而已,其他通用的部分不在这里。
还有一个j2me.xml,是一个Ant的build文件,在eclipse里面用ant打开以后,发现里面有很多target,除了what(输出一些信息)和init(建立几个目录,复制一些文件)这两个能执行成功之外,其他几乎都是失败的,所以通过这个东西是搞不出我们可用的classes来的。
没办法,只有一个个目录地看,在一个zips目录里面发现cldc_sources.zip,里面的源代码文件有很多,有点像了,于是就把它解压后放到一个Eclipse ME测试项目的src目录下,找到org.bouncycastle.crypto.examples包,里面有个MIDPTest.java文件,还有个midp_test.jad,看来这个是用来测试用的MIDP了,调出WTK的模拟器来运行一下,果然是,成功了。
下一步是在手机上运行。如果不混淆,生成的JAR包有1.6M之巨,不管了,先试一下。安装到手机上,到最后出现“授权失败”的提示,安装不成功。记起原来找文档时在index.html里面看到有这么一句话:The final caveat to this is that as the j2me distribution includes some compatibility classes starting in the java package, you need to use an obfuscator to change the package names before attempting to import a midlet using the BC API. 似乎因为用于j2me的版本有几个以java开头的包,里面包含了一些兼容类,可能是用于补充CLDC的不足,由于java开头的包是系统包,是不允许修改的,所以不处理过的话安装会失败。必须先用混淆器弄一下才行。Eclipse ME配置混淆器十分简单,我使用的是ProGuard。参照http://www.cnjm.net/eclipseme/docs/configuring.html配置即可。然后运行Create Obfuscated Package生成混淆过的包,只有13K。安装在Nokia 6681上,成功,运行,也成功了。尝试修改一下MIDPTest.java,在whichCipher方法里面,把返回值改为其他数字(0-4),就可以修改加密方式,但是由于DES加密使用的密钥长度为64bit,而默认的key是"0123456789abcdef0123456789abcdef",运行DES是会提示密钥太长,我开始以为把key改为4个字符就可以(因为Java使用Unicode,每个字符2字节,4个字符8字节=64bit),却出现了数组越界的异常,其实这个key并不是直接用作密钥,还要经过处理的。尝试了8个字符也不行,最后发现16个字符就可以了,应该是每两个字符对应一个16进制数。测试程序是对“www.bouncycastle.org”这个字符串进行加密和解密,速度还是挺快的,几乎感觉不到需要时间(在手机上启动Java程序本来就比较慢)。

中间还碰到一个问题,由于我是把源代码直接复制到一个原来建立好的MIDP项目里,那个MIDP项目里已经有个默认的JAD文件,但是里面没有表示要运行的Midlet,生成Jar包之后,安装到手机上到最后会出现安装文件无效的提示。打开那个JAD文件,在Midlets标签里点击Add按钮,把org.bouncycastle.crypto.examples.MIDPTest添加进去即可。

以上测试使用的环境是:
JDK 1.5.02
Eclipse 3.2.0
Eclipse ME 1.7.7 http://eclipseme.org
Sun WTK 2.5.2 for CLDC
ProGuard 4.1 http://proguard.sourceforge.net/



刚刚发现Bouncy Castle的下载页面http://www.bouncycastle.org/latest_releases.html里面有专用于J2ME的源代码发行包,里面只包含用于J2ME的代码,如果仅用在J2ME下的话,这个应该比较简单,不用找半天。
posted @ 2008-01-25 19:59 amp@java 阅读(1427) | 评论 (1)编辑 收藏

有时候我们需要通过程序自动发送一些纯文字的信息,通过SMS可以达到目的,但当文字达到一定长度时,就必须分割成多条短信(一般60汉字一条),增加了费用,通过MMS可以解决这一问题,MMS最大支持100KB的内容,假设每个汉字3byte(使用UTF-8编码),能发送33K个汉字,一次可以发送一篇文章了。
Now SMS/MMS提供了一个Web界面用于发送彩信,通过Apache HttpClient,我们可以模拟浏览器的行为向web服务器提交表单,发送彩信。Now SMS/MMS的Web界面提供了很多选项,但发送纯文字彩信的话我们只需要填写接收者,主题(可选),内容即可,把对应的表单内容POST到当前页面,就能发送一条彩信。(待续)
posted @ 2007-10-01 14:22 amp@java 阅读(1149) | 评论 (0)编辑 收藏

Now SMS/MMS是一个强大的短信/彩信发送服务器,它内置了彩信中心,在某些运营商的网络内可以通过普通的短信猫发送彩信,而不需要使用支持GPRS的短信猫,也就是说系统本身就具有了运营商的彩信中心的功能。然而,在中国移动的网络内是不能实现这种方式发送彩信的,必须通过移动梦网的彩信中心转发。
彩信的发送其实是两个过程:
1、向目标手机发送一条普通的提醒短信,其中包含了发送者号码、彩信的URL等信息,该URL指向彩信中心的某个web目录下的某个彩信文件;
2、目标手机接收到这条提醒短信后,主动发起GPRS连接,根据短信中的URL向彩信中心提取该彩信,并显示出来。
Now SMS/MMS 自带的彩信中心其实就是一个支持手机连接的web服务器,它把制作好的彩信放在某个web目录下,手机连接时就可以下载。
然而,实践证明,如果使用中国移动的SIM卡,无论使用的是什么短信猫,都不能通过系统自带的彩信中心发送彩信。
要设置成通过移动公司的彩信中心发送彩信,必须把系统的默认设置——使用自带的MMSC(彩信中心)——改为使用移动公司的MMSC。方法如下:
在Now SMS/MMS Gateway配置程序中选择MMSC Routing,然后点击Add按钮,在弹出的对话框中建立一个新的MMSC配置,其中Acount Name和Account Description可以随便填,只是用来识别配置名称,因为系统可以设置多个MMSC。Default Sender Address必须留空,否则发送很难成功(有极少部分能够发送成功)。Allow Sender Address Override前的复选框也必须留空。Route messages to this account for recipient phone number(s)不用填(这是一个掩码,只有符合要求的号码才用这个路由,按照官方文档的说法,不填的话是不会用这个路由的,但实际上把它设成默认路由的话,即使不填也会用这个,也许填上+861*,1*会好些,表示中国的手机都用这个发)。Route messages to VASP via选择MM1,之后会出现更多的输入框,点击Look up Operator Seetings,出现一个选择运营商的对话框,很幸运,中国移动在里面,选择China - China Mobile,然后OK即可,系统自动把适合中国移动的内容填了进去。但是Network Connection和Modem Used两个下拉列表还没选好。这里必须使用对应的GPRS猫的调制解调器(在控制面板-电话和调制解调器选项-调制解调器-添加,按照提示一步步就能找到GPRS猫,看好哪个调制解调器对应哪个端口,从而知道哪个调制解调器对应哪个GPRS猫)。填好这些东西后点击Test Connection可以检测连接,成功就可以了,检测时间比较长,要耐心等待。填好之后点击OK,在MMSC Routing标签页里的Additional Routes里面就能看到刚才的那个配置名称,然后在Default Route里面选择刚才的配置名称作为默认路由就可以了。
之后打开MMSC标签,配置自带的MMSC。虽然我们使用的是中国移动的MMSC,但通过web发送彩信时,必须先发送到系统自带的MMSC,再转发到移动的MMSC,所以必须把系统的MMSC也配置好。这里就是普通web服务器的配置(还有SMTP的配置,可选),把端口和IP选好后,还要注意Local Host Name or IP Address里要填入一个本机的IP,如果在仅在内网使用,可以直接填本机IP(外网的情况没有研究过,看看说明书先)。其他默认的就可以了。
再打开Web标签页,把Web服务器配置好。
打开浏览器,输入http://服务器IP:web服务器端口/,就能看到发送各种短信、彩信的链接,然后就可以发送彩信进行测试了。如果发送不成功,可以打开Serial #标签页,把底下的三个Debug选项都勾上,再发送一下,然后在Now SMS/MMS 的安装目录下找到MMSCDEBUG.LOG等几个LOG文件查看究竟是什么问题。文件太大会导致发送失败,系统不会自动检查的。文件大小的上限还不清楚,但超过100KB似乎就肯定不行。
今天搞了一天这个东西,终于能正常发送彩信了。在这里备忘一下。慢慢再作深入研究。


感谢:http://blog.csdn.net/phiger/archive/2006/12/19/1449176.aspx

官方文档:http://www.nowsms.com/documentation/ProductDocumentation/mms_notifications_and_content/Connecting_to_operator_MMSC.htm#SendingMMSMessages
posted @ 2007-09-18 19:32 amp@java 阅读(5369) | 评论 (18)编辑 收藏

一个使用了JSTL的JSP页面,当字符集设为GB18030时,总会随机的出现一些乱码,莫名其妙的,有时候有,有时候没有,刷新一下可能会变,也可能不变;最令人懊恼的是,利用JSTL生成的JavaScript语句经常会漏掉一两个字母,害得整个JS程序段出错,每次漏的字目都不一样,根本没法改。
似乎google搜不到相关信息,没办法,后来把编码改为UTF-8,就不会再出现这种情况了,但不知道以后还会不会出现。
有时候上网易,也会见到一些字符出现乱码,刷新一下就没有了,看来这是JSP的通病,为什么就没有人提出解决方案呢?
posted @ 2007-09-10 19:48 amp@java 阅读(1342) | 评论 (7)编辑 收藏

通过JDBC-ODBC桥连接某个ACCESS数据库,并把一个逗号分隔文本文件插入到某个表中,如果插入过程中没有其他程序使用该数据库,只需要20多秒就可以完成;如果插入的同时,还用ACCESS程序打开了那个mdb文件,虽然没有任何操作,但运行相同的插入程序,竟然需要80多秒,性能下降4倍!!
ACCESS=垃圾!
posted @ 2007-07-15 02:15 amp@java 阅读(1486) | 评论 (0)编辑 收藏

使用AVG,SUM等SQL聚合函数在Access查询,速度慢得要命,7个整数的求和或平均居然要几秒才能算出来;通过JDBC连接,把数据查出来后再自己求和或求平均,速度不知道要快多少。
奇怪,不知道Access是怎么搞的,求平均可能因为浮点运算慢,但整数求和为什么也会慢呢?
posted @ 2007-07-09 22:26 amp@java 阅读(1556) | 评论 (3)编辑 收藏

更新了某个webapp,把新的WAR包放到webapps目录下,Tomcat就会自动把原来的同名webapp删除,并把WAR包解压,运行新的webapp。但是,有时候Tomcat并不能把旧的webapp完全删除,通常会留下WEB-INF/lib下的某个jar包,必须关闭Tomcat才能删除,这就导致自动部署失败。
解决方法是在<Context>元素中增加一个属性antiResourceLocking="true",默认是"false"。但是这样会带来一个问题,Tomcat在运行对应的webapp时,会把下面的文件复制到一个临时目录里,所以如果你在服务器运行的时候修改了某个JSP,Tomcat并不会发现,看到的还是修改前的那个页面。

以下内容来自Tomcat文档:Context标签的位置

Context elements may be explicitly defined:

  • in the $CATALINA_HOME/conf/context.xml file: the Context element information will be loaded by all webapps
  • in the $CATALINA_HOME/conf/[enginename]/[hostname]/context.xml.default file: the Context element information will be loaded by all webapps of that host
  • in individual files (with a ".xml" extension) in the $CATALINA_HOME/conf/[enginename]/[hostname]/ directory. The name of the file (less the .xml) extension will be used as the context path. Multi-level context paths may be defined using #, e.g. context#path.xml. The default web application may be defined by using a file called ROOT.xml.
  • if the previous file was not found for this application, in an individual file at /META-INF/context.xml inside the application files
  • inside a Host element in the main conf/server.xml


参考资料:
Tomcat 5.5 文档:http://tomcat.apache.org/tomcat-5.5-doc/config/context.html
关于antiResourceLocking的bug:http://issues.apache.org/bugzilla/show_bug.cgi?id=37668
posted @ 2007-06-27 09:35 amp@java 阅读(1450) | 评论 (1)编辑 收藏


Google ADSense

posted @ 2007-05-08 18:07 amp@java 阅读(581) | 评论 (6)编辑 收藏

数据库服务器更换时,要把数据库整个备份出来,转移到另外的服务器上,在SQL Server 2000中,一般通过数据库的备份生成一个文件,然后在新的服务器上通过数据库的还原把数据导回来,整个过程都有图形界面,操作简单直观。
在ORACLE里,有相似功能的工具,但却是通过命令行方式。
首先,数据库的备份可以通过exp命令进行。exp命令有三种运行方式:交互式、命令行和参数文件。使用交互式一步步提示,用户只需简单输入即可进行,它的使用方法如下:
1、输入exp命令;
2、提示输入用户名,可以使用SYSTEM帐号,不能使用SYS帐号;
3、提示输入密码;
4、依次提示导出文件名及其他选项。

备份出一个文件之后,可以通过imp命令导入到新的服务器中,imp命令与exp命令有很多相似的地方,有些选项是相同的。采用全库导出之后的文件,通过imp完全导入的时候会出现表创建错误的提示,如果新的数据库中没有旧库中对应的用户,也会出现无法导入的提示,解决方法就是在新的数据库中建立同样的用户,并赋予同样权限(?)。

exp和imp的使用方法见:
http://oracle.chinaitlab.com/backup/36902.html
http://oracle.chinaitlab.com/backup/36901.html

posted @ 2007-04-17 17:23 amp@java 阅读(507) | 评论 (0)编辑 收藏

JavaScript里的Array有一个sort方法:

来自微软的Jscript文档

返回一个元素已经进行了排序的 Array 对象。

arrayobj.sort(sortfunction) 

参数

arrayObj
必选项。任意 Array 对象。
sortFunction
可选项。是用来确定元素顺序的函数的名称。如果这个参数被省略,那么元素将按照 ASCII 字符顺序进行升序排列。

说明

sort 方法将 Array 对象进行适当的排序;在执行过程中并不会创建新的 Array 对象。

如果为 sortfunction 参数提供了一个函数,那么该函数必须返回下列值之一:

  • 负值,如果所传递的第一个参数比第二个参数小。
  • 零,如果两个参数相等。
  • 正值,如果第一个参数比第二个参数大。


因此,排序的关键就在于如何设置sortfunction这个函数了。
在JavaScript中,字符串有一个localeCompare方法,能够根据区域设置来排序,中文的话就是按照拼音排序了。

来自微软的Jscript文档

返回一个值,指出在当前的区域设置中两个字符串是否相同。

stringVar.localeCompare(stringExp)

参数

stringVar
必选项。一个 String 对象文字。
stringExp
必选项。将与 stringVar 进行比较的字符串。

说明

localeCompare 可以对 stringVarstringExp 进行一个区分区域设置的字符串比较并返回 –1、0 或 +1,这取决于系统中缺省区域设置的排序。

如果 stringVar 排序在 stringExp 之前,那么 localeCompare 返回 –1;如果 stringVar 排序在 stringExp 之后,则返回 +1。如果返回值为 0,那就说明这两个字符串是相同的。


把以上两个方法结合,就可以轻松实现按照拼音顺序对多个字符串进行排序了。

例子可以见这里:
http://hi.baidu.com/aaxh/blog/item/eb6ddaf953327858252df218.html
posted @ 2007-04-16 15:08 amp@java 阅读(979) | 评论 (0)编辑 收藏

打开了连接服务器的一个Socket后,隔了一段时间没用,Socket就会自动关闭(为什么?),这时候如果获取该Socket的OutputStream,并往里面写东西,在flush的时候就会出现:
java.net.SocketException: Software caused connection abort: socket write error
的异常。因此,如果隔了一段时间没有使用某个Socket,需要重新使用它的时候,就要先判断它究竟是否还在连接当中,一共尝试了Socket的三个方法:
1、isClosed()
2、isOutputShutdown()
3、isConnected()
发现前两个都没用,即使已经断开了,返回的还是false,只有第三个可以解决问题。
具体原因未知。

2007-4-19更新:
第三个也解决不了问题,还会出现同样的异常,目前的解决方法是catch SocketException,不管socket的状态如何,都把它关闭了重新再连,但是SocketException的种类有很多,不一定是因为Socket已关闭,所以这种方法有点粗鲁,但找不到更好的方法。

posted @ 2007-04-12 12:21 amp@java 阅读(13094) | 评论 (8)编辑 收藏

通过
Runtime.getRuntime().addShutdownHook(Thread hook)
方法可以给当前的进程注册一个清理线程,当进程退出的时候,会执行线程中的代码。

1、为了统一清理线程的实现,可以做一个Destroyable接口,实现此接口的类都有一个destroy()方法,里面包含了该类的清理过程,例如关闭数据库连接、关闭Socket连接等。
Destroyable接口的代码:
 1 package net.blogjava.amplifier;
 2 
 3 /**
 4  * 建立此接口的目的是为了实现程序退出时的清理
 5  * 在Java控制台程序中,用户可以通过关闭命令行窗口或按下Ctrl+C来结束程序的运行
 6  * 这时候应该保证资源能够被正确释放,例如数据库的连接、Socket的连接应该关闭
 7  * 实现了本接口的类的实例引用可以由一个ShutdownHook线程来操作
 8  * 此线程在虚拟机退出时执行,详见Runtime.addShutdownHook()方法
 9  * @author amplifier
10  *
11  */
12 public interface Destroyable {
13     /**
14      * 实现本接口的类需要实现此方法,方法中包含该类的清理代码,
15      * 例如关闭数据库连接、关闭Socket连接等
16      *
17      */
18     void destroy();
19 }

2、建立一个继承于Thread的类ShutdownHook,里面的一个域是Destroyable实现类的引用,在该类的构造函数中可以传入该Destroyable实现类。
ShutdownHook的代码:
 1 package net.blogjava.amplifier;
 2 
 3 public class ShutdownHook extends Thread {
 4 
 5     private Destroyable desObj;
 6     
 7     public ShutdownHook(Destroyable desObj){
 8         this.desObj = desObj;
 9             }
10     
11     @Override
12     public void run() {
13         if(this.desObj!=null){
14             try{
15                 this.desObj.destroy();
16             }
17             catch (Exception e) {
18             }
19         }
20     }
21 
22     /** *//**
23      * @param args
24      */
25     public static void main(String[] args) {
26     
27 
28     }
29 
30 }

3、在main()方法中注册ShutdownHook。
 1     public static void main(String[] args){
 2         DestroyableImpl di = new DestroyableImpl();
 3         ShutdownHook sh = new ShutdownHook(di);
 4         Runtime.getRuntime().addShutdownHook(sh);
 5         Runtime.getRuntime().addShutdownHook(new Thread(){
 6             public void run(){
 7                 System.out.println("正在退出……");
 8             }
 9         });
10         di.otherMethod();
11     }
posted @ 2007-04-12 10:08 amp@java 阅读(2622) | 评论 (0)编辑 收藏

   原来用的是搜狗拼音输入法,1.0还好,速度比较快,词库也比较全,到了2.0,速度慢得受不了,可能我的破电脑跟不上时代发展了,每次输入一个词都要等一会才能看到字,这样用起来太没意思了,于是把它卸了,装了个最近新出的google拼音输入法,还是1.0的,开始的时候居然和搜狗2.0一样慢,差点就心凉了,不过用了一会发现速度快了很多,输入的准确率也比较高,有一个搜狗拼音输入法不具备的特点是它跟google账户联系起来了,可以同步词库,所以到了其他电脑上也可以用自己的词库了,这个特点不错,Google不愧是互联网公司,什么都跟互联网联系起来,呵呵。
Google,我看行!
posted @ 2007-04-12 01:11 amp@java 阅读(257) | 评论 (0)编辑 收藏

在Tomcat 5.5.20里,把一个war包丢在Tomcat的webapps目录下,启动Tomcat后并不会自动启动那个应用,需要进入Tomcat manager,手动启动那个应用。为了实现应用的自动启动,需要在Tomcat的conf/server.xml里的
<Host></Host>
之间加入一个
<Context docBase="应用的根" path="应用程序相对webapps的目录" reloadable="当配置修改时是否需要自动重新加载,是则为true,否则为false"></Context>

特此备忘!
posted @ 2007-03-19 21:59 amp@java 阅读(705) | 评论 (0)编辑 收藏