作者归档:贺 利华

关于贺 利华

正在学习编程,享受编程 热爱文学,闲来读读《读库》 有思想,没理想 正在学会专注

Android上如何正确实现程序的联网,事关WIFI/CMWAP/CMNET

我想很多Android程序开发者都曾碰到过这样的问题,那就是如何让自己的程序在国内如此复杂的网络环境下顺利的接上网络,给我们的用户一个更好的体验。
从网络上一些已有的数据来看,Android手机用户群体的联网率普遍比较高,联网的方式非常多样,最多的还是使用WIFI,当然WIFI速度和资费上的优势让她成为了每一个玩机者的首选网络接入方式,但是很多的时候我们的条件并不是那么的尽如人意。例如在公车或地铁上,我们这些诸多支付不起3G资费的人士,首选的接入方式依然是CMWAP/CMNET,而由于国内网络的一些个问题,选择这两个或者其他的APN会有一些问题,问题就是我们可能需要设置代理才可以顺利登录网络。

以下是我自己在网络上寻找解决方案的时候,收集的一些信息,记录如下:

WAP是一中手机上网的协议。CTWAP是中国电信的WAP接入名称(China Telecom WAP),CMWAP是中国移动的WAP接入名称(China Mobile WAP),  UNIWAP是联通的WAP接入名称(china Unicom WAP),  另外CTNET/CMNET/UNINET同上。
CTWAP的上网网关:10.0.0.200:80
CMWAP的上网网关:10.0.0.172:80
UNIWAP使用的网关与CMWAP一致
我们可以通过MCC+MNC码的方式来进行简单的判断,但是实际上这种方式并不是完全正确的方法(自己在项目上碰到了该问题,因为实际情况中我们总是需要面对多种网络的情况)。这个时候其实我们可以稍微Hack一下,虽然Android并没有提供非常好的API,不过我们可以通过一些方法绕过去这里有一篇非常不错的文章http://www.javaeye.com/topic/565662 ,讲解得还算全面。
下面给出我自己的解决方案:
WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
  HttpClient httpClient = new DefaultHttpClient();
  if(!wifiManager.isWifiEnabled()){
   Uri uri = Uri.parse("content://telephony/carriers/preferapn"); //获取当前正在使用的APN接入点
   Cursor mCursor = this.getContentResolver().query(uri, null, null, null, null);
   if(mCursor != null){
    mCursor.moveToNext(); //游标移至第一条记录,当然也只有一条
    String proxyStr = mCursor.getString(mCursor.getColumnIndex("proxy"));
    if(proxyStr != null && proxyStr.trim().length() > 0){
     HttpHost proxy = new HttpHost(proxyStr, 80);
     httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);
    }
   }
  }
  HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 20 * 1000);
        HttpConnectionParams.setSoTimeout(httpClient.getParams(), 20 * 1000);
        HttpGet httpGet = new HttpGet(wrapGetUrl());
 try
        {
            HttpResponse response =httpClient.execute(httpGet);
            if(response.getStatusLine().getStatusCode() == 200){
             HttpEntity entity = response.getEntity();
             InputStream content = entity.getContent();
             BufferedInputStream bis = new BufferedInputStream(content);
             StringBuilder builder = new StringBuilder();
             int b;
             while((b=bis.read()) != -1){
              builder.append((char)b);
             }
             String resultStr = builder.toString();
               Log.v("result", resultStr);
}catch(Exception e){
}finally{
httpClient.getConnectionManager().shutdown();
}
该方案在打开WIFI/CMWAP/CMNET的情况下均单独测试成功。
同理HttpPost也可以如法炮制,下面附上一段代码:
int version = 3;
  Class versionClass = VERSION.class;
  TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
  WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
  String manufacturer = "";
  String device = "";
  String networkOperatorName = tm.getNetworkOperatorName();
  String IMEI = tm.getDeviceId();
  try {
   Field field = versionClass.getField("SDK_INT");
   version = (Integer) field.get(new VERSION());
   Class buildClass = Build.class;
   Field manu_field = buildClass.getField("MANUFACTURER");
   manufacturer = (String) manu_field.get(new android.os.Build()) + " ";
   Field deviceField = buildClass.getField("DEVICE");
   device = (String) deviceField.get(new Build());
  } catch (Exception e) {

  }
  HttpClient httpclient = new DefaultHttpClient();
  try {
   if(!wifiManager.isWifiEnabled()){
    Uri uri = Uri.parse("content://telephony/carriers/preferapn");
    Cursor mCursor = this.getContentResolver().query(uri, null, null, null, null);
    if(mCursor != null){
     mCursor.moveToNext();
     String proxyStr = mCursor.getString(mCursor.getColumnIndex("proxy"));
     if(proxyStr != null && proxyStr.trim().length() > 0){
      HttpHost proxy = new HttpHost(proxyStr, 80);
      httpclient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);
     }
    }
   }
   HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 20 * 1000);
            HttpConnectionParams.setSoTimeout(httpclient.getParams(), 20 * 1000);
   HttpPost httppost = new HttpPost("YOUR_POST_URL");
   List nameValuePairs = new ArrayList();
   nameValuePairs.add(new BasicNameValuePair("os", "Android"));
   nameValuePairs.add(new BasicNameValuePair("os_version",String.valueOf(version)));
   nameValuePairs.add(new BasicNameValuePair("device", manufacturer+device));
   nameValuePairs.add(new BasicNameValuePair("uuid", md5(IMEI)));
   nameValuePairs.add(new BasicNameValuePair("network", networkOperatorName));
   httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));
   HttpResponse response;
   response = httpclient.execute(httppost);
   httpclient.execute(httppost);
   StatusLine statusLine = response.getStatusLine();
   if(statusLine.getStatusCode() == 200){
Toast.makeText(this, R.string.updatesucceed, Toast.LENGTH_SHORT).show();
   }else{
Toast.makeText(this, R.string.updatefailed, Toast.LENGTH_SHORT).show();
   }
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  } catch (ClientProtocolException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
   updateFlag = true;
   httpclient.getConnectionManager().shutdown();
  }

再见轩尼诗

这次重看《月满轩尼诗》应该是进入大学以来惟一一次主动重看一部电影了,因为有了互联网,有了BT,我们有了太多太多廉价而高质量的电影选择,重看一部电影对每个人来说都不是一个很容易做的决定,当然我们不排除有些人对某部电影或者对大部分好电影是多看不厌的。不过对于自己来说,虽然有很多自己喜欢的电影,看完之后甚至都能写下一些文字来记录自己的感受,不过能让我有急切的重看的欲望的电影,到目前为止只有《月满轩尼诗》,我对电影懂得非常少,不知道什么情节的转乘之间的关系,也不知道电影如何去阐述人性和生活才叫有深度,更不知道一个电影的后台制作的种种微妙之处,我看电影图的就是一个简单,故事的单纯或者精妙。

《月满轩尼诗》就是一部剧情简单,演出杰出的影片。从片头到片尾都是一副简单的琐碎的小市民生活的气息,香港的街道因为小,因为有山道,因为有电车,所以才显得那么的有味道。街道上空的天气总是一股南方城市固有的阴霾,让人觉得很安逸。阿来到位的懒散,以及爱莲那种坚强而不失小女人的味道十足,让整个影片都充满了小人物闲散而坚强的生命力。老板娘,姨妈,青书的角色更是让这对恋人的生活更显真实和湿润。摄影师和阿旭的出现是一个必然,但是绝对没有给影片带来任何伤痕,即便是情感线条上的歪曲都不曾有。

喜欢张学友颓废而有趣的扮相,更喜欢汤唯那种超脱的释然和内里的倔强,真的很喜欢。呵呵

初窥Android游戏开发

最近一个月的时间熟悉了一下Android平台上的游戏开发流程.因为游戏相对于软件来说还是有一定区别的,可能跟系统提供的大部分API关系并不是特别的大,主要使用的可能就是系统的图形和声音,以及影像相关的API了。

最初学习Android,只是跟着官方提供的文档和示例一个个地去抄,抄完了之后自己写,主要就学习了一下Activity的一些简单的知识,主要是Activity之间的通信,Activity的生命周期,以及Activity Stack等等一些。对于游戏开发几乎是0,因为之前自己做的工作主要是Java中间件开发,使用的技术是JNI,并没有太多的接触过业务逻辑,对于功能的实现和集成还是非常的生疏。此次游戏开发委实长了不少的经验啊。从对Android的基础绘图API和线程的控制,状态机的维护,资源的释放等等,不一而足啊。

下面列举一下,近来学习的一些方面:

  • SurfaceView的使用,我想这大概是Android为了游戏开发人员做的一个特殊的基类,通过继承该类,并实现SufaceHolder.CallBack接口便可,通过SurfaceHolder.lockCanvas()获取画布,之后的各种绘制操作均可在当前画布上执行(Canvas.draw()系列方法),之后使用SurfaceHolder.unlockCanvasAndPost(Canvas canvas)方法,将绘制刷新到屏幕。
  • Thread.sleep(long millis)方法中的millis是跟系统时钟相关的,并不是真正的实际的时长,所以在这里需要做一个换算,使用多次Thread.sleep(long millis)方法来探测当前的换算比是多少,然后使用自己所需要的时长乘以该比值,设置给sleep()方法,才能得到正确的效果。
  • 关于游戏配置资源的读写,在游戏中,通常会有几种资源文件,图像,声音,XML关卡数据,游戏运行时配置文件(ini/properties).声音,图像,XML文件通常只需要使用系统默认的资源管理方式即可,如果程序中不需要使用文件名来进行配置的话,但是如果需要使用“logo.png”类似的名字来进行配置的话,可能系统提供的通过资源预编译后ID的方式就并不是那么尽如人意了,通常这种情况下,可以通过AssetManager.open(String fileName)来打开assets目录下的文件,可以使用子目录只是fileName就应该是”subfolderName/filename”这种格式了。在写入配置文件时,Android 为每个应用程序都提供了一个私有目录,”file://data/data/fullpackagename/files/”目录(fullpackagename是当前应用程序所在的包名,例如com.xixun.games),通过调用Context.openFileInput(String name, int mode)和Context.openFileOutput(String name, int mode)来获取输入和输出流。
  • Bitmap相关方法将可能导致error:OutOfMemory,这个确实是在手持设备上的一个问题,Android Dalvik VM 的实现中,只给了每个应用程序8M(该数据从互联网查得,并未验证是否属实)的内存用于图形,当程序为Bitmap对象申请超过8M内存时,将会抛出该错误(不是异常)并退出程序,并没有什么非常好的方法一定能帮你解决这个问题,通常我们应该养成一种编程行为习惯,那就是在Bitmap不再使用的时候立刻将资源回收(调用Bitmap.recyle()方法),因为Bitmap的实现是系统级别的API,VM对这种对象的管理并不会那么尽如人意,所以最好还是程序自身来管理,否则在后续的开发中,如果再次加入更大的资源将极为频繁地出现该问题。那么什么时候可能会出现该问题呢?8M的内存,我什么时候可能知道内存快要用了呢?对,你不知道,我也不知道,不过我们要预防,而且自己在编程时也可以简单的计算一下,如果你使用png图片的话,1.5M,到了程序中,如果你将所有的png均转化成了Bitmap对象,那么你的程序中使用的内存就至少会使用4.5M的内存。而且如果图片还带透明效果的话,那么就可能是6M的内存了。

以上就是近期的一些学习总结。下次将分享一个关于Android应用程序访问网络的问题。

据说明天有雨

北京近来连日的阴雨天气,让我心中甚是欣慰啊,对北京最为不满的不是生活节奏太过于忙碌,也不是压力过大,更多的是因为城市粉尘委实严重了些,所以每日早晨醒来第一件事情就是祈祷今天老天能否下几滴雨,把这脏兮兮的天空稍微涮一涮。近日每逢傍晚必有大雨,且是雷雨交加,甚和我意啊,虽然每日加班至夜深归家,出门能有一口清新的空气可以呼吸,心中仍是大慰啊。生活总是要继续的,偶尔的惊喜已经能让逐渐麻木的神经陡然兴奋片刻。

来到喜讯已经一个月了,上月18日从超图离职,回想一下,其实超图固然有超图的好,但是好总归好,不一定是自己的,好可能是别人的,还是现在的喜讯能带来的快乐会更多一些,虽然老大近日对进度的催促已经愈来愈频繁了,不过随着对Android的日渐熟悉,对进度的把握也是慢慢的有谱了。接下来对于3D的学习可能会持续一段时间,之前对于游戏的排斥也是没来由的,现在已经踏上了游戏开发的这一条道路,究竟将来如何还需好生琢磨一番。近日希望能培养自身对游戏的热爱,延续高中时代对于街机的那种狂热的感觉。

对于Java编程的种种固有的观点以及抽象的方法,在近日的Android代码优化过程中产生了一些疑问,天下没有通用的模式也没有通用的方法,一切道皆有道,一切道皆非道,以无意应有形,是为道也。我想也许编码也是这样的吧,虽然编码总是局限于某种语言,局限于某个平台,总是需要考虑到诸多关于内存管理的不同,甚至编码习惯的改变,不过这一切的一切总是不会逃脱帮人们解决问题的法门,通过既有的一些规则,实现与机器的对话,使用电流这一工具来达成我们的意愿,我想一切还是简单为妙,编程无非就是将电流脉冲的组织方式以人能读懂的方式编写出来,简单应是最美。

大道至简,我想未窥管中一斑的我,虽未能识大道但上道的心自是有的,或许积年之后,也能成一番小事的。