Archive for the Category » 技术交流 «

关于ListView的性能,推荐一篇老外的好文章

对于纯文字内容的ListView,基本可以不看了,除非你数据量巨大,那你还是需要看。

对于有大量图片的ListView,这篇文章必须看,我之前在Android上开发应用时遇到一个ListView,每个Item里面都是若干张小图片,纠结于性能的改进很长时间,最终受益于这篇文章得以改进!

原文很长,我就不贴内容了,这里是链接地址: Performance Tips for Android’s ListView

新浪微博 Android SDK中OAuth2.0隐式授权部分的一个代码逻辑问题

在最近使用新浪微博android sdk开发微博登录的时候,从日志中发现一个问题,就是自定义的WeiboDialogListener里面的方法,比如onComplete或者onCancel等,经常会被两次调用,这样其实会导致一些隐性问题,比如增加额外的客户端和服务端的开销,因为我们通常会在onComplete()里面完成更多后续逻辑的处理,而发生这样的情况时,会被处理两次,一开始我犯懒,就在方法外面加入了一个变量 isCompleted 来进行判断,算是暂时解决了问题,后来在好几个地方要开发类似功能的时候,总感觉心里有点儿不爽,于是决定找找到底啥原因

看了看微博sdk里面的代码,在 WeiboDialog.java里面找到了问题,这个java文件主要实现的是创建OAuth的UI,并且通过实现和调用WebViewClient的方法来访问微博的api以及咱们app的callback url, 进而通过WebViewClient里面捕获当前请求的URL,分析URL参数后进行相应逻辑的判断,通过分析,问题应该是在这部分。

在WeiboDialog.java里面有个函数 handleRedirectUrl(),这个函数就是用来判断认证和授权过程中返回参数的,代码如下:

[coolcode lang=”java”]

private void handleRedirectUrl(WebView view, String url) {
Bundle values = Utility.parseUrl(url);

String error = values.getString(“error”);
String error_code = values.getString(“error_code”);

if (error == null && error_code == null) {
mListener.onComplete(values);
} else if (error.equals(“access_denied”)) {
// 用户或授权服务器拒绝授予数据访问权限
mListener.onCancel();
} else {
mListener.onWeiboException(new WeiboException(error, Integer.parseInt(error_code)));
}
}
[/coolcode]

一看就知道啥意思了,关键的逻辑就在这里面,回调我们自己实现的WeiboDialogListener里面的方法,顺藤摸瓜,调用该方法的代码就在实现WebViewClient里面,研究了一下该部分代码,找到了原因

在WebViewClient中,我们需要实现至少shouldOverrideUrlLoading()方法,该方法在每次加载新url的时候调用,另外,我们还通常会实现onPageStarted()方法,该方法也是在新url开始加载的时候进行调用(注意:在frame里面加载是不会调用的,详细文档见http://developer.android.com/reference/android/webkit/WebViewClient.html)

在WeiboDialog.java实现WebViewClient对象的时候,在shouldOverrideUrlLoading()和onPageStarted()里面都同时调用了handleRedirectUrl()来进行回调url和参数的判断,结果就导致了我遇到的问题,于是我们可以把里面的一个去掉,或者增加一个参数来进行判断,避免重复调用,目前一切正常了!

onClick / onLong / onGesture 同时存在时,点击、滑动、长按屏幕的事件处理顺序

在Android开发中,如果一个Activity里面同时继承实现了 onClickListener、onLongClickListener、onGestureListener, 此时手指在点击、滑动、长按屏幕是各个事件处理的顺序是如何的呢,通过实际项目中写日志得到如下的结果:

点击松开: onDown / onShowPress(稍长会有) / onSingleTapUp /onClick
滑动松开: onDown / onScroll(多次) / onFling / onClick / onLongClick
长按松开:onDown / onShowPress / onLongClick / onLongPress
长按并滑动后松开: onDown /onShowPress / onLongClick / onScroll(多次) / onFling

知道这个顺序后,就知道如何处理各个需要的逻辑了,同时需要注意一点:在每个过程中,任何一个事件里面return true了,后面的就将不再执行,如果要同时执行多个事件,可以在每个事件内部处理完后,return false, 后面的事件就将继续进行

RelativeLayout的选中状态变化

在layout的xml文件中设置focusable以及background,并定义background指向的xml文件中有关selector,到此还是不够的,如果不在java文件里面定义onClickListener事件的话,在手机上运行并不会看到选中状态的变化。

Android动画背景图自动播放的实现

我们在开发android应用的时候,经常会遇到类似从网络加载大图,在加载的过程中,在图片要显示的ImageView位置,先显示一个转圈的loading动画图,给用户的体验会更好一些,要实现这个动画图很简单,使用在/res/anim中定义xml的方式,通常使用…. 来实现。

不过大多数朋友都会遇到的问题是,动画是做好了,但是界面在加载的时候,动画并不会自动播放,还得通过屏幕点击等事件来触发,这就失去了意义了,实际上,android的动画AnimationDrawable 组件里面有个start()方法用于启动动画播放,但是这个方法不能直接写在onClick,onStart,onResume里面,写进去也是无效的,无法启动动画,只能写在比如事件监听当中,于是我们可以使用点小技巧来实现自动播放

目前我知道的有三种方法:

[coolcode lang=”java”]
ImageView imageView = (ImageView)findViewById(R.id.xxx);
[/coolcode]

方法一:使用Runnalbe()来加载

[coolcode lang=”java”]
imageView.setBackgroundResource(R.anim.xxxxx);
final AnimationDrawable animationDrawable = (AnimationDrawable)imageView.getBackground();
imageView.post(new Runnable() {
@Override
public void run() {
animationDrawable.start();
}
});

[/coolcode]

方法二:使用AsyncTask异步加载启动
[coolcode lang=”java”]

imageView.setBackgroundResource(R.anim.xxxxx);
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
RunAnim runAnim=new RunAnim();
runAnim.execute(“”);

class RunAnim extends AsyncTask {
@Override
protected String doInBackground(String… params) {
if (!animationDrawable.isRunning()) {
animationDrawable.stop();
animationDrawable.start();
}
return “”;
}
}
[/coolcode]

方法三:通过添加addOnPreDrawListener来自动加载
[coolcode lang=”java”]

imageView.setBackgroundResource(R.anim.xxxxx);
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
imageView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);

OnPreDrawListener preDrawListener = new OnPreDrawListener(){
@Override
public boolean onPreDraw() {
animationDrawable.start();
return true; //必须要有这个true返回
}
};[/coolcode]

以上三种方法经过测试没有问题,另外网上有一些说使用重写Activity的onWindowFocusChanged()方法来实现,但是还是有不足,得改变焦点才能触发,虽然理论可以自动实现改变焦点,感觉还是不甚可取。

用Chrome的调试工具改善网站整体访问性能

Chrome浏览器的Developer Tools里面有个Audits工具,可以很方便的帮我们发现网站存在的一些常见问题,比如缓存时间设置问题,header里面是否有CacheControl Public问题,Gzip是否打开等等,建议做系统管理的可以尝试一下。

另外,在IE下有个HttpWatch Professional的插件,这个工具也提供很全面的网站访问改进建议!

准备运行Audits

查看Audits结果

PHP 正则里面的两个重要技巧

正则用了好些年,有两个东西总结出来是在web开发中非常有用的,尤其在抓取、代码分析中经常用到。

1. .* 的贪吃性

我们用 /\*.*\*/ 去和字符串 /* first comment */ not comment /* second comment */ 匹配会失败,因为由于 .* 项目的贪吃性,会匹配成整个字符串,于是就会匹配失败,不过,如果在后面加一个问号数量符,则会停止贪吃性,而变成匹配尽可能少的数目,因此模式 /\*.*?\*/ 就会正确匹配。各种数量符的含义并没有改变,只是优先的匹配数目。不要将问号的此用法和其自己作为数量符的使用混淆。因为有两种用法,有时可以两个一起出现,例如 \d??\d 会优先匹配一个数字,但如别无选择也可以匹配两个以使剩余模式匹配。

如果设定了 PCRE_UNGREEDY 选项(此选项 Perl 中没有)则数量符默认不是贪吃型的,但是在个别模式后加上一个问号可以将其变成贪吃型的。换句话说,这可以反转默认的行为。

后面跟上一个 + 的数量符是“占有性”(possessive)的。它会匹配尽可能多的字符而不管剩余的模式。因此 .*abc 可以匹配 “aabc” 但是 .*+abc 就不会,因为 .*+ 已经匹配了整个字符串。自 PHP 4.3.3 起可以用占有性数量符可以来加快处理过程。

2. ()子模式的不捕获

如果将字符串 “the red king” 来和模式 the ((red|white) (king|queen))
进行匹配,捕获的子串为 “red king”,”red” 以及 “king”,并被计为 1,2 和 3。

简单的括号实现两种功能的事实不总是有帮助的。经常有需要一组子模式但不需要捕获的时候。如果左括号后面跟着 “?:”,子模式不做任何捕获,并且在计算任何之后捕获的子模式时也不算在内。例如,如果用字符串 “the white queen” 去和模式 the ((?:red|white) (king|queen)) 匹配,捕获的子串是 “white queen” 和 “queen”,并被计为 1 和 2。

因此,最完美的匹配网页中图片 src 部分的正则表达式处理如下:
[coolcode lang=”php”]
$str=’

‘;

$pattern=”//i”;
preg_match_all($pattern,$str,$match);

print_r($match);
[/coolcode]

以上代码将输出如下内容:
[coolcode]
Array
(
[0] => Array
(
[0] =>
[1] =>
[2] =>
[3] =>
)

[1] => Array
(
[0] => upfiles/2009/07/1246430143_4.jpg
[1] => upfiles/2009/07/1246430143_3.jpg
[2] => upfiles/2009/07/1246430143_1.jpg
[3] => http://s.yoututuan.com/team/2011/1206/13231497847660.jpg
)

)
[/coolcode]

PHP正则匹配字符串中的标签

如果标签里面有中文、英文、数字等混排,则需要对汉字进行特殊处理,由于PCRE不支持\U \P \L之类的perl字符串处理转义,使用16进制或者Unicode进行处理,范例如下:

[coolcode lang=”php”]

$str = “之二宽阔的甘家口东#标签1#标签2 #标签3。#标签4,都发$1234 ¥xc,cvm , ¥12,dflksjf如何#标签5.x #tag6.cvxcv“”;
preg_match_all(‘/#([a-zA-Z0-9\x7f-\xff]+)/’, $str, $mat);
print_r($mat);

preg_match(“/[\x{00a5}\x{ffe5}](\d+)/u”, $str, $mat);
print_r($mat);

[/coolcode]

得到的结果如下:


Array
(
[0] => Array
(
[0] => #标签1
[1] => #标签2
[2] => #标签3。
[3] => #标签4
[4] => #标签5
[5] => #tag6
)

[1] => Array
(
[0] => 标签1
[1] => 标签2
[2] => 标签3。
[3] => 标签4
[4] => 标签5
[5] => tag6
)

)
Array
(
[0] => ¥12
[1] => 12
)

补充说明:

双字节字符编码范围

1. GBK (GB2312/GB18030)
x00-xff GBK双字节编码范围
x20-x7f ASCII
xa1-xff 中文 gb2312
x80-xff 中文 gbk

2. UTF-8 (Unicode)

u4e00-u9fa5 (中文)
x3130-x318F (韩文
xAC00-xD7A3 (韩文)
u0800-u4e00 (日文)

cherokee

估计是喜欢开cherokee,最近新上线的服务器我也懒得配置Nginx,直接用cherokee了,配置起来很方便,对于当前访问量不大的站点,其实用那个都行,简单就好,简单就好!

 

牛逼的GMP数学运算库

搞了一晚上,搞定了一个需要使用无限长度数字运算的产品功能开发,实现权限模块的处理,使用了gmp库来完成,效果一级棒,性能好不好还不知道,不过好在不是用在高性能要求的部分,好久木有今晚这样的为了搞定一个问题而钻研的劲头了。

顺便记录一下,如果要在BSD系统下的PHP中使用GMP库,不要安装最新的5.0.x版本的GMP库,4.2.4以下的版本最好,要不编译不过去的!

当前功能中实际使用到的最大数字是 18446744073709107172 ,二进制的结果是如此惊人的长度: 1111111111111111111111111111111111111111111110010011011111100100