[转载]SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础 - Bēniaǒ - 博客园

mikel阅读(1034)

[转载]SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础 – Bēniaǒ – 博客园.

在前一篇博文中我们学习到了一些关于地理信息的基础知识,也学习了空间参照系统,既地球椭球体、基准、本初子午线、计量单位、投影等相关理论知 识,我们可以使用这些空间参照系统组件来定义一系列应用于地球空间上的几何图像来表示地理空间中的特定功能,表示着地球上一个一个特定的位置点。 本篇主要介绍地理空间索引的概念以及微软SQL Server 2008 R2中的空间索引的应用。

 

一、空间索引

空间索引是指依据空间对象的位置和形状或空间对象之间的某种空间关系按一定的顺序排列的一种数据结构,其中包含空间对象的概要信息。作为一种辅 助性的空间数据结构,空间索引介于空间操作算法和空间对象之间,它通过筛选作用,大量与特定空间操作无关的空间对象被排除,从而提高空间操作的速度和效 率。

 

来至百科的定义:为便于空间目标的定位及各种空间数据操作,按要素或目标的位置和形状或空间对象之间的某种空间关系来组织和存储数据的结构。

 

关于更多空间索引的知识点需要深入了解EPSG和SRID,本文主要介绍基于微软SQL Server 2008的空间数据特性实现介绍,故不详细介绍这些基础理论知识点,有兴趣的可以访问文末的推荐资料链接,了解更多关于EPSG核SRID的相关知识。

 

SQL Server 2008中的空间数据类型作为CLR系统类型来执行。SQL Server 2008增加了数据库中的CLR类型的最大规模,提高了原来在SQL Server 2005中的8000字节的限制,这使得它可以存储非常复杂的空间数据元素,例如通过许多点定义的多边形。通过在关系表中存储空间数据,SQL Server 2008 使得可以结合空间数据到其他任何商业数据类型中去;这消除了对维护一个单独的只用于空间数据存储的维护要求,并使得可以做高性能查询,它不需要结合从多个 外部来源获得的数据。

 

在SQL Server 2008中对空间索引的支持进一步增强了对空间数据的查询操作。你可以用一个集成在SQL Server数据库引擎中的适合的多级网格索引来检索空间数据。空间索引包含一个基于网格的层级,在其中每一级索引又细分为由上一级所定义的网格区域。下 图为官方给出的一个空间索引的概率模型图:

 

SQL Server查询优化器会作出基于成本的决策,决定对给定的查询使用哪种索引,并且因为空间索引是数据库引擎的一个完整部分,可以作出关于是否使用特殊的空间索引的基于成本的决策,就像其它索引一样。

 

二、SQL Server 2008的空间参考系统

微软SQL Server 2008的空间系统表(实际上是一个系统视图)”sys.spatial_reference_systems”中存储了详细的支持所有大地测量空间参考 系统标准,该表中的每一行数据对应于一个唯一的空间参考系统,可以基于这些空间参考系统在SQL Server 2008中来定义任何形式的空间数据。

select * from sys.spatial_reference_systems

 

系统表“sys.spatial_reference_systems”中实际上仅仅只是存储了应用于SQL Server 2008中地理参照坐标的参考系统,除了空间参考系统可以使用这张表中列出的参考系统标准,也可以基于这些标准来定义基于任何地理投影的空间数据。

 

 三、空间参照文本格式(WKT)

Well-Known Text (WKT) Representation of Spatial Reference Systems:顾名思义用众所周知的文字形式来描述看见坐标参考系统,它与EPSG(2005年已经换名)的坐标文字表述模型一致。WKT字符串应用于 确定空间参考系统的组成部分的描述,其定义主要包含以下10项内容:

1、一个总体的坐标系名

  2、一个地理图形坐标系统名
3、一个基准面定义
4、一个椭球体的名字。长半轴(semi-major axis)和反扁率(inverse flattening)
5、本初子午线(prime meridian)名和其与格林威治子午线的偏移值
6、投影方法类型(如横轴莫卡托)
7、投影参数列表(如中央经线等)
8、一个单位的名称和其到米和弧度单位的转换参数
9、轴线的名称和顺序
10、在预定义的权威坐标系中的编码(如EPSG)

 

select well_known_text
from sys.spatial_reference_systems
where authorized_spatial_reference_id=4326
and authority_name=EPSG

 

基于sys.spatial_reference_systems表定义了多种不同的空间参照系统的WKT描述,如上SQL语句查询出的是我们平时在地图应用开发中使用最多的EPSG:4326空间参照系统标准,则对于的WKT如下:

GEOGCS[“WGS 84”,
DATUM[“World Geodetic System 1984”,
ELLIPSOID[“WGS 84”, 6378137, 298.257223563
]],
PRIMEM
[“Greenwich”, 0],
UNIT
[“Degree”, 0.0174532925199433]
]

 

以上KWT字符串确定了EPSG:4326空间参照系统的组成部分元素描述,其主要使用了四种空间配置,分别为:基准(Datum)、椭圆 (Ellipsoid)、本初子午线(Primem)和Unit(计量单位)。关于这些空间参考系统参数在上一篇文中中介绍过,这里就不在做重复介绍,以 上KWT字符串表述的意思大致是这样的:“WGS 84坐标系,采用1984年订立的世界地理坐标系统标准,地理标准半径为:6378137……..”。

 

四、SQL Server 2008的空间索引

SQL Server 2008 引入了对空间数据和空间索引的支持。“空间索引”是一种扩展索引,允许您对空间列编制索引。空间列是包含空间数据类型(如 geometry 或 geography)数据的表列。本节中的主题介绍了空间索引。

 

在 SQL Server 2008 中,空间索引(存储在:sys.spatial_indexes表中)使用 B 树构建而成,也就是说,这些索引必须按 B 树的线性顺序表示二维空间数据。因此,将数据读入空间索引之前,SQL Server 2008 先实现对空间的分层均匀分解。索引创建过程会将空间分解成一个四级“网格层次结构”。这些级别指的是“第 1 级”(顶级)、“第 2 级”、“第 3 级”和“第 4 级”

 

每个后续级别都会进一步分解其上一级,因此上一级别的每个单元都包含下一级别的整个网格。在给定级别上,所有网格沿两个轴都有相同数目的单元 (例如 4×4 或 8×8),并且单元的大小都相同。下图显示了网格层次结构每个级别的右上角单元被分解成 4×4 网格的情况。事实上,所有单元都是以这种方式分解的。因此,以此为例,将一个空间分解成四个级别的 4×4 网格际上会总共产生 65,536 个第四级单元。针对空间索引进行的空间分解与应用程序数据使用的度量单位无关。

 

 

        untitled

 

 

网格层次结构的单元是利用多种 Hilbert 空间填充曲线以线性方式编号的。然而,出于演示目的,这里使用的是简单的按行编号,而不是由 Hilbert 曲线实际产生的编号。在下图中,几个表示建筑物的多边形和表示街道的线已经放进了一个 4×4 的 1 级网格中。第 1 级单元的编号为 1 到 16,编号从左上角的单元开始。

 

        untitle22d

 

沿网格轴的单元数目确定了网格的“密度”:单元数目越大,网格的密度越大。例如,8×8 网格(产生 64 个单元)的密度就大于 4×4 网格(产生 16 个单元)的密度。网格密度是以每个级别为基础定义的。网格配置单元数目低 :4X4 =16,中8X8 = 64,高16X16 =256,默认设置所有级别都为

 

您可以通过指定非默认的网格密度控制分解过程。例如,在不同级别指定不同网格密度对于基于索引空间的大小和空间列中的对象来优化索引可能非常有 用。空间索引的网格密度显示在 sys.spatial_index_tessellations 目录视图的 level_1_grid、level_2_grid、level_3_grid 和 level_4_grid 列中。

 

将索引空间分解成网格层次结构后,空间索引将逐行读取空间列中的数据。读取空间对象(或实例)的数据后,空间索引将为该对象执行“分割过程”。分割过程通过将对象与其接触的网格单元集(“接触单元”)相关联使该对象适合网格层次结构。从网格层次结构的第 1 级开始,分割过程以“广度优先”方式对整个级别进行处理。在可能的情况下,此过程可以连续处理所有四个级别,一次处理一个级别。

 

注:以上内容截取SQL Server 2008帮助文档,详细请查看联机帮助文档。
五、相关资料

[1]、EPSG:http://en.wikipedia.org/wiki/European_Petroleum_Survey_Group

[2]、SRID:http://en.wikipedia.org/wiki/SRID

[3]、WKT:http://en.wikipedia.org/wiki/Well-known_text

[4]、空间索引概论:http://www.cnblogs.com/terryfeng/archive/2009/05/27/1490803.html

[转载]深入浅出空间索引:2 - zhanlijun的专栏 - 博客频道 - CSDN.NET

mikel阅读(856)

[转载]深入浅出空间索引:2 – zhanlijun的专栏 – 博客频道 – CSDN.NET.

第一篇讲到了传统的索引如B树不能很好的支持空间数据,比如点(POI等)、线(道路、河流等)、面(行政边界、住宅区等)。本篇将对空间索引进行简单分类,然后介绍网格索引。(深入浅出空间索引1http://blog.csdn.net/zhanlijun/article/details/13171539

一、空间索引有哪几种?

  传统索引使用哈希和树这两类最基本的数据结构。空间索引虽然更为复杂,但仍然发展于这两种数据结构。因此可以将空间索引划分为两大类:1)基于哈希思想,如网格索引等;2)基于树思想,有四叉树、R树等。

 

二、网格索引

  哈希是通过一个哈希函数将关键字映射到内存或外存的数据结构,如何扩展到空间数据呢?

2.1. 网格索引原理

  扩展方法:对地理空间进行网格划分,划分成大小相同的网格,每个网格对应着一块存储空间,索引项登记上落入该网格的空间对象。

  举个例 子,我们将地理空间进行网格划分,并进行编号。该空间范围内有三个空间对象,分别是id=5的街道,23的河流和11的商圈。这时候我们可以按照哈希的数 据结构存储,每个网格对应着一个存储桶,而桶里放着空间对象,比如对2号网格,里面存储着id=5的空间对象,对35号网格,桶里放着id=5和 id=23的空间对象。

 

  假如我们要查询某一空间范围内有哪些空间对象,比如下面的红框就表示空间范围,我们可以很快根据红框的空间范围算出它与35号和36号网格相交,然后分别到35号和36号网格中查找空间对象,最终找出id=5和id=23的空间对象。

2.2. 网格索引缺点

1)索引数据冗余

  网格与对象之间多对多关系在空间对象数量多、大小不均时造成索引数据冗余。比如11号商圈这个空间对象在68,69,100,101这4个网格都有存储,浪费了大量空间。

2)网格的大小难以确定

  网格的划分大小难以确定。网格划分得越密,需要的存储空间越多,网格划分的越粗,查找效率可能会降低。对于图a,这个查询需要查询4个网格,由于4个网格覆盖了整个空间,因此这个查找其实是将空间范围内所有的点数据都遍历一遍,失去了索引的意义。

 

3)很多网格没有数据

  空间数据具有明显的聚集性,比如POI只在几个热点商贸区聚集,在郊区等地方很稀疏,这将导致很多网格内没有任何空间数据。

 

下一节将介绍四叉树。

[转载]深入浅出空间索引---(1)为什么需要空间索引 - zhanlijun的专栏 - 博客频道 - CSDN.NET

mikel阅读(886)

[转载]深入浅出空间索引—(1)为什么需要空间索引 – zhanlijun的专栏 – 博客频道 – CSDN.NET.

一、问题

      先思考个常见的问题:如何根据自己所在位置查询来查询附近50米的POI(point of interest,比如商家、景点等)呢(图1a)?

每个POI都有经纬度信息,我用图1b的SQL语句在mySQL中建立了POI_spatial的表,其中lat和lng两个字段来代表纬度和经度。为后续分析方便起见,我人造了40万个POI数据。

二、传统的解决思路

方法一:暴力方法

      该方法的思路很直接:计算位置与所有POI的距离,并保留距离小于50米的POI。 

      插句题外话,计算经纬度之间的距离不能像求欧式距离那样平方开根号,因为地球是个不规整的球体(图2a),按最简单的完美球体假设,两点之间的距离函数应该如图2b所示。

       该方法的复杂度为:40万*距离函数。我们将球体距离函数写为mySQL存储过程distance,之后我们执行查询操作(图3),发现花费了4.66秒。

        该方法耗时的原因显而易见,执行了40万次复杂的距离计算函数。

方法二:矩形过滤方法

      该方法分为两部:

       a)先用矩形框过滤(图4a),判断一个点在矩形框内很简单,只要进行两次判断(LtMin<lat<LtMax; LnMin<lng<LnMax),落在矩形框内的POI个数为n(n<<40万);

       b)用球面距离公式计算位置与矩形框内n个POI的距离(图4b),并保留距离小于50米的POI

矩形过滤方法的复杂度为:40万*矩形过滤函数 + n*距离函数(n<<40万)。

根据这个思路我们执行SQl查询(图5)(注: 经度或纬度每隔0.001度,距离相差约100米,由此推算出矩形左下角和右上角坐标),发现过滤后正好剩下两个POI。

此查询花费了0.36秒,相比于方法一查询时间大大降低,但是对于一次查询来说还是很长。时间长的原因在于遍历了40万次。

方法三:B树对经度或纬度建立索引

方法二耗时的原因在于执行了遍历操作,为了不进行遍历,我们自然想到了索引。我们对纬度进行了B树索引。

此方法包括三个步骤:

a)通过B树快速找到某纬度范围的POI(图6a),个数为m(m<40万),复杂度为Log(40万)*过滤函数;

b)在步骤a过滤得到的m个POI中查找某经度范围的POI(图6b),个数为n(n<m),复杂度为m*过滤函数;

c) 用球面距离公式计算位置与步骤b得到的n个POI的距离(图6c),并保留距离小于50米的POI

执行SQL查询(图7),发现时间已经大大降低,从方法2的0.36秒下降到0.01秒。

三、B树能索引空间数据吗?

这时候有人会说了:“方法三效果如此好,能够满足我们附近POI查询问题啊,看来B树用来索引空间数据也是可以的嘛!”

那么B树真的能够索引空间数据吗?

1)只能对经度或纬度索引(一维索引),与期望的不符

我 们期待的是快速找出落在某一空间范围的POI(如矩形)(图8a),而不是快速找出落在某纬度或经度范围的POI(图8b),想象一下,我要查询北京某区 的POI,但是B树索引不仅给我找出了北京的,还有与北京同一维度的天津、大同、甚至国外城市的POI,当数据量很大时,效率很低。

2)当数据是多维,比如三维(x,y,z),B树怎么索引?

比如z可能是高程值,也可能是时间。有人会说B树其实可以对多个字段进行索引,但这时需要指定优先级,形成一个组合字段,而空间数据在各个维度方向上不存在优先级,我们不能说纬度比经度更重要,也不能说纬度比高程更重要。

3)当空间数据不是点,而是线(道路、地铁、河流等),面(行政区边界、建筑物等),B树怎么索引?

对于面来说,它由一系列首尾相连的经纬度坐标点组成,一个面可能有成百上千个坐标,这时数据库怎么存储,B树怎么索引,这些都是问题。

既然传统的索引不能很好的索引空间数据,我们自然需要一种方法能对空间数据进行索引,即空间索引。

下节将对空间索引分类体系、原理、优缺点及数据库支持情况进行阐述。

[转载]iOS- 用MapKit和CoreLocation 来实现移动设备(地图与定位) - 清澈Saup - 博客园

mikel阅读(894)

[转载]iOS- 用MapKit和CoreLocation 来实现移动设备(地图与定位) – 清澈Saup – 博客园.

1.前言                              

发现在很多的社交软件都引入了地图和定位功能,如果我们要想实现这两大功能,需要利用到两个框架:MapKitCoreLocation
我们先来看看CoreLocation框架:
它可以使用硬件设备来进行定位服务,不需要地图,精度相对略差,省电。
MapKit框架:
能够使应用程序做一些地图展示与交互的相关功能,必须有地图,精度相对较高,费电。
下面我就说说它的能实现的一些常用功能

2.地图常见操作

@property (weak, nonatomic) IBOutletMKMapView *mapView;

2.1. 标记用户当前位置、跟踪用户位置                        

1     // 标记用户当前位置
2     // 跟踪用户位置
3     [_mapView setUserTrackingMode:MKUserTrackingModeFollow];

 

 

2.1. 地图的类型                                  

可以通过设置MKMapView的mapViewType设置地图类型
MKMapTypeStandard 普通地图
MKMapTypeSatellite 卫星云图
MKMapTypeHybrid普通地图覆盖于卫星云图之上
 // 地图类型
    [_mapView setMapType:MKMapTypeHybrid];

 

2.2.通过代理的方式可以跟踪用户的位置变化                     

 

2.2.1.mapViewWillStartLoadingMap: 当地图界面将要加载时调用

 

2.2.2.mapView:viewForAnnotation: 当地图上有大头针时调用

 

2.2.3.mapViewWillStartLocatingUser:当准备进行一个位置定位时调用

 

2.2.4.mapView:regionDidChangeAnimated: 当显示的区域发生变化时调用

2.2.5.mapView:didUpdateUserLocation:当用户位置发生变化时调用

1 // 通过代理的方式可以跟踪用户的位置变化
2     _mapView.delegate = self;

 

复制代码
#pragma mark - 地图代理方法
#pragma mark 会频繁调用,非常费电
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{

    // 显示用户周边信息 拉近地图 设置地图显示区域

CLLocationCoordinate2D center = userLocation.location.coordinate;

MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 100.0, 100.0);

 

[mapView setRegion:region animated:YES];

}
复制代码

 

2.3.添加默认大头针(地标)                              

1.通过MapView的addAnnotation方法可以添加一个大头针到地图上
2.通过MapView的addAnnotations方法可以添加多个大头针到地图上
头文件里说明:- (void)addAnnotation:(id <MKAnnotation>)annotation;
4.说明需要传入一个遵守了MKAnnotation协议的对象
1     MyAnnotation *annotation2 = [[MyAnnotation alloc] init];
2     annotation2.coordinate = CLLocationCoordinate2DMake(30, 106);
3     annotation2.title = @"重庆";
4     annotation2.subtitle = @"重庆详细描述";
5     annotation2.imageName = @"head0";
     [_mapView addAnnotation:annotation2];

 

2.4.添加自定义大头针(重用地标)                            

复制代码
 1 #pragma mark 自定义大头针视图
 2 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
 3 {
 4     // 判断annotation参数是否是MyAnnotation
 5     // 如果不是MyAnnotaion说明是系统的大头针,无需做处理
 6     if (![annotation isKindOfClass:[MyAnnotation class]]) {
 7         // 使用系统默认的大头针
 8         return nil;
 9     }
10     
11     // 可重用标示符
12     static NSString *ID = @"MyAnno";
13     
14     // 查询可重用的大头针
15     MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:ID];
16     
17     // 如果没有找到,再去实例化大头针
18     if (annoView == nil) {
19         annoView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
20         
21         // 自定义大头针视图,如果要接受用户响应,需要设置此属性
22         annoView.canShowCallout = YES;
23     }
24     
25     // 设置大头针
26     annoView.annotation = annotation;
27     // 转换成MyAnnotation
28     // 设置大头针视图的图像
29     MyAnnotation *anno = annotation;
30     annoView.image = [UIImage imageNamed:anno.imageName];
31     
32     NSLog(@"自定义大头针");
33     
34     return annoView;
35 }

复制代码

 3.定位                              

 

   // 定位服务管理器
    CLLocationManager *_locationManager;

    // 使用地理编码器
    CLGeocoder          *_geocoder;

 

 

 

 

 1. 在开发LBS类的应用时,获取用户定位信息之前,一定要判断一下定位服务是否允许  

 

locationServicesEnabled

1     
2     if (![CLLocationManager locationServicesEnabled]) {
3         NSLog(@"定位服务不可用!");
4         return;
5     }
6

 

 

 2. 开启定位,获取自己的当前位置                            

[_locationManager startUpdatingLocation];

 

 3. 根据经纬度,知道准确的地名                              

reverseGeocodeLocation

 

复制代码
 1 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
 2 {
 3     NSLog(@"位置变化: %@", locations[0]);
 4     
 5     // 根据经纬度查找(去苹果后台查找准确的位置,必须联网才能用)
 6     [_geocoder reverseGeocodeLocation:locations[0] completionHandler:^(NSArray *placemarks, NSError *error) {
 7         
 8         NSLog(@"%@", placemarks[0]);
 9         
10     }];
11 }
复制代码

 

 

 

 

 

 4. 根据定名,获取到经纬度,一般用在导航                        

geocodeAddressString

复制代码
1    [_geocoder geocodeAddressString:@"王府井" completionHandler:^(NSArray *placemarks, NSError *error) {
2         
3         for (CLPlacemark *placemark in placemarks) {
4             NSLog(@"aaaa______%@ %lu", placemark, (unsigned long)placemarks.count);
5         }
6         
7     }];
复制代码

 

[转载]ASP.NET MVC快速生成缩略图 - 迷失的醉猫 - 博客园

mikel阅读(1289)

[转载]快速生成缩略图 – 迷失的醉猫 – 博客园.

相信逛过淘宝的都知道,同一张图片,展示在不同的地方,尺寸是不同的,如果强制对原图进行拉伸,有可能会变形,影响视觉效果,在这种情况下,对同一张图 片,根据需要,生成不同的缩略图,需要的时候再调用,就可以很好的解决这一问题。下面通过一个实例来详细的说明一下这种情况:

下图是百度图片广场的列表页:

列表页 图-1

通过Chrome浏览器,我们可以看到,在这里这张图片所占的大小为188px X 188px(列表页 图-2),也就是此时只需要188px X 188px大小的图片就足够了,如果将原图500px X 500px 的图片强制压缩放在这里,是可以展示出来,但是加载的却是原来的大图,对于用户的流量来说,这就浪费了很大一部分,如果网速不给力的话,加载也会非常的 慢。当我们点击这张图片的时候,会进入二级列表页,这里的尺寸又会不一样(二级列表 图-3),当我们再点进去之后就会到详细页面,这里展示的又是另外一个尺寸(详细页 图-4)。

 

列表页 图-2

二级列表 图-3

详细页 图-4

      稍微细心一点你也可以发现,每张图片显示的都很合适,没有明显的拉伸或者压缩的现象,这就用到了我们今天所说的缩略图。当然,这只是作者自己做的一个小 Demo,里边肯定会有很多不足的地方,也会有很多考虑不周的地方,如果愿意分享一下您浏览之后的感想,十分感谢。

先说一下大致的思路:首先选定一个文件夹,找出里边所有的图片文件,然后再根据需要生成缩略图的尺寸和生成缩略图的模式,生成缩略图文件,然后保存到指定 的路径下。这里需要说明一下的就是生成缩略图的模式,既然是缩略图,肯定和原图的尺寸不一样,根据不同的模式,裁剪的方式也不一样。本文中共提出了5中缩 略图,实际中您可以根据自己的情况,增加需要或者删除不需要的缩略图模式。以下是五种生成缩略图的模式:

1、自动缩放:如果需要生成缩略图的宽度比高度大,那么效果跟第二种模式产生的效果一样;如果宽度比高度小,则效果跟第三种模式产生的效果一样。

2、指定宽高按比例:这种模式下,宽度即为需要生成的图片宽度,高度为需要生成的缩略图的宽度与原图宽度的比再乘以需要生成的缩略图的高度。

3、指定高宽按比例:这种模式证号和第二种模式相反,高度一定,宽度乘以比值。

4、指定高宽缩放:按照指定的尺寸生成缩略图。

5、指定高宽裁剪:指定高,把两边的宽裁剪掉。

这里示例就采用传统的WebForm的方式来做演示,也可以用MVC来实现。这里新建了一个WebApplication项目,如图-5,里边包含一个页 面展示页面,一个缩略图模式的枚举,一个生成缩略图的Handler,一个JQuery脚本(用来提交数据)。这里值得注意的就是,Handler需要在 WebConfig里做配置,才能生效。还有一点就是,会默认调用Handler里的ProcessRequest方法,只需要将生成缩略图的代码放到这 个Request方法里就可以了。

图-5

       由于仅仅只是做演示,所以这里忽略掉了样式,元素只是简单地叠加,并没有美化。开始界面如图-6:

图-6

public enum MakeThumbnailMode
{
Auto,//自动裁剪模式
W,//指定宽,高按比例
H, //指定高,宽按比例
HW,//指定高,宽缩放
Cut//指定宽,高裁剪
}

以下部分是生成缩略图的两个关键方法:

static Bitmap getThumBitmap(Image originalImage, int width, int height, MakeThumbnailMode mode, out Graphics graphics)
{
Bitmap bitmap;
int thumbWidth = width;
int thumbHeight = height;
int x = 0;
int y = 0;
int originalWidth = originalImage.Width;
int originalHeight = originalImage.Height;
if (mode == MakeThumbnailMode.Auto)
{
if (thumbWidth &gt; thumbHeight)
{
mode = MakeThumbnailMode.W;
}
else
{
mode = MakeThumbnailMode.H;
}

}
if (originalHeight &lt; thumbHeight &amp;&amp; originalWidth &lt; thumbWidth) { thumbWidth = originalWidth; thumbHeight = originalHeight; } switch (mode) { case MakeThumbnailMode.W: thumbHeight = originalHeight * width / originalWidth; break; case MakeThumbnailMode.H: thumbWidth = originalWidth * height / originalHeight; break; case MakeThumbnailMode.HW: break; case MakeThumbnailMode.Cut: if ((double)originalWidth / (double)originalHeight&gt;(double)width / (double)height)
{
originalHeight = originalImage.Height;
originalWidth = width * originalHeight / height;
y = 0;
x = (originalWidth - width) / 2;

}
else
{
originalWidth = originalImage.Width;
originalHeight = height * originalWidth / width;
x = 0;
y = (originalHeight - height) / 2;
}
break;
}
bitmap = new Bitmap(thumbWidth, thumbHeight);
bitmap.MakeTransparent(Color.Transparent);
graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.Transparent);
graphics.DrawImage(originalImage, new Rectangle(0, 0, thumbWidth, thumbHeight), new Rectangle(x, y, originalWidth, originalHeight), GraphicsUnit.Pixel);
return bitmap;
}
static void MakeThumbPic(string originalImagePath, string thumbnailPath, int width, int height, MakeThumbnailMode mode, ImageFormat imageFormat)
{
using (Image image = Image.FromFile(originalImagePath))
{
Graphics graphics;
Bitmap bitmap = getThumBitmap(image, width, height, mode, out graphics);
try
{
bitmap.Save(thumbnailPath, imageFormat);
}
catch (Exception)
{
throw;
}
}
}

然后在ProcessRequest方法里调用MakeThumbPic方法,即可生成缩略图了。由于代码都不是太难,所以这里就给出一个思路,详细的代码就不敲了,有需要的可以发邮箱。通过context上下文获取到提交过来的数据,然后遍历图片文件,获得所有的图片文件,然后循环,每张图片都调用生成缩略图的makeThumbpic方法,即可生成需要的缩略图了。

如您有任何的建议和看法,欢迎和我联系,大家一起学习,一起进步,谢谢!
以下是定义的枚举:

[转载]12306订票小工具 - 罗旭成 - 博客园

mikel阅读(1116)

[转载]12306订票小工具 – 罗旭成 – 博客园.

前言:一年一度刷票接近尾声了,最近本人也经常在刷 票,也在为这个事情而烦恼,花了很多时间和精力去抢票,甚至一起叫了许多人一起抢票,上班时间想的都是抢票,真是辛苦,经过多天的折磨,终于拿到回家的车 票了,不容易。最近一段时间也在博客园看了很多文章,看到很多人写的关于12306的一些工具,感觉有些还不错,但是能真的用起来还存在差距,特别是在验 证码这块还是存在问题,到了关键时候还是用不了,感觉不靠谱,还不如用12306自带的刷票功能。

在这里还真要说说12306这个网站,每年这个网站都是要被别人吐槽,每年一到关键时刻就出问题,不是刷不了票,要不就是资料泄露,最近都出现订票出错的 情况,导致明明订的的是长沙自动帮你订到三亚去了,这真是怪哉,O(∩_∩)O哈哈~每天都有很多文章是关于12306这个网站的,真的这个网站做好就有 那么难,每年都要投入那么多经费,到头来还是一样,让人订不到票,黄牛缺可以在十多分钟内订走上千张票,真是不可思议,看来这样的年代还是有技术才有王 道。还是希望国家能多收铁路,增大运力,这是解决问题根本,哎…扯远了。

开发这个工具主要是用.NET开发的,主要用到修改网站本山的JavaScript,可以让刷新时间变短,这样增加刷票的次数,增加成功率,究竟是否真的有效,还需各位自己验证,本人并不敢打包票。

以下为html代码:

复制代码
<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <script type="text/javascript">
        var time = 5000;
    </script>
</head>
<body>
    在下面的文本框输入“有票了”或者等待10秒钟试试看:<br />
    <input type="text" id="txtName" /><input type="button" value="输入内容" />
    <script type="text/javascript">
        window.setTimeout("txtName.value ='有票了'", 10000);
        //autosubmitcheckticketinfo.style.
    </script>
    <div id=autosubmitcheckticketinfo style="POSITION: fixed; LEFT: 381px; Z-INDEX: 20000; DISPLAY: none; TOP: 100px" dhxbox="1" jquery1910021022635977561266="490">
        <div class="up-box w664" id=content_autosubmitcheckticketinfo>
            <div class=up-box-hd>已查到车票,请核对以下信息 </div>
            </div>
        </div>
</body>
</html>
复制代码

界面如下:

点击“继续刷票”按钮即可打开12306网站,其他动作就和进入12306一模一样了,输入用户和密码,选好乘车人和乘车日期等等相关信息后,选好 最上面的刷新间隔即可,最多可以选择5秒钟,最低可以选择1秒钟,把如上信息全部填好后,点击“查询”即实现刷票了,这样就会按照你设定的时间间隔进行刷 票了。

以下从源码层面进行分析:

FormLoad中主要加载默认网址,并设定默认的刷新时间。

复制代码
        #region * FormLoad
        private void Form1_Load(object sender, EventArgs e)
        {
            this.numericUpDown1.Value = 5;
            string uriString = ((DateTime.Now.Second % 2) == 0) ? "http://www.cnblogs.com/jara/" : "http://www.cnblogs.com/jara/";
            this.webBrowser1.Url = new Uri(uriString);
        }
        #endregion
复制代码

开始测试页按钮实现的代码(加载默认的网址,并且启动时间计时器):

复制代码
    private void btnOpenUrl_Click(object sender, EventArgs e)
        {
            string text = this.txtUrl.Text;
            if (text == "TestHtml.html")
            {
                text = Application.StartupPath + @"\" + text;
            }
            this.webBrowser1.Url = new Uri(text);
            this.timer1.Start();
        }
复制代码

开始刷票实现的代码(一旦有票就会将出票窗口弹出到最前端,即便你在工作状态,也不会错过订票的机会,这样就会你在上班时间工作更安心):

复制代码
        private void btnOpenUrl2_Click(object sender, EventArgs e)
        {
            if (this.btnOpenUrl2.Text == "继续刷票")
            {
                this.timer1.Start();
                this.lblMsg.Text = "弹窗监视器工作中... ...当期刷新间隔:" + this.autoSearchTime + " 毫秒。";
            }
            else
            {
                this.txtUrl.Text = "https://kyfw.12306.cn/otn/leftTicket/init";
                this.btnOpenUrl.PerformClick();
                MessageBox.Show("请在购票网页中登录并输入你的购票信息,开启自动提交功能,\r\n然后,本程序将在网页提交订单的时候自动弹出窗体提醒你及时进行操作!");
                this.btnOpenUrl2.Text = "继续刷票";
            }
        }
复制代码

Timer实现的事件:

复制代码
 private void timer1_Tick(object sender, EventArgs e)
        {
            if (this.webBrowser1.Document != null)
            {
                if (this.txtUrl.Text == "TestHtml.html")
                {
                    this.TestDom();
                }
                else if (this.txtUrl.Text.Contains("leftTicket"))
                {
                    this.leftTicket();
                }
            }
        }

        private void TestDom()
        {
            HtmlElement elementById = this.webBrowser1.Document.GetElementById("txtName");
            if (elementById != null)
            {
                IHTMLInputTextElement domElement = (IHTMLInputTextElement)elementById.DomElement;
                if (domElement.value == "有票了")
                {
                    this.timer1.Stop();
                    mshtml.IHTMLElement element4 = (mshtml.IHTMLElement)this.webBrowser1.Document.GetElementById("autosubmitcheckticketinfo").DomElement;
                    element4.style.display = "";
                    base.TopMost = true;
                    MessageBox.Show("有票了!!!");
                    base.TopMost = false;
                    ((mshtml.IHTMLWindow2)this.webBrowser1.Document.Window.DomWindow).execScript("time=1000;alert('时间改变了:'+time);", "javascript");
                }
            }
        }

        private void leftTicket()
        {
            HtmlElement elementById = this.webBrowser1.Document.GetElementById("autosubmitcheckticketinfo");
            if (elementById != null)
            {
                if (this.changeSearchTime)
                {
                    ((mshtml.IHTMLWindow2)this.webBrowser1.Document.Window.DomWindow).execScript("autoSearchTime = " + this.autoSearchTime + " ;", "javascript");
                    this.changeSearchTime = false;
                }
                mshtml.IHTMLElement domElement = (mshtml.IHTMLElement)elementById.DomElement;
                if ((domElement.style.display == "block") || string.IsNullOrEmpty(domElement.style.display))
                {
                    this.timer1.Stop();
                    if (base.WindowState == FormWindowState.Minimized)
                    {
                        base.WindowState = FormWindowState.Maximized;
                        domElement.style.left = "400px";
                    }
                    base.TopMost = true;
                    MessageBox.Show("有票了,请在浏览器中完成操作!\r\n 如果你返回修改了订票规则(比如修改席别),请单击下【继续刷票】按钮!");
                    this.lblMsg.Text = "弹窗监视器已经停止,请单击【继续刷票】按钮开启!";
                    base.TopMost = false;
                }
            }
        }
复制代码
      private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            this.changeSearchTime = true;
            this.autoSearchTime = 0x3e8 * ((int)this.numericUpDown1.Value);
        }

以上即可实现抢票的,这个工具的亮点主要在于可以实现一秒钟刷票的功能,用12306只能是5秒钟刷票,这样的话刷新的频率提高了,这样即可实现更快刷到票,此工具主要目的是好玩,不可用于其它目的,本人也声明不承担一切后果。

运行这个工具需要电脑安装了.net框架,一般情况下Win7、Win8都有自动的.net框架的,XP系统应该没有,如果没有这个框架在使用这个工具之前还需安装.NET框架,在百度里搜索就有。

程序:http://files.cnblogs.com/jara/V1.0.0.1.rar V.1.0.0.1版。

待续…

Android中引入第三方Jar包的方法(java.lang.NoClassDefFoundError解决办法)

mikel阅读(737)

1、在工程下新建lib文件夹,将需要的第三方包拷贝进来。
2、将引用的第三方包,添加进工作的build path。
3、(关键的一步)将lib设为源文件夹。如果不设置,则程序编译可以通过,但运行的时候,会报:

QQ截图20140111101838

    java.lang.NoClassDefFoundError

 

 

升级了 adt18.真奇怪。。。

[转载].net下开源轻量级ORM框架Dapper扩展系列1 - 树上的蜗牛 - 博客园

mikel阅读(1369)

[转载].net下开源轻量级ORM框架Dapper扩展系列1 – 树上的蜗牛 – 博客园.

轻量级ORM框架Dapper相信很多人了解,也用过,可能也有很多人可能还不知道
Dapper官网:https://code.google.com/p/dapper-dot-net/
我在网上复制一下别人对Dapper的描述:

Dapper是一个轻型的ORM类。代码就一个SQLMapper.cs文件。文件见下。编译后就40K的一个很小的Dll.

Dapper很快,有多快。实验下就知道了。官方给了点测试包,想玩的时候就去测试下。Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。

Dapper支持什么数据库。Dapper支持MySQL,SqlLite,Mssql2000,Mssql2005,Oracle等一系列的数据库,当然如果你知道原理也可以让它支持Mongo db.

Dapper的r支持多表并联的对象。支持一对多 多对多的关系。并且没侵入性,想用就用,不想用就不用。无XML无属性。代码以前怎么写现在还怎么写。

Dapper原理通过Emit反射IDataReader的序列队列,来快速的得到和产生对象。性能实在高。

Dapper支持net2.0,3.0,3.5,4.0。

Dapper的语法是这样的。语法十分简单。并且无须迁就数据库的设计。

为什么要扩展Dapper:
了解Dapper都知道,在书写代码时,我们还是会手动写SQL,扩展的目的就是在完全不改变dapper源代码和使用基础上,进行一次封闭,达到零SQL,实现完全对象操作。
接下来,我们开始Dapper的扩展之旅第一章。。。
下载Dapper后,我们新建类库项目:DapperEx ,并把Dapper项目加载到项目中:

1.在DapperEx 中添加Dapper引用 ,如下:

2.为了存储数据库类型,以及根据数据库类型,使用不同的参数化操作数据库,添加一个DbBase类:

复制代码
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;

namespace Dapper
{
    public class DbBase : IDisposable
    {
        private string paramPrefix = "@";
        private string providerName = "System.Data.SqlClient";
        private IDbConnection dbConnecttion;
        private DbProviderFactory dbFactory;
        private DBType _dbType = DBType.SqlServer;
        public IDbConnection DbConnecttion
        {
            get
            {
                return dbConnecttion;
            }
        }
        public string ParamPrefix
        {
            get
            {
                return paramPrefix;
            }
        }
        public string ProviderName
        {
            get
            {
                return providerName;
            }
        }
        public DBType DbType
        {
            get
            {
                return _dbType;
            }
        }
        public DbBase(string connectionStringName)
        {
            var connStr = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
            if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
                providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
            else
                throw new Exception("ConnectionStrings中没有配置提供程序ProviderName!");
            dbFactory = DbProviderFactories.GetFactory(providerName);
            dbConnecttion = dbFactory.CreateConnection();
            dbConnecttion.ConnectionString = connStr;
            dbConnecttion.Open();
            SetParamPrefix();
        }

        private void SetParamPrefix()
        {
            string dbtype = (dbFactory == null ? dbConnecttion.GetType() : dbFactory.GetType()).Name;

            // 使用类型名判断
            if (dbtype.StartsWith("MySql")) _dbType = DBType.MySql;
            else if (dbtype.StartsWith("SqlCe")) _dbType = DBType.SqlServerCE;
            else if (dbtype.StartsWith("Npgsql")) _dbType = DBType.PostgreSQL;
            else if (dbtype.StartsWith("Oracle")) _dbType = DBType.Oracle;
            else if (dbtype.StartsWith("SQLite")) _dbType = DBType.SQLite;
            else if (dbtype.StartsWith("System.Data.SqlClient.")) _dbType = DBType.SqlServer;
            // else try with provider name
            else if (providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.MySql;
            else if (providerName.IndexOf("SqlServerCe", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SqlServerCE;
            else if (providerName.IndexOf("Npgsql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.PostgreSQL;
            else if (providerName.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.Oracle;
            else if (providerName.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SQLite;

            if (_dbType == DBType.MySql && dbConnecttion != null && dbConnecttion.ConnectionString != null && dbConnecttion.ConnectionString.IndexOf("Allow User Variables=true") >= 0)
                paramPrefix = "?";
            if (_dbType == DBType.Oracle)
                paramPrefix = ":";
        }

        public void Dispose()
        {
            if (dbConnecttion != null)
            {
                try
                {
                    dbConnecttion.Dispose();
                }
                catch { }
            }
        }
    }
    public enum DBType
    {
        SqlServer,
        SqlServerCE,
        MySql,
        PostgreSQL,
        Oracle,
        SQLite
    }
}
复制代码

此类功能利用DbProviderFactories实现数据源连接接口IDbConnection,通过数据库判断设置参数前缀为@、?、:的一种。
3.和大多数实体映射一样,为了更方便的操作数据库,我们添加几个特性,来实现实体与数据库表的映射关系:

BaseAttribute:特性基类

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    public class BaseAttribute:Attribute
    {
        /// <summary>
        /// 别名,对应数据里面的名字
        /// </summary>
        public string Name { get; set; }
    }
}
复制代码

ColumnAttribute:字段列特性

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    /// <summary>
    /// 列字段
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class ColumnAttribute : BaseAttribute
    {
        /// <summary>
        /// 自增长
        /// </summary>
        public bool AutoIncrement { get; set; }
        public ColumnAttribute()
        {
            AutoIncrement = false;
        }
        /// <summary>
        /// 是否是自增长
        /// </summary>
        /// <param name="autoIncrement"></param>
        public ColumnAttribute(bool autoIncrement)
        {
            AutoIncrement = autoIncrement;
        }
    }
}
复制代码

IdAttribute:主键列特性

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    /// <summary>
    /// 主键
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class IdAttribute : BaseAttribute
    {
        /// <summary>
        /// 是否为自动主键
        /// </summary>
        public bool CheckAutoId { get; set; }

        public IdAttribute()
        {
            this.CheckAutoId = false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="checkAutoId">是否为自动主键</param>
        public IdAttribute(bool checkAutoId)
        {
            this.CheckAutoId = checkAutoId;
        }
    }
}
复制代码

IgnoreAttribute:忽略列特性

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    /// <summary>
    /// 忽略字段
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class IgnoreAttribute:BaseAttribute
    {
    }
}
复制代码

TableAttribute:数据库表特性

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    /// <summary>
    /// 数据库表
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class TableAttribute : BaseAttribute
    {
    }
}
复制代码

4.接下来建立一个生成SQL时参数里面的列名和对应值名称的对应类:ParamColumnModel

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dapper
{
    /// <summary>
    /// 生成SQL时参数里面的列名和对应值名称
    /// </summary>
    public class ParamColumnModel
    {
        /// <summary>
        /// 数据库列名
        /// </summary>
        public string ColumnName { get; set; }
        /// <summary>
        /// 对应类属性名
        /// </summary>
        public string FieldName { get; set; }
    }
}
复制代码

 

5.建立一个公共类,方便对特性、属性进行操作和取值等操作Common

复制代码
using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;

namespace DapperEx
{
    public class Common
    {

        /// <summary>
        /// 获取对象对应数据库表名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static string GetTableName<T>()
        {
            var ty = typeof(T);
            var arri = ty.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
            if (arri is TableAttribute && (!string.IsNullOrEmpty((arri as BaseAttribute).Name)))
            {
                return (arri as BaseAttribute).Name;
            }
            return ty.Name;
        }
        /// <summary>
        /// 在没有指定排序时,获取一个默认的排序列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static string GetDefaultOrderField<T>()
        {
            var name = "";
            foreach (var propertyInfo in typeof(T).GetProperties())
            {
                var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
                if (arri is IgnoreAttribute)
                {
                    arri = null;
                    continue;
                }
                name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
                break;
            }
            return name;
        }
        /// <summary>
        /// 获取要执行SQL时的列,添加和修改数据时
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static IList<ParamColumnModel> GetExecColumns<T>() where T : class
        {
            var columns = new List<ParamColumnModel>();
            foreach (var propertyInfo in typeof(T).GetProperties())
            {
                var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
                if (arri is IgnoreAttribute)
                {
                    arri = null;
                    continue;
                }
                else if (arri is IdAttribute)
                {
                    if ((arri as IdAttribute).CheckAutoId)
                    {
                        arri = null;
                        continue;
                    }
                }
                else if (arri is ColumnAttribute)
                {
                    if ((arri as ColumnAttribute).AutoIncrement)
                    {
                        arri = null;
                        continue;
                    }
                }
                string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
                columns.Add(new ParamColumnModel() { ColumnName = name, FieldName = propertyInfo.Name });
            }
            return columns;
        }
        /// <summary>
        /// 获取对象的主键标识列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="PropertyName">对应实体属性名</param>
        /// <returns></returns>
        public static string GetPrimaryKey<T>(out string PropertyName) where T : class
        {
            string name = "";
            PropertyName = "";
            foreach (var propertyInfo in typeof(T).GetProperties())
            {
                var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
                if (arri is IdAttribute)
                {
                    name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
                    PropertyName = propertyInfo.Name;
                    break;
                }

            }
            if (string.IsNullOrEmpty(PropertyName))
            {
                throw new Exception("没有任何列标记为主键特性");
            }
            return name;
        }
        /// <summary>
        /// 通过属性名获取对应的数据列名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static string GetExecCloumName<T>(string propertyName) where T : class
        {
            var propertyInfo = typeof(T).GetProperty(propertyName);

            var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
            if (arri is IgnoreAttribute)
            {
                arri = null;
            }
            string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
            return name;
        }
        /// <summary>
        /// 通过表达示树获取属性名对应列名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static string GetNameByExpress<T>(Expression<Func<T, object>> expr) where T : class
        {
            var pname = "";
            if (expr.Body is UnaryExpression)
            {
                var uy = expr.Body as UnaryExpression;
                pname = (uy.Operand as MemberExpression).Member.Name;
            }
            else
            {
                pname = (expr.Body as MemberExpression).Member.Name;
            }
            var propertyInfo = typeof(T).GetProperty(pname);
            var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
            if (arri is IgnoreAttribute)
            {
                throw new Exception(string.Format("{0}不能进行SQL处理", pname));
            }
            string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
            return name;
        }
        /// <summary>
        /// 字符串中连续多个空格合并成一个空格
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string UnitMoreSpan(string str)
        {
            Regex replaceSpace = new Regex(@"\s{1,}", RegexOptions.IgnoreCase);
            return replaceSpace.Replace(str, " ").Trim();
        }
    }
}
复制代码

6.有了以上基础,现在开始进行扩展操作,新建DapperEx类:

本系列,我们先扩展两个方法:添加一个实体 和 批量添加
代码比较简单:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dapper;
using System.Data;
using DapperEx;

namespace Dapper
{
    public static class DapperEx
    {
        /// <summary>
        /// 扩展插入数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <param name="t"></param>
        /// <param name="useTransaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public static bool Insert<T>(this DbBase dbs, T t, bool useTransaction = false, int? commandTimeout = null) where T : class,new()
        {
            var db = dbs.DbConnecttion;
            IDbTransaction tran = null;
            if (useTransaction)
                tran = db.BeginTransaction();
            var result = false;
            var tbName = Common.GetTableName<T>();
            var columns = Common.GetExecColumns<T>();

            var flag = db.Execute(CreateInertSql(tbName, columns, dbs.ParamPrefix), t, tran, commandTimeout);
            if (tran != null)
            {
                try
                {
                    tran.Commit();
                    result = true;
                }
                catch
                {
                    tran.Rollback();
                }
            }
            else
            {
                return flag == 1;
            }
            return result;
        }
        /// <summary>
        /// 批量插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <param name="lt"></param>
        /// <param name="useTransaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public static bool InsertBatch<T>(this DbBase dbs, IList<T> lt, bool useTransaction = false, int? commandTimeout = null) where T : class,new()
        {
            var db = dbs.DbConnecttion;
            IDbTransaction tran = null;
            if (useTransaction)
                tran = db.BeginTransaction();
            var result = false;
            var tbName = Common.GetTableName<T>();
            var columns = Common.GetExecColumns<T>();
            var flag = db.Execute(CreateInertSql(tbName, columns, dbs.ParamPrefix), lt, tran, commandTimeout);
            if (tran != null)
            {
                try
                {
                    tran.Commit();
                    result = true;
                }
                catch
                {
                    tran.Rollback();
                }
            }
            else
            {
                return flag == lt.Count;
            }
            return result;
        }
        /// <summary>
        /// 组装插入语句
        /// </summary>
        /// <param name="tbName"></param>
        /// <param name="colums"></param>
        /// <returns></returns>
        private static string CreateInertSql(string tbName, IList<ParamColumnModel> colums, string ParamPrefix)
        {
            StringBuilder sql = new StringBuilder();
            sql.Append(string.Format("INSERT INTO {0}(", tbName));
            for (int i = 0; i < colums.Count; i++)
            {
                if (i == 0) sql.Append(colums[i].ColumnName);
                else sql.Append(string.Format(",{0}", colums[i].ColumnName));
            }
            sql.Append(")");
            sql.Append(" VALUES(");
            for (int i = 0; i < colums.Count; i++)
            {
                if (i == 0) sql.Append(string.Format("{0}{1}", ParamPrefix, colums[i].FieldName));
                else sql.Append(string.Format(",{0}{1}", ParamPrefix, colums[i].FieldName));
            }
            sql.Append(") ");
            return sql.ToString();
        }
    }
}
复制代码

今天要讲的都已经完成,接下来,我们进行使用测试:
在解决方案中添加一个测试库:DapperExTest,并在测试库中添加一个本地数据库:dbSqlCeEx.sdf,并在数据库中中添加一个表:Account

注意最后个一字段。Flag是一个字增长列.
在测试库中添加一个应用程序配置文件:App.config,并修改成自己相应的连接数据库字符串

在测试库中添加一个相应的实体类:Account

复制代码
namespace DapperExTest
{
    public class Account
    {
        [Id]
        public virtual string Id { get; set; }
        public virtual string Name { get; set; }
        public virtual string Password { get; set; }
        public virtual string Email { get; set; }
        public virtual DateTime CreateTime { get; set; }
        public virtual int Age { get; set; }
        [Column(true)]
        public virtual int Flag { get; set; }
        [Ignore]
        public virtual string AgeStr
        {
            get
            {
                return "年龄:" + Age;
            }
        }
    }
}
复制代码

 

 


上述工作完成后,在测试类UnitTest1中添加如下代码:

 public string connectionName = "strSqlCe";
        public DbBase CreateDbBase()
        {
            return new DbBase(connectionName);
        }

现在我们对添加功能,进行测试,添加方法:

复制代码
[TestMethod]
        public void Insert()//插入一条数据
        {
            var model = new Account()
            {
                Id = "1",
                Name = "张三1",
                Password = "123456",
                Email = "123@qq.com",
                CreateTime = DateTime.Now,
                Age = 15
            };
            using (var db = CreateDbBase())
            {
                var result = db.Insert<Account>(model);
                if (result)
                    Console.WriteLine("添加成功");
                else
                    Console.WriteLine("添加失败");
            }
        }
复制代码

右键运行测试,测试成功,成功添加一条数据:

现在测试批量添加:

复制代码
[TestMethod]
        public void InsertBatch()//插入多条数据
        {
            var list = new List<Account>();
            for (int i = 2; i < 21; i++)
            {
                var model = new Account()
                {
                    Id = i.ToString(),
                    Name = "张三" + i.ToString(),
                    Password = "123456",
                    Email = "123@qq.com",
                    CreateTime = DateTime.Now,
                    Age = 15
                };
                list.Add(model);
            }
            using (var db = CreateDbBase())
            {
                var result = db.InsertBatch<Account>(list, true);
                if (result)
                    Console.WriteLine("添加成功");
                else
                    Console.WriteLine("添加失败");
            }
        }
复制代码

运行完成后,查看数据库:

成功!!!

OK,今天的扩展到此结束,后续系列将会对修改、批量修改、删除、批量删除、查询、分页进行扩展
希望大家多多关注,觉得对自己有所帮助或有意见的,欢迎留言,觉得不错的,不要吝啬你的鼠标,点点支持,点点推荐,谢谢啦!!!

本系列源码:http://pan.baidu.com/s/1dDh4T7F

[转载]应用内使用新浪微博SDK发送微博(不调用微博客户端) - 木杉是天才 - 博客园

mikel阅读(1018)

[转载]应用内使用新浪微博SDK发送微博(不调用微博客户端) – 木杉是天才 – 博客园.

需求

手头的一个应用需要添加分享到新浪微博的功能,这个功能在现在的应用上是非常的普遍的了。

分享到新浪微博,其实就是发送一条特定内容的微博,所以需要用到新浪微博SDK了。

微博SDK

SDK的下载地址 http://open.weibo.com/wiki/SDK,包括很多平台的封装,其中就有Android版本的。

下载后,请务必读一下SDK文档,运行其中自带的demo,对sdk的使用有个大概的了解。

发送微博的困扰

运行DEMO后发现,其中发送微博的功能竟然是通过调用微博客户端的方式来实现的,如果用户没有安装,会提示用户下载,或者选择放弃发送。。。这明显不靠谱啊,因为还是有很多用户没有安装微博客户端的。

网上也有不少人遇到这个问题,从他们的说法中可以看出微博sdk前一个版本中似乎还是有发送微博这个功能的,不过新版的sdk中没有了。

不过demo中还是给了提示了:如果不想使用微博客户端进行分享,请参考OpenAPI。

那我们就来看看这OpenAPI有什么功能。

OpenAPI

OpenAPI的文档:http://open.weibo.com/wiki/%E5%BE%AE%E5%8D%9AAPI

可以看出OpenAPI就是通过Http请求的方式来进行一些操作,可以进行的操作基本涵盖了微博应用的所有方面。

我们找到发送微博的API:statuses/update

文档的说明很详细,改接口通过POST方法,请求参数有很多,不过必须的只有一个。不过,改接口是需要登录的,所以参数中还需加上登录授权过的AccessToken:

参数名 必选 类型及范围 说明
status true string 要发布的微博文本内容,必须做URLencode,内容不超过140个汉字。
access_token true string 登录后得到,详见后文

现在,发送微博的逻辑很明确了,就是一个post请求。还需要解决的一个问题就是AccessToken,即登录授权。

登录授权

并不是你想让用户在你的应用里登录微博就可以直接写代码实现的,因为用户是在你的应用里输入账户信息,所以对于用户的账户安全来说,这是有风险的,所以如果你的应用需要加入微博功能,是需通过新浪审核的。这个流程我也没有完整的走过,不过大家可以参考微博文档的帮助:移动客户端接入

这篇文章主要讲程序,所以不在这些流程上纠结。总之,我们需要的是一个APPKEY,登录授权需要它。我们现在可以先用SDK微博应用demo的APPKEY来实验。

DEMO实践步骤

  1. 因为使用的是微博DEMO的APPKEY所以需要替换默认的 Debug.keystore
    在 C:\Users\XXXXX.Android 目录下,把 Android 默认的 Debug.keystore 替换成官方在 GitHub 上提供的debug.keystore。
    请 注意:这一步是必须的,如果没有替换,demo 程序在运行时将无法正确的授权成功。用户在替换前,最好先备份一下原始的 debug.keystore。另外,该 debug.keysotre 是新浪官方的,除了编译运行官方 DEMO 外,请不要直接使用它,出于安全的考虑,您应该为自己的应用提供一份 keysotre。
  2. 新建android工程,包名取名为:com.sina.weibo.sdk.demo
  3. 拷贝微博DEMO中的Constants.java到工程中
  4. 在AndroidManifest.xml中添加权限:
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  5. 主界面的代码,登录授权部分的代码直接取自微博官方DEMO,发送微博部分是上文分析的实现:
     public class MainActivity extends Activity {
         private static final String TAG = "MainActivity";
    
         /** 微博 Web 授权类,提供登陆等功能 */
         private WeiboAuth mWeiboAuth;
    
         /** 封装了 "access_token","expires_in","refresh_token",并提供了他们的管理功能 */
         private Oauth2AccessToken mAccessToken;
    
         private TextView mTokenInfoTV;
         private EditText mMessageET;
         private Button mSendBtn;
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
    
             mWeiboAuth = new WeiboAuth(this, Constants.APP_KEY, Constants.REDIRECT_URL, Constants.SCOPE);
    
             mTokenInfoTV = (TextView) findViewById(R.id.textView_tokenInfo);
             mMessageET = (EditText) findViewById(R.id.editText_message);
             mSendBtn = (Button) findViewById(R.id.button_sendWeibo);
    
             // 获取Token
             findViewById(R.id.button_getToken).setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     mWeiboAuth.anthorize(new AuthListener());
                 }
             });
    
             // 发送微博
             mMessageET.setVisibility(View.GONE);
             mSendBtn.setVisibility(View.GONE);
             mSendBtn.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     sendWeibo(mMessageET.getText().toString());
                 }
             });
         }
    
         // 发送微博
         protected void sendWeibo(String string) {
             //组织post参数
             List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();
             params.add(new BasicNameValuePair("status", string));
             params.add(new BasicNameValuePair("access_token", mAccessToken.getToken()));
    
             HttpClient httpClient = new DefaultHttpClient();
    
             //传入post方法的请求地址,即发送微博的api接口
             HttpPost postMethod = new HttpPost("https://api.weibo.com/2/statuses/update.json");
             try {
                 postMethod.setEntity(new UrlEncodedFormEntity(params, "utf-8"));
                 HttpResponse httpResponse = httpClient.execute(postMethod);
    
                 //将返回结果转为字符串,通过文档可知返回结果为json字符串,结构请参考文档
                 String resultStr=EntityUtils.toString(httpResponse.getEntity());
                 Log.e(TAG, resultStr);
    
                 //从json字符串中建立JSONObject
                 JSONObject resultJson = new JSONObject(resultStr);
    
                 //如果发送微博失败的话,返回字段中有"error"字段,通过判断是否存在该字段即可知道是否发送成功
                 if (resultJson.has("error")) {
                     Toast.makeText(this, "发送失败", Toast.LENGTH_SHORT).show();
                 } else {
                     Toast.makeText(this, "发送成功", Toast.LENGTH_SHORT).show();
                 }
    
             } catch (UnsupportedEncodingException e) {
                 Log.e(TAG, e.getLocalizedMessage());
             } catch (ClientProtocolException e) {
                 Log.e(TAG, e.getLocalizedMessage());
             } catch (IOException e) {
                 Log.e(TAG, e.getLocalizedMessage());
             } catch (ParseException e) {
                 Log.e(TAG, e.getLocalizedMessage());
             } catch (JSONException e) {
                 Log.e(TAG, e.getLocalizedMessage());
             }
         }
    
         //登录授权接口
         class AuthListener implements WeiboAuthListener {
    
             //登录成功
             @Override
             public void onComplete(Bundle values) {
                 // 从 Bundle 中解析 Token
                 mAccessToken = Oauth2AccessToken.parseAccessToken(values);
                 if (mAccessToken.isSessionValid()) {
                     // 显示 Token
                     updateTokenView();
    
                     // 显示发送微博的按钮和输入框
                     mSendBtn.setVisibility(View.VISIBLE);
                     mMessageET.setVisibility(View.VISIBLE);
    
                     // 保存 Token 到 SharedPreferences
                     // AccessTokenKeeper.writeAccessToken(MainActivity.this,
                     // mAccessToken);
                     Toast.makeText(MainActivity.this, "授权成功", Toast.LENGTH_SHORT).show();
                 } else {
                     // 当您注册的应用程序签名不正确时,就会收到 Code,请确保签名正确
                     String code = values.getString("code");
                     String message = "授权失败";
                     if (!TextUtils.isEmpty(code)) {
                         message = message + "\nObtained the code: " + code;
                     }
                     Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
                 }
             }
    
             @Override
             public void onCancel() {
                 Toast.makeText(MainActivity.this, "取消授权", Toast.LENGTH_LONG).show();
             }
    
             @Override
             public void onWeiboException(WeiboException e) {
                 Toast.makeText(MainActivity.this, "Auth exception : " + e.getMessage(), Toast.LENGTH_LONG).show();
             }
         }
    
         //显示token信息
         private void updateTokenView() {
             String date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new java.util.Date(mAccessToken
                     .getExpiresTime()));
             String format = "Token:%1$s \n有效期:%2$s";
             mTokenInfoTV.setText(String.format(format, mAccessToken.getToken(), date));
         }
    
         @Override
         public boolean onCreateOptionsMenu(Menu menu) {
             // Inflate the menu; this adds items to the action bar if it is present.
             getMenuInflater().inflate(R.menu.main, menu);
             return true;
         }
    
     }
  1. 主界面的布局代码:
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools"
         android:id="@+id/LinearLayout1"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
         tools:context=".MainActivity" >
    
         <Button
             android:id="@+id/button_getToken"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="获取token" />
    
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="Token 信息:"
             android:textSize="20sp" />
    
         <TextView
             android:id="@+id/textView_tokenInfo"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text=""
             android:textSize="20sp" />
    
         <EditText
             android:id="@+id/editText_message"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:ems="10" >
    
             <requestFocus />
         </EditText>
    
         <Button
             android:id="@+id/button_sendWeibo"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="发送微博" />
    
     </LinearLayout>

运行效果

发送微博DEMO下载地址

http://files.cnblogs.com/mushan/WeiboSDKTest.zip

作者:木杉

出处:木杉的博客–http://www.cnblogs.com/mushan

您的支持是对博主最大的鼓励,感谢您的认真阅读。

本文版权归作者所有,欢迎转载,但请保留该声明。

[转载]Android之TelephonyManager&GsmCellLocation类的方法详解 - sonny的日志 - 网易博客

mikel阅读(923)

[转载]Android之TelephonyManager&GsmCellLocation类的方法详解 – sonny的日志 – 网易博客.

TelephonyManager类

主要提供了一系列用于访问与手机通讯相关的状态和信息的get方法。其中包括手机SIM的状态和信息、电信网络的状态及手机用户的信息。在应用程序中可以使用这些get方法获取相关数据。

TelephonyManager 类的对象可以通过Context.getSystemService(Context.TELEPHONY_SERVICE)方法来获得,需要注意的是有 些通讯信息的获取对应用程序的权限有一定的限制,在开发的时候需要为其添加相应的权限。

以下列出TelephonyManager类所有方法及说明:

package com.ljq.activity;
import java.util.List;
import Android.app.Activity;
import Android.content.Context;
import android.os.Bundle;
import android.telephony.CellLocation;
import android.telephony.NeighboringCellInfo;
import android.telephony.TelephonyManager;
public class TelephonyManagerActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
/**
* 返回电话状态
*
* CALL_STATE_IDLE 无任何状态时
* CALL_STATE_OFFHOOK 接起电话时
* CALL_STATE_RINGING 电话进来时
*/
tm.getCallState();
//返回当前移动终端的位置
CellLocation location=tm.getCellLocation();
//请求位置更新,如果更新将产生广播,接收对象为注册LISTEN_CELL_LOCATION的对象,需要的permission名称为ACCESS_COARSE_LOCATION。
location.requestLocationUpdate();
/**
* 获取数据活动状态
*
* DATA_ACTIVITY_IN 数据连接状态:活动,正在接受数据
* DATA_ACTIVITY_OUT 数据连接状态:活动,正在发送数据
* DATA_ACTIVITY_INOUT 数据连接状态:活动,正在接受和发送数据
* DATA_ACTIVITY_NONE 数据连接状态:活动,但无数据发送和接受
*/
tm.getDataActivity();
/**
* 获取数据连接状态
*
* DATA_CONNECTED 数据连接状态:已连接
* DATA_CONNECTING 数据连接状态:正在连接
* DATA_DISCONNECTED 数据连接状态:断开
* DATA_SUSPENDED 数据连接状态:暂停
*/
tm.getDataState();
/**
* 返回当前移动终端的唯一标识
*
* 如果是GSM网络,返回IMEI;如果是CDMA网络,返回MEID
*/
tm.getDeviceId();
//返回移动终端的软件版本,例如:GSM手机的IMEI/SV码。
tm.getDeviceSoftwareVersion();
//返回手机号码,对于GSM网络来说即MSISDN
tm.getLine1Number();
//返回当前移动终端附近移动终端的信息
List<NeighboringCellInfo> infos=tm.getNeighboringCellInfo();
for(NeighboringCellInfo info:infos){
//获取邻居小区号
int cid=info.getCid();
//获取邻居小区LAC,LAC: 位置区域码。为了确定移动台的位置,每个GSM/PLMN的覆盖区都被划分成许多位置区,LAC则用于标识不同的位置区。
info.getLac();
info.getNetworkType();
info.getPsc();
//获取邻居小区信号强度
info.getRssi();
}
//返回ISO标准的国家码,即国际长途区号
tm.getNetworkCountryIso();
//返回MCC+MNC代码 (SIM卡运营商国家代码和运营商网络代码)(IMSI)
tm.getNetworkOperator();
//返回移动网络运营商的名字(SPN)
tm.getNetworkOperatorName();
/**
* 获取网络类型
*
* NETWORK_TYPE_CDMA 网络类型为CDMA
* NETWORK_TYPE_EDGE 网络类型为EDGE
* NETWORK_TYPE_EVDO_0 网络类型为EVDO0
* NETWORK_TYPE_EVDO_A 网络类型为EVDOA
* NETWORK_TYPE_GPRS 网络类型为GPRS
* NETWORK_TYPE_HSDPA 网络类型为HSDPA
* NETWORK_TYPE_HSPA 网络类型为HSPA
* NETWORK_TYPE_HSUPA 网络类型为HSUPA
* NETWORK_TYPE_UMTS 网络类型为UMTS
*
* 在中国,联通的3G为UMTS或HSDPA,移动和联通的2G为GPRS或EGDE,电信的2G为CDMA,电信的3G为EVDO
*/
tm.getNetworkType();
/**
* 返回移动终端的类型
*
* PHONE_TYPE_CDMA 手机制式为CDMA,电信
* PHONE_TYPE_GSM 手机制式为GSM,移动和联通
* PHONE_TYPE_NONE 手机制式未知
*/
tm.getPhoneType();
//返回SIM卡提供商的国家代码
tm.getSimCountryIso();
//返回MCC+MNC代码 (SIM卡运营商国家代码和运营商网络代码)(IMSI)
tm.getSimOperator();
tm.getSimOperatorName();
//返回SIM卡的序列号(IMEI)
tm.getSimSerialNumber();
/**
* 返回移动终端
*
* SIM_STATE_ABSENT SIM卡未找到
* SIM_STATE_NETWORK_LOCKED SIM卡网络被锁定,需要Network PIN解锁
* SIM_STATE_PIN_REQUIRED SIM卡PIN被锁定,需要User PIN解锁
* SIM_STATE_PUK_REQUIRED SIM卡PUK被锁定,需要User PUK解锁
* SIM_STATE_READY SIM卡可用
* SIM_STATE_UNKNOWN SIM卡未知
*/
tm.getSimState();
//返回用户唯一标识,比如GSM网络的IMSI编号
tm.getSubscriberId();
//获取语音信箱号码关联的字母标识。
tm.getVoiceMailAlphaTag();
//返回语音邮件号码
tm.getVoiceMailNumber();
tm.hasIccCard();
//返回手机是否处于漫游状态
tm.isNetworkRoaming();
// tm.listen(PhoneStateListener listener, int events) ;
//解释:
//IMSI是国际移动用户识别码的简称(International Mobile Subscriber Identity)
//IMSI共有15位,其结构如下:
//MCC+MNC+MIN
//MCC:Mobile Country Code,移动国家码,共3位,中国为460;
//MNC:Mobile NetworkCode,移动网络码,共2位
//在中国,移动的代码为电00和02,联通的代码为01,电信的代码为03
//合起来就是(也是Android手机中APN配置文件中的代码):
//中国移动:46000 46002
//中国联通:46001
//中国电信:46003
//举例,一个典型的IMSI号码为460030912121001
//IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称
//IMEI由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的
//其组成为:
//1. 前6位数(TAC)是”型号核准号码”,一般代表机型
//2. 接着的2位数(FAC)是”最后装配号”,一般代表产地
//3. 之后的6位数(SNR)是”串号”,一般代表生产顺序号
//4. 最后1位数(SP)通常是”0″,为检验码,目前暂备用
}
}


GsmCellLocation类
extends CellLocation
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

java.lang.Object
? android.telephony.CellLocation
? android.telephony.gsm.GsmCellLocation

获取方法为:TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation location = (GsmCellLocation) tm.getCellLocation();
public int getCid() 获取cellid
Returns
gsm cell id, -1 if unknown, 0xffff max legal value
public int getLac() 获取本地区域代码
Returns
gsm location area code, -1 if unknown, 0xffff max legal value
综合使用方法:
其实,对于Ophone跟Android来说有许许多多的不一样,比如说联网方式,还比如一些UI的细节实现等

下边的是我收集到得跟Android略有不同的获取CellId跟IMEI的代码:

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation location = (GsmCellLocation) tm.getCellLocation();

int cellid = location .getCid();

String imei = tm.getDeviceId();

注:别忘加权限啊

<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”></uses-permission>
<uses-permission android:name=”android.permission.READ_PHONE_STATE”></uses-permission>