转自:http://blog.csdn.net/yihui823/article/details/6741411
今天看到一段很糟糕的代码。于是做了一个工程,模拟这段代码,向大家说明一下线程在使用中要注意的几点。这个例子适合给新手,也欢迎各位高手来指点一下。
首先,上代码。
第一个类LoginService,这是一个模拟类,把业务剥离出去了。只是模拟登录操作而已。
第二个类就是我们的Activity了。
增加了Log,以便查看登录情况。模拟业务代码只改了一行,就是登录永远是失败。现在运行一下呢。停在页面上没有动静了,logcat里也不断的打出:
we are login
这个也不会有什么错误,对吧。但是,我们如果按“返回”键退出页面,再看看logcat呢?
we are login的log还在不停的输出,是吗?
我想现在大家应该知道哪里出了问题了。就是说,我们的线程启动之后,就没法停掉了。
这里我要说一下。我一直认为,
new Thread(new Runnable() {…}().start();
这种代码写的非常的不好。你直接构造了一个对象,但是这个对象你没有任何的变量去指向它。这个线程被你启动之后,你已经无法再去跟踪、调用、管理了。这个线程,只能自生自灭,永远游离在你的控制范围之外。你会不会觉得,这个线程跟僵尸一样?对,这就是僵尸进程,如果它没有停止的条件,就永远在你的系统里消耗你的资源。
所以我觉得使用线程的一个基本认识:生成的线程类,一定要有一个变量去指向它,以便在合适的时候销毁。
这里说到销毁,这就是另一个问题了。Thread类已经废弃了stop方法了,因为线程需要自行去释放该释放的资源,不能光依赖于运行框架的控制。我们需要在Thread里面,加上他自己停止的代码。也就是说,不论如何,线程应该会自己去停止掉,而不应该是无限制的运行。
另外,我们在Android里面,还应该注意Activity的各个状态周转。一般来说,线程的启动在onCreate里是不合适的,我们必须考虑到onResume和onPause的情况。
那么,我们总结下,Activity里使用线程有三个注意:
1, 线程对象一定要有变量指向它,以便我们可以控制。
2, 线程类一定要有停止条件,以便外界通知线程自行停止。
3, 在onResume里启动线程,在onPause里停止线程。
我们根据以上三点,重新写一下Activity。
现在,我们的线程可以在页面退出的时候正常停止了。
但是这段代码还是有问题的。我们仔细看看,线程在Activity构造的时候就已经创建了,然后在程序进到前台的时候启动,退到后台的时候停止。但是线程有这么一个特性:
一旦线程的run()函数运行结束了,这个线程就销毁了,不能再启动了。
现在我们的程序,在退出后将不可能再次显示,所以系统会马上回收掉Activity。如果我们的页面增加一个按钮,迁移到另一个页面,那么在那个页面返回的时候,就会有异常出现。我们修改一下代码来试试。
增加一个Activity:
摘自http://www.cnblogs.com/maorongmaomao/archive/2012/08/29/2662258.html
写在开始之前:
早就想要好好总结下javascript的基础知识了,所以从这篇文章起,我会开始总结各种js的语法知识,作为一名以js开发为生计的前端工 程师,深深的着迷于js的语言魅力,而js最吸引人最强大的地方,便在于他独特的语法,能深刻的理解js的语法,是作为一个前端工程师的基本素质,在这 里,笔者在总结的同时,也希望前端朋友们给予自己的补充和见解。那么就让我们从js最最独特的语法,闭包,原型,词法作用域开始,接下来,笔者也会讨论 this,正则,浏览器的能力检测,事件代理等细节问题,以及html5,css3等前沿领域,今天我先试着总结下原型链相关的知识。笔者的经验和知识也 许会有很多不足之处,欢迎大家的更正与建议。
1.首先要知道的:
什么是原型链呢?
我们首先来说说继承,继承是面向对象语言的重要机制,通俗地讲就是子类可以拥有父类的方法和属性,js的原型链实际上也是一种继承,在ECMAScript标准中,只支持实现继承,而其实现实现继承就是主要依靠于原型链实现的。
那么,我们再来说说原型,原型其实就是上述所说的继承中的父类。
这样,原型链 显而易见的 可以理解为,利用原型串起一个继承链,让一个引用类型继承另一个引用类型的属性和方法,再以此类推下去。
构造函数,原型,与实例的关系:
这三者的关系用一句话概括为,每个构造函数都有一个原型,当new 一个构造函数的时候就会生成一个原型链上携带该构造函数的原型的实例。
2.原型链的生成过程:
上面基本了解了原型链的相关知识,那么原型链的生成过程是什么呢?
1 function parent(){ this.parent = "world"; 2 } 3 parent.prototype = { 4 a : function(){ 5 }, 6 b : "hello" 7 } 8 var child = new parent()
而child就是parent的一个实例,在执行构造函数的时候,js引擎创建一个空白对象,把__proto__指向parent的 prototype,然后把这个对象作为this调用parent进行初始化,这样一个简单的原型链就形成了 即 child --> parent.prototye -->Object.prototype。
prototype,constructor,__proto__ :
上面多次提到这几个属性,在原型链的概念中,如果能理解这几个属性的关系,那么离胜利就不远了。下面我们逐一的谈谈这几个属性:
1.prototype
prototype是构造函数的属性,指的就是构造函数的原型,在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类,
注意,只有构造函数这个属性才有这种效果哦~如果一个构造函数没有指定该属性,那么该属性下的__proto__会默认的指向原生Object的原型对象,该属性会变成一个对象,其中constructor属性指向本身。
2.constructor
constructor,如果通俗的理解,可以理解成原型对象的构造函数,当把一个对象(对象字面量)赋给一个构造函数的原型,constructor 会被复写,如果没有进行prototype的操作的话,constructor是函数声明时指定的,指定为本身:
例如:
function A() {}
则 A.prototype.constructor === A;
但是这个属性往往是不准确的:
1 function A() {} 2 function B() {} 3 B.prototype = new A(); 4 var b = new B(); 5 b.constructor; // A
上面的代码,按照constructor的含义,b的constructor应该指向B,但是确指向了A,原因如下
B.prototype = new A();之后B.prototype被复写为A的实例,
则B.prototype.constructor === A;
而b是B的实例则b.constructor === A;
如果想要实现继承,一般要进行constructor修复,即:
B.prototype = new A();
B.prototype.constructor = B;
1 function parent(){ 2 this.a = "world"; 3 this.b = "world"; 4 } 5 parent.prototype = { 6 a : "aaa", 7 b : "bbb", 8 c : "!" 9 } 10 function temp(){ 11 this.a = "hello"; 12 } 13 temp.prototype = new parent(); 14 var child = new temp(); 15 console.log(child.a +" " + child.b+child.c);//hello world!
上面的代码运行结果就为 : “hello world!”生成的原型链即是
child(temp的实例) > __proto__ > temp.prototype(parent的实例) > __proto__ >parent.prototype > __proto__ > Object.prototype >__proto__ > null
3.原型链的调用
如上所示,原型链由__proto__属性串联而成,经过上面的分析,调用的理解就变得很简单了,比如上例的原型链中,当调用child.c属性时,那么程序会按如下方式运行:
在child本层查找c,查询未果,会通过__proto__属性向上查找temp.prototype,查找未果,继续沿着 __proto__向上查找parent.prototype,oyeah~终于找到了~所以child.c在这里就会是 parent.prototype.c了~
怎么样,简单吧^ ^
最后的最后,呈上一个常用函数给大家:
1 function A() { 2 // do something here 3 } 4 A.prototype = { 5 a : 'aaa', 6 b : function() { 7 } 8 }; 9 function B() { 10 } 11 extend(B, A); 12 function extend(child, parent) { 13 function temp() { 14 this.constructor = child; 15 } 16 temp.prototype = parent.prototype; 17 child.prototype = new temp(); 18 }
extend函数 两个参数都为构造函数,目的是让一个构造函数(子类)继承另一个构造函数(父类)的原型,并且不调用父类的构造函数(有时候构造函数会需要一些参数或者做一些事情破坏了原型链),这个方法可以用来生成想要的原型链哦。
关于Android平台上短、彩信的拦截,网上已经有不少介绍性的文章。那些文章大多是介绍具体的实现方法,其提供的方法并不一定能够成功拦截。今天我们就来详深入地介绍一下拦截短、彩信的内部原理,并分析一下拦截方法的优劣。
Android接收短、彩信的原理正常的短信在到达手机后,会根据不同的制式(GSM、CDMA等)被解析并封装成SmsMessage对象。该对象会被SMSDispatcher进一步解析并分发。系统会将短信的“format”、“pdu”放到action为“android.provider.Telephony.SMS_RECEIVED”的Intent中。之后系统会将Intent以有序广播(Orderd Broadcast)的形式发出去。监听这两个广播的app收到广播后,通过对pdu的解析,就可以得到短信的具体内容。
彩信的情况与短信类似,只不过运营商首先会发一条WapPush短信。这类短信不包含彩信的具体内容,而是包含一条指向真实内容的URL地址。系统会将WapPush短信解析,提取“pduType”、“header”、“pdu”、“format”等信息放到action为“android.provider.Telephony.WAP_PUSH_RECEIVED”的Intent中。同样,系统会以有序广播的形式将Intent发出去。收到广播的app将intent解析,并提取其中的URL。之后访问该URL地址并下载实际的彩信内容。
有序广播的原理经过以上的分析,拦截短、彩信的关键就是拦截action为“android.provider.Telephony.SMS_RECEIVED”和“android.provider.Telephony.WAP_PUSH_RECEIVED”的广播。由于该广播是有序广播,所有Receiver 是按照顺序接受广播的。先收到广播的Receiver有权利终止该广播的传播。因此只要一个Receiver能够第一个收到广播并终止继续传播,那么就可以实现短、彩信的拦截。
这里简单介绍一下有序广播的传播方式。BroadcastReceiver 有静态和动态两种方式注册。
1. 静态注册:
静态注册是指在AndroidManifest.xml 中注册。方法如下:
<receiver android:name=“.XXXMessageReceiver”>
<intent-filter android:priority=“1000”>
<action android:name=“android.provider.Telephony.SMS_RECEIVED” />
</intent-filter>
</receiver>
2. 动态注册:
动态注册是通过调用Context类的register方法来注册。方法如下:
registerReceiver(BroadcastReceiver receiver,
IntentFilter filter);
registerReceiver(BroadcastReceiver receiver,
IntentFilter filter,
String broadcastPermission,
Handler scheduler);
注意,动态注册Receiver 的优先级是通过IntentFilter.setPriority(int)方法来设置的。当一个广播发生时,系统会将静态和动态的Receiver 按优先级(priority)进行排序,优先级越高越早处理。对于优先级相同的Receiver ,先处理动态的。
第三方网上的通常是采用静态注册的方式注册BroadcastReceiver ,并设置一个较高的优先级,例如,1000、9999等。经过以上对于广播原理的分析,可以看出,这种方式其实并不能够保证接受者会第一时间被触发。比较好的实现方法需要注意两点:
1. 采用动态方式而非静态方式注册。监听系统开机广播,系统启动之后采用动态方式注册BroadcastReceiver 。这样可以保证在第一时间将需要的BroadcastReceiver注册成功;
2. 并将优先级设为最高。注意,系统的最高优先级不是网上所说的1000、9999,而是2147483647。这是int类型能够表示的最大数值。
做到这两点可以大大增加拦截的成功率。由于第三方app本身的局限性,即使采用优化过的方法,第三方app还是不能够保证拦截的成功率。例如,如果有两个app都采用动态注册的方式,都设置其优先级为最高。那么其中一个就会被另一个拦截。
系统级拦截方案拦截短、彩信的根本方法还是需要在系统层面来解决。需要在系统提供一个专用的Service(类似于ActivityManagerService、PackageManagerService),用于判断收到的短信是否需要拦截。接口如下:
boolean needToBlockMsg(Intent intent);
系统在收到短信后,将短信内容组装到需要广播的Intent中。然后调用该接口判断是否需要拦截。如果需要拦截,则抛弃该短信,或者将其保存到特定的数据库中;如果不需要拦截,那么就照常发送广播。这样可以一劳永逸地解决短、彩信的拦截问题。
由于需要修改“/system/framework/framework.jar”之类的系统文件,第三方app是无法实现的,因此需要设备生产厂商的支持。
更多内容请关注http://blog.sina.com.cn/u/3194858670以及sina微博@安卓安全小分队