《白帽子讲Web安全》读书笔记

最近一直在忙着易信公众平台的开发工作,一直没能抽出空来总结一下。周末终于有了一些空闲,就把这本书的笔记写了一下。

整本书四篇十八章,包括世界观安全、客户端脚本安全、服务端应用安全以及互联网公司安全运营四大部分。

一、世界观安全

  1. 黑帽子和白帽子这两个概念,前者指的是利用安全技术进行破坏的哪一类黑客,后者则指的是工作在反黑客领域的安全技术专家。
  2. 安全问题的本质是信任的问题。并且安全是一个持续的过程,并不存在所谓的银弹。
  3. 安全三要素:机密性、完整性、可用性
  4. 一个安全评估的过程可以分为4个阶段:资产等级划分、威胁分析、风险分析、确认解决方案。其中威胁分析的一种建模方法是微软提出的STRIDE模型;风险分析则是DREAD模型,Risk = Probability * Damage Potenial。
  5. 白帽子并发有以下几个原则:Secure By Default原则;纵深防御原则(Defense in Depth);数据与代码分离原则;不可预测性原则。

杂感一篇

最近两部关于青春的片子《致青春》和《中国合伙人》,对于前者这种爱情的东西,从某个时间段开始,我早已不再关注。后者讲述的奋斗、梦想,却是我特别想看一看的。虽然我知道成功学这东西,完全是一群成功了的人在吹嘘当年自己有多苦逼,后来变得多NB。但是他们的成功却的确能触动自己心里的那根神经,唤起自己当初的激情。

十几年前,自己无意之中说出的一句话,到现在也算一直坚持了下来。虽然没有功成名就,但到现在还是不至于令自己失望的。平淡的日子往往让自己忘了自己当初的梦想,也忘了走向它的这条路。今天看《中国合伙人》这部影片又像当初看《社交网络》一样让自己全身瞬间充满了能量,同时也让自己拷问着自己:还坚持当初的梦想吗?正在为梦想努力吗?努力有收获吗?其实,现实中大多数人在说自己梦想的时候都能侃侃而谈,而真正能付诸于努力的人却寥寥可数。为梦想而努力,结果不一定是成功,但没有为此而努力过,那也许会成为一辈子最大的憾事。

电影里三个人的友情是让我感触颇深的另一个地方。人的一生会不停地结交到各种朋友,但最后真正能称得上朋友的人其实就那么几个人。和他们在一起,你会很确定不管你怎么样,他们都不会嫌弃你;不管你跟他们说了什么话,他们也不会介意。心情不好的时候,一个电话,他们不管有多忙也会立马赶到你的身旁。其实,以前的自己挺自私的,有点像孟晓峻,自从经历了某些事情之后,我才体会到友情是多么的珍贵,也才弥足珍惜现在的朋友们。

无题

5.11-5.12,176公里,千岛湖镇-汾口-千岛湖镇,环湖一周。相信这将会是我未来永远难忘的一段经历。

开始的计划是5.11中午从千岛湖镇出发,历经96公里前往汾口,18:00左右到达目的地。没想到走了大约20公里之后遇到了各种上坡路,尤其一段一公里的大上坡,让我们实在无法保持速度。全身各种疼,大腿完全是无知觉地在做机械运动,有无数次想放弃的冲动。如此艰难的挣扎,造成的后果就是从枫树岭开始的20公里山路只能摸黑前进,中间好几次撞到石头差点栽到地上。所幸的是有一辆自行车是有车灯的,不然还真不知道如何前行。最后到达汾口的时候已经是晚上快9点了。虽然远远晚于原定计划,但顺利到达目的地的喜悦还是让我们挺开心的。

5.12的路程相对来说是比较容易的,一路都是大缓坡,咬咬牙就都过了。中间在界首农庄吃了顿午饭。之后连续10个隧道,一路都还是挺舒服的。下午4点多,终于回到了出发点。算是完成了这次环千岛湖骑行。

这一段行程,真的感觉是对自己的一种历练+超越。在很多次想要放弃的时候,我都告诉自己,人只有超越自己、超越极限,才能成为一个无比强大的个体。和别人比较是没有任何意义的,能够战胜自己的人才是最优秀的。

PS:一路上各种哈雷机车,那叫一个帅气。。。据说是哈雷机车110周年纪念日,全国的机车爱好者都过来环湖庆祝。望而兴叹,啥时候自己也能成为一个哈雷机车车友呢?

Android异步加载图片

对于Android中的异步加载图片,自己总结了两种方式,如下:

1.

/*
 * 异步读取图片,需要传递三个参数:Imageview imageView,String imagePath,int maxNumpixels
* @author Bryant
/
public class AsyncLoadImage extends AsyncTask<Object, Object, Void> {
@Override
protected Void doInBackground(Object… params) {

    try {  
        ImageView imageView=(ImageView) params[0];  
        String path=(String) params[1];  
        int maxNumOfPixels = (Integer)params[2];

        Bitmap bm = CompressPicture.compress(path,maxNumOfPixels);

        publishProgress(new Object[] {imageView, bm});  
    }catch (Exception e) {  
        e.printStackTrace();  
    }  

    return null;
}  

protected void onProgressUpdate(Object... progress) {  
    ImageView imageView = (ImageView) progress[0];  
    Bitmap bm = (Bitmap) progress[1];
    if(bm != null){
        imageView.setImageBitmap(bm); 
    }

}  

}

/**
 * 图片异步加载
 * @author Bryant
 *
 */
public class AsyncImageLoader {
    private Map> imageCache=new HashMap>();
    private int maxNumPixels;
    
    public Bitmap loadBitmap(final String imagePath,int maxNumPixels,final ImageCallback callback){
        this.maxNumPixels = maxNumPixels;
        
        if(imageCache.containsKey(imagePath)){
            SoftReference softReference=imageCache.get(imagePath);
            if(softReference.get()!=null){
                return softReference.get();
            }
        }
        final Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                callback.imageLoaded((Bitmap) msg.obj, imagePath);
            }
        };
        new Thread(){
            public void run() {
                Bitmap bitmap=loadImageFromPath(imagePath);
                imageCache.put(imagePath, new SoftReference(bitmap));
                handler.sendMessage(handler.obtainMessage(0,bitmap));
            };
        }.start();
        return null;
    }
    
    protected Bitmap loadImageFromPath(String imagePath) {
        try {
            return CompressPicture.compress(imagePath, maxNumPixels);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public interface ImageCallback{
        public void imageLoaded(Bitmap imageBitmap,String imagePath);
    }
}

经过实际使用,AsyncImageLoader的效率比较高,但是调用比较麻烦。 调用示例:

private AsyncImageLoader imageLoader = new AsyncImageLoader();
imageLoader.loadBitmap(imagePath,maxNumPixels, new ImageCallback() {
    public void imageLoaded(Bitmap imageBitmap, String imagePath) {
        imageView.setImageBitmap(imageBitmap);
    }
}); 

AsyncLoadImage调用较简单,调用示例如下:

new AsyncLoadImage().execute(new Object[]{imageView,imagePath,maxNumPixels});

《七周七语言》读后感

在微博上看到Robbin老大推荐《七周七语言》一书,去豆瓣上查了一下发现大家对这本书的评价也是非常高,于是就想着去读一下。书买了之后由于找工作一直没有来得及看,随着前段时间把工作定了,就着手对这本书的学习了。从书名来看,顾名思义就是讲了七种不同的编程语言。自己浏览了一下目录,心里也大致衡量了一下,对于这七种语言,我觉得我以后会用到的或者说是能去学的应该也就Ruby、Scala两个,所以就计划大体地浏览一下这七门语言,先有个大概的了解就好。这样每天利用上午的两个小时,一共大约两周的时间,大体把这本书看完了。

这本书一共讲了四种编程范型:面向对象编程、原型编程、约束-逻辑编程、函数式编程。面向对象编程有Ruby和Scala;原型编程是Io;约束-逻辑编程是Prolog;函数式编程有Scala、Erlang、Clojure、Haskell。对于这几种编程范型,书里面最后一章进行了很好的总结,在这里也就不重复了。

在阅读这本书的过程中,经常会在看到某个语言的某个特性时让自己有种狂喜的感觉,让自己感到特别兴奋,有一股强烈的冲动去学习它、去运用到实际中。自己在这过程中也产生了一些想法,思考了一些问题。

  • 书的一开始是Ruby那一章。其实几年前就曾听闻Ruby on rails的威名,也知道ItEye就是使用的这个技术,知道Ruby以注重程序员效率为主。之前花了点时间进行了学习,但一直没有一个练手的项目,所以也就停留在似懂非懂的地步。这次看这一章,第一感觉仍然是惊喜于Ruby语法的灵活。写Ruby代码就觉得是在说话一样,而且各种方式都可以,让人非常舒服。另外,Ruby的纯面向对象和动态类型特性都极大地提高了编程效率。对于Ruby在性能、并发方面的弱点,随着现在硬件性能的提高以及Ruby自身的改进,已经越来越不是问题。

  • 对于Io这个语言,自己很陌生,是第一次听说。不过它的原型编程,自己曾在JavaScript中接触过。Io的另一个特色是万事万物皆消息,这个东西确实挺新颖,在处理并发的时候使用actor发送消息、处理消息能达到不错的效果。此外,它的future让我想起了Java中的FutureTask和Future,不知道Java是不是参考Io的。这个机制在多线程环境中也是个很不错的方案。

  • Prolog,也是第一次听说,更是第一次听说声明式编程语言和逻辑编程语言。貌似只要描述一个事实和推论,就能写出解决很多难题的程序来。里面对于数独和八皇后问题的编码解决,让我真的是叹为观止。此外,自己也第一次知道了尾递归优化这个技术,对于递归调用,这个优化的确能够大大节省内存占用。但是真的对于这种语言很不适应,也就没太仔细地看。不过,不得不承认的是DSL语言在特定领域确实是无可比拟的。

  • Scala,一门混合编程语言,算是面向对象语言与函数式语言之间的桥梁。其运行于JVM之上,加上对函数式编程的支持,使得很多基于Java开发的系统可以在之前的基础上进行改进,极大地提高了编程效率。而且其使用val关键字声明值不可变的变量以及其actor使用了线程池的方式都是对并发不错的解决方案。看到这个语言,我强烈地感觉Scala是一个能够取代Java的高级语言,也特别想去学习一下。也有了计划去进一步接触和使用。

  • Erlang,这个语言之前看到过,知道是一个针对并发的编程语言。不过看了书才知道其是基于Prolog而来的。其“就让它崩溃”的错误处理策略显得非常健壮,另外并发方面采用的是开销极小的轻量级进程也是一个优势。最让我惊叹的是它的列表解析功能,一个典型的例子就是能够轻易的向一个列表中加入新的属性。

  • Clojure,又一个陌生的语言,一个JVM上的Lisp实现,也就是一个Lisp方言。对于Lisp自己也是从未接触过。不过从Lisp字面上的意思来看就是一个列表语言,即取列表第一个元素作函数,其余元素做参数。另外就是在Lisp中数据即代码、代码即数据。这里最让我惊异的是其采用事务内存STM和原子来解决并发问题,算是一个亮点。此外,使用延迟计算解决斐波那契数列和阶乘问题以及Clojure-Java互操作对Java语言的扩展也都是很突出的优势。

  • 最后一个语言Haskell是一个纯函数编程语言,也是本书一个让自己看了很久也没搞明白的语言。可能是对于函数式编程自己就是个小白的缘故吧。不过好歹是弄明白了柯里化的意思,也看到了惰性计算的好处。此外,对其中的monad这个为了弥补函数式编程在命令式风格编程方面的缺陷采用的技术,自己看了好几遍还是很迷糊。书上说这是Haskell的精髓,弄懂这个能学到很多东西。自己打算以后有时间再回过头来好好研究一下这个东西。

总之,这本书确实是一本不可多得的好书,值得好好研读一下。强烈推荐!

关于terracotta在tomcat集群中做session共享的问题

手头上的一个项目“陕西省专业技术人员继续教育学习与管理平台”是服务于全陕西省130万专技人员的高并发且事务逻辑较复杂的一个系统,不管是编码上还是系统架构部署上都具有一定的挑战性。在考虑了现有设备以及系统负载的基础上,整个系统架构采用了六台服务器,分别是:WEB门户服务器(WEB门户+Oracle数据库)、学习管理平台服务器、系统核心数据库、资源下载服务器、在线点播服务器、数据库备份服务器。

系统运行了数月,WEB门户采用的apache+mod_jk+memcached+4tomcat能够正常应付目前的访问。管理学习系统虽然由于一些在线课程还未上线,最大的并发还未开始,但目前课程文件的上传、人员的表单录入等对服务器的造成的负载经常使服务器响应缓慢甚至不响应。考虑到开始采用的nginx+4tomcat+ip_hash,ip_hash本身策略的缺陷使负载不能很好地均衡,于是使用memcached做集群的session共享。此时出现了一个到现在还让我搞不清楚的现象:系统session中存入一些timestamp的Date对象,读取的时候,竟然会读成UNIX原始时间戳。一直没办法解决这个问题,只能采用ip_hash的方式运行着,一旦出现服务器不响应的情况就重启tomcat。

这几天上网查资料的时候,发现terracotta可以做tomcat集群的session共享,由于其是jvm级别的cluster解决方案且采用find-gained changes机制,因此在性能要优于memcached,关键是其是jvm堆级别的复制,储存任何值都没任何问题。按照官网的说明把所有东西都配置好了,却发现session无法正常共享。写了个测试的jsp,打印出session id,单独访问某一个tomcat,发现id在不停的换,我一开始怀疑是terracotta没安好的问题,就又重装了一遍,问题依旧。这时突然发现在IE下session是正常的,其他浏览器下session就不正常,总是不停地换,按说没有关闭浏览器是不会重新生成session的。使用fiddler来检测请求和响应的header,发现一个奇怪的问题,chrome请求一个页面时比使用IE时,多了一个对/favicon.ico的请求,而这个请求得到的响应(404)中会产生新的session id使session发生变化。(什么原因,怎么解释?)在网站的根目录下上传一个favicon.ico即可解决此问题。为了证实是favicon.ico不存在造成的问题,我在页面中请求了一个不存在的js文件,结果session id又开始不停地变。把所有不存在的文件的引用都删掉能够解决此问题,但是在没有使用terracotta前根本不存在此问题,我猜想是不是terraccota在检测到404错误的时候,就会重新生成session的缘故啊。查了一下tomcat的文档,发现context有个属性sessionCookiePath,尝试着把这个值设置为了”/“,结果一切正常了,即使再有404错误,session也不会再变了。这是为什么?在文档中对sessionCookiePath的解释是:

The path to be used for all session cookies created for this context. If set, this overrides any path set by the web application. If not set, the value specified by the web application will be used, or the context path used if the web application does not explicitly set one. To configure all web application to use an empty path (this can be useful for portlet specification implementations) set this attribute to / in the global CATALINA_BASE/conf/context.xml file.

不指定此值的时候,是使用的web app中的或者是直接使用context path。我猜测terracotta是接受了此值,但在处理404资源的时候,这个path无法识别,故以为是新的一个会话,造成session改变的缘故。

不管咋样,暂且算是解决了此问题,原因也是猜测的。有空得看一下terracotta的源代码,看看究竟是什么原因造成的。

其实这个项目在部署运行中,出现过各种问题,因此也做过很多架构的变动。到目前还是不能让人满意,只能在后续的维护中继续改进了。

Android中dip、dp、sp、pt和px的区别

Android有以下几个度量单位:

  • dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。

  • px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。

  • pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;

  • sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。

由此,根据 google 的建议,TextView 的字号最好使用 sp 做单位,而且查看TextView的源码可知Android默认使用sp作为字号单位。

关于换算(以 sp 和 pt 为例) 查看 TextView 等类的源码,可知:

case COMPLEX_UNIT_PX: 
      return value; 
case COMPLEX_UNIT_SP: 
      return value * metrics.scaledDensity; 
case COMPLEX_UNIT_PT: 
      return value * metrics.xdpi * (1.0f/72); 

-------------------------- 
scaledDensity = DENSITY_DEVICE / (float) DENSITY_DEFAULT; 
xdpi = DENSITY_DEVICE; 

-------------------------- 
DENSITY_DEFAULT = DENSITY_MEDIUM = 160; 

============================================ 

所以: 假设 pt 和 sp 取相同的值 1,则可设 1pt 和 1sp 之间系数为 x,

1 * DENSITY_DEVICE / 72 = x * 1 * DENSITY_DEVICE / 160  => 
x = 160 / 72 = 2.2222 

也就是说在 Android 中, 1pt 大概等于 2.22sp

以上供参考,如果 UI 能够以 sp 为单位提供设计是最好的,如果设计中没有 sp 的概念,则开发人员也可以通过适当的换算取近似值。

过去,程序员通常以像素为单位设计计算机用户界面。例如,定义一个宽度为300像素的表单字段,列之间的间距为5个像素,图标大小为16×16像素 等。这样处理的问题在于,如果在一个每英寸点数(dpi)更高的新显示器上运行该程序,则用户界面会显得很小。在有些情况下,用户界面可能会小到难以看清 内容。

与分辨率无关的度量单位可以解决这一问题。Android支持下列所有单位。

  • px(像素):屏幕上的点。

  • in(英寸):长度单位。

  • mm(毫米):长度单位。

  • pt(磅):1/72英寸。

  • dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。

  • dip:与dp相同,多用于android/ophone示例中。

  • sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。

为了使用户界面能够在现在和将来的显示器类型上正常显示,建议大家始终使用sp作为文字大小的单位,将dip作为其他元素的单位。当然,也可以考虑使用矢量图形,而不是用位图。

多activity中退出整个程序

问题

多activity中退出整个程序,例如从A->B->C->D,这时我需要从D直接退出程序。

网上资料

finish()和system(0)都只能退出单个activity。杀进程等的等方式都不行~~~

解决问题

我们知道Android的窗口类提供了历史栈,我们可以通过stack的原理来巧妙的实现,这里我们在D窗口打开A窗口时在Intent中直接加入标志Intent.FLAG_ACTIVITY_CLEAR_TOP,再次开启A时将会清除该进程空间的所有Activity。

在D中使用下面的代码:

Intent intent = new Intent(); 
intent.setClass(D.this, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  //注意本行的FLAG设置
startActivity(intent);
finish();//关掉自己

在A中加入代码:

@Override
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
//退出
        if ((Intent.FLAG_ACTIVITY_CLEAR_TOP & intent.getFlags()) != 0) {
finish();
}
}

A的Manifest.xml配置成android:launchMode=“singleTop”

原理总结

一般A是程序的入口点,从D起一个A的activity,加入标识Intent.FLAG_ACTIVITY_CLEAR_TOP这个过程中会把栈中B,C,都清理掉。因为A是android:launchMode=“singleTop” 不会调用oncreate(),而是响应onNewIntent()这时候判断Intent.FLAG_ACTIVITY_CLEAR_TOP,然后把A finish()掉。 栈中A,B,C,D全部被清理。所以整个程序退出了。

Android中的intent调用代码总结

显示Web网页

Uri uri = Uri.parse("http://www.android123.com.cn");
Intent it = new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);

显示Google地图

Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW,uri);
startActivity(it);

Maps路径规划

Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");
Intent it = new Intent(Intent.ACTION_VIEW,URI);
startActivity(it);

拨打电话

Uri uri = Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL, uri);   
startActivity(it);  

Uri uri = Uri.parse("tel.xxxxxx");
Intent it =new Intent(Intent.ACTION_CALL,uri);

注意需要权限android.permission.CALL_PHONE

发送SMS/MMS

Intent it = new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body", "android开发网欢迎您");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);  

发送短信

Uri uri = Uri.parse("smsto:10086");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "10086"); //正文为10086
startActivity(it);  

发送彩信

Uri uri = Uri.parse("content://media/external/images/media/10"); //该Uri根据实际情况修改,external代表外部存储即sdcard
Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra("sms_body", "android123.com.cn"); 
it.putExtra(Intent.EXTRA_STREAM, uri);
it.setType("image/png");
startActivity(it);

发送Email

Uri uri = Uri.parse("mailto:android123@163.com");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(it);

Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_EMAIL, "android123@163.com");
it.putExtra(Intent.EXTRA_TEXT, "android开发网测试");
it.setType("text/plain");
startActivity(Intent.createChooser(it, "选择一个Email客户端"));  

Intent it=new Intent(Intent.ACTION_SEND);   
String[] tos={"android123@163.com"};     //发送到 
String[] ccs={"ophone123@163.com"};    //抄送
it.putExtra(Intent.EXTRA_EMAIL, tos);   
it.putExtra(Intent.EXTRA_CC, ccs);   
it.putExtra(Intent.EXTRA_TEXT, "正文");   
it.putExtra(Intent.EXTRA_SUBJECT, "标题");   
it.setType("message/rfc822");    //编码类型
startActivity(Intent.createChooser(it, "选择一个Email客户端"));

Email添加附件

Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_SUBJECT, "正文");
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/nobody.mp3"); //附件为sd卡上的nobody MP3文件
sendIntent.setType("audio/mp3");
startActivity(Intent.createChooser(it, "选择一个Email客户端"));

播放多媒体

Intent it = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/nobody.mp3");
it.setDataAndType(uri, "audio/mp3");
startActivity(it);

Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1"); //从系统内部的MediaProvider索引中调用播放
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);  

Uninstall卸载程序

Uri uri = Uri.fromParts("package", packageName, null); //packageName为包名,比如com.android123.apkInstaller
Intent it = new Intent(Intent.ACTION_DELETE, uri);
startActivity(it);

进入联系人界面

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);

查看某个联系人,当然这里是ACTION_VIEW,如果为选择并返回action改为ACTION_PICK,当然处理intent时返回需要用到startActivityforResult

Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, ID);//最后的ID参数为联系人Provider中的数据库BaseID,即哪一行
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);

选择一个图片

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);  
intent.addCategory(Intent.CATEGORY_OPENABLE);  
intent.setType("image/*");//此处->audio/*对应选择音频 video/*对应选择视频
startActivityForResult(intent, 0);

调用Android设备的照相机,并设置拍照后存放位置

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/cwj", android123 + ".jpg"))); //存放位置为sdcard卡上cwj文件夹,文件名为android123.jpg格式
startActivityForResult(intent, 0);

搜索指定package name在market上

Uri uri = Uri.parse("market://search?q=pname:com.android123.cwj");   
Intent intent = new Intent(Intent.ACTION_VIEW, uri);   
startActivity(intent); 

Android位图总结

由于项目中牵扯到了对位图(android.graphics.Bitmap)的操作,于是对照Android的参考文档详细地研究了一下Android提供的位图相关功能。

一、对位图的获取

在Android的SDK中提供了一个BitmapFactory 类。采用此类的几个方法能够从一个文件路径或者输入流中得到位图。

  • 包:android.graphics
  • 类:BitmapFactory
  • Android SDK中的简介:Creates Bitmap objects from various sources, including files, streams, and byte-arrays.

此类其提供了以下方法来获取Bitmap,这些方法都是静态的:

  1. decodeByteArray(byte[] data, int offset, int length)从一个字节数组中得到数据转换为位图。
  2. decodeFile(String pathName)由一个图像文件路径的得到位图(支持jpg、png、bmp格式)
  3. decodeFileDescriptor(FileDescriptor fd)由一个文件描述符得到位图。
  4. decodeResource(Resources res, int id)由资源ID获取位图
  5. decodeStream(InputStream is)由图片输入流获取位图。

这里,由于Android系统本身对apk程序的限制,当加载大图片时会产生OutOfMemoryError。可以采取两种办法解决:

  • 先使用android.provider.MediaStore.Images.Media类的query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy)方法获得对应的图片所对应的id,然后使用android.provider.MediaStore.Images.Thumbnails类的queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)获得图片对应的缩略图的路径,再使用BitmapFactory的decodeFile(String pathname)获取到图片的缩略图,这样即可避免内存溢出的发生。
  • 采取压缩图片的方法对图片进行压缩。这里主要还是使用BitmapFactory类,对于上面提到过的能够获取位图的几个方法,都对应着另一个多了一个参数的方法,例如decodeFile(String pathName)对应着decodeFile(String pathName, BitmapFactory.Options opts)这个方法,其他的可查阅参考文档。其中,通过引入opts这个参数,对其进行相关的设置,最后能得到一个被压缩的图片。

二、对位图信息的获取

要获取位图信息,比如位图大小、是否包含透明度、颜色格式等,获取得到Bitmap就迎刃而解了,这些信息在Bitmap的函数中可以轻松获取到。

Android SDK中对Bitmap有详细说明,这里辅助说明以下2点:

  1. 在Bitmap中对RGB颜色格式使用Bitmap.Config定义,仅包括ALPHA_8、ARGB_4444、ARGB_8888、RGB_565,缺少了一些其他的,比如说RGB_555,在开发中可能需要注意这个小问题;
  2. Bitmap还提供了compress()接口来压缩图片,不过Android SDK只支持PNG、JPG格式的压缩;其他格式的需要Android开发人员自己补充了。

三、位图的显示

位图的显示,一般可以在界面中放置ImageView控件,然后调用android.widget.ImageView类的setImageBitmap(Bitmap bm)方法将位图显示出来。另外,可以使用核心类android.graphics.Canvas的drawBirmap()方法显示位图,或者借助于BitmapDrawable来将Bitmap绘制到Canvas。

四、位图的缩放

位图的缩放,在Android SDK中提供了2种方法:

  1. 将一个位图按照需求重画一遍,画后的位图就是我们需要的了,与位图的显示几乎一样:drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
  2. 在原有位图的基础上,缩放原位图,创建一个新的位图: createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) 归结到底,位图缩放的本质就是将原始位图按照需求显示出来,创造一张新的位图。

五、位图的旋转

位图的旋转,离不开Matrix。Matrix在线性代数中都学习过,Android SDK提供了Matrix类,可以通过各种接口来设置矩阵。例子如下“

Matrix matrix = new Matrix();matrix.setRotate(90,120,130);canvas.drawBitmap(mbmpTest, matrix, mPaint);

除了这种方法之外,我们也可以在使用Bitmap提供的函数如下:

public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

在原有位图旋转的基础上,创建新位图。

六、位图的截取

对于位图的规则形状如矩形的截取,很容易通过Matrix进行实现。例子如下:

Bitmap croppedImage;
// 取得裁剪的矩形
Rect r = mCrop.getCropRect(); 
if(r == null) return;
int width = r.width();
int height = r.height();
// 生成裁剪的图片
croppedImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(croppedImage);
Rect dstRect = new Rect(0, 0, width, height);
canvas.drawBitmap(mBitmap, r, dstRect, null);

而对于位图的不规则形状的截取,找不到很好的解决办法。曾想过直接对像素进行操作,最终因为算法的的复杂而放弃。一次偶然的机会,在阅读一篇博文时,我发现了使用android.graphics.Canvas的clipPath方法不仅仅可以达到高亮显示路径的目的,也能够达到任意形状截取图片的目的,从而解决了此问题。

以上就是对Android的位图功能进行的总结,以及一些自己实践中得出的想法和结论。更深入的内容需要仔细地去阅读研究Android SDK文档并付之于实践。