Monthly Archives: July 2013

android的进程倒底有没有退出?

android强调activity, 弱化了进程的概念。

按返回键退出activity时,除非显式在onDestroy()方法里使用System.exit(0),否则进程并不会退出,而是驻留在后台 (未必会出现在运行列表里)。 当你重新进入某个程序时,系统把后台程序直接推到前台,减少重建进程的开销,让用户获得比较快的启动体验。

可是如果程序驻留过多,内存会不会浪费甚至耗尽?  系统看内存有点紧张时,会自动杀掉一些里程;当然系统在这方面做的并不完美,驻留程序多了,手机还是会卡的。作为开发者,最好还是提供显式退出功能。

Android与OAuth: 用户在哪授权? (还没研究透)

OAuth之所以适合三方协作,就是因为用户不需要在第三方系统里直接用户名/密码;而是在OPEN ID提供者的系统里输入,然后再跳到第三方系统。

在B/S环境下,OAuth通过浏览器跳来跳去完成这种事;但Android这种C/S环境下,怎么搞?

我初略地做了些研究,得出的结论是大概有几种方式:

1. 新浪微博的OAUTH2:
第三方应用通过web view或者什么方式弹出浏览器,让用户在一个很小的新浪授权页中输入用户名/密码,然后回到第三方应用。

2. 新浪微博的SSO:
第三方应用通过Intent启动本机中安装的微博Android应用,选择用户后回到第三方应用。 严格地说,这可能已经不是OAUTH了。 (我写这篇文章时weibo客户端的SDK没有源码,所以我无法深入研究,给出答案)

3. Google的AccoutManager.getAuth(): 第三方应用调用AccountManager.getAuth()时,Android会
启动AccountManager自带的一个Activity让用户决定是否授权
原文:During the AccountManager.getAuthToken() call the AccountManager will check if your application has been authorized to access the Tasks API. If your application has not yet been authorized an Activity is started by the AccountManager which displays an authorization dialog to the user so that they can Allow or Deny your application to use the API on their account.) . 看来AccountManager内置了对OAuth的支持。 由于google api被盾,这里的细节是怎么样,我也不得而知,只能以后再看了。

Android开源项目研究: last.fm android的用户认证

概述

last.fm android的认证机制与cookie类似,但与cookie无关。

类似于OAUTH,它也有appKey/appSecret体系,以允许第三方开发者开发应用;然而,这个体系跟OAUTH没有任何关系,last.fm android这个APP也跟第三方开发者没有什么关系。

登录、认证流程

1. 用户输入用户名、密码

2. 客户端把用户名、密码经加密处理后传给服务端,服务端认证通过后,返回一段XML;客户端再把这个XML反序列化为一个JAVA对象:fm.last.api.Session. 这个对象包含userName和一个token(代码中称为sessionKey)

3. 客户端会把对象中的
userName和sessionKey存入到settings中 (SharedPreference)

4. 同时还会把这个Session对象作为一个全局变量放在内存中,相当于setting中所存session信息的缓存

5. 后续的访问中,如果客户端需要表明身份,则会传入sessionKey; 服务端会通过sessionKey来判断该请求是否代表合法的用户。

整个过程类似于服务端返回一个cookie, 客户端在后续请求中发出cookie .

如何记住登录

启动后,在LastFMApplication这个activity的onCreate()中会
去settings中取出已登录的userName和sessionKey ,然后封装出Session对象,作为全局变量放到内存里;接下来直接进入LastFm Activity或Profile Activity,整个过程并没有网络交互。

服务端并没有校验,是不是有安全漏洞? 如果我在首次登录后,把用户名换成另一个人的,就不是可以这个人的身份操作本APP? 答案是:你只能成功进入LastFm和Profile,但无法继续以受害者的身份进行操作;因为在后续的业务数据访问请求中,还是要附带上sessionKey,服务端会根据sessionKey找出你的真实身份。

登出时的行为

登出时会把内存中的Session全局变量置为NULL,并去settings中把userName和sessionKey清除。

这就类似于浏览器中的cookie清除 .

具体使用的通信协议

1.走的是http , 直接用HttpURLConnection;  API叫 Parser.getItem(url, params, …)

2.用了https,但不校验任何证书(方法名:trustAllHosts())

附:加密的细节信息

			String md5Password = MD5.getInstance().hash(pass);  //一次哈希
			String authToken = MD5.getInstance().hash(user + md5Password);  //再哈希



....
//最后传给服务端的东西
				params.put("username", username);
				params.put("authToken", authToken);
				params.put("method", "auth.getMobileSession"); //应该是服务端的一个方法 
				params.put("api_key", api_key);	
				signParams(params); //对所有参数做摘要,其中用到了appSecret作为盐

附:AccountManager在这里有没有起到作用?

last.fm android的认证过程中反复调用了AccountManager的API,但这里AccountManager跟认证没有半毛钱关系。

登录成功后,客户端会把useName保存到settings,同时也会顺便把userName保存到AccountManager中; 登出时,也会去AccountManager里清一下。 然而,在进行远程业务操作时,并不会依赖AccountManager中的任何东西。 

那么AccountManager在客户端上到底有什么用?我搜了一下代码,发现它只跟ContentProvider有一点点关系,跟认证没有任何关系。

大屏、小屏、横屏与无线UI布局的关系

UI的布局只是指界面组件之间的相对位置,而跟组件的大小及长宽比例无关。

对于普通的联系人界面,把手机横过来,你会看到组件变得扁平了,但这时布局没有变。

对于小屏(phone)变大屏(pad),如果程序没做特殊设置的话,布局也不会变。

如果两个组件在竖屏是垂直排列,在横屏时变成水平排列,这才叫自适应布局。 这种东西一般要写程序的人做好支持,比如在安卓里写两个layout文件,res/layout/*.xml代表竖屏的布局,res/layout-land/*.xml代表横屏的布局。

大小屏与此类似,如果要搞自适应布局,在安卓里也要写多个布局文件。

32位机器中long, double的读写不是原子的

在32位机器中,由于long, double数据较长,跨多个地址;导致这个long, double的读写不是原子的。

所以,对long, double的读写操作,在必须情况理应该加锁。

在64位机上,理论上long,double是原子的;但由于指针压缩技术,long,double等仍可能跨地址存储,所以对它们的读写操作可能仍不是原子的。

Java反射代码片断:你这个“类型”是哪种类型?

Class是哪种Class ?

clazz.isPrimitive(); //是否primitive type

//是否为primitive type的封装类?看看它在不在下面的map里(这个map节选自spring的ClassUtils.java)

      primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
      primitiveWrapperTypeMap.put(Byte.class, byte.class);
      primitiveWrapperTypeMap.put(Character.class, char.class);
      primitiveWrapperTypeMap.put(Double.class, double.class);
      primitiveWrapperTypeMap.put(Float.class, float.class);
      primitiveWrapperTypeMap.put(Integer.class, int.class);
      primitiveWrapperTypeMap.put(Long.class, long.class);
      primitiveWrapperTypeMap.put(Short.class, short.class);


clazz.isArray(); //是否为数组类型

拿到的是java.lang.reflect.Type类型,它是哪种Type ?

if (javaReflectType instanceof ParameterizedType); //是否为泛型
if (javaReflectType instanceof Class) //是否为类

是哪种泛型

ParameterizedType pt = (ParameterizedType) javaReflectType;
Type rawType = pt.getRawType(); //尖括号前面的类型,如List<String>中的List

//Collection<SomeClass> 
if (Collection.class.isAssignableFrom(rawClass)) {
    Type type = pt.getActualTypeArguments()[0]; //类集中的元素类型
}

//Map<KeyClass, ValueClass>
if (Map.class.isAssignableFrom(rawClass)) {
                  Type keyType = pt.getActualTypeArguments()[0];  //Map中的key类型
                  Type valueType = pt.getActualTypeArguments()[1];//Map中的value类型
}

Complete CORS setup on server side

		headers.add("Access-Control-Allow-Origin", "*");
		headers.add("Access-Control-Allow-Methods",
				"GET, POST, PUT, DELETE, OPTIONS, HEAD");
		headers.add(
				"Access-Control-Allow-Headers",
				"Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");		
		headers.add("Access-Control-Expose-Headers", "WWW-Authenticate");