‘编程学习’ 分类下的所有文章
20114 月8

告别程序员生涯,一点感慨与诸君共勉

再过几天,我就正式告别程序员生涯了,这也是我最后一次以职业程序员身份在CSDN发表文章。小弟谈谈入行几年来的感受,做一个人生阶段的自我总结,同时希望能给后来者带来点参考意见,能在这段路上走的更好。

本人2002年下半年正式入行,至今2007年4月一直从事软件开发工作。

上大学选择这个专业是阴差阳错,但接触之后对计算机产生了强烈的兴趣,对写软件有一种强烈的冲动。软件成型后,那种成就感和自豪感能给我难以名状的满足。

那时,喜欢看侯捷的书,对核心技术和核心技术人员由衷的崇拜,对技术的追求和水平的提高有一种莫名的狂热,当时我想只要能从事软件开发的工作,起初的薪金待遇可以不计,等我技术方面成熟后,自己就有更高更好的选择了,程序员-系统分析-项目经理-自己的软件公司,这是当时我一个朦胧的程序人生规划。

现在想来,不禁一番唏嘘。

我记得我在培训的时候,一个培训的老师当时是本地一家有名的高科技企业的CTO,确实是专业人才,就是不太会讲课。我问他做程序员的感受,他说经常写程序经常写到凌晨2点钟,很累不过很有意思,因为他喜欢这份职业(当然喜欢啊,他月薪7000-8000,2001年,济南),但也干不长啊,他已经做好了转行的准备,去做和计算机相关的行业。那年他28岁,我22岁。其实他那番话让我和我的同学已经很羡慕,我们羡慕他的技术深度和现在的岗位层次,金领啊,他就是我们眼里的金领啊。

而另一个技术水平很高的老师(在外企写单片机的,30岁,月薪8000-1万),告诉我的是:迟早要转行,就像他现在来当培训教师一样,原因:太累。

我没在乎他们的感慨,因为我年轻啊,加班到夜里2点很轻松啊,何况写写自己喜欢的软件,很高兴啊。30岁那时对我来说只是一个遥远的数字。工作后,感觉完全不一样了。首先很惭愧自己的机遇和能力都不是太好,一直从事基于数据库的信息管理系统的开发(我认为是软件开发里最简单和最基础的方面),换了三家公司,从基础程序员作到了系统分析的层次,现在开始往对外和管理方面发展。可以说粗略的沿着我以前设计的程序人生轨迹走了走。

其间也有过失业的落魄,吃不上饭的紧张,我记的最难的时候到CSDN上来发表文章,得到了很多兄弟姐妹的祝福和支持,给了我很大的鼓励,真的谢谢。

我今年28岁,未婚,彻底烦了。为什么?累;没有希望。先说说我的一点感悟。

软件行业分析:
1、开发出售行业适用的单机版软件。
2、开发行业适用的网络版(B/S)软件,一般是大单,几十万到几百万。
3、和行业的政府主管部门合作,推行一些行业方面的应用软件。
补充:做软件一定要做行业软件,才有前途。

这是本人几年来对这个行业的一点分析。

其实第一种情况是软件公司最通常的盈利模式,这种模式软件价格不高,但只要质量站得住脚,可以细水长流,保证公司的成本没问题,做的好还可以盈利不少,但想做大公司很难。

第二种情况,是真正挣钱的情况,接一个大单,什么钱都挣出来了。可以锻炼开发队伍,建立完整的大的开发框架,而且在这个行业里可以造成很大影响,在一个地方实施成功后可以低成本的再推广,占领一片市场。总之一句话可以让一个小公司真正的成长起来。

第三种情况纯粹就是敛财了,和主管部门合作,强行推广软件,绝对的只赚不赔,我想各个地区都有这样的案例,如税务方面的。缺点是这样的公司都受地域性限制,老板钱拿的太舒服,没什么上进心,公司很难做大,不过也成了地方的行业一霸了,也不错。

累,大家都知道就不说了。为什么没有希望呢?因为我发现一个公司真正勤勤恳垦的实干是挣不到大钱的。

真正能挣到大钱的公司完全都是靠老板的个人关系到什么程度,要想在某个行业里成为软件老大,要看你和这个行业里的政府主管部门的关系如何。我看到了太多软件和他们公司的产品,一个字“烂”。

可那赚钱的速度,呵呵。其实赚多少钱,都是老板的,我们打工的不就是拿个死工资吗,我们更多的人不是连个受剥削的机会都找不到吗?每次面试刚从大学出来的计算机的本科生,我真想对他说:你何必要选择这一行?每次面试那些工作经历比我长,年龄比我大程序员,看着他唯唯诺诺的目光,我就想:曾几何时我也像他这样,被人横眉冷对的面试多少次,以后我是不是还会像他这样,再去看人家的脸色啊?心寒啊!
看着同期毕业的同学,都转了行的,在自己的行业里都混的不错,大部分都比自己挣钱多,有干头;就是挣钱少的,他也干的轻松啊,最简单得到就是和自己项目接洽企业或政府的信息部主管或网管,懂的不多,轻轻松松,钱比我们的多,有问题老找我们,面对他犯的低级错误,我们还得笑呵呵。我心里确实不平衡啊。

一句话,不当程序员后悔,当了程序员更后悔。

出路在哪?我在找…

1、从程序员,到系统分析,到项目经理。条件:必须是大公司,工资高,福利好,有完整的发展曲线;个人对软件开发有持续的热情。
2、转行到大型企业,事业单位,政府做信息化方面的工作(可以说是网管)。生活有保障,不必太辛苦。条件:一定的能力,一定的人际关系。
3、考研,考博再深造,出国或留校搞教学,培养一代不如一代的本科生,闲时打着大学的名义做做项目,赚个房钱。条件:高学历,一定的经济基础和家庭背景。
4、创业:这个谈起来大发了。这里只说条件:很好的项目,创业精神,一定的经济基础。
5、共享软件:很多程序员的梦想,自己写个软件全世界的卖,光注册费够一家人生活的了。成功少数,但只要有的都发达了。如ACDsee,优化大师,超级兔子,千千静听(可到共享软件区查询)。但我告诉你,这方面基本是没法干了,写个小东西挣钱玩玩可以,要靠他吃饭,饿死吧。条件:过硬的专门的软件技术,富有创意的头脑。
6、网站:基本情况和共享软件差不太多,只是比共享软件更好干点。但奇迹照样有,可看看hao123的神话和现在很牛的80后的富翁。关键你有没有这个本事和这个命了。
7、行业信息化咨询顾问:随着各个行业信息化的普及,企业对这方面人才需求很大。真正实现信息化的企业都需要这样一个既懂软件,又懂行业知识的人员,他和网管还是有区别的,他的要求更高些,更像一个自由职业者,专家类型的,这样的人放在企业里小的是个主管,大的是个副总。条件:很深的行业内部的技术或管理经验,较强的软件开发或实施经验;通常35岁以上才是成熟人才,因为经验是要经过历练的。其实就是个人物了。
8、转行,彻底的转行。干不下去,精力不够了,脑子不灵了,钱太少。只要你够理由,你就走。从新开始另一段新的生活,有什么了不起的,哪里也饿不死我这个干软件的。

我是哪种人,我说我是第8种人,看看能不能兼第5,6种人。
我大学由于种种原因没毕业,最高学历是高中,呵呵。就学历而讲,能干到我目前这个水平我觉的可以了,是时候激流勇退了。
就职业规划和财富而言,我这几年走的路并不成功,最起码无奈的转行本身就是一种失败。就我的人生而言,我觉的很成功。我了却了自己的一个人生梦想,在短时间内品尝了一个“高科技”行业的酸甜苦辣,技术出身也使我比别人多了一份淡定和从容。现在我可以放下这段旅程,再来一个新的开始。

程序兄弟们别自卑,说到优势我们有很多:

1、聪明的头脑,较高的智商。有人说程序员呆,不会为人处事,只会和机器打交道,没前途没希望。我告诉你,程序员愿意和机器打交道是因为他专注于技术,是职业特点,如果我们程序员的头脑用到一般行业,企业,政府单位里去耍耍阴谋诡计,骗骗人,卖卖产品或套套别人的话,我敢说,他们十个人也玩不过我们一个人。俗一点:就他们那点智商,也就骗骗鬼啊。

2、创新精神,学习能力和频繁的知识更新速度。做软件的都知道,干一行的软件,就得学一行的知识,这一行的知识越丰富,软件才可能写的越好。我们都具备着很好的学习能力,学习新知识,新技术的能力。不敢说每个做过的行业我们多么了解,最起码我们总是站在风头浪尖上,高屋建瓴,问题看得远,想的长(要不你怎么去设计数据库啊,呵呵)。我们能以非专业人士的角度,系统的分析出一个行业某方面的流程,那当我们就做的这个行业时,我们对我们的能力还没有信心吗?

3、扎实的工作态度,未雨绸缪的危机意识。扎实的工作态度是每个合格的程序员都应该具备的,因为我们要对代码负责;谈到危机意识,我想大多数程序员都和我一样吃者碗里的,看着锅里的吧,也是被社会逼的没办法。其实这都成了我们的优点了,以后从事哪个行业,都需要这两点精神。

我要走了,去干个和软件根本不搭边的行业,我去干是因为我是老板之一,而且钱绝对比现在好赚。过年的时候,我有个外甥刚大学毕业,非要做软件,我给他了以下建议,算是为后来者留一点东西:

1、能进大公司就别去小公司,在大公司里你能接受真正正统软件开发教育,比到小公司当个什么啥都干,啥都不精的主管强。

2、不断的学习,注意技术积累和更新,那是你唯一的资本。

3、做软硬件结合方面的开发,单片机的开发,嵌入式系统的开发,比较有前途而且门槛高。但凡基于数据库的开发,不管是.NET平台的,J2EE平台的,VC,DELPHI,PB,VB都是扯淡,其核心价值是开发人员的经验而不是技术本身。因为真正的核心技术都在国外,中国没有,我发现不管那种语言,最好用的类库或组件都是老外写的。

4、要有个好点的学历,别像我一样。毕竟是个高学历的行业,学历低人家都瞧不起你,你的发展也很有限 。30岁之前,可考虑弄个高程,CCNA,数据库管理员之类比较有含金量的证书打扮打扮自己,过了35岁其实意义就不大了

何去何从,我们都有自己的路要走。我转行了,我就不再是程序员了吗,不!我只是不在做为别人打工的职业程序员了,我要做自己的终身程序员。闲来时我会为自己写程序,写我愿意写的。当写程序不再是一种职业而是一种兴趣和热情时,他才会陪伴我一辈子。我还会再来CSDN,做为一个非专业人士,一个轻松的真正的程序员而来。未来的一天,当我老的时候,不管那时我有什么成就,或不名一文,如果别人问起我以前是干什么的,我希望仍能自豪的回答:“我曾经是一名软件工程师”。

转载自:http://cnbeta.com/articles/139515.htm

20114 月1

Android开发:屏幕常亮 PowerManager和PowerManager.WakeLock

前言

  学习android一段时间了,为了进一步了解android的应用是如何设计开发的,决定详细研究几个开源的android应用。从一些开源应用中吸收点东西,一边进行量的积累,一边探索android的学习研究方向。这里我首先选择了jwood的 Standup Timer 项目。本文将把研究的内容笔记整理,建立一个索引列表。

PowerManager.WakeLock

  PowerManager.WakerLock是我分析Standup Timer源代码时发现的一个小知识点,Standup Timer 用WakeLock保证程序运行时保持手机屏幕的恒亮(程序虽小但也做得相当的细心,考虑的很周到)。PowerManager 和PowerManager.WakerLock7用于对Android设备的电源进行管理。
  PowerManager:This class gives you control of the power state of the device.
  PowerManager.WakeLock: lets you say that you need to have the device on.
  Android中通过各种Lock锁对电源进行控制,需要注意的是加锁和解锁必须成对出现。先上一段Standup Timer里的代码然后进行说明。
private void acquireWakeLock() {
         if (wakeLock == null) {
                Logger.d("Acquiring wake lock");
                PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
                wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass().getCanonicalName());
                wakeLock.acquire();
            }

    }

private void releaseWakeLock() {
        if (wakeLock != null && wakeLock.isHeld()) {
            wakeLock.release();
            wakeLock = null;
        }

    }

 

acquireWakeLock()方法中获取了 SCREEN_DIM_WAKE_LOCK锁,该锁使 CPU 保持运转,屏幕保持亮度(可以变灰)。这个函数在Activity的 onResume中被调用。releaseWakeLock()方法则是释放该锁。它在Activity的 onPause中被调用。利用Activiy的生命周期,巧妙的让 acquire()和release()成对出现。

    @Override
    protected void onResume()
    {
        super.onResume();
                //获取锁,保持屏幕亮度
        acquireWakeLock();
        startTimer();
    }
protected void onPause()
    {
        super.onPause();
         synchronized(this) {
                cancelTimer();
                releaseWakeLock();

                if (finished) {
                    clearState();
                } else {
                    saveState();
                }
            }
    }

 

PowerManager和WakeLock的操作步骤
  1.   PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);通过 Context.getSystemService().方法获取PowerManager实例。
  2.   然后通过PowerManager的newWakeLock((int flags, String tag)来生成WakeLock实例。int Flags指示要获取哪种WakeLock,不同的Lock对cpu 、屏幕、键盘灯有不同影响。
  3.   获取WakeLock实例后通过acquire()获取相应的锁,然后进行其他业务逻辑的操作,最后使用release()释放(释放是必须的)。

关于int flags

  各种锁的类型对CPU 、屏幕、键盘的影响:

PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。

SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯

SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯

FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度

ACQUIRE_CAUSES_WAKEUP:Normal wake locks don’t actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.

ON_AFTER_RELEASE:f this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer. This can be used to reduce flicker if you are cycling between wake lock conditions.

权限获取

要进行电源的操作需要在AndroidManifest.xml中声明该应用有设置电源管理的权限。
<uses-permission android:name="android.permission.WAKE_LOCK" /> 你可能还需要 <uses-permission android:name="android.permission.DEVICE_POWER" />

另外WakeLock的设置是 Activiy 级别的,不是针对整个Application应用的。

 

摘自:http://fanwei51880.blog.163.com/blog/static/324067402010108105416798/

20113 月10

MySQL 数据库引擎InnoDB转换成MyISAM

1、备份数据库

为了安全,我采用了2个备份语句分别备份带表信息的sql和不带表信息的sql:

A:mysqldump -h localhost -uroot -ppass -R  mydb> F:\dbbak\mydb%DATE:~0,10%.2.sql  (默认utf8格式)

B:mysqldump -h localhost -uroot -ppass -R –default-character-set=gbk –compatible=No_table_options mydb> F:\dbbak\mydb%DATE:~0,10%.2.sql

2、修改MySQL 默认引擎

因为上述,备份的SQL语句中不包含表信息,还原数据库的时候采用默认引擎,即:

default-storage-engine=MyISAM

数据字段比较大的话,要添加

max_allowed_packet = 300M

3、执行MySQL还原数据库命令

C:>mysql -uroot -ppassword –default-character-set=gbk

mysql>create databse mydb;
mysql>use mydb;
mysql>source mydb.sql;

20113 月2

囧,升级 WordPress 3.1 导致网站不能访问

昨天升级之后就放着没管,也没去前台看看,谁知道不能访问了,额,一天时间。。

解决方法:
修改 wp-include/template-loader.php 文件,把下面两段话注释。

//if ( defined(‘WP_USE_THEMES’) && WP_USE_THEMES )
// do_action(‘template_redirect’);

20111 月26

Android 开发:TextView 文字阴影效果

res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AudioFileInfoOverlayText">
    <item name="android:paddingLeft">4px</item>
    <item name="android:paddingBottom">4px</item>
    <item name="android:textColor">#f1f1f1</item>
    <item name="android:textSize">60sp</item>
    <item name="android:shadowColor">#4d4d4d</item>
    <item name="android:shadowDx">0</item>
    <item name="android:shadowDy">-3</item>
    <item name="android:shadowRadius">3</item>
</style>
</resources>

res/layout/main.xml

<TextView style="@style/AudioFileInfoOverlayText" 
        android:id="@+id/title"
		android:layout_gravity="bottom"
		android:gravity="center_vertical|center_horizontal"
		android:layout_width="640px" android:layout_height="151px"
		android:background="@drawable/compass_bottom"
		></TextView>
201012 月31

JAVA发送EMAIL的例子

import javax.mail.*;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

/**
 * Created by IntelliJ IDEA.
 * User: Wizzer
 * Date: 2010-12-29
 * Time: 16:39:50
 * To change this template use File | Settings | File Templates.
 */
public class Mail {
    public static void main(String args[]) throws MessagingException, UnsupportedEncodingException {
    Properties props = new Properties();
    props.put("mail.smtp.host","smtp.qq.com");
    props.put("mail.smtp.auth","true");
    PopupAuthenticator auth = new PopupAuthenticator(); 
    Session session = Session.getInstance(props, auth);
    MimeMessage message = new MimeMessage(session);
    Address addressFrom = new InternetAddress(PopupAuthenticator.mailuser+"@qq.com", "George Bush");
    Address addressTo = new InternetAddress("116****@qq.com", "George Bush");//收件人
    message.setText("邮件发送成功");
    message.setSubject("Javamal最终测试");
    message.setFrom(addressFrom);
    message.addRecipient(Message.RecipientType.TO,addressTo);
    message.saveChanges();
    Transport transport = session.getTransport("smtp");
    transport.connect("smtp.qq.com", PopupAuthenticator.mailuser,PopupAuthenticator.password);
    transport.send(message);
    transport.close();
    }

}
class PopupAuthenticator extends Authenticator {
public static final String mailuser="wizzer"; 
public static final String password="********";
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(mailuser,password);
}
}
201011 月18

MyCity 我的城市街拍,新浪微博街拍Android客户端 1.0发布

MyCity —我的城市街拍 1.0

是Android上的一个新浪微博的客户端,目的是实现手机上街拍发布图片和地理位置信息到微博的功能。
现阶段主要实现以下功能:
1. 通过MyCity拍照,可以把图片发布到微博
2. 通过MyCity获得GPS地理位置,分享到微博(通过google地图展示)
3. 用户可以附加100字以内的说明文字

下载地址: /MyCity.apk

使用说明:
按MENU键或第一次按发布键绑定微博帐号,进行拍照后附加文字发布微博,若在室外空旷处运行获得GPS地理位置信息后程序会自动在微博内容中添加google地图链接。

更新日志:
1、2010-11-18 9:00 修改位置信息未添加到微博中的bug,囧,我的错。

应用效果

201011 月17

新浪微博Android 客户端通过HTTP POST发布图片和文字源代码(作废)

1、发送图片+文字

要特别注意,图片的文件名要为 pic 才会被新浪接收。

              Map map = new HashMap();
	   map.put("source", "appkey");//改成自己的key
	   map.put("status", txt);
	   postImg("http://api.t.sina.com.cn/statuses/upload.json",map,Environment.getExternalStorageDirectory()+ "/temp.jpg"
								,"帐号名字","密码");
              /**
	 * 直接通过HTTP协议提交数据到服务器,实现表单提交功能
	 * @param actionUrl 上传路径
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param filename 上传文件
	 * @param username 用户名
	 * @param password 密码
	 */
	private void postImg(String actionUrl,Map<String, String> params, String  filename,String username,String password) {
		try {
			String BOUNDARY = "--------------et567z"; //数据分隔线
			String MULTIPART_FORM_DATA = "Multipart/form-data";  

			URL url = new URL(actionUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 

			conn.setDoInput(true);//允许输入
			conn.setDoOutput(true);//允许输出
			conn.setUseCaches(false);//不使用Cache
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Connection", "Keep-Alive");
			conn.setRequestProperty("Charset", "UTF-8");
			conn.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + ";boundary=" + BOUNDARY);
			String usernamePassword=username+":"+password;
			conn.setRequestProperty("Authorization","Basic "+new String(SecBase64.encode(usernamePassword.getBytes())));

			StringBuilder sb = new StringBuilder();  

			//上传的表单参数部分,格式请参考文章
			for (Map.Entry<String, String> entry : params.entrySet()) {//构建表单字段内容
				sb.append("--");
				sb.append(BOUNDARY);
				sb.append("\r\n");
				sb.append("Content-Disposition: form-data; name=\""+ entry.getKey() + "\"\r\n\r\n");
				sb.append(entry.getValue());
				sb.append("\r\n");
			}
			//            System.out.println(sb.toString());
			DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
			outStream.write(sb.toString().getBytes());//发送表单字段数据
			byte[] content = readFileImage(filename);
			//上传的文件部分,格式请参考文章
			//System.out.println("content:"+content.toString());
			StringBuilder split = new StringBuilder();
			split.append("--");
			split.append(BOUNDARY);
			split.append("\r\n");
			split.append("Content-Disposition: form-data;name=\"pic\";filename=\"temp.jpg\"\r\n");
			split.append("Content-Type: image/jpg\r\n\r\n");
			System.out.println(split.toString());
			outStream.write(split.toString().getBytes());
			outStream.write(content, 0, content.length);
			outStream.write("\r\n".getBytes());  

			byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();//数据结束标志
			outStream.write(end_data);
			outStream.flush();
			int cah = conn.getResponseCode();
			//            if (cah != 200) throw new RuntimeException("请求url失败:"+cah);
			if(cah == 200)//如果发布成功则提示成功
			{
				/*读返回数据*/
				//String strResult = EntityUtils.toString(httpResponse.getEntity()); 

				new AlertDialog.Builder(Main.this)
				// Main.this视情况而定,这个一般是指当前显示的Activity对应的XML视窗。
				.setTitle("")// 设置对话框的标题
				.setPositiveButton("确定",// 设置对话框的确认按钮
						new DialogInterface.OnClickListener() {// 设置确认按钮的事件
					public void onClick(DialogInterface dialog, int which) {

					}})
					.setMessage(" 发布成功 ")// 设置对话框的内容
					.show();
			}
			else if(cah == 400)
			{
				new AlertDialog.Builder(Main.this)
				// Main.this视情况而定,这个一般是指当前显示的Activity对应的XML视窗。
				.setTitle("")// 设置对话框的标题
				.setPositiveButton("确定",// 设置对话框的确认按钮
						new DialogInterface.OnClickListener() {// 设置确认按钮的事件
					public void onClick(DialogInterface dialog, int which) {

					}})
					.setMessage(" 发布失败  \n 不可连续发布相同内容 ")// 设置对话框的内容
					.show();
			}else{
				throw new RuntimeException("请求url失败:"+cah);
			} 

			//            InputStream is = conn.getInputStream();
			//            int ch;
			//            StringBuilder b = new StringBuilder();
			//            while( (ch = is.read()) != -1 ){
			//                b.append((char)ch);
			//            }
			outStream.close();
			conn.disconnect();
		}
		catch (IOException e)
		{
			e.printStackTrace(); 

		}
		catch (Exception e)
		{
			e.printStackTrace();  

		}  

	}

             public static byte[] readFileImage(String filename) throws IOException {
		BufferedInputStream bufferedInputStream = new BufferedInputStream(
				new FileInputStream(filename));
		int len = bufferedInputStream.available();
		byte[] bytes = new byte[len];
		int r = bufferedInputStream.read(bytes);
		if (len != r) {
			bytes = null;
			throw new IOException("读取文件不正确");
		}
		bufferedInputStream.close();
		return bytes;
	}

2、只发文字

  //POST发布文本信息
	    public  void sendMsg(String status,String username,String password){
			HttpClient httpclient = new DefaultHttpClient();
	        HttpPost httppost = new HttpPost("http://api.t.sina.com.cn/statuses/update.json");
		        //NameValuePair实现请求参数的封装

		        List  params = new ArrayList ();
		        params.add(new BasicNameValuePair("source", "4016954419"));
		        params.add(new BasicNameValuePair("status", status));
		        try
		        { 

		          //添加请求参数到请求对象

		        	httppost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
		        	httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); 

		        	String data=username+":"+password;
		        	httppost.addHeader("Authorization","Basic "+new String(SecBase64.encode(data.getBytes())));
		        	httppost.addHeader("Content-Type", "application/x-www-form-urlencoded");

		          //发送请求并等待响应
		          HttpResponse httpResponse = new DefaultHttpClient().execute(httppost);
		          //若状态码为200 ok
		          if(httpResponse.getStatusLine().getStatusCode() == 200)
		          {
		            //读返回数据
		            //String strResult = EntityUtils.toString(httpResponse.getEntity()); 

		            new AlertDialog.Builder(Main.this)
					// Main.this视情况而定,这个一般是指当前显示的Activity对应的XML视窗。
					.setTitle("")// 设置对话框的标题
					.setPositiveButton("确定",// 设置对话框的确认按钮
				    new DialogInterface.OnClickListener() {// 设置确认按钮的事件
				        public void onClick(DialogInterface dialog, int which) {

				    }})
					.setMessage(" 发布成功 ")// 设置对话框的内容
					.show();
		          }
		          else if(httpResponse.getStatusLine().getStatusCode() == 400)
		          {
		        	  new AlertDialog.Builder(Main.this)
						// Main.this视情况而定,这个一般是指当前显示的Activity对应的XML视窗。
						.setTitle("")// 设置对话框的标题
						.setPositiveButton("确定",// 设置对话框的确认按钮
				    new DialogInterface.OnClickListener() {// 设置确认按钮的事件
				        public void onClick(DialogInterface dialog, int which) {

				    }})
						.setMessage(" 发布失败  \n 不可连续发布相同内容 ")// 设置对话框的内容
						.show();
		          } 

		        }
		        catch (ClientProtocolException e)
		        {
		          e.printStackTrace();
		          et.setText(et.getText()+" Error1:"+e.getMessage());
		        }
		        catch (IOException e)
		        {
		          e.printStackTrace();
		          et.setText(et.getText()+" Error2:"+e.getMessage());
		        }
		        catch (Exception e)
		        {
		          e.printStackTrace();
		          et.setText(et.getText()+" Error3:"+e.getMessage());
		        }  

		 }

3、加密类 SecBase64.java

package wizzer.cn.app;

public class SecBase64 {
private static final byte[] encodingTable = { (byte) 'A', (byte) 'B',
    (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
    (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
    (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
    (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
    (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
    (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
    (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
    (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
    (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
    (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
    (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
    (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
    (byte) '+', (byte) '/' };
private static final byte[] decodingTable;
static {
   decodingTable = new byte[128];
   for (int i = 0; i < 128; i++) {
    decodingTable[i] = (byte) -1;
   }
   for (int i = 'A'; i <= 'Z'; i++) {
    decodingTable[i] = (byte) (i - 'A');
   }
   for (int i = 'a'; i <= 'z'; i++) {
    decodingTable[i] = (byte) (i - 'a' + 26);
   }
   for (int i = '0'; i <= '9'; i++) {
    decodingTable[i] = (byte) (i - '0' + 52);
   }
   decodingTable['+'] = 62;
   decodingTable['/'] = 63;
}

//加密

public static byte[] encode(byte[] data) {
   byte[] bytes;
   int modulus = data.length % 3;
   if (modulus == 0) {
    bytes = new byte[(4 * data.length) / 3];
   } else {
    bytes = new byte[4 * ((data.length / 3) + 1)];
   }
   int dataLength = (data.length - modulus);
   int a1;
   int a2;
   int a3;
   for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) {
    a1 = data[i] & 0xff;
    a2 = data[i + 1] & 0xff;
    a3 = data[i + 2] & 0xff;
    bytes[j] = encodingTable[(a1 >>> 2) & 0x3f];
    bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f];
    bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f];
    bytes[j + 3] = encodingTable[a3 & 0x3f];
   }
   int b1;
   int b2;
   int b3;
   int d1;
   int d2;
   switch (modulus) {
   case 0: 
    break;
   case 1:
    d1 = data[data.length - 1] & 0xff;
    b1 = (d1 >>> 2) & 0x3f;
    b2 = (d1 << 4) & 0x3f;
    bytes[bytes.length - 4] = encodingTable[b1];
    bytes[bytes.length - 3] = encodingTable[b2];
    bytes[bytes.length - 2] = (byte) '=';
    bytes[bytes.length - 1] = (byte) '=';
    break;
   case 2:
    d1 = data[data.length - 2] & 0xff;
    d2 = data[data.length - 1] & 0xff;
    b1 = (d1 >>> 2) & 0x3f;
    b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
    b3 = (d2 << 2) & 0x3f;
    bytes[bytes.length - 4] = encodingTable[b1];
    bytes[bytes.length - 3] = encodingTable[b2];
    bytes[bytes.length - 2] = encodingTable[b3];
    bytes[bytes.length - 1] = (byte) '=';
    break;
   }
   return bytes;
}

//解密

public static byte[] decode(byte[] data) {
   byte[] bytes;
   byte b1;
   byte b2;
   byte b3;
   byte b4;
   data = discardNonBase64Bytes(data);
   if (data[data.length - 2] == '=') {
    bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
   } else if (data[data.length - 1] == '=') {
    bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
   } else {
    bytes = new byte[((data.length / 4) * 3)];
   }
   for (int i = 0, j = 0; i < (data.length - 4); i += 4, j += 3) {
    b1 = decodingTable[data[i]];
    b2 = decodingTable[data[i + 1]];
    b3 = decodingTable[data[i + 2]];
    b4 = decodingTable[data[i + 3]];
    bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
    bytes[j + 2] = (byte) ((b3 << 6) | b4);
   }
   if (data[data.length - 2] == '=') {
    b1 = decodingTable[data[data.length - 4]];
    b2 = decodingTable[data[data.length - 3]];
    bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
   } else if (data[data.length - 1] == '=') {
    b1 = decodingTable[data[data.length - 4]];
    b2 = decodingTable[data[data.length - 3]];
    b3 = decodingTable[data[data.length - 2]];
    bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
   } else {
    b1 = decodingTable[data[data.length - 4]];
    b2 = decodingTable[data[data.length - 3]];
    b3 = decodingTable[data[data.length - 2]];
    b4 = decodingTable[data[data.length - 1]];
    bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
    bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
   }
   return bytes;
}

//解密

public static byte[] decode(String data) {
   byte[] bytes;
   byte b1;
   byte b2;
   byte b3;
   byte b4;
   data = discardNonBase64Chars(data);
   if (data.charAt(data.length() - 2) == '=') {
    bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
   } else if (data.charAt(data.length() - 1) == '=') {
    bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
   } else {
    bytes = new byte[((data.length() / 4) * 3)];
   }
   for (int i = 0, j = 0; i < (data.length() - 4); i += 4, j += 3) {
    b1 = decodingTable[data.charAt(i)];
    b2 = decodingTable[data.charAt(i + 1)];
    b3 = decodingTable[data.charAt(i + 2)];
    b4 = decodingTable[data.charAt(i + 3)];
    bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
    bytes[j + 2] = (byte) ((b3 << 6) | b4);
   }
   if (data.charAt(data.length() - 2) == '=') {
    b1 = decodingTable[data.charAt(data.length() - 4)];
    b2 = decodingTable[data.charAt(data.length() - 3)];
    bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
   } else if (data.charAt(data.length() - 1) == '=') {
    b1 = decodingTable[data.charAt(data.length() - 4)];
    b2 = decodingTable[data.charAt(data.length() - 3)];
    b3 = decodingTable[data.charAt(data.length() - 2)];
    bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
   } else {
    b1 = decodingTable[data.charAt(data.length() - 4)];
    b2 = decodingTable[data.charAt(data.length() - 3)];
    b3 = decodingTable[data.charAt(data.length() - 2)];
    b4 = decodingTable[data.charAt(data.length() - 1)];
    bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
    bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
    bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
   }
   return bytes;
}

private static byte[] discardNonBase64Bytes(byte[] data) {
   byte[] temp = new byte[data.length];
   int bytesCopied = 0;
   for (int i = 0; i < data.length; i++) {
    if (isValidBase64Byte(data[i])) {
     temp[bytesCopied++] = data[i];
    }
   }
   byte[] newData = new byte[bytesCopied];
   System.arraycopy(temp, 0, newData, 0, bytesCopied);
   return newData;
}

private static String discardNonBase64Chars(String data) {
   StringBuffer sb = new StringBuffer();
   int length = data.length();
   for (int i = 0; i < length; i++) {
    if (isValidBase64Byte((byte) (data.charAt(i)))) {
     sb.append(data.charAt(i));
    }
   }
   return sb.toString();
}

private static boolean isValidBase64Byte(byte b) {
   if (b == '=') {
    return true;
   } else if ((b < 0) || (b >= 128)) {
    return false;
   } else if (decodingTable[b] == -1) {
    return false;
   }
   return true;
}

//测试类
public static void main(String[] args) {
   String data = "wizzer@qq.com:etpass";
   byte[] result = SecBase64.encode(data.getBytes());// 加密
   System.out.println("Basic "+data);
   System.out.println("Basic "+new String(result));
   System.out.println(new String(SecBase64.decode(new String(result))));// 解密
   }
}
201011 月16

Android 2.1 GPS定位和拍照功能代码

1、GPS功能代码

private void getLocation()
	{
		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
				 200, 0, locationListener);

	}
	private final LocationListener locationListener = new LocationListener() {
	    public void onLocationChanged(Location location) { //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
	        // log it when the location changes
	        if (location != null) {
	        	Lat.setText(String.valueOf(location.getLatitude()));
	        	Lon.setText(String.valueOf(location.getLongitude()));

	        }
	    }

	    public void onProviderDisabled(String provider) {
	    // Provider被disable时触发此函数,比如GPS被关闭
	    }

	    public void onProviderEnabled(String provider) {
	    //  Provider被enable时触发此函数,比如GPS被打开
	    }

	    public void onStatusChanged(String provider, int status, Bundle extras) {
	    // Provider的转态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
	    }
	};

2、拍照功能代码

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     // Hide the window title.
		requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.main);
        imageView = (ImageView) this.findViewById(R.id.iv1);
		Button button = (Button) this.findViewById(R.id.bt1);
		button.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
				intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri
						.fromFile(new File(Environment
								.getExternalStorageDirectory(), "temp.jpg")));
				intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
				startActivityForResult(intent, 0);
			}
		});

    }
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == 0 && resultCode == Activity.RESULT_OK) {
			this.imageView.setImageDrawable(Drawable.createFromPath(new File(
					Environment.getExternalStorageDirectory(), "temp.jpg")
					.getAbsolutePath()));

		}
	}

3、退出程序确认

public boolean onKeyDown(int keyCode, KeyEvent event) {

		//按下键盘上返回按钮
		if(keyCode == KeyEvent.KEYCODE_BACK){
			new AlertDialog.Builder(Main.this)
			// Main.this视情况而定,这个一般是指当前显示的Activity对应的XML视窗。
			.setTitle("")// 设置对话框的标题
			.setMessage(" 确定退出? ")// 设置对话框的内容
			.setPositiveButton("确定",// 设置对话框的确认按钮
			    new DialogInterface.OnClickListener() {// 设置确认按钮的事件
			        public void onClick(DialogInterface dialog, int which) {
			            //退出程序
			            android.os.Process.killProcess(android.os.Process.myPid());
			    }})
			.setNegativeButton("取消",// 设置对话框的取消按钮
			    new DialogInterface.OnClickListener() {// 设置取消按钮的事件
			        public void onClick(DialogInterface dialog, int which) {
			            // 如果你什么操作都不做,可以选择不写入任何代码
			            dialog.cancel();
			    }}
			).show();

			return true;
		}else{		
			return super.onKeyDown(keyCode, event);
		}
	}
201011 月12

mysql : Lock wait timeout exceeded; try restarting transaction

使用的InnoDB   表类型的时候,
默认参数:innodb_lock_wait_timeout设置锁等待的时间是50s,
因为有的锁等待超过了这个时间,所以报错.
可以把这个时间加长,或者优化存储过程,事务避免过长时间的等待.

my.ini文件:
#innodb_lock_wait_timeout = 50
改成
innodb_lock_wait_timeout = 500

 重启mysql服务。

20109 月29

C# Windows Mobile 实现休眠状态下定时任务

OpenNETCF.WindowsCE.LargeIntervalTimer llTimer = new OpenNETCF.WindowsCE.LargeIntervalTimer(); 

//第一次触发时间

llTimer.FirstEventTime = DateTime.Now;

llTimer.Interval = new TimeSpan(0, 30, 0);

//是否只触发一次

 llTimer.OneShot = false;

llTimer.Tick += new EventHandler(llTimer_Tick);

llTimer.Enabled = true;

static void llTimer_Tick(object sender, EventArgs e)

 {

             //

  }

 

来源:http://www.cnblogs.com/fox23/archive/2008/02/03/AtTime.html

opennetcf下载:http://www.opennetcf.com/Products/SmartDeviceFramework/tabid/65/Default.aspx

20109 月28

C# Windows Mobile 6 通过注册表实现开机自运行

方法1:制作CAB安装包

VS2005,新建项目–其他项目类型–安装和部署–智能Cab安装项目–在Program files里增加文件夹myexe

添加要可执行 a.exe。

打开注册表视图,在HKEY_LOCAL_MACHINE 增加 Init 文件夹,新建字符串 Launch98,值为

“\Program files\myexe\a.exe”

这样,安装cab之后重启下即可自运行。

private void Form1_Activated(object sender, EventArgs e)
        {
            this.Hide();  //隐藏窗体
        }

方法2:通过程序运行注册键值

 

  //注册开机
  //Registry.LocalMachine 对应于 HKEY_LOCAL_MACHINE主键
  RegistryKey key = Registry.LocalMachine.OpenSubKey("init", true);
  if (key.GetValue("Launch77") == null)
  {
       key.SetValue("Launch77", Assembly.GetExecutingAssembly().GetName().CodeBase);
       MessageBox.Show("注册成功!", "提示");
   }
   key.Close();
20109 月22

WM windows mobile 6.1 C#网络开发,程序自动升级等

终于的终于,还是用C#开发了,仿照了一些M8上软件界面。。

分享一些经验,由于时间太紧,有些功能是比较土的方法暂时实现的,之后还需升级。

1、网络传输

 public static string Login(string userName, string password)
        {
            string LOGIN_RES = "";
            try
            {

                string url = com.LOGIN_URL ;//url
                url = url + "&username=" + userName;
                url = url + "&password=" + password;

                HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
                objRequest.Method = "GET";
                objRequest.Timeout = 60 * 1000;
                //WebProxy proxy = new WebProxy("192.168.0.2:80", true);
                // proxy.Address = new Uri("");//按配置文件创建Proxy 地置
                //proxy.Credentials = new NetworkCredential("用户名", "密码");//从配置封装参数中创建
                //objRequest.Proxy = proxy; 

                HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();

                Stream objStream = objResponse.GetResponseStream();
                StreamReader objReader = new StreamReader(objStream, Encoding.GetEncoding(com.PageEnCode));
                LOGIN_RES=objReader.ReadToEnd();
                if (LOGIN_RES != null) LOGIN_RES = LOGIN_RES.Trim();
                objReader.Close();
                objStream.Close();

                return LOGIN_RES;

            }
            catch (Exception ex){
                Console.Write(ex.Message);
                return null;

            }

        }

2、程序升级

利用1里面的方法,读取服务器某网页文件,获取版本号和当前程序版本进行比较,若有最新版,

则在WM里打开浏览器进行下载:

private void checkUpdate()
        {
            int k = comHTTP.AutoUpdate();
            if (k > com.COPYRIGHT)
            {
                MessageBox.Show("\r\n系统监测到新版本,程序将自动打开下载,请安装后继续使用.\r\n\r\n", "提示");
                System.Diagnostics.Process.Start("IEXPLORE.EXE", "/autoupdate.cab");//打开IE,
                Application.Exit();

            }
        }
20109 月21

windows mobile模拟器如何联网?

windows mobile开发的时候模拟器如何联网?

1. 安装Active sync,到微软网站上面去找找,有免费下载。

2. 打开Active sync连接选项,里面有下拉选框的那个(允许连接到以下其中一个端口),选择DMA

3. 在VS2005里面启动模拟器成功(要知道自己连的是哪个)之后,Tools菜单里面有个”Device Emulator Manager”,找到刚刚连上的模拟器(前面会有个小图标,其他没启动就没有),右键点击,选择”Cradle” 这时候Active sync跳出来说找到一个device,不理他,等待emulator里面跳出个小对话框,告诉你连接成功(很快就自己消失),OK,这时候就可以上网了。

注意:Active Sync会吃掉所有的UDP包,所以用这种方式开发UDP客户端行不通了。

20109 月17

Java ME/J2ME 环境搭建、WM6.1运行JBED虚拟机手机实现JSR179定位

接触Java ME,到现在已经过去五天了。利用一款带GPS模块的WM6.1系统的手机实现定位和数据上报项目,周期为18天,公司没有一个人搞过手机开发,囧,身为研发部负责人责无旁贷(目前是光杆司令)……

废话不多讲,刚开始用MyEclispe + MyEclispeMe + WTK + JDK 搭建了开发环境(这也是费了九牛二虎之力)

一、Java ME/J2ME环境的搭建

1、安装 MyEclipse

MyEclipse_6.5.1GA_E3.3.2_Installer.exe、myEclipse6.5汉化包.rar、MyEclipse注册码.txt

2、安装 MyEclispeMe 开发插件

运行MyEclispe –>帮助–>Software Updates–>Find and Install –>搜索新部件

新建站点:http://eclipseme.org/updates/ (可能要翻翻哦),确定,全部勾选,安装,完毕。

3、安装 JDK

JDK6.0.21版本,如果找不到就先安装 jdk-6u20-windows-i586.exe 再打21补丁 JavaSetup6u21.exe。

4、安装 JavaME SDK

sun_java_me_sdk-3_0-win.exe

5、安装 WTK

WTK2.5.2_01版本,sun_java_wireless_toolkit-2.5.2_01-win.exe

MyEclispe–>首选项–>J2ME–>设备管理–>选择WTK目录–>refresh

(以上这些软件你就百度吧,不行就google,再不行必应)

注意:MyEclipse 编译的JAR包,很可能在虚拟机上安装不了。提示什么907啊,30的错误。

解决办法就是打开,JAR,编辑META-INF文件夹下的 MANIFEST.MF 文件,增加一行:

MIDlet-1: Test,,demo.LocationMIDlet  名字为Test,入口类为demo.LocationMIDlet

有的环境可能需要JAD包,相应的里面也要加上这一行。

二、支持JSR179的虚拟机

WM6.1上安装4EsmertecJbed_v20090217.5.1a_Chs.cab,狗狗搜,搜不到就找我要吧。

三、测试代码

package demo;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.location.Coordinates;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationProvider;
//import javax.microedition.location.QualifiedCoordinates;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class LocationMIDlet extends MIDlet implements CommandListener, Runnable
{
    private Display myDisplay;
    private Command okcmd;
    private Command ecmd;
    private Form form;
    private List myList;
    private boolean yes;

    public LocationMIDlet()
    {
        myDisplay = Display.getDisplay(this);
        myList = new List("测试真机是否支持 JSR179", List.IMPLICIT);
        okcmd = new Command("测试", Command.OK, 1);
        ecmd = new Command("退出", Command.OK, 1);
        form = new Form("GPS");
        String cellid=System.getProperty("CellID");
        if(cellid==null||"".equals(cellid)){
        	cellid=System.getProperty("Cell-ID");
        }
        if(cellid==null||"".equals(cellid)){
        	cellid=System.getProperty("CELLID");
        }
        if(cellid==null||"".equals(cellid)){
        	cellid=System.getProperty("CELL-ID");
        }
        form.append("CellID:"+cellid);
        form.append("JSR:"+System.getProperty("Version"));
        myList.addCommand(okcmd);
        form.addCommand(ecmd);
        form.setCommandListener(this);
        myList.setCommandListener(this);
    }
    protected void destroyApp(boolean arg0)
            throws MIDletStateChangeException {}
    protected void pauseApp(){}
    protected void startApp() throws MIDletStateChangeException
    {
        myDisplay.setCurrent(myList);
    }
    public void commandAction(Command arg0, Displayable arg1)
    {
    	if(arg0 == okcmd)
        {
            String version = System.getProperty("microedition.location.version");
            yes = (version != null && !version.equals(""));
            Thread t = new Thread(this);
            t.start();
        }
    	if(arg0 == ecmd)
        {
           this.notifyDestroyed();
        }
    }
    public void run()
    {
    	myDisplay.setCurrent(form);
        //测试真机是否支持 jsr179
        if(yes)
        {
            // Set criteria for selecting a location provider:
            // accurate to 500 meters horizontally
            // 设置精度
            Criteria myCriteria = new Criteria();
            myCriteria.setHorizontalAccuracy(500);

            double lat = 0;
            double lon = 0;

            // Get an instance of the provider
            // 找卫星,找服务
            try
            {
                LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria);

                // Request the location, setting a one-minute timeout
                // 请求位置,并设置超时时间
                Location myLocation = myLocationProvider.getLocation(5000);
                Thread.sleep(1000);
                Coordinates myCoordinates = myLocation.getQualifiedCoordinates();
                for(int i=0;i<10;i++){
                if(myCoordinates != null)
                {
                    // Use coordinate information
                    // 得到经纬度
                    lat = myCoordinates.getLatitude();
                    lon = myCoordinates.getLongitude();
                }

                form.append("真机支持JSR179,纬度坐标:" + String.valueOf(lat) + ",经度坐标:" + String.valueOf(lon));

                form.append("------------"+i);

                try{
                    Thread.sleep(1000);
                    }catch(Exception e){

                    }
                }
            }
            catch (LocationException e)
            {
            	form.append("LocationException 发生异常");
                e.printStackTrace();
            } catch (InterruptedException e)
            {
            	form.append("InterruptedException 发生异常");
                e.printStackTrace();
            }

        }
        //真机不支持 jsr179
        else
        {
        	form.append("真机不支持JSR179");

        }

    }

}

四、遗留问题

WM6.1手机系统运行JBED虚拟机上的Java ME软件,通过JSR179获取经纬度已实现。
(先运行WM上的GPS软件,后执行虚拟机里的测试代码)
两个问题:
1、室内等信号弱的地方无法定位;
2、JSR179是否需要WM上的程序进行GPS初始化待验证。
初步验证失败,貌似需要WM上先初始化GPS拨号找到卫星,虚拟机里程序才能读取数据,杯具。。。
再研究研究。。。
20109 月14

MyEclipse+Lwuit:java.lang.NoClassDefFound

在运行Lwuit例子时报:
java.lang.NoClassDefFoundError:com/sun/lwuit/uidemo/UIDemoMIDlet 错误
解决办法比较简单:
在Myeclipse中,选择Project->properites->Java Build Path->Order and Export,选中lwuit即可。

ps:
命苦的人儿还在加班。JAVA ME 从〇开始。。 两周要开发出成品。55

20109 月10

最封闭的开源系统:Android的八宗罪

  你以为 Android 是开放的吗?Google 采用了一系列的控制手段来保证每一部 Android 手机上都有它指定的软件和硬件规格。然而,他们同时又利用 Android SDK 里面的 Apache 许可证来大肆鼓吹 Android 是开放的。

  没错,Google 的移动平台是当前最聪明的利用开源来驱动商业议程的实现。但在我们深入探讨这个为什么之前,我们先说说为什么 Android 的成功和开源并没有什么关系吧。

  是什么成就了 Android

  虽然早期饱受质疑,Google 的 Android 移动平台已经在移动行业得到了营运商和手机厂商的广泛支持,仅剩固执的诺基亚。Android 从 08 年的一款机型发展到 10 年的 50 多款,发展之快让绝大多数的业内观察家们吃惊。

  Android 的成功和开源毫无关系。它的成功依靠下列三个主要因素:

JAVA手机网[www.cnjm.net]  - 苹果. 这点看起来很奇怪,Android 竟然是靠它的主要对手发家的?让我来分析下。在 iPhone 空前绝后的成功以及苹果对网络营运商傲慢苛刻的态度下,营运商们迫切的在寻找一种更便宜的选择。因此这些第一层最大的营运商们开始积极的用 Android 来开发手机给那些买不起 iPhone 的用户,更重要的是,他们不需要每卖一部手机就给 Apple 300 欧元以上的回扣。

  - 全世界的营运商们迫切希望自己鹤立鸡群. Android 给他们提供了一个统一的软件平台。他们可以很方便的定制自己想要的系统,而且花费的代价也很低(3 个月的时间,这个比 SavaJe 12 个月以上的定制周期要短很多)。对大型的营运商来说,Android 也降低了他们在智能手机软件方面的投资。这也是为什么大多数的 Android 手机项目背后都是营运商和 OEM 厂商的组合。

JAVA手机网[www.cnjm.net]  - 高通. 这个市值 100 亿美元的芯片厂商对 Android 的崛起功不可没。手机开发产商可以直接拿高通已经为 Android 集成好的方案,在 9-12 个月的时间内向市场上推广。(相比起来摩托罗拉的 CLIQ 花了 16 个月,而 HTC G1 则花费了 2 年多的时间)。除了高通,我们还有 TI 的 OMAP3 平台(摩托罗拉 Droid/Milestone 基于此方案)。ST Ericsson 和 Broadcom 也在做 Android 的集成方案。
JAVA手机网[www.cnjm.net]
  换句话说,在 Android 手机上,大多数的 OEM 预算花在了定制方面。而 Symbian 的绝大部分预算花在无线通信的移植和硬件整合上了(Symbian 2001 年所做决定的结果)。总的来说,Android 使 OEM 厂商可以大幅削减研发预算,把钱花在定制这个刀刃上。当然我们不能忘记 Android 是免费的。这个免费让众多厂商激动不已。

  话说回来,Android 用开源来做市场宣传,非常成功的搅乱了整个行业,导致了诺基亚对 Symbian 的收购以及 Windows Mobile 的全面崩溃(不过译者觉得 iPhone OS 4 的多重任务机制的发布让 WP7 真正成了杯具帝)。不过更重要的是,利用开源的名号和 Google 的魅力,Android 吸引了成千上万的开发者,虽然 Android 并不能让开发者们赚到很多钱,而且 Android 手机的数量不到苹果产品的十分之一(连支持收费的国家都比苹果少6倍)。

  在开源的面纱后面

  让人更惊讶的是 Android 到底有多封闭,尽管外面包裹着 Google“不作恶”的口号和 Apache 授权许可证模式。借用亨利福特在 Model-T 相关的书里的一句话:“任何人都可以自由挑选 Android 的颜色,只要那是黑色”(anyone can have Android in their own colour as long as it’s black)。Android 是一个绝好的商业案例——展现一家公司是如何用开源来赢得关注和社区参与,而且同时保持一个非常严密的商业运作。

JAVA手机网[www.cnjm.net]  Google 是如何控制着每台 Android 手机里采用什么服务、软件和硬件的?这个搜索巨人建立了一套很完善的控制管理系统。为了挖掘更多的信息,我们花了两个月,和很多与 Android 有着紧密联系的内部人士进行了讨论。我们发掘出的事实让人震惊。从宏观方面说,Google 控制 Android 手机构成以下八宗罪:

  1. 私有分支. Android 有多个私有分支,这些只给几个特定合作伙伴,往往是那些开发 Android 的 OEM 厂商,而且这些只提供给需要知道的人。这些私有分支比已经公布的 SDK 要超前起码 6 个月,也是 OEM 厂商可以保持竞争力的关键。而公开的 SDK 则是为第三方应用提供私有分支里发布的最新功能。

  2. 封闭的评估流程. 所有的代码评估员似乎都是 Google 员工,也就是说从社区提交的代码只有 Google 才有权力决定是否接受。而且 Google 内部还流传着“并非此处发明”的一种思考文化,他们觉得 Google 员工写的代码是天下无敌。随便问任何一个给 Android 提交过补丁的人,你会得到一样的答复:几乎没有什么提交被 Google 接受,而被拒绝的时候往往没有任何理由和解释。

  3. 进化的速度. Google 对 Android 的创新的速度是移动行业内绝无仅有的,他们在 18 个月里发布了四个大版本。想在 Android 上面做文章的 OEM 厂商只得紧跟 Google 的步伐(这里想起了移动杯具的OMS),不然就跟不上新功能的发布和 bug 修复。Nexus One、Droid、G1 和其它带有 Google体验应用的手机给 Google 提供了创新的测试场。

  4. 不完善的软件. 用公开的 SDK 并不能完整的建造手机。缺少的几个关键的部份包括无线通信的集成模块、国际化语言包、营运商信息包以及闭源的 Google 应用,比如 Market、Gmail 和 Gtalk。虽然 Cyanogen 可以自己定制 ROM,但里面包含的那些应用没有授权,所以不能发布在商业用途的 Android 手机上。

  5. 闭门的开发者社区. Android Market 是唯一一个拥有超过四万个程序并和每个手机 OEM 厂商都签有合约的 android 程序商店。这个限制很要命,因为没有一个 OEM 厂商愿意发布没有 Market 的 Android 手机(天朝是另类)。当然,在 Market 上发布应用是个非常简单的事情,没有什么审批的步骤,这个和苹果的 AppStore 刚好相反。

  6. 反分化合约. 外界几乎不清楚原来 OHA 的成员都签署了反分化的合约。但这个合约更可以被理解为不能发布没有通过 CTS 兼容测试的手机。(下面细说 CTS)(译者注:貌似中国移动已经被踢出 Android 的私有分支,是不是因为他们建立的 OMS 违反了这个协定呢?)

  7. 保密的发展蓝图. Android 的发展蓝图是很杯具的,到目前为止,公开发布的发展蓝图还停留在 2009 年的第一季度。如果想要看到内部的发展蓝图,你需要 Google 的赐福.

  8. Android 商标. Google 掌握着 Android 的注册商标和冠名权。任何想用 Android 品牌的厂商都需要得到Google的授权。简单的说:进 Google 的门,或者没有门。如果你要自己做 Android 分枝,你就全部靠自己了,比如你需要中国移动那么大的公司。

  Android 的传奇中还有个大篇章:CTS(兼容测试组),也就是 Google 一套测试 Android 手机是不是达到 Google 的标准。根据我们的线人消息,CTS 不仅仅测试软件的 API 部份,它还包括性能测试,硬件功能,设备设计,UI 用户界面需求,和机内打包的服务。CTS 决定了你可以添加额外功能,但不能从最基础的配置中削减功能。除了 CTS 以外,OEM 厂商还要和 Google 签订授权合同,这样他们才能打包 Google 的服务,比如 Gmail、YouTube 等等。

  CTS 限制了 OEM 定制弱化版 Android 手机的想法(译者注:山寨的机会啊!MTK、中微星,年底发布些低端 Android 手机吧!)。这也大大限制了 Android 开拓低端市场的能力。CTS 和向前兼容 4 万多个应用的事实,极大的挑战着 Google 想占领智能手机市场2位数的市场份额目标。这些限制,还有 Google 与 OEM 亦敌亦友的合作关系,使得 OEM 圈内掀起了建立 Android 基金的讨论。

  Google 的终极目标

  手握 Android,Google 的目标是为自己产生收入的服务提供一个稳定的平台。在当前,这个广告生意。但未来,Google 的目标在语音服务(几十亿没有数据服务的用户)和 Google Checkout(比如变成移动领域的 visa 卡)。但不管 Google 的终极目标是什么,我们应该意识到 Android 和 Windows Mobile、Mac OSX 或 PalmOS 相比,并没有开放多少。Android 是用开源来驱动商业议程的最聪明的案例之一。Android 骨子里并没有我们潜意识里所灌输的那么多不作恶思想。

20108 月26

一家公司发展的胡言乱语

终于一天早上,睁开极不情愿被睁开的眼睛,厌倦了文档、厌倦了没完没了的BUG、需求反复、项目延期,做出一个极为重要的决定:自己干。忽悠到2个人,于是创业开始。

第一个项目时间很紧张,是经过层层外包转包而来,尽管利润微薄,但是3个人在一起非常开心,我们做持续集成、做自动化测试,所有问题都经过集体讨论解决,很累,但每个人都很努力,因为大家的目的都是一致的。终于,项目按时完成,我们拿到自己挣到的第一笔钱。

由于第一个项目完成的很不错,我们很快接到了第二个项目,第二个项目比较大,公司很快又招了好几个人,公司的规模扩充到10人左右,因为只有一个项目,大家还是在一个项目组中共同努力,每个人都比较开心,尽管有些时候,也会为一些编程问题发生争吵,不过现在这还不是一个问题。我们很敏捷,第二个项目也完成的很好。

一年之后,公司发展顺利,已经扩大到50人左右,可以同时进行多个项目了。我们按照项目组建团队,每个团队都是全功能团队,包括了业务分析、程序员、测试和项目经理。项目经理对整个项目负责,同时向我汇报工作。我不再参加编程,因为我现在很忧愁:上个月,因为没有项目,那帮程序员天天聊QQ,这真让我苦闷!这个月突然又有了好几个项目,靠,那帮程序员不够用了!项目经理向我抱怨要增加人手。这种忧愁的生活让我生活像坐过山车,不踏实。我已经将自己的全部精力投入到市场和销售中去,我需要有市场计划,但是这个好像不受我的控制。不管怎么样,稳定的项目来源是我最关注的,我决定多找些市场和销售人员。

不过开发这块还是让我很放心的,每个项目组都很敏捷,每个项目组都很独立,没有争吵。但是也存在问题,因为项目来源很杂,基本上是只要赚钱就要做,所以所用的技术也很杂,java\c#\ruby\groovy\php,这对有些程序员来说是好事,扩大了他们的视野,但是也有些程序员很不喜欢,因为他们认为太泛,要不停的学习,他们喜欢找一门语言专下去。同时,我也发现了另外一些问题,就是项目质量也会受到技术的影响,其中就有个java的项目,因为是swing,很多人不熟悉,结果项目发生了延期。我很生气,找到了该项目的项目经理,项目经理很委屈:为什么我们什么项目都接,都不能选择性的接吗?这又回到了市场的原因,我们还远未强大到选择接项目的地步。同时,项目组之间产生了互斥的关系,项目经理们都很不愿意就技术问题互相帮助,A项目组以前有人做过PHP,B项目组现在做PHP,当B项目组需要帮组时,A项目组以工作忙进行了拒绝。就这个问题,我找一些骨干成员进行了讨论,决定成立相应的开发社区,这是个虚拟组织,定期组织相关的技术培训,这个组织跨越项目组的界限。但是后来我又发现,其实这样的效果也不是很好,技术积累很有限,一些项目做得相当初级,关键原因还是技术太杂,人员变动太大。这时,我决定找个专职的HR,从入职入手,只找符合公司标准的人才,要尽量找到聪明的程序员,为此,每次面试,我都尽量参加。

不管怎么说,除去市场和销售,我还是感到满意的。现在公司按照项目组建项目性团队,每个团队都是全功能团队,这样最重要的好处就是解决了工作中的工作相依性,BA\Dev\QA都在一个项目组中,有任何问题都可以进行非正式的沟通,这样如果需求有任何变化,整个项目组能够非常容易的进行调整,同时,通过将客户拉入项目,一方面能够迅速的进行反馈和拥抱变化,另一方面,客户也会承担一定的项目风险。

又一年过去了,公司取得了进一步的成功,这次的成功来自于市场,由于我们在电信行业进行了一系列成功的项目,使得我们的软件在电信行业小有名气,这时我做出了一个重要的决定:公司性质的转型,由项目性公司转为行业公司,我们只做电信相关的项目,同时技术栈也固定为c++和java。这样的好处是很明显的,首先就是通过进入细分行业,项目的来源比较稳定了,二来我们也比较容易的积累业务和技术。

同时,我发现了一些问题:项目规模开始扩大,有项目经理向我抱怨,他根本没有办法同时管理20多个人的项目团队,除去客户之外,他每天的工作就是在听组员汇报、处理冲突,忙不过来。我意识到,当项目组主要采取有机结构和非正式沟通时,项目管理者根本管理不过来超出20人的团队。同时,因为电信企业都拥有相同的业务背景和标准,他们的需求都非常相似,处于不同项目组的BA私下里经常沟通,互相请教问题,因为电信一系列的标准化,所以对BA也提出了要求,他们需要了解电信的这些业务和标准,而不是什么都去问客户。这样,很快,我将所有的BA都从项目组里抽出来,组成了单独的需求部门,项目经理的管理人数减少了,BA们可以聚集在一起解决问题,并对相似的问题进行整理,编写文档,业务知识能够很容易积累。但是这样做也有成本,就是Dev需要找BA确定需求,这样割裂了原有的工作相依性,但是因为需求比较稳定,Dev和BA的沟通成本尽管上升,但是总的次数减少了,这是我希望看到的。很多时候,BA通过文档与Dev沟通,这并没有想象中的那么坏,需求的稳定将这个坏处减少了,同时,项目经理总是可以组织BA和Dev不定期进行非正式沟通。

因为技术栈和需求稳定,我开始独立出单独的产品部,将通用的功能和底层技术栈进行封装。我开始重视文档,我要求产品部尤其重视文档和代码注释,文档在于产品部与项目部的沟通大部分要依靠文档,在不能进行非正式沟通的情况下,文档是唯一的选择,同时,文档在需要长期维护的代码里也非常关键,尽管结对,但是我不能认为每个人都能对所有代码都很熟悉,同时,项目规模的扩大,必然要划分模块组,结对也只是限定在模块组内,即只要有部门的划分,则必然需要文档来沟通!尽管很多人相信代码是最好的文档,但是很多人也忘记了一个很重要的前提,即好的代码才是最好的文档,我更愿意相信由于种种管理因素(部门之间的隔阂、成员之间水平的差异、进度)的影响,代码必然是逐渐腐化的,所以,在代码水平做不到很好时,就必须有文档,必须有注释!

公司继续发展,项目规模越来越大,我按照模块对项目进行了划分,每个模块对应于一个开发小组,每个小组一个负责人员,他们向项目经理负责,项目经理向我负责,同时项目部门依赖于产品部门。部门之间的沟通通常情况下依赖文档,由我统一进行两个部门之间的协调。

终于,发生了一件很严重的事情,交付的项目出现了一个严重的BUG,这让我很生气,开始追究责任,最后发现BUG出现在两个开发小组之间的一个接口约定,于是文档的要求进一步严格了,同时,公司成立了独立的质量保证部,对交付的项目进行统一测试。很自然,为了激励测试部的积极性,我规定按发现BUG的数量发放测试部的奖金。

于是,我发现,项目中到处都是文档、开发人员与QA对立严重,代码质量以安全第一,不愿重构,这一切,都是我当初最反感的。我怀念起最开始的日子。于是,我开始找敏捷咨询,通过外部力量进行部分的改进。。。。

很明显,我个人所理解的全功能团队(即所有成员都在一个部门里)将变得不太可行,而必然会产生某种形式的分组,不管是正式的还是非正式的,这种分组而产生的沟通成本可以认为是管理成本的一部分。@ronghao  JavaEye

20108 月18

UCenter忘记创始人密码的解决方法

修改data/config.inc.php文件

找到UC_FOUNDERPW,UC_FOUNDERSALT分别改成一下的字符串:

define(‘UC_FOUNDERPW’, ‘047099adb883dc19616dae0ef2adc5b6’);
define(‘UC_FOUNDERSALT’, ‘311254’);

修改完后,创始人密码变为:123456789

别忘记修改密码。:)

20108 月14

网站变灰代码

祖国多灾多难,希望网站变灰的次数越少越好。。

html { filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); }