2012年9月27日星期四

CMWAP and CMNET (Android)

CMWAP and CMNET (Android)


# 描述
很多人都知道移动提供的两个不同的接入点,即:CMWAP 和 CMNET ,前者是为手机WAP上网而设立的,后者则主要是为PC、笔记本电脑、PDA等利用GPRS上网服务的。它们在实现方式上并没有任何差别,但因为定位不同,所以和CMNET相比,CMWAP便有了部分限制,资费上也存在差别。我们常说的手机包月上网,就是指的的包月的wap。为了从应用中区别两者的定位,移动对CMWAP作了一定的限制,主要表现在CMWAP接入时只能访问GPRS网络内的IP(10.*.*.*),而无法通过路由访问Internet。我们用CMWAP浏览Internet上的网页就是通过WAP网关协议或它提供的HTTP代理服务实现的。也就是需要通过移动GPRS网络唯一的一个WAP网关:10.0.0.172。CMNET拥有完全的Internet访问权,这里就不多说了,主要让我们来看看CMWAP。因为有了上面提到的限制,CMWAP的适用范围就要看WAP网关所提供的支持了。目前,移动的WAP网关对外只提供HTTP代理协议(80和8080端口)和WAP网关协议(9201端口)。

因此,只有满足以下两个条件的应用才能在移动的CMWAP接入方式下正常工作:
1. 应用程序的网络请求基于HTTP协议。
2. 应用程序支持HTTP代理协议或WAP网关协议。
而cmnet则不受任何的限制。其实就是说,通过cmwap的方式访问,需要走移动的网关通过http协议去连接,这样的后果就是速度会变慢,而通过cmnet来连接的,就是直接连接到Internet上的服务器,速度会比cmwap的快一些。
(呵呵!相信已经使用过的朋友,都会感受过两者的区别。 )

使用cmwap需要设置代理,而使用wifi和cmnet则不需要,设置后反而读不到数据,方法如下:
Android 中如何使用 CMWAP 联网
访问中国移动的Wap网络需要设置代理: Host:”10.0.0.172″ Port:80。有两种不同的HttpClient类,
a. 一个是Java.net包中的:HttpURLConnection,它设置代理代码如下:
     Proxy proxy=new Proxy(java.net.Proxy.Type.HTTP,new InetSocketAddress(“10.0.0.172″,80));
     HttpURLConnection connection=(HttpURLConnection) url.openConnection(proxy);
b. 还有一个是Apache的HttpClient: 
     DefaultHttpClient httpclient = new DefaultHttpClient();
     HttpHost proxy = new HttpHost(“10.0.0.172″, 80);
     httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

# 感想

以下内容纯属,个人之见侃侃而谈,看不过的可直接略过!
关于CMWAP的详细介绍参见百度百科
CMWAP 和 CMNET 只是中国移动人为划分的两个GPRS接入方式。
--
CMWAP就是中国移动的一大SB的败笔之作,很早之前投入很大的物力财力,本想在移动体系自建属于自己的网络(大型局域网),幻想让移动设备彼此间通信的流量都是在一个这样大局域网的完成的,这样可以节省下大量移动流量。虽然看似眼光很不错,很高 ……
但没想到这么一个美好的幻想,被移动互联网直接迅速地掩盖而过了,如今 CMWAP 过时了。

---
(完)

2012年9月24日星期一

Android开发笔记(六)

  • Android开发笔记(六)

     -- GPS定位开发总结



  • 接口说明
简单介绍Android SDK 提供的GPS定位接口,主要包含三要点,如下
* Android系统定位服务管理类LocationManager
android.location.LocationManager
You do not instantiate this class directly; instead, retrieve it through Context.getSystemService(Context.LOCATION_SERVICE).

* 请求更新定位信息requestLocationUpdates
void android.location.LocationManager.requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)

public void requestLocationUpdates (String provider, long minTime, float minDistance, LocationListener listener)

* 获取系统缓存最新的定位信息

Location android.location.LocationManager.getLastKnownLocation(String provider)

public Location getLastKnownLocation (String provider)


  • 示例说明
* 开启位置服务的监听

     private class MyLocationListner implements LocationListener {
          @Override
          public void onStatusChanged(String provider, int status, Bundle extras) {
          }
          @Override
          public void onProviderEnabled(String provider) {
          }
          @Override
          public void onProviderDisabled(String provider) {
          }
          @Override
          public void onLocationChanged(Location location) {
               // Called when a new location is found by the location provider.
               if(currentLocation!=null){
                    if(isBetterLocation(location, currentLocation)){
                         currentLocation=location;
                         showLocation(location);
               } else {
                    currentLocation=location;
                    showLocation(location);
               }
               //移除基于LocationManager.NETWORK_PROVIDER的监听器
               if(LocationManager.NETWORK_PROVIDER.equals(location.getProvider())){
                    locationManager.removeUpdates(this);
               }    
          }
     };
    
    
     private LocationListener gpsListener=null;
     private LocationListener networkListner=null;
     private void registerLocationListener(){
          networkListner=new MyLocationListner();
          locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 0, networkListner);
          gpsListener=new MyLocationListner();
          locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, gpsListener);
     }

* 位置精度的判断

     private static final int CHECK_INTERVAL = 1000 * 30;
     protected boolean isBetterLocation(Location location, Location currentBestLocation) {
          if (currentBestLocation == null) {
                    // A new location is always better than no location
                    return true;
          }

          // Check whether the new location fix is newer or older
          long timeDelta = location.getTime() - currentBestLocation.getTime();
          boolean isSignificantlyNewer = timeDelta > CHECK_INTERVAL;
          boolean isSignificantlyOlder = timeDelta < -CHECK_INTERVAL;
          boolean isNewer = timeDelta > 0;

          // If it's been more than two minutes since the current location,
          // use the new location
          // because the user has likely moved
          if (isSignificantlyNewer) {
                    return true;
                    // If the new location is more than two minutes older, it must
                    // be worse
          } else if (isSignificantlyOlder) {
                    return false;
          }

          // Check whether the new location fix is more or less accurate
          int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
                              .getAccuracy());
          boolean isLessAccurate = accuracyDelta > 0;
          boolean isMoreAccurate = accuracyDelta < 0;
          boolean isSignificantlyLessAccurate = accuracyDelta > 200;

          // Check if the old and new location are from the same provider
          boolean isFromSameProvider = isSameProvider(location.getProvider(),
                              currentBestLocation.getProvider());

          // Determine location quality using a combination of timeliness and
          // accuracy
          if (isMoreAccurate) {
               return true;
          } else if (isNewer && !isLessAccurate) {
               return true;
          } else if (isNewer && !isSignificantlyLessAccurate
               && isFromSameProvider) {
               return true;
          }
          return false;
     }
     /** Checks whether two providers are the same */
     private boolean isSameProvider(String provider1, String provider2) {
          if (provider1 == null) {
               return provider2 == null;
          }
          return provider1.equals(provider2);
     }
    
* 结束监听

     if(gpsListener != null) {
          locationManager.removeUpdates(gpsListener);
          gpsListener=null;
     }



  •  问题与总结
1. 系统开启GPS服务,都是比较耗电的;
2. (由于耗电)绝大部分用户默认不开启GPS服务;
3. 首次获取GPS定位信息,可能需要比较长的时间;
4. 经诸多实验结果得出结论在室内几乎无法获取GPS定位信息。

PS. 缺点2,3都是比较致命的。按理说,GPS服务是借助卫星通信定位的,在没有网络连接情况下也能用。

以上示例代码在室内几乎无法GPS定位(NETWORK或基站定位没问题),所以建议再真正的实际项目里,至少使用NETWORK和GPS两种不同的Location Provider实现定位功能。两种Provider提供了不同精度的定位服务,我们可以根据情况来使用。一般来说,先使用NETWORK来得到1个精度较差的位置,再使用GPS来得到更准确的位置。
参照Android官方提供的Dev Guide中,提供的一个关于GPS使用的时间线。如下:
“概括起来就是2句话:“快速反应,渐进式精确”。在实际的使用中也要根据自己的情况画1个时间线,好决定何时开始监听,何时结束监听。”


1. 注册两个LocationListener,同时监听GPS_PROVIDER和NETWORK_PROVIDER的定位结果;
2. 可以调用getLastKnownLocation获得一个Location值,作为最初的备选值;
3. 在用户可接受的时间内,接收从onLocationChanged返回的位置信息,并同旧值做比较选取最佳结果,需选取算法;
4. 判断筛选后的最佳结果,是否可接受。若可接受则返回给用户,否则告之无法定位等提示。



  •  参考资料


2012年9月20日星期四

Android开发笔记(五)


Android开发笔记(五)

    -- Android底层通信调研(部分)

> 资料
      Android 底层内核的实现源码,即Linux的Kernel代码,纯C实现的。来自网上https://android.googlesource.com/kernel/common
     需要使用Git版本工具从网上获取,具体操作可以在网上搜索ca。话说Linux的内核代码绝对是精良之作,值得学习的地方太多了:D ~!截图如下:

     
     
     Android开发的SDK源码,可以用Eclipse的“Android SDK Manager”帮助获取得到,都是Java源文件。分类很清晰,一看即明白那种所以不必多说了。截图如下:

     
     这里值得一提的是,这些源码也只是包装了下,对于更加底层一点的内容没包含在里面比如IoBridge类等libcore的代码。
     可参考在线源码http://source-android.frandroid.com/libcore/ 。

> Java-Socket的来源

以下内容,就简单地针对Socket源码(封装)实现逻辑的层次说明。


Socket/ServerSocket.java (java.net)
     => SocketImpl.java     (java.net)
          => PlainSocketImpl.java (java.net)
               => libcore.io.IoBridge (libcore.io)
              
              
SocketChannel.java (java.nio.channels)
     => SocketChannelImpl.java (java.nio)
          => libcore.io.IoBridge (libcore.io)


libcore OS => libcore.io.Posix.java => libcore_io_Posix.cpp => linux posix api  <sys/socket.h> 
libcore IoBridge => libcore/io/Posix.java => libcore_io_Posix.cpp => linux posix api  <sys/socket.h>

引用别人的一张描述的层次图,如下:
          
虽比较粗糙但可达到说明的意思勒)

     Posix.java通过JNI (libcore_io_Posix.cpp)调用linux的底层网络通信接口posix API,例如accept, sendto, recvfrom等。


     上面整理的内容都是概要说明,主要想帮助我们如何快速地上手、研究android底层结构与细节。至于细节的了解说明,需后续针对性地进行分析解读(源码)。后续再分享。

    (完)

2012年9月14日星期五

Android开发笔记(四)


Android开发笔记(四)
     -- Wifi-Direct开发问题总结篇

# Wifi-Direct开发概述
     在多台Android4.0+设备在没有外网辅助的环境下,实现互联组网,同时运行互发文件。这里提及的互联组网是自动的,包括创建,加入,退出,重建网络等多种操作,专门有一套自选举算法帮助完成。
     简单示例,假使现有设备A,B,C三台,该应用应用场景,如下:
     > 创建网络
          两种不同方式创建网络(Group群组),如下:
          1. A 主动创建群组createGroup,若成功则网络创建完成。
          2. 还有另一种情况,A主动连接connect B,B接受A连接请求且连接成功,则网络创建完成,同时B也加入了网络。定义此时设备A是Owner,作为网络创建者。不过,这种操作场景,也可能是在设备B端创建网络的,不一定设备A创建网络,这取决于Wifi-Direct协议这样定义:
          “The Wi-Fi Direct devices negotiate when they first connect to determine which device acts as an access point.”
          具体谁是Owner,影响元素包括,设备电量、信号强弱、机器是否处于省电模式等。PS. 在诸多的具体实验操作过程中得到验证的。

     > 加入网络
          按照以上第一种方式成功创建了网络Net,那么这时候设备B,C就分别可主动去发起请求连接网络Net,在设备A端只需确认接受请求。顺利的话则连接成功,那么此时三台设备ABC,就处于同一个网络,这时候彼此都可以传输数据,或是广播数据给同网络的设备。
          按照以上第二种方式创建了网络,则其他设备C加入网络方式类似,只要选择其中作为Owner的设备A或是B,对其发起连接请求。

     > 退出网络
          结合如上描述,退出网络同样有着两种情况,非Owner退出与Owner退出。
          1. 非Owner退出网络,只需要在对于设备做断开连接操作cancelConnect,如设备B或C退出网络,则设备A(Owner)的网络Net仍存在的。
          2. Owner退出网络,情况则比较复杂。设备A是网络Net的创建与拥有者,若退出了则网络即不存在了。在设备A退出之前需先通知设备B和C,然后让两者重新组建网络,由自选举算法推选出一个作为创建网络新的Owner,另一设备则加入网络(同上描述的加入网络)。 

     > 重建网络
          参照如上“退出网络”第二点描述。


# 遭遇的技术问题
Wifi-Direct(应用)开发过程遇到的技术问题,罗列如下:
1. 多设备间状态同步不及时

2. 系统广播Wifi-Direct消息不及时

3. Wifi-Direct消息缓存机制

4. socket(或channel)连接connect不成功
      “no route to host ... ”

(后续)

2012年9月13日星期四

高管VP会议什么样子!

高管VP会议什么样子!
     -- 公司VP会议初体验心得.20120828.AM


20120828的上午,已经是一个难忘的时间了,一次至今难得的体验经历,虽然这种经历不是令人所爱的。
经历情绪波折,百感交集(夸张了点:D),欲知详情,且听下文描述 ……

简单的故事情节如下:
     由于项目工作需要,有幸跟着Team Leader一起到公司高管会议报告项目进展与Demo演示。而我们其他兄弟几个仅是赶场旁听,就坐在后面的一排的角落位置。

     公司大老板对于报告表示了不满,提出了几点批评与建议。直接的说其情况就是我们几个兄弟在后面看着TL在上面挨着老板的批评,场面挺尴尬的,我们在会后都有些佩服TL可以这样挺过来呢,不容易了啊!我们心里当然都很不爽不开心啊,毕竟这段时间确实努力地辛苦地付出。可能方向错误的原因吧。大老板与TL之间其实还隔着两三层的组织关系呢,领导的想法与我们的行动达不到一致。
     其实,这过程我心里有些紧张的,有这样的假设,上面TL若是我,我将什么个情况 …… 负责的心情啊。

     由于PPT报告结果不理想(糟糕的),所以我们没有做Demo演示,拿不出台面有两个原因:
          1. Demo程序过于简单,演示效果不明显。
          2. Demo的功能点与CEO & VP们的期望不一致。

     最后,总结几点关于CEO以及VP们对于项目报告的期望点在哪里,包括:
          1. 功能新颖的Demo演示。个人觉得这里可以不必太追求程序稳定。
          2. 配合简洁的(加漂亮的)PPT演说。功能点说明清晰,可借鉴乔布斯的产品发布演示视频。
          3. 搜集罗列说明下业界相关的产品情况与技术进展。
          4. 尽量不要展望(画饼),要低调。不过,可私下与CEO交流。
          5. 涉及到开发实现的东西,一律不必在PPT或会议提及。

     作为基础研发的项目,老邹(CEO)强调要基于未来一年两年之内的产品发展方向,而非从事当前产品研发,但可从现有产品中存在的问题,难以一时填补的问题摄取出发点,着眼于未来一两年时间。

     有幸见识到公司CEO主持会议的风范。但也总听说起他一贯的霸气,有点‘独裁’的说辞,其他的较多的旁听的份儿。小辈子的我们,确实可以从中学习到很多东西。以上描述到的这些心得总结,算是其中一小部分。


     后续努力,同事们再接再厉吧!

--
     20120910.PM 续
     “今天下午在项目汇报研讨会上,我们的项目汇报进展顺利,接近满意效果。能拿出来演示的基本都顺利在DEMO演示效果表现出来,而且我们TL演讲效果良好,所以整个项目汇报效果比较良好。至少我们这么觉得,包括身边的其他开发同事以及HR同事。”

(完)

2012年9月11日星期二

PC为何没落?苹果为何崛起?(转)



转载一篇IT业界关于“PC为何没落?苹果为何崛起?”这么话题的文章。内容尤佳,值得分享。


有人提问:


PC为何没落?苹果为何崛起?

  1. IBM将PC业务卖给联想,是PC没落的标志。
  2. Wintel联盟将利润都吸干了,PC说是开放,PC硬件厂商其实,只是Wintel市场上的硬件APP而已。IBM看透了,终于放弃了自己开创的IBM PC事业。
  3. 苹果APP激发了更多成本更低的程序员加入,因为成本低,苹果APP变得有利可图。
  4. 一定要让自己的核心合作伙伴有利可图。
  5. 康柏、DEC、AST无数非常优秀的PC倒下了。哪里还会有聪明的人、聪明的钱愿意投身Wintel事业呢?
  6. 苹果打败了Wintel,不是封闭打败了开放。是轻巧的有利可图打败了大投资的风险重重。
转载内容的重点如下,@tinyfool(sina)这么回复的,如下:

本来准备写一个非常详尽的答案,但是查了查资料发现想全部表达,简直可以写一本微型计算机简史了,于是TODO里面加上写本《微型计算机简史-果粉观点》,感谢刘韧大哥的激发。我先做一个简单回答。

1、IBM是PC的发起者,但是PC行业的实际领导者是微软。Gates当年有一个巨大无比的Vision,叫做人人桌面上有个电脑,现在已经实现。我觉得以Gates的想象力,已经没有Vision了,所以行业停滞不前。

很多人以Gates为神,但是我认为微软是一个Vision有限,市场份额为王的公司,这样的公司适合守业,不适合开拓。举例说明,Netscape最早做浏览器的时候,微软不为所动,但是大火后,微软成功超过。再有一个很好玩的例子是,Jobs被赶走后,10来年,几乎每年Mac都升级大版本,我认为苹果是有产品文化的,不是市场份额动物。而微软在IE6独揽天下以后,不再更新,甚至解散开发团队达到三年之久,直到Firefox达到将近10%市场份额的时候,才重新组建IE团队。这充分说明微软是市场份额动物。

2、一般人从Jobs第二次回到苹果开始了解Jobs,认识多有偏颇。不知道他当年创建的苹果,是个人电脑行业的真正发端(使这个行业形成,而不说是第一台),不知道当年Apple II有多么普及。也不知道Mac在1985年的体验,跟1995年PC上的Windows都可以说更好一些。一般世人诟病Jobs的Mac也抄袭自施乐实验室。但是我愿意把Jobs比作盗火者,他把科学家的玩物带给了世人。如果你看过1984年的Mac和1985年的windows 1你就明白Jobs说微软一直在向他学习是什么意思了。实际上微软在1995年才跟上了1985年的Mac。
(本来有图片为证,不过目前再donews这边权限不够贴不出来)

3、Jobs离开苹果创建的NeXT一直被业界低估,但是你仔细研究历史就会发现,WWW的创始人Tim Berners-Lee发明WWW就是在NeXT上。很多人可能会以为这是一个巧合,我以前也不会特别把这个当作一个要点。但是当对Cocoa也就是目前iOS的开发框架了解后,我才知道它来自于NeXT当时做的面向对象UI库,NeXTSTEP,这个东西当年的水平,放到现在也是不可小视的。事实上,Tim Berners-Lee应该不是一个开发高手,在1990发明WWW时,PC上的开发工具还非常简单原始,面向对象用的人很少(Turbo C++ 1990年才发布)。而如果你把NeXTSTEP和Turbo C++比较的话,简直一个就是宇宙飞船,一个就是拖拉机。

4、Jobs回到苹果的时候,提到他在施乐学到的其实是三样东西(图形界面、面向对象和网络)。大多数人把Jobs的两次苹果经历割裂来看,我认为其实是统一的,表面上他第二次回来,重点不在Mac上,在iPod,后来在iPhone和iPad。这其实是外行观点。本质上,iPod/iPhone/iPad都是计算机。Jobs的Vision,虽然可能没有说清楚过,但是我认为从第一次苹果时代到第二次苹果时代,都没有改变。就是把从施乐盗来的火,分享给大众。细节点说,就是生产更好用,更容易使用的电脑。

5、苹果的Vision现在是什么?我认为是移动互联网改变世界。iPhone已经做到了,我们不多提了。我想单独提iPad。iPad是功能和用户场景最接近个人电脑的东西,一般人在个人电脑上的90%的操作,在iPad都可以完成。剩余10%并不是iPad的缺点而是优点。

当我给1岁半的女儿一个iPad,然后她在完全没有指导的前提下,玩了几天就会玩,玩到现在完全自得其乐。我得到的体会是,iPad砍去了一些功能,但是得到了更多,它成为了一个任何人都能学会的电脑。孩子可以学会,老人可以学会,人人可以学会。这样人类就从gates的一个桌子上一个电脑,轻松的进化到了,每个人手里一个电脑。对于一个五口之家,以前最多有两台电脑,现在可以有五台,而且更好用。

IT界很多人难以理解这个观点因为他们的工作用iPad无法完成,或者无法优雅完成。但是我认为这才是未来,PC/Mac将是专业人士的专宠,从科学家来,回到科学家专业人士那里去。普通人,音乐家、老师、孩子、医生,其他的认为不以IT为业的人需要的就是iPad这样的电脑。

这样的Vision足以支撑苹果再伟大很多很多年。


(内容转自国内某问答社区DoNews原文地址

(完)

2012年9月4日星期二

Android开发笔记(三)


Android开发笔记(三)
     -- 问题积累篇

只简述三个简单的问题,”问题清楚了同时解决了,自然简单了。“

1. Android Wifi-Direct
    中文说法为wifi直连,即目前在比较高端的移动终端设备Android系统4.0+以上支持这款技术功能。Wifi-Direct,设计初衷是允许移动通讯设备(目前主要是手机或Pad)在无公网的条件下实现P2p传输的前沿技术,该技术的实现是基于Wifi技术至上。详情可以参见百度百科维基
     这里提一个之前遇到的疑问点,很简单。刚开始从事wifi-direct开发时了解到,一般情况下两台设备D1、D2(均支持Wifi-Direct),若D1连接D2成功,则D2作为热点(Owner端)接收了D1的连接请求,即被连接者作为热点。但是开发与调试过程,偶尔发现一种情况,两者连接成功了,确是D1(连接者)作为热点了!一时困惑也没去理会,直到后来有时间了去查询情况方知真相,(引文)如下:    

The Wi-Fi Direct devices negotiate when they first connect to determine which device acts as an access point.

PS. 可以参考该链接页面的描述。

2. Java类成员构造顺序
由于Java语言允许类成员的声明与定义(包括初始化)放在一起,所以我们(新手)经常喜欢并习惯这么做。但是,这样做容易导致程序异常错误,最典型的就是使用null空指针的异常错误。出错的原因在于,作为新手你估摸不准那些涉及到的对象,彼此有相互引用,他们的构造顺序(构造顺序是Java虚拟机帮你默默完成的)。当然高手就不然了,正所谓高手写的程序先于在机器允许已在心中运行了。不如,以下举一个简单的示例吧,伪码描述如下:
public class MainActivity extends Activity {
     private Fragment frag = new XXXFragment(...);
     public Fragment getFragment {
          renturn frag;
     }
 
     private Service service = new XXXService(this);
}

class XXXService extends Service {
     private Fragment frag
     XXXService(MainActivity activity) {
          frag = activity.getFragment();//Maybe frag is null here.
          ...
     }
 
     void run() {
          ... use frag... // Maybe throw nullPointerException here.
     }
}

3. Android Service子类
Android系统服务Service类型需定义为public(同时与对应所在的java源文件同名,该点属于Java开发语言规定的),用时需要在AndroidManifest.xml文件声明,如下:
// NetworkService.java
public class NetworkService extends Service {
     ...
}
    <uses-permission android:name= "android.permission.SERVCE" />
AndroidManifest.xml

...
    <application
               ...
        <!-- 声明下面的服务 -->
        <service android:enabled="true" android:name= ".NetworkService" />
               ...
    </application >
...


 --- 更新于20120915 --
5. 官方针对Handler子类定义的说明(注意点),如下:
Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread's MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.
---

4. 不应该在UI主线程加锁,且加锁无效果。
          //lockSendFile.lock(); // 貌似在UI主线程加锁无效。

(希望后续更新,待续……)