SegmentFault Kylin_Mountain最新的文章
2016-12-16T17:24:31+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
JavaScript学习笔记第五天_错误处理与
https://segmentfault.com/a/1190000007828671
2016-12-16T17:24:31+08:00
2016-12-16T17:24:31+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<p><a href="https://link.segmentfault.com/?enc=xuRtEmuAKBCvxE69bxa5LQ%3D%3D.%2FD5vH8y53mSB5sNGBEPPz%2BaTsHIWMffsW8nwHsgBrr%2Fcu75Xx9yzN%2BPq3HOPI%2FfM9MtSA%2BVer2%2FOY7OLvvo4Ly%2FaVMhK7YtjIpS%2Fm0NNX5%2FS3sTep3YpP0GUsYneG4xY" rel="nofollow">廖雪峰老师的javascript教程学习笔记</a></p>
<h3>1. 错误处理</h3>
<p>JavaScript 提供了像Java一样的错误处理机制,即try catch finally.<br>例如:</p>
<pre><code>try{
var s = null;
s.length; // TypeError: Cannot read property 'lenght' of null
}catch(err){
console.log('null pointer' + err);
}finally{
// clear resource.
}</code></pre>
<p>其中catch不是必须的,finally也不是必须的,但二者必须有其一,其中finally是必定会被执行的。</p>
<p>错误类型,TypeError继承于Error,还有ReferenceError等错误处理对象,可以通过捕获到的e,通过instance判断出的具体的Error类型。</p>
<p>它还允许抛出异常,关键字throw,像java一样。</p>
<pre><code>var n = 'ss';
if(isNaN(n)){
throw new Error('input error');
}
</code></pre>
<p>实际上,JavaScript允许抛出任意对象,包括数字、字符串。但是,最好还是抛出一个Error对象。</p>
<h3>2. 错误传播</h3>
<p>向上一层传递,就像Java的Exception一样,Java会把整个调用栈都会打出来,JS也同样。</p>
<h3>3. 异步错误处理</h3>
<p>JavaScript引擎是一个事件驱动的执行引擎,代码总是以单线程执行,而回调函数的执行需要等到下一个满足条件的事件出现后,才会被执行。</p>
<pre><code>function printTime() {
throw new Error();
}
try {
setTimeout(printTime, 1000);
console.log('done');
} catch (e) {
alert('error');
}
</code></pre>
<p>其中window.setTimeout(func, delay, pars...)其中func函数将在超时后执行。由于是异步,所以setTimeout会立即执行完成,并执行下一句打印log,然后等到超时后,被触发执行,然后throw error,而这时并不会被catch。</p>
JavaScript学习笔记第四天_面向对象编程
https://segmentfault.com/a/1190000007817533
2016-12-15T18:54:45+08:00
2016-12-15T18:54:45+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h3>1. 基础</h3>
<p>JavaScript不区分类和实例的概念,而是通过原型来实现面向对象编程。<br>Java是从高级的抽象上设计的类和实例,而JavaScript的设计理念,听起来就好比Heros里的Peter,可以复制别人的能力。JavaScript就是别人的所有属性都拷贝过来,成为自己的一部分,并能够保留自身的能力。</p>
<p>看廖老师的图片,应该就能感觉出咋回事了,xiaoming这个实例把自己的__proto__指向Student就实现了继承,这种继承关系是脆弱的,也是动态可以修改的。<br><img src="/img/bVGXH2?w=423&h=168" alt="l" title="l"></p>
<p>但是JavaScript是不推荐直接使用__proto__进行继承的。提供了一个<code>Object.creat(原型)</code>来创建对象。这是JavaScript的一种原型继承的方法,如下实例。</p>
<pre><code>var Student = {
name : "robot",
height:180,
run:function(){
console.log(this.name + " is running");
},
grade:()=>"4"+"grade"
};
function createStudent(name){
var s = Object.create(Student);
// init new object
s.name = name;
return s;
};
var xiaoming = createStudent("xiaosfsffsfsf");
</code></pre>
<h3>2. 创建对象</h3>
<blockquote><p>当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。</p></blockquote>
<p>以上说明JavaScript引擎有个追朔系统,优先使用当前域的进行查找,不行则向上一个原型内进行查找。</p>
<p>除了使用{...}进行创建对象,还可以使用new 的方法,需要先定义一个构造函数,如下所示。如果使用new那么这个函数就会默认返回this这个对象,如果不是用new,直接调用,那么这个函数就返回undefined,像普通的函数一样。</p>
<pre><code>function Student(name){
this.name = name;
this.hello = function(){
alert('hello'+name);
}
}
var stu = new Student('XiaoMing');</code></pre>
<p>新创建的stu的原型链是</p>
<pre><code>stu ----> Student.prototype ----> Object.prototype ----> null
</code></pre>
<p>用new Student创建的对象stu,还从Student上继承了constructor属性,它指向Student本身。</p>
<pre><code>stu.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true
Object.getPrototypeOf(stu) === Student.prototype; // true
stu instanceof Student; // true
</code></pre>
<p>这个原型链还是盗用liaoxuefeng老师的图<br><img src="/img/bVCy6S?w=632&h=250" alt="l" title="l"><br>可以看出实际上Student,xiaoming,xiaohong的原型都是指向Student.prototype。当前每个对象的hello方法都是不同的,属于不同的对象。但根据方法查找规则,如果把hello放在Student.prototype上,就可以实现共用同一个方法,节省内存。即:</p>
<pre><code>function Student(name) {
this.name = name;
}
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
};
</code></pre>
<p>另外,注意到构造函数里的属性,都没有经过var进行初始化,而是直接使用<strong>this.xxx</strong>进行绑定。所以如果没用new,而是直接调用构造函数,那么将会使this指向window,然后内部的各个属性都将添加到window上,无意中添加全局变量。并且在strict模式下,构造函数没有使用new进行调用,也会导致报错。</p>
<pre><code>***调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。***
</code></pre>
<p>练习</p>
<pre><code>function Cat(name) {
this.name = name;
}
Cat.prototype.say = function(){
return "Hello, "+this.name+"!";
}
</code></pre>
<p>在此基础上,我们还可以创建一个createCat()函数,在内部封装所有的new 操作。</p>
<pre><code>function Cat(props) {
this.name = props.name || '波斯猫';
this.color = props.name || '黑白';
}
Cat.prototype.say = function(){
return "Hello, I am " + this.color + this.name+"!";
}
function createCat(props){
return new Cat(props || {});
}</code></pre>
<p>这样就不需要new 操作了,参数也很灵活,可以不传入,也可以传少量的,其他的属性将会由默认的值替代。而且参数不需要考虑顺序,可对收到的JSON直接生成对象。</p>
<h3>3. 原型继承</h3>
<p>JavaScript的原型继承实现方式就是:<br>定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this;<br>借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;<br>继续在新的构造函数的原型上定义新方法。</p>
<p>廖老师的一张图简单扼要说明这个继承模型。<br><img src="/img/bVGX7v?w=624&h=163" alt="l" title="l"></p>
<h3>4. class继承</h3>
<p>其实3是不太重要的,因为ES6已经推出了class这个关键字来解决繁琐的原型链继承的复杂性,就像java一样好,但底层其实还是原型链继承实现,这一点并没有变化。</p>
<p>class Cat extends Animal{</p>
<pre><code>constructor(name){
super(name);
}
say(){
return 'Hello, '+this.name+'!';
}</code></pre>
<p>}</p>
<p>say()方法,实例依然是共享的。but,这个需要较新的浏览器支持,一般还用不了,但是可以使用Babel这个库去兼容这个玩意,其实我不了解这是个啥库。</p>
JavaScript学习笔记第三天_对象
https://segmentfault.com/a/1190000007790647
2016-12-13T20:48:12+08:00
2016-12-13T20:48:12+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h2>
<a href="https://link.segmentfault.com/?enc=%2BlYbFRq6JLo1Y6FdW8JBsQ%3D%3D.qpCTS5Yp7IMLqSyROeImSwwbDVi6Ax%2FgKxCCfc7chqlx%2BnmIGjtqQrvdZi%2FLKt%2B7thXzPz%2Fhj0vqrZoOkoZZZnZ4QgasqbRT%2F71TbIkC400CxkcWLJlHhkD5hovQFM9o" rel="nofollow">廖雪峰的JavaScript教程</a>学习笔记</h2>
<h3>1. 标准对象</h3>
<p>使用typeof判断值的类型,可以看到NaN是number类型,null是obejct类型,undefined 就是undefined类型,另外还有一个function这种基础类型。</p>
<pre><code>typeof 123; // 'number'
typeof NaN; // 'number'
typeof 'str'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Math.abs; // 'function'
typeof null; // 'object'
typeof []; // 'object'
typeof {}; // 'object'
</code></pre>
<p>JavaScript也有包装类型,如Number,String,Boolean。但没有Integer,因为javascript没有int类型,所以也没有Integer。这些类型也可以用来直接对对象进行转换,就像是parseInt一样。你可以使用Number来对String进行转换,如:</p>
<pre><code>var a = Number('123');
</code></pre>
<p>也可以使用</p>
<pre><code>var b = new Number(123);
</code></pre>
<p><strong><em>Caution:廖老师最好不要使用包装对象,不然会有很多问题,尤其是string。</em></strong></p>
<p>注意点</p>
<ul>
<li><p><strong>不要使用</strong>new Number()、new Boolean()、new String()创建包装对象;</p></li>
<li><p>用parseInt()或parseFloat()来转换任意类型到number;</p></li>
<li><p><em>用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;</em></p></li>
<li><p>通常不必把任意类型转换为boolean再判断,因为可以直接写<code>if (myVar) {...}</code>;</p></li>
<li><p>typeof操作符可以判断出<code>number、boolean、string、function和undefined</code>;</p></li>
<li><p>判断Array要使用<code>Array.isArray(arr)</code>;</p></li>
<li><p>判断null请使用<code>myVar === null</code>;</p></li>
<li><p>判断某个全局变量是否存在用<code>typeof window.myVar === 'undefined'</code>;</p></li>
<li><p>函数内部判断某个变量是否存在用<code>typeof myVar === 'undefined'</code>。</p></li>
</ul>
<p>其中并不是每个类型都支持直接调用某个对象的toString,比如number, null, undefined。<br>对function调用toString,会返回整个function的定义,如下:</p>
<pre><code>var a = function(x){return 10};
a.toString();
> "function (x){return 10}"</code></pre>
<p>生气的javaScript还给number类型,添加了神奇的方式来使能toString()的直接调用,就像虾米那这样。</p>
<pre><code>123..toString(); // '123', 注意是两个点!
(123).toString(); // '123'</code></pre>
<p>我也是醉了呀。</p>
<h3>2. Date</h3>
<p>这个看起来没什么好说的额,就是Date的一些API。</p>
<pre><code>var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳
</code></pre>
<p><strong>注意月份范围是0~11,5表示六月</strong></p>
<p>有两种创建日期的方式,<code>var d = new Date(2015, 5, 19, 20, 15, 30, 123);</code>其中123是毫秒。第二种是使用ISO 8601标准方式创建<code>var d = Date.parse('2015-06-24T19:49:22.875+08:00');</code>返回时间戳,然后可以通过new Date(d);进行转换。</p>
<p>Date.now()可以直接返回时间戳。</p>
<h3>3. RegExp正则表达式</h3>
<p>用\d可以匹配一个数字,\w可以匹配一个字母或数字。<br>.可以匹配任意字符<br>用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符<br>[]表示范围,即匹配对象在[]范围中。如常见的[0-9a-zA-Z_]<br>^表示行的开头,^d表示必须以数字开头。<br>$表示行的结束,d$表示必须以数字结束。</p>
<p>JavaScript中,使用<code>/正则表达式/</code>或者是<code>new RegExp('正则表达式')</code>。然后使用test('str')方法进行测试判断,返回true/false。</p>
<pre><code>var re = /^\d{3}\-\d{3,8}$/;
var re1 = new RegExp('^\d{3}\-\d{3,8}$}');
re.test('010-2345');
> true
</code></pre>
<p>切分字串<code>'a,b;; c d'.split(/[s,;]+/); // ['a', 'b', 'c', 'd']</code><br>用()进行提取字串,他会将()里的正则表达式匹配的字串进行分组,使用exec()方法,返回Array数组,第一个为整个字串,然后是匹配的字串,如果提取失败返回null。</p>
<pre><code>var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
</code></pre>
<p>特性是:贪婪匹配,匹配尽可能多的字串,如下,第一个为整个字串,第二个是匹配\d+, 第三个空串为匹配0*,因为字串已经被\d+匹配了,因此后者没得匹配了。所以在写匹配表达式是要尽可能降低匹配的长度。</p>
<pre><code>var re = /^(\d+)(0*)$/;
re.exec('102300'); // ['102300', '102300', '']
</code></pre>
<p>全局匹配,类似搜索,需要加入标志位。/regexp/g 或者是new RegExp('regexp', g);<br>正则表达式还可以指定i标志,表示忽略大小写,m标志,表示执行多行匹配。如</p>
<pre><code>var s = 'JavaScript, VBScript, Fuckscript,JScript and ECMAScript';
var re = /[a-zA-z]+script/gi;</code></pre>
<p>忽略大小写匹配带有Script的全字母字串。</p>
<p>最后匹配邮箱<code>var re = /^[a-zA-Z0-9.#-]+@[a-zA-Z0-9]+.[a-zA-Z0-9]+$/;</code></p>
<h3>4. JSON</h3>
<p>JSON包含以下几种数据类型,字符集必须是UTF-8,JSON的字符串规定必须用双引号"",Object的键也必须用双引号""。</p>
<pre><code>number:和JavaScript的number完全一致;
boolean:就是JavaScript的true或false;
string:就是JavaScript的string;
null:就是JavaScript的null;
array:就是JavaScript的Array表示方式——[];
object:就是JavaScript的{ ... }表示方式。</code></pre>
<p>JavaScript内置JSON解析引擎,可直接将对象序列化为JSON字串序列。</p>
<pre><code>var xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};
JSON.stringify(xiaoming);
// 或者使用带缩进的显示,最后一个参数是四空格的字串,也可以使用-等其他缩进的符号
// JSON.stringify(xiaoming, null, ' ');
// 输出如下
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" Middle School",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}
</code></pre>
<p>定义</p>
<blockquote><p>JSON.stringify(value[, replacer [, space]])</p></blockquote>
<p>其中replacer是个可选参数,它可填入以下值:</p>
<ul>
<li><p>如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;</p></li>
<li><p>如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;</p></li>
<li><p>如果该参数为null或者未提供,则对象所有的属性都会被序列化;</p></li>
</ul>
<p>所以你也可以填入一个函数或者数组,进行转换或者过滤。<br>如需精确控制序列化,可以在对象中自定义一个toJSON函数,那么JSON.stringify(xiaoming)时会自动调用toJSON方法,没看过源码还不清楚怎么实现的,感觉应该是看这个对象是否有hasOwnProperty(toJSON)吧,然后再判断返回的是不是一个函数,若是就调用该函数。并将结果返回。</p>
<pre><code>var xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp'],
toJSON: function () {
return { // 只输出name和age,并且改变了key:
'Name': this.name,
'Age': this.age
};
}
};
</code></pre>
<p>反序列化,即从JSON字符串中还原出一个对象,使用方法就是JSON.parse();</p>
<pre><code>JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
JSON.parse('true'); // true
JSON.parse('123.45'); // 123.45
</code></pre>
<p>这个感觉蛮简单的。<br>语法:</p>
<blockquote><p>JSON.parse(text[, reviver])</p></blockquote>
<p>如果是一个函数,在被返回之前,规定了原始值如何被解析改造。<br>看看<a href="https://link.segmentfault.com/?enc=Ha70hUYJlkaweVXcPXnHWg%3D%3D.2v%2BnUS%2FTv7BGgl4MABWAdI58E1ROX%2BN1V6XL8RlVNpupfuNMVyaEc5TzA9kjL29ECdPrlMFVlc9rwkDhmdMgioW9KTelAJTTW3a5vsQykp%2FGtaNtPp0XDhbaq%2BfjYS73" rel="nofollow">MDN</a>的对于这个函数的解释吧,略长,我把重点加粗一下。<br>如果指定了 reviver 函数,则解析出的 JavaScript 值(解析值)会经过一次转换后才将被最终返回(返回值)。更具体点讲就是:解析值本身以及它所包含的所有属性,会按照一定的顺序(从最最里层的属性开始,一级级往外,最终到达顶层,也就是解析值本身)分别的去调用 reviver 函数,在调用过程中,<strong><em>当前属性所属的对象会作为 this 值</em></strong>,<strong>当前属性名和属性值会分别作为第一个和第二个参数传入 reviver 中</strong>。<strong>如果 reviver 返回 undefined,则当前属性会从所属对象中删除</strong>,如果返回了其他值,则返回的值会成为当前属性新的属性值。</p>
<p>当遍历到最顶层的值(解析值)时,传入 reviver 函数的参数会是空字符串 ""(因为此时已经没有真正的属性)和当前的解析值(有可能已经被修改过了),<strong>当前的 this 值会是 {"": 修改过的解析值}</strong>,在编写 reviver 函数时,要注意到这个特例。</p>
<p>按照以上解释,我们可以尝试返回undefined,然后删除某个属性。</p>
<pre><code>JSON.parse('{"name":"xiaoming","age":14, "height":180}', function(k,v){
if(typeof v === 'string'){
return undefined;
}
return v;
});
// 输出,删除了name属性及其值。
> Object {age: 14, height: 180}
</code></pre>
<p>美化一下<a href="https://link.segmentfault.com/?enc=BEKfFm9gnM6mWXJFbHZ1kQ%3D%3D.Na6Za%2Fl1OSPnxDgfNrK6UfhKG%2B%2FYcC7e%2FVq62GXFyjB5M6RObzMiT4XG82E0IPZHHvgDZ49qS91mnOLJP52Qow%3D%3D" rel="nofollow">yahoo 天气</a>返回的JSON。<br>大概是这样子的:</p>
<pre><code>JSON.stringify(weather, null, ' ');
// 输出
"{
"query": {
"count": 1,
"created": "2016-12-14T12:40:29Z",
"lang": "zh-CN",
"results": {
"channel": {
"units": {
"distance": "mi",
"pressure": "in",
"speed": "mph",
"temperature": "F"
},
"title": "Yahoo! Weather - Beijing, Beijing, CN",
"link": "http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/",
"description": "Yahoo! Weather for Beijing, Beijing, CN",
"language": "en-us",
"lastBuildDate": "Wed, 14 Dec 2016 08:40 PM CST",
"ttl": "60",
"location": {
"city": "Beijing",
"country": "China",
"region": " Beijing"
},
"wind": {
"chill": "28",
"direction": "215",
"speed": "11"
},
"atmosphere": {
"humidity": "33",
"pressure": "1025.0",
"rising": "0",
"visibility": "16.1"
},
"astronomy": {
"sunrise": "7:29 am",
"sunset": "4:50 pm"
},
"image": {
"title": "Yahoo! Weather",
"width": "142",
"height": "18",
"link": "http://weather.yahoo.com",
"url": "http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"
},
"item": {
"title": "Conditions for Beijing, Beijing, CN at 07:00 PM CST",
"lat": "39.90601",
"long": "116.387909",
"link": "http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/",
"pubDate": "Wed, 14 Dec 2016 07:00 PM CST",
"condition": {
"code": "31",
"date": "Wed, 14 Dec 2016 07:00 PM CST",
"temp": "34",
"text": "Clear"
},
"forecast": [
{
"code": "32",
"date": "14 Dec 2016",
"day": "Wed",
"high": "36",
"low": "21",
"text": "Sunny"
},
{
"code": "32",
"date": "15 Dec 2016",
"day": "Thu",
"high": "42",
"low": "21",
"text": "Sunny"
},
{
"code": "30",
"date": "16 Dec 2016",
"day": "Fri",
"high": "40",
"low": "21",
"text": "Partly Cloudy"
},
{
"code": "30",
"date": "17 Dec 2016",
"day": "Sat",
"high": "44",
"low": "21",
"text": "Partly Cloudy"
},
{
"code": "34",
"date": "18 Dec 2016",
"day": "Sun",
"high": "44",
"low": "24",
"text": "Mostly Sunny"
},
{
"code": "32",
"date": "19 Dec 2016",
"day": "Mon",
"high": "46",
"low": "25",
"text": "Sunny"
},
{
"code": "30",
"date": "20 Dec 2016",
"day": "Tue",
"high": "47",
"low": "29",
"text": "Partly Cloudy"
},
{
"code": "30",
"date": "21 Dec 2016",
"day": "Wed",
"high": "45",
"low": "29",
"text": "Partly Cloudy"
},
{
"code": "30",
"date": "22 Dec 2016",
"day": "Thu",
"high": "41",
"low": "28",
"text": "Partly Cloudy"
},
{
"code": "30",
"date": "23 Dec 2016",
"day": "Fri",
"high": "38",
"low": "24",
"text": "Partly Cloudy"
}
],
"description": "<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/31.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Clear\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Wed - Sunny. High: 36Low: 21\n<BR /> Thu - Sunny. High: 42Low: 21\n<BR /> Fri - Partly Cloudy. High: 40Low: 21\n<BR /> Sat - Partly Cloudy. High: 44Low: 21\n<BR /> Sun - Mostly Sunny. High: 44Low: 24\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>",
"guid": {
"isPermaLink": "false"
}
}
}
}
}
}"</code></pre>
JavaScript学习笔记第二天_函数
https://segmentfault.com/a/1190000007755878
2016-12-10T10:20:37+08:00
2016-12-10T10:20:37+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h2>
<a href="https://link.segmentfault.com/?enc=zAttFSc0ElSG8he4t%2BCYNA%3D%3D.bJRKhtjHZVMpXhL3dnAmbPElkOPl7J%2Bza%2BEG6ZqJkZ4lvsy%2BFUCC09ptTYVHgcRFSgxRCCDim57oYC6wwh%2BUi%2FwmgeLKejQk%2BLGDqgLj5xOVVfNV6kkCaFD1ClXcRg9A" rel="nofollow">廖雪峰的JavaScript教程</a>学习笔记</h2>
<h3>1. 变量作用域</h3>
<p><code>var</code> 不能声明块级的变量,js的函数内变量声明会被提升至函数体开头<br><code>let</code> 则用来解决这个块级变量声明,于ES6引入。<br><code>const</code>用于声明常量,也是ES6引入。</p>
<h3>2. 命名空间</h3>
<p>全局变量会被默认绑定到<code>window</code>,不同JS文件如果定义了相同的名称的全局变量或者顶级函数,那么就会导致冲突。因此,解决方法就是把自己的全局变量绑定到一个全局变量中,类似于Java中的class,个人感觉。</p>
<h3>3.this 关键字</h3>
<pre><code>var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
</code></pre>
<p>因为xiaoming是个对象,所以方法内部的this就是指当前xiaoming这个对象,和java对象内部使用的this是一个意思。但是如果在方法内不又定义了方法,并在这个内部方法里引用this,那么这个this将报错,undefined或者window。如果从java角度看的话,有点像是内部类里使用this因而出错。 但也可以在内部方法的外层,捕获this对象,比如赋值给that。</p>
<h3>4. apply 和 call</h3>
<p>每个函数都自带<code>apply</code>和<code>call</code>方法,其中<code>apply</code>接受两个参数<code>(object, arguments[])</code>, 即传入对象本身和参数数组,这样就方法体内的<code>this</code>就会自动指向<code>object</code>,从而避免this乱指。</p>
<p><code>call</code>方法与<code>apply</code>具有同样的功能,第一个也是<code>object</code>自己,之后是参数列表,非<code>Array</code>。<br>普通函数一般将obejct赋值为<code>null</code>。<br>比如</p>
<pre><code>Math.max.call(null, 3,4,4,5,6,6,67,8,9);
Math.max.apply(null, [3,4,4,5,6,6,67,8,9]);
</code></pre>
<h3>5. 装饰器</h3>
<p>javascript所有的对象都是动态,即使是内置的函数,也可以重新指向新的函数。<br>如window上有个全局函数parseInt()函数,需要统计parseInt被调用次数。</p>
<pre><code>var count = 0;
var oldParseInt = parseInt; // 保存原函数
window.parseInt = function () {
count += 1;
// arguments是函数的参数数组
return oldParseInt.apply(null, arguments); // 调用原函数
};
// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3
</code></pre>
<p>可以看出来JavaScript如此轻易就实现了装饰器,java哭晕在厕所。</p>
<h3>6. 高阶函数</h3>
<p>定义:高阶函数英文叫Higher-order function。<br>JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。这个厉害了,WOW。<br>里面也涉及到一个javascript的函数的参数是可以传入多个或不传的原理,所以方法体内才可以直接对传入变量输入参数,并且个数不限制。直接show me the fucking source code。</p>
<pre><code>function add(x, y, f){
return f(x) + f(y);
}
var f = Math.abs;
add(6, -3, f); // 输出9
</code></pre>
<h3>7. 高阶函数map/reduce</h3>
<p>666啊,这个map/reduce不是大数据用的么?居然是js的高阶函数啊,看起来好吊啊,真是吊打java了。<br>网上找了一段解释:</p>
<blockquote><p><code>array.map(callback[, thisArg]);</code><strong>map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数</strong>。callback 每次执行后的返回值组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。</p></blockquote>
<p>callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。<br>如果 thisArg 参数有值,则每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象。如果省略了 thisArg 参数,或者赋值为 null 或 undefined,则 this 指向全局对象 。</p>
<p>如对每个元素进行平方运算。</p>
<pre><code>var pow = function(x){
return x*x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow);
</code></pre>
<p>输出结果是<code>[1, 4, 9, 16, 25, 36, 49, 64, 81]</code>。<br>所以结合上面的解释就是map对数组arr的每个元素调用一次callback即pow()函数。由于map会给callback传入三个参数,而pow只取了第一个参数,即数组元素,所以结果是对每个元素平方,然后组成成新的数组。</p>
<p>我们可以试着修改pow,去把传入到callback的第二个参数也使用上,那么我们来试试给给每个元素平方后加上他的索引值。</p>
<pre><code>var pow = function(x){
return x*x+arguments[1];
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow);</code></pre>
<p>输出结果为<code>[1, 5, 11, 19, 29, 41, 55, 71, 89]</code>。<br>更为具体的map定义可以看<a href="https://link.segmentfault.com/?enc=V1aj5Sc4WDCWnJhoIdMrHA%3D%3D.j1IExjF058iXPUfAGewRIvDJ3IEl2Ay8PR5FSmRDHLHbnObXOJZJeI%2FengznOJ89mVf%2BOrW%2BoCrsp82ScZQ1i4cClAzVmKMFRNtGYeTAAcU%3D" rel="nofollow">MSDN</a>的解释。</p>
<p>接下来看看<a href="https://link.segmentfault.com/?enc=iP2F3NK7H9M33vxvbLlkYg%3D%3D.klEZBae8nyPPnOlACDx03i9eftZJRT3L59rUpgUOesHQiJ%2F2vu9%2FAzjLX%2BMNX%2FYYHK0YMQjA8GizNWwg%2FLoc36rcQqiurHKlgZXaIqwrnqM%3D" rel="nofollow">reduce</a>函数。同样作为array的方法,reduce是对数组的每一个元素与其下一个元素调用callback一次方法,并将callback的结果作为下一次的参数与下一个元素再次调用callback方法,进行累积运算。有点绕,看数学公式可能更好理解</p>
<blockquote><p>[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)</p></blockquote>
<p>所以我们来进行一个求和,计算1到100和运算。</p>
<pre><code>var arr = [];
for(var i = 1; i <= 100; i++){
arr[i-1] = i;
}
arr.reduce(function (x, y){
return x+y;
});</code></pre>
<p>输出结果为<code>5050</code>。<br>再来看看具体reduce定义</p>
<blockquote><p>arr.reduce(callback[,initialValue])<br>initialValue作为reduce方法的可选参数,callback接收4个参数, accumulator 是累积值,如果reduce方法设定了initialValue,那么accumulator的初始值就是initialValue。</p></blockquote>
<ul>
<li><p>accumulator</p></li>
<li><p>currentValue</p></li>
<li><p>currentIndex</p></li>
<li><p>array</p></li>
</ul>
<p>所以我们可以继续尝试设定初始的方式</p>
<pre><code>var arr = [];
for(var i = 1; i <= 100; i++){
arr[i-1] = i;
}
arr.reduce(function (x, y){
return x+y;
}, 100);</code></pre>
<p>我们将初始值设为100,那么输出结果是5150。</p>
<p>测试题</p>
<ol>
<li>
<p>不要使用JavaScript内置的parseInt()函数,利用map和reduce操作实现一个string2int()函数:</p>
<p>function string2ints(s){</p>
<pre><code>return s.split('').map(function(x){
return x*1}).reduce(function(x, y){
return x*10+y*1;
});</code></pre>
<p>}</p>
</li>
<li>
<p>请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:<code>['adam', 'LISA', 'barT']</code>,输出:<code>['Adam', 'Lisa', 'Bart']</code>。</p>
<pre><code>// comment on this
var arr = ["LINda", "adam", "barT"];
arr.map(function(x){
return x.toLowerCase();}).map(function(x){
return x[0].toUpperCase()+x.slice(1);
});</code></pre>
</li>
<li><p>小明的疑惑在与没搞懂parseInt的接受的参数,因为map的callback默认是会传入三个参数,而parseInt的参数有两个parseInt(string, radix);那么此时parseInt自动忽略传入的第三个参数array,而传入了(element, index)。索引改变了进制,因而得到错误的结果。<br>这个感觉是个坑,还不清楚IDE是否会提示函数或者方法的参数为啥。</p></li>
</ol>
<h3>8.高阶函数filter</h3>
<p>语法:<code>var new_array = arr.filter(callback[, thisArg])</code><br>filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。与map相同的是,它也接受一个函数callback,然后对每个元素调用函数进行处理,不同的地方在于,函数需要返回true或false,用户决定是保留该元素还是删除该元素,最后生成一个新的数组,如其名字所决定的一样。thisAgrs不用解释。</p>
<p>其中callback同样接收3个参数element,index,array。</p>
<p>简单的做个去重操作和过滤掉奇数。</p>
<pre><code>var arr = [1,2,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9];
arr.filter(function(x){return x % 2 === 0;})
.filter(function(element, index, self){
return self.indexOf(element) === index;}
);
</code></pre>
<p><code>self.index(element)</code>只会返回找到的第一个元素的index,因此后续的index就不再相等了。</p>
<p>在做一个找出1-100以内所有的质数或者说素数。<br>质数或素数定义</p>
<blockquote><p>质数大于等于2 不能被它本身和1以外的数整除</p></blockquote>
<p>做法:在一般领域,对正整数n,如果用2到<code>sqrt(n)</code>之间的所有整数去除,<strong>均无法整除</strong>,则n为质数。<br>代码:</p>
<pre><code>function getPrimes(arr){
return arr.filter(function(x){
if(x <= 3) return x>1; // 大于等于2才可以为质数,所以1除外
// 2到sqrt(n)之间均无法整除,即只要有一个可以整除即非质数。
var end = Math.round(Math.sqrt(x));
for(let i=2; i <= end; i++){
if(x % i === 0) return false;
}
return true;
});
}</code></pre>
<h3>9.高阶函数sort</h3>
<p>[1, 2,3,10,24].sort();<br>输出结果是[1, 10, 2, 24, 3]。javascript或默认将数组里数字转换成string去比较,那么根据ASCII嘛,自然是1要比2小,所以10就在2的前面。<br><img src="/img/bVGJvF?w=150&h=150" alt="l" title="l"></p>
<p>But, 高阶函数嘛,那么自然sort也是可以接受函数来进行修改比较算法的,类似于要实现Java里的comparable接口,规则也是类似的,若x < y, 则返回-1,表示小于;若x === y, 返回0;若x > y,则返回1。理论上,并不要定义为1或者-1,只要用正数表示大于,负数表示小于就好了。所以你可以直接使用x-y;</p>
<p>那就数字排个序好了。</p>
<pre><code>arr = [12343,5435,6,7,2,3,77,88,322];
arr.sort(function(x, y){
return x-y;
});
</code></pre>
<blockquote><p><strong>Caution:</strong><strong>这个sort方法会将排序结果直接作用在数组本身上,即会修改arr。</strong></p></blockquote>
<h3>10.闭包</h3>
<p>高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。<br>比如返回求和的函数。</p>
<pre><code>function lazy_sum(arr){
var sum = function(){
return arr.reduce(function(x, y){
return x+y;
});
return sum;
}
// 使用时
var f = lazy_sum([1,3,2,5]);
f();
var f1 = lazy_sum([1,3,2,5]);
f === f1; // result is false.
</code></pre>
<p>由于返回的是个函数,即使传入的参数相等,也相当于是返回了不同的函数对象。</p>
<p><strong>返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。</strong><br>这个例子返回的函数引用了局部变量, 如果局部变量后续会变化,那么最好是再创建一个函数,绑定局部变量作为参数。</p>
<p>创建匿名函数并立刻执行.</p>
<pre><code>(function (x) { return x * x }) (3);</code></pre>
<p>其中function (x) { return x * x }是匿名函数,需要使用括号括起来才可以立刻执行,不然会报语法错误。</p>
<p>使用闭关封装一个私有变量private。</p>
<pre><code>function create_counter(initial){
var x = initial || 0;
return {
inc:function(){
x += 1;
return x;
}
}
}
</code></pre>
<p>create_counter()返回了一个匿名对象,这个对象有个属性inc,而这个inc的值又是一个函数。那么就可以获取这个匿名对象,然后调用函数inc()即可实现对private的x加1.</p>
<p>缩减参数列表的用法</p>
<pre><code>function make_pow(n){
return function(x){
return Math.pow(x,n);
}
}
var pow2 = make_pow(2);// 返回一个Math.pow(x, 2)的函数。
var pow3 = make_pow(3);// 返回一个Math.pow(x, 3)的函数。</code></pre>
<p>pow2 和 pow3就只需要接收一个参数,即可实现power的操作。</p>
<p>底下的Lambda表达式加法没能理解呀。</p>
<h3>11.箭头函数</h3>
<p>ES6 新引入的</p>
<pre><code>x => x * x</code></pre>
<p>相当于如下的匿名函数,其中x为参数。</p>
<pre><code>function (x){
return x*x;
}
</code></pre>
<p>如果有多个参数需要使用括号括起来,如<code>(x,y) => x+y;</code>。<br>用法呢和匿名函数也差不多,比如:</p>
<pre><code>var pow2 = x=> x*x;
pow2(2); // 4
// 创建匿名函数并执行
(x => x*x)(2); // 4</code></pre>
<p>还有一种包含多条语句的,需要使用<code>{}</code>将其包起来。如:</p>
<pre><code>(x, y) => {
if(x>y){
return x-y;}
else{
return y-x;}
}</code></pre>
<p>还有类似于<code>()=>3.14</code>的写法,虽然看起来没啥卵用。</p>
<p>可变参数写法,就是参数变了一下</p>
<pre><code>(x, y, ...rest) =>{
var sum= x =y;
for(var x of arguments){
sum += x;
}
return sum;
}
</code></pre>
<p>廖老师说<code>x => { foo: x }</code>这样写会语义错误,然而并没有。但是确实没能够被正确解析,所以当返回匿名对象的时候,使用<code>()</code>将其包起来,如x => ({ foo: x })。</p>
<p>需要注意的是<strong><em>箭头函数内部的this是词法作用域,由上下文确定。</em></strong>箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj。那么在第4节里说的每个函数都自带apply和call方法,他们的第一个参数object就都不在有用,因为箭头函数会自动绑定外层调用者obj。</p>
<h3>12. generator</h3>
<p>ES6新引入的数据类型,看上去像是一个函数,但是可以返回多次。<br>与函数定义不同的是,使用<strong><em><code>function*</code></em></strong>来定义一个generator,除了使用return退出函数之后,还可以使用yield交出控制权,这时并没有退出整个函数,使用next时,就会从当前yield处往下执行。</p>
<p>以斐波那契数列为例:<code>[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]</code><br>普通的使用数组来保存结果的斐波那契数列生成方法</p>
<pre><code>function fib(max){
var
t,
a = 0,
b = 1,
arr = [0, 1];
while (arr.length < max){
t = a + b;
a = b;
b = t;
arr.push(t);
}
return arr;
}
</code></pre>
<p>以generator生成器生成斐波那契数列的方法。</p>
<pre><code>function* fib2(max){
var t,
a = 0,
b = 1,
n = 1;
while (n < max){
yield a;
t = a + b;
a = b;
b = t;
n++;
}
return a;
}
> var f = fib2(5);
undefined
> f.next(); // 输出0返回。
Object {value: 0, done: false}
> f.next();
Object {value: 1, done: false}
> f.next();
Object {value: 1, done: false}
> f.next();
Object {value: 2, done: false}
> f.next();
Object {value: 3, done: true}
> f.next();
Object {value: undefined, done: true}
</code></pre>
<p>可以看到每次调用next,都会促使整段代码执行到下一个yield,然后输出一个匿名对象<code>{value:xx, done:true/false}</code> 其中xx是yield的返回值,done是表明此时是否生成结束,即是否执行到return。</p>
<p>还有一种使用方式,就是<code>for...of</code>,无需判断是否执行完毕。</p>
<pre><code>for (var x of fib(5)) {
console.log(x); // 依次输出0, 1, 1, 2, 3
}
</code></pre>
<p>But这个generator有啥卵用呢?我暂时想起来一个状态转换的,即每次调用都会切换到下一个状态,直到切换完成,像Android中的StateMachine一样。</p>
Android 之System Permission
https://segmentfault.com/a/1190000002572025
2015-03-02T10:07:27+08:00
2015-03-02T10:07:27+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>每个应用都运行在自己的砂箱中,并且具有不同的UserId和进程。</p>
<ol>
<li>签名<br>
每个开发者都具有自己的私有签名key,</li>
<li>User IDs and File Access<br>
由于每个应用都具有自己的UserID和进程号,因此其打开的文件也都只能自己访问。若需要两个app共享文件,那么需要两个app设置shareUserId并且使用相同的签名。</li>
<li>使用权限<br>
uses-permission,声明在Manifest中</li>
</ol>
<pre><code>xml</code><code><manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>
</code></pre>
<ol>
<li>声明和强制权限<br>
自定义自己的权限,需要在Manifest中使用<code><permission></code>tag首先声明。如:</li>
</ol>
<pre><code><manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.me.app.myapp" >
<permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
android:label="@string/permlab_deadlyActivity"
android:description="@string/permdesc_deadlyActivity"
android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="dangerous" />
...
</manifest>
</code></pre>
<p>其中<code>description</code>是对该权限的解释,将在用户安装App时显示。<code>permissionGroup</code>是表示该权限所属组,如本例中的消费金钱。<code>protectionLevel</code>是方便Android在安装时以何种方式通知用户,本例为危险,因为需要花钱。<br>
可以使用Settings App查看个App的应用权限,或者使用cmd<br><code>adb shell pm list permissions -s</code></p>
<ol>
<li>Manifest中的强制Permission<br>
对四大组件均可以在其tag中设定权限,如果某个调用者无权限调用该组件,将会得到 SecurityException的异常。其中ContentProvider具有写入权限和读取权限两种。</li>
<li>URIs Permission<br>
对于某些应用而言,其数据比较私密,对于不具备权限的App根本无法读取或者写入。如Email中的图片,当调用其他App来显示该图片时,由于不具备读取权限,因而报异常。因此呢,Android引入了URI权限,即在发送Intent时,附带 <code>Intent.FLAG_GRANT_READ_URI_PERMISSION</code> 和或者<code>Intent.FLAG_GRANT_WRITE_URI_PERMISSION</code>。</li>
</ol>
Android Service 之 AIDL
https://segmentfault.com/a/1190000002572019
2015-03-02T10:06:14+08:00
2015-03-02T10:06:14+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<ol>
<li>Create .aidl文件<br>
定义Service接口</li>
</ol>
<blockquote>
<p><strong>Caution: AIDL 做的任何改变,都必须保持后向兼容</strong><br>
方法支持参数和返回值,参数和返回值可以是任意类型。<br>
1. 所有Java原型都支持(int, long, float, char ,boolean等)<br>
2. String<br>
3. Charsequence<br>
4. List,元素必须是支持的数据类型,也可是其他AIDL所申明的,或者是parcelables。同时也可使用泛型申明。实际使用ArrayList来存储元素。<br>
5. Map,也list要求相同。但不可使用泛型,实际使用HashMap来存储。</p>
</blockquote>
<p>如果要使用除以上数据的类型其他类型数据,则必须使用import进行申明。</p>
<p>此外,在定义接口时,还需注意的有:<br>
1. 方法可为0个或多个参数,可返回值或者void<br>
2. 所有非原型参数,需要方向标签,in,out,inout。<br>
3. 所有代码注释都会被生成到java接口中,除了import和package申明之前的。<br>
4. 只支持方法,不支持静态成员变量的申明。</p>
<p>案例</p>
<pre><code>// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
</code></pre>
<ol>
<li>实现一个接口<br>
Android SDK tools会根据aidl文件自动生成一个java语言的编程接口,这个接口包含一个继承于Binder的抽象类和aidl中的接口方法实现。必须继承Stub然后实现方法。</li>
</ol>
<p>保存到/src目录下,ADT tool 会在/gen目录下生成IBinder接口文件。注意包名。<br>
生成的接口包含一个子类Stub, 是对接口的抽象实现。因此,需要继承该Stub,然后实现抽象方法。<br>
其中Stub还有一个助手方法asInterface(),传递IBinder并返回stub接口实例。<br>
实例代码如下:</p>
<pre><code>private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
</code></pre>
<blockquote>
<p>Caution: 注意多线程,要确保方法是线程安全的。RPC calls 通常是同步的,所以在client调用时,请使用子线程避免hand up the main thread and encounter ANR.此外,Server端不会返回Exception。</p>
</blockquote>
<ol>
<li>Expose the interface to the clients<br>
继承Service,重写onBind(),然后返回Stub 类的实现,即mBinder。</li>
</ol>
<pre><code>public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
}
</code></pre>
<p>Client调用,如果与Server不再同一个Application下,那么需要将aidl文件拷贝到Client,并且保持包名的一致。</p>
<h2>Passing Objects over IPC</h2>
<p>支持Parcelable,将允许Android系统decompose objects into primitives that can be marshalled over processes, 意思就是允许Android系统将objects 解耦成primitives,而这些primitives可在进程间传递。<br>
Step:<br>
1. 让需要传递的类实现Parcelable接口,<br>
2. 实现writeToParcel()方法,它将把对象的当前状态写到一个Parcel<br>
3. 添加静态字段CREATOR,它时实现Parcelable.Creator接口。<br>
4. create a aidl, 然后申明这个Parcelable 类。</p>
<p>aidl 申明Rect类代码如下:</p>
<pre><code>package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
</code></pre>
<p>接下来是Rect类实现Parcelable接口代码:</p>
<pre><code>import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() {
}
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
</code></pre>
<h2>Calling an IPC Method</h2>
<p>调用远程服务的实例,在之前的Service节中已经出现过了。这里不妨再赘上,有效长,:)</p>
<pre><code>public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null;
Button mKillButton;
TextView mCallbackText;
private boolean mIsBound;
/**
* Standard initialization of this activity. Set up the UI, then wait
* for the user to poke it before doing anything.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill);
mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(true);
mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mSecondaryService != null) {
try {
int pid = mSecondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
mCallbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};
// ----------------------------------------------------------------------
// Code showing how to deal with callbacks.
// ----------------------------------------------------------------------
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}
</code></pre>
Android Service 之 Bound Services
https://segmentfault.com/a/1190000002572016
2015-03-02T10:05:33+08:00
2015-03-02T10:05:33+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>A bound Service 提供了一种CS模式的Service。<br>
A bound service allows components (such as activities) to bind to the service, send requests, receive responses, and even perform interprocess communication</p>
<p>Server--Extend a Service, 然后实现onBind()方法,返回IBinder对象。<br>
Client--bindService(),然后必须实现ServiceConnection接口,其具有两个方法,onServiceConnected(),可通知连接服务的状态,并返回IBinder对象。<br>
其中onBinder只在第一次服务被连接时才会被调用,之后将直接返回IBinder对象。<br>
当最后一个client unbind时,系统将destroy 该Service.</p>
<h2>Creating a Bound Service</h2>
<ul>
<li>继承Binder类<br>
然后将它的一个实例作为onBind()返回值。适用于Server和Client在同一进程内。</li>
</ul>
<p>创建Server</p>
<pre><code>public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
</code></pre>
<blockquote>
<p>Tip: 在LocalBinder中提供了getService()方法,使得客户端可以获得Service的实例,这样就可以调用Service的公共方法。</p>
</blockquote>
<p>Client调用:</p>
<pre><code>public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
</code></pre>
<blockquote>
<p>可注意到Service的bind,使用以及unBind()的使用。</p>
</blockquote>
<ul>
<li>使用Messenger<br>
在Service中Create 一个Handler,然后可以接收和处理不同的Message type,同时Messenger 也可返回一个IBinder对象给客户端。此外,客户端也可定义自己的Messenger,然后Service可以发送Message以便返回结果。最简单的实现IPC通信。适用于跨进程。</li>
</ul>
<pre><code>public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
</code></pre>
<p>以下是Messenger的定义</p>
<blockquote>
<p>Reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by creating a Messenger pointing to a Handler in one process, and handing that Messenger to another process.</p>
</blockquote>
<p>意思是它是对Handler的引用,其他组件可通过它发送消息到handler。允许基于message的handle IPC。它是对Binder的简单包装。</p>
<p>Client 调用</p>
<pre><code>public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
</code></pre>
<p>就是一个事件传递。</p>
<ul>
<li>使用AIDL<br>
上个使用Messenger的方式,底层结构也是使用AIDL。但Messenger则只是依次处理。但若想同时处理,直接使用AIDL。适用于跨进程。</li>
</ul>
<blockquote>
<p>Tip: 大部分应用无需使用AIDL来创建bound service, 它可能需要多线程处理能力以及更复杂的实现。</p>
</blockquote>
<hr>
<blockquote>
<p><strong>Caution:Only activities, services, and content providers can bind to a service—you cannot bind to a service from a broadcast receiver.</strong></p>
</blockquote>
<p>bindService(intent, mConnection, Context.BIND_AUTO_CREATE);<br>
第三个参数BIND_AUTO_CREATE指create service if its not already alive. 还可以是BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUD, 或者0。</p>
<p>建议:you should bind during onStart() and unbind during onStop().如果希望在App可见时。<br>
若想在activity接收响应在stopped时候,可bind during onCreate() and unbind during onDestroy().</p>
Android之Service
https://segmentfault.com/a/1190000002572013
2015-03-02T10:04:52+08:00
2015-03-02T10:04:52+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>startService(): run indefinetly, 需要在适当时候stopSelf()<br>
onBind(): 提供一个CS模式的服务,runs as long as the component bound to it. 当所有组件都unBind() 时,Service也就destroy了。</p>
<p>Service运行于主进程,因此勿做耗时工作,如果需要,可以开启新的线程来做耗时的work,以避免ANR。<br>
声明周期lifecycle<br>
onStartCommand()<br>
当用户调用startService()请求启动服务,系统就会调用该方法。当该方法运行后,service就会在后台runs indefinitely.如果自己实现该方法,则需要自己调用stopSelf()或者stopService()来关闭服务。<br>
onBind()<br>
其他component调用bindService(),系统就会调用该方法。如不想被bind,则返回null,否则返回一个IBinder()。<br>
onCreate()<br>
当Service第一次被调用时,系统将调用该方法进行初始化,然后才会调用onStartCommand或者onBind. 如果该Service已经启动则跳过该步骤。<br>
onDestory()<br>
清理资源,诸如线程,注册的listeners,receivers等。</p>
<blockquote>
<p>Caution: <strong>always use an explicit intent when starting or binding your Service</strong></p>
</blockquote>
<p>可将android:exported属性设为false, 这样就可以组织其他应用,即使是使用explicit intent也无法启动Service。</p>
<p>Traditionally,有两种Create Service的方式:<br>
Service, 如有Intensive work,需要手动创建线程进行处理,否则容易造成ANR, as it is hosted in main thread<br>
IntentService, Service的子类,使用worker thread 依次处理所有请求,需要继承onHandleIntent(),然后处理从onStartCommand收到的intent。</p>
<h2>Extending the IntentService class</h2>
<p>优点:</p>
<ul>
<li>create 默认的worker thread, 处理所有来自onStartCommand收到的Intent请求。</li>
<li>Create a work queue, 依次传递收到intent到onHandleIntent(),依次处理。</li>
<li>在处理完所有请求后,就会自动销毁Service,无需自己调用stopSelf()</li>
<li>提供默认的onBind()实现,返回null。</li>
<li>提供一个默认的onStartCommand实现,它会将intent发送到work queue,并依次传递到onHandleIntent()</li>
</ul>
<p>实例:<br>
All you need: a constructor and an implementation of onHandleIntent().</p>
<pre><code>public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
* 一个构造函数,且需调用super,并传入该service名
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
</code></pre>
<p>注意,如果override 其他的方法,则必须调用super,让其帮助管理声明周期。比如对于onStartCommand()</p>
<pre><code>@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
</code></pre>
<h2>Extending the Service class</h2>
<p>如果需要同步处理多个请求,而不是按照队列依次处理,则继承Service,然后再对每个请求启动一个thread进行处理。<br>
实例代码(有许多需要说明)</p>
<pre><code>public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
</code></pre>
<p>第一点,新建HandleThread,与普通Thread无异,但是多了一个Looper成员。<br>
第二点,正常来讲,Service是可以使用handler的,因为它是主线程,默认已经具有了Looper。但是官方demo还是根据新建的HandlerThread的成员looper,传入了Handler。<br>
第三点,对于onStartCommand返回值START_STICKY,总共可以返回三个值,定义了在系统杀掉服务后系统如何继续服务。</p>
<ol>
<li><p>START_NOT_STICKY<br>
当系统杀掉Service后,不进行recreate.</p></li>
<li><p>START_STICKY<br>
当系统杀掉Service后,recreate and call onStartCommand(), 不传递last intent.适合于media play, 不执行commands,只是runs indefinitely.</p></li>
<li><p>START_REDELIVER_INTENT<br>
当系统杀掉Service后,recreate and call onStartCommand(), 携带last intent. 适合于正在执行任务的需要被立刻被恢复的,如下载。</p></li>
</ol>
<p>Application Component 与Service交互,除了startService,无其他活动。若想得到Service的返回结果,可用PendingIntent 携带一个getBroadcast()对象,这样在Service使用完成后,即可发送发送广播。</p>
<h2>Creating a Bound Service</h2>
<p>可通过调用bindService()<br>
To Create a Bound Service,必须实现onBind()返回一个IBinder对象,该对象定义了与Service通信的接口。<br>
当Client不再需要Service,需要调用unbindService() 进行解绑,使得服务科正常关闭。</p>
<h2>Sending Notifications to the User</h2>
<p>使用Toast 或者状态栏notification。<br>
状态栏Notification时best practise, 可在service结束后,发送通知,然后用户可点击通知栏的通知,然后启动一个Activity。</p>
<h2>Running a Service in the Foreground</h2>
<p>A foreground service必须提供一个notification for the status bar, 将被放置在正在运行的条目下,不可移除,除非Service运行完毕或者被移除前台。<br>
实例:</p>
<pre><code>Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
</code></pre>
<p>现在Notification已经使用Notification.Build进行建立了。<br>
可使用stopForeground(),但不会停止Service。但是stopService()可以移除notification。</p>
<h2>Managing the Lifecycle of a Service</h2>
Android 之 Tasks and Back Stack
https://segmentfault.com/a/1190000002572008
2015-03-02T10:03:27+08:00
2015-03-02T10:03:27+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<hr>
<p>默认行为<br>
1. 当Activity A启动Activity B, A stopped, 系统保存A状态。当Back pressed, 则A resumed 并恢复相关状态。<br>
2. 当Home button pressed, 则当前activity stopped, task 进如background,系统保存每个task的状态。当用户启动了之前开始task的app,则此task进入前台,并且恢复task栈顶的Activity。<br>
3. 当用户点击Back, 则当前Activity会被弹出task stack 并且被destroy,系统也不再retain其状态。<br>
4. Activities可被实例化多次,甚至时从其他tasks。</p>
<h2>Saving Activity State</h2>
<p>当Activity stopped, 系统会默认保存其状态。当也还是建议使用回调函数保存Activity的状态,以防止系统在内存不存进行内存回收,销毁掉进入后台的task。这种情况下,当系统再次进入该task时,系统仍然具有task链,但是在将task栈顶的Activity进行显示时,将会是重新create,而不是resume。所以应主动使用回调函数 onSaveInstanceState() 来保存Activity State。</p>
<h2>Managing Tasks</h2>
<p>Principal < activity > attributs</p>
<pre><code>taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
</code></pre>
<p>Principal intent flags:</p>
<pre><code>FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
</code></pre>
<h3>Define launch modes</h3>
<ol>
<li>使用Manifest指定, task</li>
<li>使用Intent Flags</li>
</ol>
<p>当用A启动B时,A指定了启动B的Flag,而B也在Manifest中指定了不同task。此种情况下,在Intent中的指定Flags将覆盖掉Manifest指定的task。</p>
<p>此外,有些launchmode 在flag中有,但在Manifest中没有,同样,在Manifest中有,flag中有。</p>
<ul>
<li>使用Manifest File</li>
</ul>
<p>指定activity的launchMode属性:</p>
<pre><code>|standard|singleTop|singleTask|singleInstance|
|--- | | | |
|默认的,可被实例化多次,可分属不同task,也可在同一task| 当要启动的Activity 存在栈顶,在该模式下,系统将把启动该Activity的Intent通过调用onNewIntent()方法将路由至栈顶的Activity实例,不再启动的新的实例。如果要启动的Activity的实例不再栈顶,则启动新的实例。注意:该种模式下,back button无法返回调用onNewIntent之前的状态,因为task back stack还是保持原样。|创建新的task,实例化Activity到task的栈底。但若时此时现有task stack中已经有该Activity的实例,那么系统就不会启动新的task和实例,而是路由到该task中的Activity,并销毁task stack中在这之上的Activity,使之成为栈顶,然后将该Intent通过onNewIntent路由到Activity。 同一时刻,Activity只有一个实例。|类似于singleTask,Activity只有一个实例,并且也会时task的为一个栈元素,它启动的任何Activity都将会出现在其他的task栈中。
</code></pre>
<p>Android Browser就是一个使用singleTask模式的应用。<br>
此外,对于back stack,不论Activity时被启动到一个新的task还是在启动它的Activity所在task stack,back button都可以返回到上一个Activity。<br>
另外,对于启动使用singleTask模式的Activity,那么系统首先按照它本身模式启动,之后会将该Activity所在的task stack整体迁移到启动它的back stack。如图所示:<br><img src="http://cfile29.uf.tistory.com/R480x0/176ABE3550616DDC1D14CD" alt=""></p>
<ul>
<li>使用Intent Flags</li>
</ul>
<p>在startActivity可指定的flag包括:<br></p>
<table>
<thead><tr>
<th>FLAG_ACTIVITY_NEW_TASK</th>
<th>FLAG_ACTIVITY_SINGLE_TOP</th>
<th>FLAG_ACTIVITY_CLEAR_TOP</th>
</tr></thead>
<tbody><tr>
<td>与singTask属性相同</td>
<td>与singleTop相同</td>
<td>如果要启动的Activity已经存在task栈中,那么系统将会clear掉在Activity元素到task 栈顶的所有Activity,然后将Intent通过onNewIntent()传递给已经在栈顶的Activity。该flag通常和FLAG_ACTIVITY_NEW_TASK一起使用。</td>
</tr></tbody>
</table>
<h3>Handling affinities</h3>
<p>affinity 属性暗示了Activity更倾向加入的task。默认,同一个Application里的Activity更倾向于在同一个task里。可通过修改activity元素的taskAffinity属性。该属性的值时一个字符串,默认的为包名,因此,如果希望倾向于启动在不同的task,那么指定以个不同于包名的字符串即可。</p>
<p>使用Affinity的两种情况<br>
- 使用FLAG_ACTIVITY_NEW_TASK</p>
<blockquote>
<p>当A启动B时,若指定了FLAG_ACTIVITY_NEW_TASK,那么系统首先寻找倾向的task来装载这个新的B。如果存在与B具有相同Affinity的task,则将其启动到该task,否则启动新的task。比如InCallScreen,当用户按Home离开该Activity,该如何回到该Activity,除了在launch里启动之外(如果launchMode为main的话),还可以在notification bar中显示。<br>
- activity元素的属性allowTaskReparenting 为true,<br>
意思就是本Appliancation中的Activity A 具有其他Application里的Activity B相同的affinity。那么在A中启动B时,B将隶属于A的task,那么在B所属的Application启动后,B将从A所属的task中通过Reparenting到B所属的Application应用。</p>
</blockquote>
<h3>Clearing the back stack</h3>
<p>若用户离开task 很长时间,系统将会清理task,只留下task stack的root activti。当用户重返task,只会restore root Activtiy。但几个Activity可以修改这样的行为</p>
<ul>
<li>alwaysRetainTaskState<br>
若该属性被设置到root activity of a task,那么默认行为就不会发生。</li>
<li>clearTaskOnLaunch<br>
若root activity of a task的该属性设为true,不论用户离开还是重新进入task, 都会清掉除了root activity之外的activities。 与alwaysRetainTaskState刚好相反。</li>
<li>finishOnTaskLaunch<br>
仅作用于单个Activity,不保存状态,离开即销毁。若其在一个task中,当离开后,该activity即被销毁,将其从task stack栈中弹出,在进入该task时,将会是该task的上一个activity在前台。也可作用于root activity of task。 比如InCallScreen用完后,即可销毁。</li>
</ul>
Android之Fragments
https://segmentfault.com/a/1190000002571998
2015-03-02T10:02:03+08:00
2015-03-02T10:02:03+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>定义:A Fragment represents a behavior or a portion of user interface in an Activity.<br>
一个Activity可包括多个Fragments,用来建立一个多pane的UI,并可在多个Activity中重用这些Fragment。此外,可将其看作是一个Activity的模块化部分,它有自己的生命周期,可接受用户输入,可在Activity运行时进行添加或者修改。<br>
一个Fragment必须嵌入到一个Activity中,且受宿主Activity的影响。如Activity pause了,那么所有的Fragment都将进入pause,当所有Activity destory了,所有的Fragment也都destory了。当Activity在运行时,可添加或者删除fragment,还可以将其加入back stack,这样可通过back button 进行回退。<br>
当添加一个Fragment时,它将作为ViewGroup的一个部分,并且可定义自己的View。可通过在Activity的代码中插入< fragment>标签,或者在运行时将其添加到一个已经存在ViewGroup。<br>
然而Fragment并不一一定要作为Activity Layout的一部分,它也可以是无UI的,作为Activity不可见的Worker。</p>
<h2>Design Philosophy</h2>
<p><img src="http://hi.csdn.net/attachment/201202/13/0_13291408575rIR.gif" alt=" An example of how two UI modules defined by fragments can be combined into one activity for a tablet design, but separated for a handset design."></p>
<h2>Creating a Fragment</h2>
<h3>1. OverView</h3>
<ol>
<li>需要继承fragment class。</li>
<li>生命周期<br><img src="http://img.my.csdn.net/uploads/201211/29/1354170699_6619.png" alt="Framgent生命周期"><br>
通常必须实现以下三个回调。</li>
</ol>
<p>- OnCreate(), 初始化必须的组建,即需要在fragement的onPause, onStop, onResume等状态时retain这些。<br>
- onCreateView(), 为Fragemnt创建UI,并且需要返回布局的根View。如果该fragment不需要界面,也可以返回Null。<br>
- onPause(), 这是指示用户需要离开该fragment,因此需要提交需要持久化的数据。</p>
<p>还有3个Fragment可以继承的:</p>
<ul>
<li>dialogFragment, 显示一个悬浮的对话框。是对Activity的dialog helper method一个很好的替换,因为你可以将一个Fragment合并到由Activity管理的fragment 的backstack,这样就允许用户返回到之前消失的fragment。</li>
<li>ListFragment, 显示一个由Adapter管理的列表式的item,类似与ListActivity。</li>
<li>PreferenceFragment, 用list显示一个层级结构的Preference对象。类似与PreferenceActivity,对于创建settings比较有用。</li>
</ul>
<h3>2. Adding A user Interface</h3>
<p>提供用户接口,必须实现onCreateView(),返回布局的根View。若是ListFragment,则返回ListView。<br>
To return a layout from onCreateView(), 可inflate an xml layout resource。 onCreateView的默认参数包含了一个LayoutInflater的对象。<br>
代码如下:</p>
<pre><code>public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
</code></pre>
<p>其中 container 是Activity布局里的ViewGroup对象,然后inflate出来的布局会被出入到该container。Bundler则是用于传递参数或者其他恢复布局的状态。然而,inflate方法的3个参数,第一个为布局的resource id, 第二个为container, 第3个指示是否需要attach 到 viewgroup,由于指定了container,因而此处应设为false,否则Android System将会create a redundant view group in the final layout.</p>
<h3>3. Adding a fragment to an activity</h3>
<p>two ways to add。<br>
1. 在activity的xml layout文件中,声明fragment xml 布局文件。</p>
<pre><code><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
</code></pre>
<p>其中android:name 属性为指定Fragment类来初始化layout。在系统初始话该布局时,它会初始化每个fragment类,然后调用onCreateView()方法,获取到每个fragment的布局。<br>
为了重用the fragment,需要为每个fragment分配一个unique id:使用android:id; 使用adndroid:tag; 不设置,系统使用container的id。<br>
2. 代码中添加到一个现存的ViewGroup</p>
<pre><code>// 获取fragmentTransaction,就可实现诸如add, remove, or replace a fragment等行为。
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 添加一个fragment到R.id.fragment_container,并提交。
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
</code></pre>
<h3>4. Adding a fragment without a UI</h3>
<p>可将该fragment作为一个后台worker。<br>
方法:</p>
<blockquote>
<p>使用add(Fragment, String), 其中String是一个字符串tag,由于不提供layout给activity,因此该fragment也无需创建onCreateView()。tag将作为该fragment的标识,并使用findFragmentByTag()方法来寻找。</p>
</blockquote>
<h2>Managing Fragments</h2>
<ol>
<li>使用FragmentManager,可通过getFragmentManager()获得。</li>
<li>可做的事情包括:</li>
</ol>
<p>- getFragmentById(), or getFragmentByTag();<br>
- pop fragments off the back stack with popBackStack()<br>
- addOnBackStackChangedListener(), register a listener for changes。<br>
- open a FragmentTransaction, 处理add, remove fragment等事务。</p>
<h2>Performing Fragment Transactions</h2>
<p>Steps:<br>
1. acquire an instance of FragmentTransaction</p>
<pre><code>FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
</code></pre>
<ol>
<li>using methods such as add(), remove(), and replace(), and then call commit() to apply the transaction.</li>
<li>before calling commit(), you might want to call addToBackStack(), 以便于将该fragment当前的状态保存,即将其加入backstack,这样用户可使用按钮回退到该界面。<br>
示例:</li>
</ol>
<pre><code>// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
</code></pre>
<p>上述代码替换掉container中的当前fragment。<br>
若调用了多个操作,add,replace等且调用了addToBackStack,在commit之前的所有操作都会被保存,并可被一起恢复。<br>
commit必须被最后执行,添加多个fragment到同一个container,添加顺序将绝定每个fragment在view hierarchy的顺序。</p>
<blockquote>
<p><strong>Tip:</strong>, 调用setTransition(),可为fragment之间的切换添加动画。另,若不调用addToBackStack,则调用remove,该fragment就会被destory。若是调用了addToBackStack,则该fragment将进入stopped,并且可被resumed when the user navigate it。</p>
</blockquote>
<p>此外,commit()不是立刻执行transation,而是会根据主线程调度情况执行。除非必须要立刻执行,可调用executePendingTransaction()。<br>
最后需要注意的是,commit()必须先于activity的saving its state(即pause或stop),否则将导致异常。解决这个问题,可调用cimmitAllowingStateless()。</p>
<h2>Communicating with the Activity</h2>
<h3>1. Overview</h3>
<ul>
<li>the fragment can access the Activity instance.<br><code>View listView = getActivity.findViewById(R.id.list)</code>
</li>
<li>the activity can also can methods in fragment by acquiring a refrence to the Fragment from FragmentManager<br><code>ExampleFragment fragment = getFragmentManager.findFragmentById(R.id.example_fragment);</code>
</li>
</ul>
<h3>2. Creating event callbacks to the activity</h3>
<ul>
<li>在fragment的子类中,申明一个内部的回调接口,这样activity实现该接口,即可实现回调。</li>
</ul>
<pre><code>public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
</code></pre>
<ul>
<li>保证容器Activity实现该回调,在fragment的onAttach时,实例化该接口。</li>
</ul>
<pre><code>public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
</code></pre>
<ul>
<li>在fragment中,若有事件发生需要传递给Activity,则调用<br><code>mListener.onArticleSelected(uri)</code><br>
即可通知到宿主activity。</li>
</ul>
<h3>Adding items to the Action Bar</h3>
<ol>
<li>实现onCreateOptionsMenu(), 然后在onCreate必须调用setHasOptionsMenu() 以便于接受options的调用。</li>
<li>也接受onOptionsItemSelected()方法回调,</li>
<li>可使用ContextMenu:registerForContextMenu(),并可在onCreateContextMenu()中接受到调用通知,也可在onContextItemSelected()中收到选中item的调用通知。</li>
</ol>
<p>事件处理流程是Activity先处理,发现无人处理后才会向下传递到fragment中。</p>
<h2>Handling the Fragment Lifecycle</h2>
<p><img src="http://wear.techbrood.com/images/activity_fragment_lifecycle.png" alt="The effect of the activity lifecycle on the fragment lifecycle."></p>
<p>管理一个生命周期,仍然包括三个态:<br>
- Resumed,运行时,可见。<br>
- Paused, 被另一个Activity挡住了,但本Activity仍然可见(未被完全遮住,如dialog)。<br>
- Stopped, 完全不可见,但all state and member information is retained by the system。</p>
<p>可用onSaveInstanceState()保存数据,并在onCreate(), onCreateView(), or onActivityCreated()中重装。</p>
<p>其中Activity会自动进入BackStack,而fragment需要明确调用addToBackStack()。</p>
<h3>1. Coordinating with the activity lifecycle</h3>
Android之Activities
https://segmentfault.com/a/1190000002571992
2015-03-02T10:00:49+08:00
2015-03-02T10:00:49+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>Activity是一个应用组件,提供一个用户界面,用户可与之交互。每个Activity都被分配一个Window以便于绘制用户接口。<br>
Android采用back stack机制,即LIFO队列,当一个新的Activity启动后,将会被存至backstack并被赋予焦点。<br>
它有一个具体的生命周期,控制着Activity与其他Activity转换的流程,这些控制方法采用几个回调函数实现:如onCreate,onStart,onResume,onPause,onStop,onDestory。</p>
<ul>
<li>onCreate<br>
必须实现的回调函数,系统使用他创建Activity,可初始化一些必须的参数,必须调用setContentView()来定义用户接口,即布局。</li>
<li>onPause<br>
提交或者保存任何应该被持久化的数据。</li>
</ul>
<h2>Implementing a User Interface</h2>
<p>一个Activity的UI是由层层的view表示,这些view派生自View类。每个View控制Activity Window窗口内的一个矩形区域,<br>
- Widgets,提供交互或者可视的元素,诸如button,check box<br>
- Layouts,派生自ViewGroup,为子view提供独一无二的布局模型,诸如linearlayout。</p>
<h2>在Manifest中声明Activity</h2>
<pre><code><manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
</code></pre>
<h2>Using Intent Filters</h2>
<p>这些都在Intent Filters章节中说明了。</p>
<h2>Starting an Activity</h2>
<p>使用 startActivity()即可</p>
<h2>Starting an activity for a result</h2>
<p>调用 startActivityForResult() 。<br>
实现回调函数onActivityResult。需要定义请求码,并在回调函数中判断,以及执行的结果是否为RESULT_OK。</p>
<h2>Shutting Down an Activity</h2>
<p>finish() or finishActivity().</p>
<h2>Managing the Activity Lifecycle</h2>
<p>三个必要的状态:<br>
- Resumed, Activity在前台显示,并且拥有用户焦点<br>
- Paused, 另外一个Activity在前台显示,并且拥有焦点,但该Activity仍然可见,并且完全存活,它的Activity对象被保留在内存中,维持这所有的状态和成员信息,且还附着在Window Manager上。但在内存极低的条件下,可被系统kill掉。<br>
- Stopped, 完全被另外一个Activity遮蔽,但依然处于存活状态,与Paused相比,它不再附着Window Manager上。且在系统需要内存时可被kill掉。</p>
<p>如果activity处于paused或则和stopped,系统可通过调用其finish方法或者是直接kill掉其process。当被重新打开时,必须完全重构。</p>
<h2>Implementing the lifecycle callbacks</h2>
<pre><code>public class ExampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
// The activity is about to be destroyed.
}
}
</code></pre>
<blockquote>
<p>Tip: 每个回调,都必须实现自己的父类方法super.onXXX</p>
</blockquote>
<ul>
<li>完整的生命周期,自onCreate到onDestory。</li>
<li>可视周期,onStart到onStop</li>
<li>前台周期,onResume到onPause<br>
可被杀掉的周期包括onPause到onDestory。</li>
</ul>
<p>其中onStop后再回来,需要调用onRestart,然后才是onStart。</p>
<h2>Saving activity state</h2>
<p>当其他Activity来到前台,应调用onSaveInstanceState()来保存状态(一般会在onStop之前也可能在onPause之前调用),这些状态以KVP保存到Bundle中。当Activity重新回到前台后,可通过onCreate或 onRestoreInstanceState()来重建状态。如果无任何状态,Bundle是null。</p>
<blockquote>
<p>Tip: 一般来讲,所有的View都已经默认实现了onSaveInstanceState,可保存在UI所做的改变,且在Activity重回前台时,重装这些值。前提是每个需要保存的view都已经设置android:id这个标签。<br>
如果重写onSaveInstanceState方法,那么必须调用其父类的该方法实现,并且相应地重写onRestoreInstanceState()。</p>
</blockquote>
<blockquote>
<p>Caution:由于无法保证onSaveInstanceState方法一定会被调用,因此,最好将一些持久化的数据放到onPause方法中。</p>
</blockquote>
<p>测试方法,可简单旋转屏幕,以使得Activity可以重新onCreate。</p>
<h2>Handling configuration changes</h2>
<p>有些配置(such as screen orientation, keyboard availability, and language)可在运行过程中进行修改,当这些修改完成后,将会导致Android 重建该Activity(系统调用onDestory,然后立即调用onCreate)。<br>
最好的方式处理这些问题,就是调用onSaveInstanceState() and onRestoreInstanceState() (or onCreate())。</p>
<h2>Coordinating activities</h2>
<p>两个Activity的切换,其生命周期是可以预测,比如<br>
A 启动 B,那么其流程为:</p>
<ol>
<li>A 的 onPause方法启用。</li>
<li>B 的onCreate,onStart,onResume依次启用。</li>
<li>当A不再可见,调用onStop。</li>
</ol>
<p>因此,如果A有数据共享给B,那么可在onPause里进行数据存储,然后B可直接使用,而不是在onStop里。</p>
Android之Activity--Loaders
https://segmentfault.com/a/1190000002566045
2015-02-26T14:03:56+08:00
2015-02-26T14:03:56+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<hr>
<p>特性:<br>
Available For Every Activity and Fragment<br>
Asynchronous loading data<br>
monitor source data and deliever new results when the content changes<br>
reconnnect to the last loader's cursor.</p>
<h2>Loader API Summary</h2>
<ul>
<li>LoaderManager, 抽象类,关联到Activity(or Fragment), 用于管理LoaderManager的实例。每个Activity或者Fragmente只拥有一个LoaderManager,但一个LoadManager可拥有多个loaders, 常用的有CursoLoader</li>
<li>LoaderManager.LoaderCallbacks, 接口类,用于Client与LoaderManager交互 </li>
<li>Loader, 抽象类, 执行异步loading data, 具体类CursorLoader, 它会监视源文件,并在内容变化时传递变化结果</li>
<li>AsyncTaskLoader, 抽象Loader, 提供一个AsyncTask 以便执行任务。</li>
<li>CursorLoader, AsyncTaskLoader子类,查询ContentResolver 并返回Cursor。实现了Loader协议,并使用AsyncTaskLoader将任务放至后台进行处理。最好的异步加载数据 from ContentResolver的解决方案</li>
</ul>
<h2>Starting a Loader</h2>
<h3>1. onCreate()</h3>
<p><code>getLoaderManaer().initLoader(int id, Bundle args, LoaderCallbacks<D> callback)</code><br>
其中<br>
id设为0,标示loader, 若该id已被使用,则直接连接上使用该id的loader,否则create one.<br>
args设为null, 无配置数据<br>
callback 设为this, loadmanager将汇报loader event。</p>
<p>当检索条件改变,希望重新查询的时候,使用<code>getLoaderManager().restartLoader(0, null, this);</code><br>
具体initLoader所做工作,可看源码追踪的时序图。</p>
<h3>2. Using the LoaderManager Callbacks</h3>
<p>在实现LoaderManager.LoaderCallbacks接口时,三个方法必须实现:<br>
- onCreateLoader() — 根据所给id 实例化loader and 返回一个新的loader<br>
- onLoadFinished() — 数据加载完成时, 参数data即是返回的结果。<br>
- onLoaderReset() — 当数据源更新时。</p>
<p>可在onLoadFinished方法中,发送EVENT至Handler,然后在Handler中处理数据刷新,更新UI等工作。</p>
<h2>Tracking Source Code</h2>
<p>当开发者在实现LoaderCallbacks接口的onCreateLoader()方法,返回CursorLoader实例时,<br>
在LoadManager.LoadManagerImpl.LoaderInfo.start()方法中会调用<br><code>mLoader = mCallbacks.onCreateLoader(mId, mArgs);</code><br>
这样就将我们CursorLoader的实例传入了LoaderInfo的实例中。<br>
其中mCallbacks在LoaderInfo的构造方法中进行赋值,也就是传入我们实现的LoaderCallbacks接口的实例。<br><code>public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks)</code><br>
接下来会调用<br><code>mLoader.startLoading()</code><br>
而Loader类的startLoading()方法会调用onStartLoading(),它是一个空方法。<br>
CursorLoader 作为Loader的子类,重写了onStartLoading()方法,代码如下:</p>
<pre><code>Java</code><code>@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
</code></pre>
<p>可见,当mCursor为空时,或者contentChanged时,都将调用forceLoad()。也即数据源更新时,CursorLoader会自动调用onLoadComplete(),所以开发者只需在此处发送事件给Handler,让其更新数据即可。<br>
当mCursor不为空,则直接传递结果,此处将会调用onLoadFinish()方法,将数据传递给客户端onLoadComplete()。<br>
具体代码流程如图所示。<br><img src="/img/bVkVHB" alt="此处输入图片的描述"></p>
<p>对于数据源更新,如何自动调用onLoadComplete()方法,简单来说,就是在进行onCreateLoader()方法中,会注册观察者,使其在数据更新时,将takeContentChanged()置为true。不再进行详细展开。</p>
<h2>Example</h2>
<pre><code>Java</code><code>public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No phone numbers");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
@Override public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
}
// These are the Contacts rows that we will retrieve.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}
</code></pre>
Android之Intents 和Intent Filters
https://segmentfault.com/a/1190000002512919
2015-01-26T14:22:46+08:00
2015-01-26T14:22:46+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
2
<hr>
<h2>1. Intents 和Intent Filters</h2>
<ul>
<li>to start an Activity<br><code>startActivity or startActivityForResults</code>
</li>
<li>to start a service<br><code>startService or bindService</code>
</li>
<li>to deliever a broadcast<br><code>sendBroadcast, sendOrderedBroadcast or sendStickyBroadcast</code>
</li>
</ul>
<h3>1.1 Intent Types</h3>
<ul>
<li>Explicit intents<br>
指定请求组件的全限定名,就包名+类名。</li>
<li>Implicit intents<br>
申明通用的动作去执行,允许其他App处理该动作。</li>
</ul>
<h3>1.2 Building Intent</h3>
<ul>
<li>Component name<br>
是决定该Intent是否为Explicit Intent,可通过<code>set Component(), setClass(), setClassName()</code>, 或者是作为Intent()的构造器的参数。若没指定该参数,则系统使用一下item进行匹配。其中Service必须指定Component Name。</li>
<li>Action<br>
是一个String字符串,指定通用动作去执行,如View or Pick。该字段通常能够决定Intent的其他字段如何构造,尤其是在data域和extras域。可自定义Action,但更推荐使用系统提供的。以下为常用的Action, ACTION_VIEW(使用地图显示地址或使用相册显示图片),ACTION_SEND(使用其他App共享数据,诸如电子邮件或者社交网络共享).<br>
可以通过setAction(),或者Intent的构造器进行指定。定义自己的ACTION,请加上包名,如下<br><code>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</code>
</li>
<li>Data<br>
使用URI指定MIME类型的数据,通常需要指定数据类型。但是使用<code>content: URI</code>的URI数据,可不指定类型,因为系统可识别出该数据位于设备上,并且受到ContentProvider控制,因此MIME类型就变为可见。<br>
指定方式为<code>setData(), setType() or setDataAndType()</code>,其中setData和setType不可同时使用,若需要,请使用setDataAndType()。</li>
<li>Category<br>
一个String,包含可处理该Intent的组件种类。每个Intent可包含任意数量的Category,但大部分的Intent都不需要,通常设置为<code>android.intent.category.DEFAULT</code>。常见的Category:<code>CATEGORY_BROWSABLE</code>和<code>CATEGORY_LAUNCHER</code>。前者表示该数据可为浏览器打开,后者表示该Activity是一个任务的初始Activity,并且将会被显示在Launcher里。可用'addCategory()'来指定该项。<br>
>Tip 以上介绍的component name, action, data, 和category限定了一个intent的特性,Android系统可知道如何决定一个组件来处理或启动。</li>
<li>Extras<br>
携带键值对信息,KV来完成特定的Action。可通过putExtras(K, V),或者创建一个Bundle携带所有的KVP,然后putExtras到Intent。指定自己的Extras Key,务必加上包名,如<code>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</code>
</li>
<li>FLAG<br>
定义Intent的元素据metadata,可指导Android系统如何启动一个Activity(task所属),或者启动后如何对待(是否属于其他已经启动的Activity)</li>
</ul>
<h3>1.2 Examples</h3>
<ul>
<li>Explicit Intent</li>
</ul>
<pre><code>// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
</code></pre>
<ul>
<li>Implicit Intent<br>
模糊的Intent,可能会没有App响应,导致App Crash。因此可通过resolveActitvity()来验证是否有Activity响应,然后再决定是否启动Activity。</li>
</ul>
<pre><code>// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
</code></pre>
<p>若有多个App响应,则显示列表对话框以供用户选择,如果只有一个App响应,则直接启动。<br>
- 强制出现App选择器<br>
如分享文件或者数据到各种App,ACTION_SEND。可使用Intent的creatChooser来操作,如下:</p>
<pre><code>Intent intent = new Intent(Intent.ACTION_SEND);
...
// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);
// Verify the intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
</code></pre>
<h3>1.3 Receiving an Implicit Intent</h3>
<p>在Manifest文件中申明< intent-filters >标签,通过action, data, and category等字段制定Intent可接受的类型。<br>
一个Explicit Intent无视组件所申明的接受Intent类型,直接传递过去。<br>
不同job应申明不同的< intent-filters >,可包括action, data, and category3个字段。<br>
-action, 指定可接受的intetn action。<br>
-data,可接受的数据类型,URI (scheme, host, port, path, etc.) and MIME type。<br>
-category,必须是一个Action的string值。</p>
<blockquote>
<p>Tip: 为了收到Implicit Intents,必须设定<code>CATEGORY_DEFAULT</code>。这样, startActivity() and startActivityForResult() 才可调用,否则没有任何Implicit可启动该Activity。</p>
</blockquote>
<p>举例:</p>
<pre><code><activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
</code></pre>
<p>只有三个item都通过验证,Android才会传递Intent给该Activity。对于Broadcast Receiver,可通过<code>registerReceiver()</code> or <code>unregisterReceiver()</code>动态绑定。<br>
- Example Filters</p>
<pre><code><activity android:name="MainActivity">
<!-- This activity is the main entry, should appear in app launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- This activity handles "SEND" actions with text data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>
</code></pre>
<h3>1.4 Using a Pending Intent</h3>
<p>它是对Intent的包装器,主要用途是给予其他App权限获取本进程的数据。主要用途如下:</p>
<ul>
<li>Notification Manager</li>
<li>AppWidget</li>
<li>AlarmManager</li>
</ul>
<p>由于每个Intent都是为特定的组件设计的,因此PendingIntent也同样。</p>
<ul>
<li>PendingIntent.getActivity(),启动Activtiy</li>
<li>PendingIntent.getService(),启动Service</li>
<li>PendingIntent.getBroadcast(), 启动BroadcastReceiver()。</li>
</ul>
<h3>1.5 Intent Resolution</h3>
<p>Android 系统匹配最优的组件的算法,基于以下三方面的匹配测试:</p>
<ul>
<li>The intent action</li>
<li>The intent data (both URI and data type)</li>
<li>The intent category<br>
其中data的URI 结构和MIME 类型。<br>
URI:<code><scheme>://<host>:<port>/<path></code><br>
ex:<code>content://com.example.project:200/folder/subfolder/etc</code><br>
这4个元素是线性依赖的:<br>
>If a scheme is not specified, the host is ignored.<br>
If a host is not specified, the port is ignored.<br>
If both the scheme and host are not specified, the path is ignored.</li>
</ul>
<p>PackageManager 可以调用以下几个方法列出可接受该Intent的组件</p>
<ul>
<li>queryIntentActivities(), 返回所有相似的Activity</li>
<li>queryIntentServices(),返回相似的所有Service</li>
<li>queryIntentBroadcastReceivers(), 返回所有</li>
</ul>
<h3>1.6 Common Intents</h3>
<h4>1 AlarmClock</h4>
<p>Action: ACTION_SET_ALARM<br>
Extras:....<br>
ex:</p>
<pre><code>public void createAlarm(String message, int hour, int minutes) {
Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_HOUR, hour)
.putExtra(AlarmClock.EXTRA_MINUTES, minutes);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<p>并其需要使用权限:<br><code><uses-permission android:name="com.android.alarm.permission.SET_ALARM" /></code><br>
相应的Intent-Filter</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.intent.action.SET_ALARM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>2 Timer</h4>
<p>Action:ACTION_SET_TIMER<br>
Extras:。。。<br>
e.g.</p>
<pre><code>public void startTimer(String message, int seconds) {
Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_LENGTH, seconds)
.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<p>Permission:<br><code><uses-permission android:name="com.android.alarm.permission.SET_ALARM" /></code><br>
Intent-Filter:</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.intent.action.SET_TIMER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>3 显示所有Alarms</h4>
<p>Action:ACTION_SHOW_ALARMS</p>
<h4>4 添加Calendar Event</h4>
<p>Action:ACTION_INSERT<br>
Data URI:Events.CONTENT_URI<br>
MIME Type: "vnd.android.cursor.dir/event"<br>
Extras: 。。。</p>
<h4>4 拍摄照片或摄制录像</h4>
<p>Action:ACTION_IMAGE_CAPTURE or ACTION_VIDEO_CAPTURE<br>
Extras:EXTRA_OUTPUT<br>
使用<code>onActivityResult()</code>接收返回数据:</p>
<pre><code>static final int REQUEST_IMAGE_CAPTURE = 1;
static final Uri mLocationForPhotos;
public void capturePhoto(String targetFilename) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.withAppendedPath(mLocationForPhotos, targetFilename);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bitmap thumbnail = data.getParcelable("data");
// Do other work with full size photo saved in mLocationForPhotos
...
}
}
</code></pre>
<p>相应的Intent-Filter</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>5 Start a camera app in still image mode</h4>
<p>Action: INTENT_ACTION_STILL_IMAGE_CAMERA</p>
<h4>6 Start a camera app in video mode</h4>
<p>Action: INTENT_ACTION_VIDEO_CAMERA</p>
<h4>7 选择一个联系人</h4>
<p>Action:ACTION_PICK<br>
MIME Type:Contacts.CONTENT_TYPE<br>
e.g.:</p>
<pre><code>static final int REQUEST_SELECT_CONTACT = 1;
public void selectContact() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_SELECT_CONTACT);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
Uri contactUri = data.getData();
// Do something with the selected contact at contactUri
...
}
}
</code></pre>
<h4>8 选择特定的联系人</h4>
<p>Action: ACTION_PICK<br>
MIME Type:<br>
CommonDataKinds.Phone.CONTENT_TYPE。Pick from contacts with a phone number.<br>
CommonDataKinds.Email.CONTENT_TYPE,Pick from contacts with an email address.<br>
CommonDataKinds.StructuredPostal.CONTENT_TYPE,Pick from contacts with a postal address.</p>
<pre><code>static final int REQUEST_SELECT_PHONE_NUMBER = 1;
public void selectContact() {
// Start an activity for the user to pick a phone number from contacts
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == RESULT_OK) {
// Get the URI and query the content provider for the phone number
Uri contactUri = data.getData();
String[] projection = new String[]{CommonDataKinds.Phone.NUMBER};
Cursor cursor = getContentResolver().query(contactUri, projection,
null, null, null);
// If the cursor returned is valid, get the phone number
if (cursor != null && cursor.moveToFirst()) {
int numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER);
String number = cursor.getString(numberIndex);
// Do something with the phone number
...
}
}
}
</code></pre>
<h4>9 显示一个联系人</h4>
<ul>
<li>ACTION_PICK返回的contactURI,然后使用ACTION_VIEW查看。</li>
<li>直接访问联系人列表,需要READ_CONTACTS权限</li>
</ul>
<pre><code>public void viewContact(Uri contactUri) {
Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<h4>10 编辑一个联系人</h4>
<p>Action:ACTION_EDIT<br>
URI: 之前ACTION_PICK返回的contactURI。<br>
Extras:ContactsContract.Intents.Insert中定义的条目都可编辑</p>
<pre><code>public void editContact(Uri contactUri, String email) {
Intent intent = new Intent(Intent.ACTION_EDIT);
intent.setData(contactUri);
intent.putExtra(Intents.Insert.EMAIL, email);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<h4>11 插入一个联系人</h4>
<p>Action:ACTION_INSERT<br>
MIME Type:Contacts.CONTENT_TYPE<br>
Extras:One or more of the extras defined in ContactsContract.Intents.Insert.<br>
e.g.:</p>
<pre><code>public void insertContact(String name, String email) {
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType(Contacts.CONTENT_TYPE);
intent.putExtra(Intents.Insert.NAME, name);
intent.putExtra(Intents.Insert.EMAIL, email);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<h4>12 Email</h4>
<h4>13 File Storage</h4>
<h4>14 Fitness</h4>
<h4>15 Local Actions</h4>
<h4>16 Maps, show a location on a map</h4>
<p>Action: ACTION_VIEW<br>
URI: geo:latitude,longtitude<br>
e.g.:</p>
<pre><code>public void showMap(Uri geoLocation) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(geoLocation);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<p>intent filter:</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>17 MUSIC or VIDEO</h4>
<p>Action: ACTION_VIEW<br>
Data URI Scheme:</p>
<ul>
<li>file:</li>
<li>content:</li>
<li>http:</li>
</ul>
<p>MIME Type:</p>
<ul>
<li>"audio/*"</li>
<li>"application/ogg"</li>
<li>"application/x-ogg"</li>
<li>"application/itunes" Or any other that your app may require.</li>
</ul>
<p>example intent filter:</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:type="audio/*" />
<data android:type="application/ogg" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>18 Phone</h4>
<p>Action: ACTION_DIAL , ACTION_CALL<br>
其中ACTION_DIAL,需要传输一个号码,然后跳转到拨打界面,需要点击拨打按钮。<br>
而ACTION_CALL,需要传输一个号码,然后直接开始拨打,但是需要权限<br><code><uses-permission android:name="android.permission.CALL_PHONE" /></code><br>
Data URI Scheme:'tel:< phone-number>,voicemail:< phone-number>'<br>
e.g.:</p>
<pre><code>public void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phoneNumber));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<h4>19 Settings</h4>
<p>Action:</p>
<ul>
<li>ACTION_SETTINGS ACTION_WIRELESS_SETTINGS</li>
<li>ACTION_AIRPLANE_MODE_SETTINGS ACTION_WIFI_SETTINGS</li>
<li>ACTION_APN_SETTINGS ACTION_BLUETOOTH_SETTINGS ACTION_DATE_SETTINGS</li>
<li>ACTION_LOCALE_SETTINGS ACTION_INPUT_METHOD_SETTINGS</li>
<li>ACTION_DISPLAY_SETTINGS ACTION_SECURITY_SETTINGS</li>
<li>ACTION_LOCATION_SOURCE_SETTINGS ACTION_INTERNAL_STORAGE_SETTINGS</li>
<li>ACTION_MEMORY_CARD_SETTINGS</li>
</ul>
<h4>20 Text Messaging</h4>
<p>Action: ACTION_SENDTO, ACTION_SEND, ACTION_SEND_MULTIPLE<br>
Data URI Scheme:</p>
<pre><code>sms:<phone_number>
smsto:<phone_number>
mms:<phone_number>
mmsto:<phone_number>
</code></pre>
<p>MIME Type:<br>
PLAIN_TEXT_TYPE ("text/plain")<br>
"image/<em>"<br>
"video/</em>"<br>
Extras:<br>
"subject",'sms_body','EXTRA_STREAM'</p>
<p>e.g.:</p>
<pre><code>public void composeMmsMessage(String message, Uri attachment) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setType(HTTP.PLAIN_TEXT_TYPE);
intent.putExtra("sms_body", message);
intent.putExtra(Intent.EXTRA_STREAM, attachment);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<p>若想要确保intent被短信应用处理,使用 ACTION_SENDTO,并且设置data schmem为‘smsto:’.<br>
相应的Intent-filter</p>
<pre><code><activity ...>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="text/plain" />
<data android:type="image/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</code></pre>
<h4>21. WebBrowser</h4>
<p>加载网址<br>
- Action: ACTION_VIEW<br>
- Data URI Scheme:<br>
http:< URL><br>
https:< URL><br>
- MIME Type:<br>
PLAIN_TEXT_TYPE ("text/plain")<br>
"text/html"<br>
"application/xhtml+xml"<br>
"application/vnd.wap.xhtml+xml"</p>
<p>Web查询<br>
Action: ACTION_WEB_SEARCH<br>
Extras:SearchManager.QUERY The search string.</p>
<pre><code>public void searchWeb(String query) {
Intent intent = new Intent(Intent.ACTION_SEARCH);
intent.putExtra(SearchManager.QUERY, query);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
</code></pre>
<h4>22 Verify Intents with the Android Debug Bridge</h4>
<ol>
<li>Set up an Android device for development, or use a virtual device.</li>
<li>Install a version of your app that handles the intents you want to support.</li>
<li>Fire an intent using adb:<br><code>adb shell am start -a <ACTION> -t <MIME_TYPE> -d <DATA> \
-e <EXTRA_NAME> <EXTRA_VALUE> -n <ACTIVITY></code><br>
For example:<br><code>adb shell am start -a android.intent.action.DIAL \
-d tel:555-5555 -n org.example.MyApp/.MyActivity</code><br>
If you defined the required intent filters, your app should handle the intent.</li>
</ol>
Convert Sorted List to Binary Search Tree
https://segmentfault.com/a/1190000001519665
2014-11-14T11:18:38+08:00
2014-11-14T11:18:38+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h2>1. Problem</h2>
<p>Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.</p>
<h2>2. Anylysis</h2>
<p>升序的单链表转换成平衡二叉树,那么链表的中点即为二叉树的根节点,然后依次查找出左右的中点,将其作为二叉树的左右子节点。</p>
<h2>3. Answer</h2>
<pre><code>/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; next = null; }
* }
*/
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode sortedListToBST(ListNode head) throws NullPointerException{
int len = 0;
ListNode mHead = head;
while(mHead != null){
len ++;
mHead = mHead.next;
}
return sortedListToBST(head, 0, len-1);
}
private TreeNode sortedListToBST(ListNode head, int start, int end) {
if( start > end )
return null;
int mid = start + (end-start)/2;
ListNode mHead = head;
int mMid = mid;
while(mMid-- > 0 && mHead != null){
mHead = mHead.next;
}
TreeNode root = new TreeNode(mHead.val);
root.left = sortedListToBST(head, start, mid-1);
root.right = sortedListToBST(head, mid+1, end);
return root;
}
}
</code></pre>
<h2>4. Result</h2>
<h4>Time Limit Exceeded,测试用例的输入数据从-999到4093,大约5000个数据。</h4>
<p>时间太长了,分析一下。<br>
每次查找中间节点,为O(N/2)<br>
每次左右子树,需要O(lgN)<br>
因此结果应该是O(NlgN),不知道分析对不对。<br>
栈调用次数太多了,如何优化呢?<br>
网上有篇文章<a rel="nofollow" href="http://leetcode.com/2010/11/convert-sorted-list-to-balanced-binary.html">Convert Sorted List to Balanced Binary Search Tree (BST)</a>。该方法无需寻找链表的中点,采用从底向上遍历建树。</p>
<pre><code>BinaryTree* sortedListToBST(ListNode *& list, int start, int end) {
if (start > end) return NULL;
// same as (start+end)/2, avoids overflow
int mid = start + (end - start) / 2;
BinaryTree *leftChild = sortedListToBST(list, start, mid-1);
BinaryTree *parent = new BinaryTree(list->data);
parent->left = leftChild;
list = list->next;
parent->right = sortedListToBST(list, mid+1, end);
return parent;
}
BinaryTree* sortedListToBST(ListNode *head, int n) {
return sortedListToBST(head, 0, n-1);
}
</code></pre>
alibaba 2015 校招面经
https://segmentfault.com/a/1190000000670408
2014-09-16T10:41:38+08:00
2014-09-16T10:41:38+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<p>[一面]<br>
1. 自我介绍一下<br>
2. 你是应聘Android开发,写下Android生命周期吧<br>
3. Android应用锁频后,生命周期如何变化<br>
4. 如何保存信息<br>
5. Android的web客户端,用户登录是如何实现的,如何保存用户会话状态(即用户登陆后,如何确认该用户,是每次都会发送用户名和密码进行验证吗)<br>
API,AppKey,启动后发送设备标识,屏幕大小,图片等自使用适配,返回。<br>
6. HTTP的GET和POST区别<br>
7. 说说数组和链表的区别<br>
8. 你是如何做版本管理的和依赖的<br>
9. 说说hashtable<br>
10. 聊聊你的项目<br>
11. 如何推广你的App,你的受众,金钱有限的情况下<br>
12. 给你一篇文章,统计出出现次数最多的单词,如何编程。</p>
<p>不知道还能不能有第二面了,,,,</p>
[转载]递归解决全排列生成算法
https://segmentfault.com/a/1190000000666583
2014-09-12T19:48:48+08:00
2014-09-12T19:48:48+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<p>排列:从n个元素中任取m个元素,并按照一定的顺序进行排列,称为排列;<br>
全排列:当n==m时,称为全排列;</p>
<p>比如:集合{ 1,2,3}的全排列为:<br>
{ 1 2 3}<br>
{ 1 3 2 }<br>
{ 2 1 3 }<br>
{ 2 3 1 }<br>
{ 3 2 1 }<br>
{ 3 1 2 }</p>
<h2>方法一:递归</h2>
<p>我们可以将这个排列问题画成图形表示,即排列枚举树,比如下图为{1,2,3}的排列枚举树,此树和我们这里介绍的算法完全一致;</p>
<p><img src="http://img.my.csdn.net/uploads/201209/16/1347810678_7019.png" alt=""><br>
算法思路:<br>
(1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀);<br>
(2)出口:如果只有一个元素的全排列,则说明已经排完,则输出数组;<br>
(3)不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组;</p>
<pre><code>public class Test {
public static int arr[] = new int[]{1,2,3};
public static void main(String[] args) {
perm(arr,0,arr.length-1);
}
private static void swap(int i1, int i2) {
int temp = arr[i2];
arr[i2] = arr[i1];
arr[i1] = temp;
}
/**
* 对arr数组中的begin~end进行全排列
*
* 比如:
* arr = {1,2,3}
* 第一步:执行 perm({1,2,3},0,2),begin=0,end=2;
* j=0,因此执行perm({1,2,3},1,2),begin=1,end=2;
* j=1,swap(arr,0,0)-->arr={1,2,3}, perm({1,2,3},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{1,2,3}
* swap(arr,1,1) --> arr={1,2,3};
* j=2,swap(arr,1,2)-->arr={1,3,2}, perm({1,3,2},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{1,3,2}
* swap(arr,2,1) --> arr={1,2,3};
* j=1,swap(arr,0,1) --> arr={2,1,3}, perm({2,1,3},1,2),begin=1,end=2;
* j=1,swap(arr,1,1)-->arr={2,1,3} perm({2,1,3},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{2,1,3}
* swap(arr,1,1)--> arr={2,1,3};
* j=2,swap(arr,1,2)后 arr={2,3,1},并执行perm({2,3,1},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{2,3,1}
* swap(arr,2,1) --> arr={2,1,3};
* swap(arr,1,0) --> arr={1,2,3}
* j=2,swap(arr,2,0) --> arr={3,2,1},执行perm({3,2,1},1,2),begin=1,end=2;
* j=1,swap(arr,1,1) --> arr={3,2,1} , perm({3,2,1},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{3,2,1}
* swap(arr,1,1) --> arr={3,2,1};
* j=2,swap(arr,2,1) --> arr={3,1,2},并执行perm({2,3,1},2,2),begin=2,end=2;
* 因为begin==end,因此输出数组{3,1,2}
* swap(arr,2,1) --> arr={3,2,1};
* swap(arr,0,2) --> arr={1,2,3}
*
* @param arr
* @param begin
* @param end
*/
public static void perm(int arr[], int begin,int end) {
if(end==begin){ //一到递归的出口就输出数组,此数组为全排列
for(int i=0;i<=end;i++){
System.out.print(arr[i]+" ");
}
System.out.println();
return;
}
else{
for(int j=begin;j<=end;j++){
swap(begin,j); //for循环将begin~end中的每个数放到begin位置中去
perm(arr,begin+1,end); //假设begin位置确定,那么对begin+1~end中的数继续递归
swap(begin,j); //换过去后再还原
}
}
}
}
</code></pre>
<h2>方法二:循环</h2>
<p><img src="http://img.my.csdn.net/uploads/201209/17/1347841576_1926.GIF" alt=""></p>
<pre><code>public class Test2 {
public static int arr[] = new int[]{0,0,0};
public static void main(String[] args) {
perm(3);
}
/**
* 数组变化过程:
* 3 0 0
* 3 2 0
* 3 2 1
* 3 2 0
* 3 0 0
* 3 0 2
* 3 1 2
* 3 0 2
* 3 0 0
* 0 0 0
* 0 3 0
* 2 3 0
* 2 3 1
* 2 3 0
* 0 3 0
* 0 3 2
* 1 3 2
* 0 3 2
* 0 3 0
* 0 0 0
* 0 0 3
* 2 0 3
* 2 1 3
* 2 0 3
* 0 0 3
* 0 2 3
* 1 2 3
* 0 2 3
* 0 0 3
* 0 0 0
* @param m
*/
private static void perm(int m) {
if(m==0){
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
System.out.println();
return;
}
else{
for(int i=0;i<arr.length;i++){
if(arr[i]==0){
arr[i] = m;
perm(m-1);
arr[i] = 0;
}
}
}
}
}
</code></pre>
<p>参考文献:<br>
1.全排列的递归算法实现 李盘荣<br>
2.全排列递归算法在算法教学中的重要性 吴素萍<br>
3.排序算法与全排列生成算法研究 陈卫东, 鲍苏苏</p>
<p>论文下载地址:<a rel="nofollow" href="http://yunpan.cn/lk/05qsom5mle">http://yunpan.cn/lk/05qsom5mle</a></p>
<p>私以为写的很好,特转收藏。<br>
转自 <a rel="nofollow" href="http://blog.csdn.net/xiazdong/article/details/7986015">http://blog.csdn.net/xiazdong/article/details/7986015</a></p>
Java 面试准备[二]
https://segmentfault.com/a/1190000000660181
2014-09-05T17:53:11+08:00
2014-09-05T17:53:11+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h2>关于字符串的10个面试问题</h2>
<h3>1. == 和 equals()方法</h3>
<p>== 是用于比较对象的引用地址的,或者说是否指向同一个变量。<br>
equals是比较对象的真实值的</p>
<h3>2. 为什么针对安全保密高的信息,char[]比String更好?</h3>
<p>char[] 是可以修改的,String是不可变对象,创建之后无法修改,无法删除,只能等到JVM垃圾回收。</p>
<h3>3. 我们可以针对字符串使用switch条件语句吗?</h3>
<p>JDK7 是支持在switch中使用字符串比较的,但Android目前还是基于JDK6的。</p>
<h3>4. 如何将字符串转化成int?</h3>
<p>int a = Integer.partInt("124");</p>
<h3>5. 如何将字符串用空白字符分割开?</h3>
<p>String [] a = aString.split(" ");</p>
<h3>6. substring()方法到底做了什么?</h3>
<p>JDK7 中 subString(m, n) 会创建一个新的字符串。</p>
<h3>7. String vs StringBuilder vs StringBuffer</h3>
<p>String 不可变对象。<br>
StringBuilder 线程不安全类,速度比StringBuffer快。<br>
StringBuffer 线程安全类,内部使用同步机制。</p>
<h2>关于集合框架</h2>
<h3>1. Java集合框架都有哪些最佳实践呢?</h3>
<p>根据实际的使用情况选择合适的数据结构,例如固定大小的还是需要增加大小的,有重复元素的还是没有的,需要保持有序还是不需要,遍历是正向的还是双向的,插入是在末尾的还是任意位置的,更多的插入还是更多的读取,是否需要并行访问,是否允许修改,元素类型是相同的还是不同的,等等。另外,还需要尽早考虑多线程,原子性,内存使用量以及性能等因素。</p>
<p>不要假设你的集合里元素的数量一直会保持较小,它也有可能随着时间增长。所以,你的集合最好能够给定一个合适的大小。</p>
<p>针对接口编程优于针对实现编程。例如,可能在某些情况下,LinkedList是最佳的选择,但是后来ArrayList可能因为性能的原因变得更加合适<br>
不好的方式</p>
<pre><code>ArrayList list = new ArrayList(100);
</code></pre>
<p>好的方式,针对接口编程</p>
<pre><code>// program to interface so that the implementation can change
List list = new ArrayList(100);
List list2 = new LinkedList(100);
List emptyList = Collections.emptyList( );
Set emptySet = Collections.emptySet( );
</code></pre>
<p>在取得列表的时候,<strong>如果返回的结果是空的话,最好返回一个长度为0的集合或者数组,而不要返回null</strong>。因为,返回null的话可能能会导致程序错误。调用你的方法的开发人员可能会忘记对返回为null的情况进行处理。</p>
<p>封装好集合:一般来说,集合都是不可变的对象。所以尽量不要把集合的成员变量暴露给调用者。因为他们的操作一般都不会进行必要的校验。</p>
Java 面试准备
https://segmentfault.com/a/1190000000659691
2014-09-05T11:28:07+08:00
2014-09-05T11:28:07+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<p>ImportNew 网站的Java面试专题学习笔记</p>
<h2>1. 非可变性和对象引用</h2>
<pre><code class="Java">String s = " Hello ";
s += " World ";
s.trim( );
System.out.println(s);</code></pre>
<p>输出为“ Hello World ”,前后皆有空格。<br>字符串是不可变对象。<br>s.trim()虽然生成了一个新的字符串对象,但是却没有变量指向这个心生成的对象,s 仍然指向字符串s += " World "。<br>下图说明了,生成对象以及垃圾回收过程。<br><img src="/img/remote/1460000004876818" alt="生成对象过程,以及垃圾回收" title="生成对象过程,以及垃圾回收"></p>
<p>可用StringBuilder来构造,因为其底层使用的是字符数组,所有操作都直接在字符数组上直接操作,而且他不是一个线程安全的类,执行速度上,相比于StringBuffer要快。</p>
<p>这一点如果深入理解了<strong>String的Interning机制</strong>,就更好理解了。<br>Java程序在编译时,会将所有确定下来的,存在双引号内的字符串,都存入常量池中,该常量池是类文件(.class)的一部分。常量池中存着许多表,其中 Constant_Utf8_info 表中,记录着会被初始化为 String 对象的字符串的字面值(iteral)。 在JVM 中,相应的类被加载运行后,常量池对应的映射到 JVM 的运行时常量池(run time constant pool)中。</p>
<p>关于String的intern()方法</p>
<blockquote><p>当 intern 方法被调用,如果字符串池中已经拥有一个与该 String 的字符串值相等(即 equals()调用后为 true)的 String 对象时,那么池中的那个 String 对象会被返回。否则,池中会增加这个对象,并返回当前这个 String 对象。</p></blockquote>
<p>现代的 JVM 实现里,考虑到垃圾回收(Garbage Collection)的方便,将 heap 划分为三部分: young generation 、 tenured generation(old generation)和 permanent generation( permgen )。字符串池是为了解决字符串重复的问题,生命周期长,它存在于 permgen 中。</p>
<p>因此,对于</p>
<pre><code>String str = new String("abc");</code></pre>
<p>JVM会生成两个字符串,一个是在常量池中,另外一个是new在heap堆中。</p>
<h2>2. equals 和 ==</h2>
<pre><code class="Java">Object s1 = new String("Hello");
Object s2 = new String("Hello");
if(s1 == s2) {
System.out.println("s1 and s2 are ==");
}else if (s1.equals(s2)) {
System.out.println("s1 and s2 are equals()");
}</code></pre>
<p>输出结果为s1 and s2 are equals()<br>主要考察对equals和==的理解,==比较引用的地址是否相同,equeal比较对象中真正的值。<br>详细的过程可见下图<br><img src="/img/remote/1460000004876825" alt="" title=""></p>
<p>另外,如果不是用 new关键字强制创建字符串对象的话,而是采用==,那么Java会默认采用字符串池,减少对象的创建。</p>
<p>String对象会创建一个字符串池(a pool of string),如果当前准备新创建的字符串对象的值在这个池子中已经存在,那么就不会生成新对象,而是复用池中已有的字符串对象。flyweight 模式的精髓就是对象复用。<br><img src="/img/remote/1460000004876826" alt="" title=""></p>
<h2>3. 重载(overloading)和重写(overriding)</h2>
<p>重写发生在子类继承父类时,子类覆盖父类的方法时发生,是在运行时发生。<br>重载是在同一个类中,同一个方法,不同参数时发生,是在编译期发生。</p>
<p>在Java 5中使用注解@override,来标示方法重写,如果编译时发现没有重写,则JVM会抛出编译异常。</p>
<h2>4. 迭代和递归</h2>
<p>可重入方法(re-entrant method)是可以安全进入的方法,即使同一个方法正在被执行,深入到同一个线程的调用栈里面也不会影响此次执行的安全性。一个非可重入方法则不是可以安全进入的。例如,加入写文件或者向文件中写入日志的方法不是可重入方法时,有可能会毁坏那个文件。</p>
<p>如果一个方法调用了其自身的话,我们称之为递归调用。假定栈空间足够的话,尽管递归调用比较难以调试,在Java语言中实现递归调用也是完全可行的。递归方法是众多算法中替代循环的一个不错选择。所有的递归方法都是可重入的,但是不是所有可重入的方法都是递归的。</p>
<p>栈遵守LIFO(Last In First Out)规则,因此递归调用方法能够记住“调用者”并且知道此轮执行结束之返回至当初的被调用位置。递归利用系统栈来存储方法调用的返回地址。 Java是一种基于栈设计的编程语言。</p>
<p>循环的方式可以达到目的,不必采用递归。但是在某些情况下采用递归方式则代码会更加简短易读。递归方法在循环树结构以及避免丑陋的嵌套循环的情况下是非常好用的。</p>
<p>常规递归方法(亦称,头递归)在上面演示了,这种方式会增加调用栈的大小。每次递归,其入口需要被记录在栈中。方法返回之前需要给countA(input.substring(1)的结果加一个count。因此,最后需要做的事其实是加法运算,而非递归本身。</p>
<p>在尾递归中,最后要做的是递归,加法运算在之前就已经完成了。栈调用减少带来了内存消耗减少并且程序的性能更好。如下代码</p>
<pre><code class="java">public class TailRecursiveCall {
public int countA(String input) {
// exit condition – recursive calls must have an exit condition
if (input == null || input.length() == 0) {
return 0;
}
return countA(input, 0) ;
}
public int countA(String input, int count) {
if (input.length() == 0) {
return count;
}
// check first character of the input
if (input.substring(0, 1).equals("A")) {
count = count + 1;
}
// recursive call is the last call as the count is cumulative
return countA(input.substring(1), count);
}
public static void main(String[] args) {
System.out.println(new TailRecursiveCall().countA("AAA rating"));
}
}</code></pre>
<h2>5. 关于ArrayList</h2>
<ul><li><p>ArrayList的大小是如何自动增加的?你能分享一下你的代码吗?</p></li></ul>
<blockquote><p>使用ensureCapacity, 在进行添加元素时,检查容量是否足够,不够的话,就将容量扩大3/2,并将旧数组中的元素使用Arrays.copyOf拷贝到新数组中。</p></blockquote>
<ul><li><p>什么情况下你会使用ArrayList?什么时候你会选择LinkedList?</p></li></ul>
<blockquote><p>ArrayList是在访问的次数远大于插入和删除的次数,使用ArrayList,因为ArrayList底层使用数组,访问的复杂度为O(1), 但是插入和删除就得频繁使用System.arraycopy复制数组。 LinkList主要在访问次数远小于插入和删除的次数时使用,其删除和插入的复杂度,但访问元素时几乎为O(n)。</p></blockquote>
<ul><li><p>当传递ArrayList到某个方法中,或者某个方法返回ArrayList,什么时候要考虑安全隐患?如何修复安全违规这个问题呢?</p></li></ul>
<blockquote><p>当array被当做参数传递到某个方法中,如果array在没有被复制的情况下直接被分配给了成员变量,那么就可能发生这种情况,即当原始的数组被调用的方法改变的时候,传递到这个方法中的数组也会改变。</p></blockquote>
<p>将其副本拷贝出来再进行修改。</p>
<ul><li><p>如何复制某个ArrayList到另一个ArrayList中去?写出你的代码?</p></li></ul>
<blockquote>
<p>使用clone()方法,比如ArrayList newArray = oldArray.clone();</p>
<pre><code>使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject);
使用Collection的copy方法...
</code></pre>
</blockquote>
<ul><li><p>注意1和2是浅拷贝(shallow copy),何为浅拷贝?</p></li></ul>
<blockquote>
<p>浅拷贝就比如像引用类型,而深拷贝就比如值类型。浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。举个例子,一个人一开始叫张三,后来改名叫李四了,可是还是同一个人,不管是张三缺胳膊少腿还是李四缺胳膊少腿,都是这个人倒霉。</p>
<p>深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。</p>
</blockquote>
<p>还可用序列化技术来进行深拷贝,对象实现序列化接口,然后写入流,并读出来</p>
<pre><code>// 将对象写到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
// 从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());</code></pre>
<p>但是串行化却很耗时,在一些框架中,我们便可以感受到,它们往往将对象进行串行化后进行传递,耗时较多。</p>
<ul><li><p>在索引中ArrayList的增加或者删除某个对象的运行过程?效率很低吗?解释一下为什么?</p></li></ul>
<p>频繁插入和删除,会频繁调用System.arrayCopy....效率低</p>
<p>参考地址<br>-[1] <a href="https://link.segmentfault.com/?enc=o5kJAcqCm%2FzZvD55AJ%2BQUw%3D%3D.P7eng7VHhUPowHHqjomAuvjns6B%2FyJMUfamhEt3GbTBgZOAuBt5yTS0T3XURy4h%2B" rel="nofollow">http://www.importnew.com/2228...</a><br>-[2] <a href="https://link.segmentfault.com/?enc=NlHl2q1UzV%2Fpu5vq1hssrw%3D%3D.AS93LJjA4OeBaMm%2FAbMcnZiQweY7j2ytlPEer50HVYbRgpZjtB4E1BNWqkLuxz32" rel="nofollow">http://www.importnew.com/2223...</a><br>-[3] <a href="https://link.segmentfault.com/?enc=9EhieLStzbBoBz5reoymGg%3D%3D.Ox0%2BsxOGR4Mt2Ez0idvjLzJ9Z%2FvyuuhmeI0kVQIoSNsj%2FWrsfO%2F7xGdfGGROUb%2Fn" rel="nofollow">http://www.importnew.com/2217...</a><br>-[4] <a href="https://link.segmentfault.com/?enc=ZRlegscApL5VoCuXZZaycA%3D%3D.b8BaB0ofSKxVF9aR75td6GidQpiwsXcshnn4u945%2FkrEliBOJvZMrIQaH0eTC4Vw" rel="nofollow">http://www.importnew.com/2329...</a><br>-[5] <a href="https://link.segmentfault.com/?enc=dm1JdAVAK86BftdLgrAKEQ%3D%3D.U8iSOZuSJWYRMmrOhGjbAgQm%2BFW1Qum4kYoVxcRlpM0pYgsk5cAuThNPp%2F7%2BNKhw" rel="nofollow">http://www.importnew.com/9928...</a><br>-[6] <a href="https://link.segmentfault.com/?enc=4YaLgecTqzK1tTfGW7RByA%3D%3D.1pgChCnr%2Fwt2N%2FEE03xxmSMYzezsr3%2FNKT%2FUbuEr%2FihIFgDPTTYZeapI%2FZs7rfen5PZDmGO7S7c%2Bds4dOUhbWw%3D%3D" rel="nofollow">http://www.cnblogs.com/shuaiw...</a><br>-[7] <a href="https://link.segmentfault.com/?enc=ayfcIbiHtqd0j3ecb9hLZA%3D%3D.wrE9lNptVe1KsDhJujIP8OgtOFsylOYhoyCBy5PY7kDR%2FBMzSauiWGdxVoWqGZSeuy6hG%2FLVagjWSpGxPqFZDA%3D%3D" rel="nofollow">http://blog.csdn.net/biaobiao...</a></p>
算法导论-学习笔记与进度
https://segmentfault.com/a/1190000000626450
2014-08-03T10:01:13+08:00
2014-08-03T10:01:13+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
3
<h2>算法导论 阅读进度</h2>
<h3>第一部分 基础知识</h3>
<h4>第一章 计算中算法的角色 Done</h4>
<h5>1.1 算法</h5>
<ul>
<li>输入与输出</li>
<li>算法可以解决哪些问题</li>
<li>数据结构</li>
<li>技术</li>
<li>一些比较难的问题</li>
</ul>
<h5>1.2 作为一种技术的算法</h5>
<ul>
<li>效率</li>
<li>算法和其他技术</li>
</ul>
<h4>第二章 算法入门 Done</h4>
<h5>2.1 插入排序</h5>
<p>伪代码如下:</p>
<pre><code>INSERTION_SORT(A)
for j = 2 to length[A]
do key = A[j]
i = j-1
// 将其与已经排好序的数组进行挨个比较
while i>0 and A[i] > key
do A[i+1] = A[i]
i = i-1
A[i+1] = key
</code></pre>
<p>原理</p>
<blockquote>
<p>其实就是在每次进入一个元素key后,将其与已经排好序的序列进行比较,如果key值小于序列的第一个值(默认为从小到大排序),那么就表明该key应该置入这个序列当中,并且将比较过的元素自动向后移动,以腾出空间放置key。最后将key值置入空位中。</p>
</blockquote>
<p>现实中就是在接牌时,插入和调整扑克牌顺序的问题了,算法复杂度为O(n^2)</p>
<p>插入排序采用的是增量方法(incremental),采用分治法的合并排序时间复杂度为O(nlgn)</p>
<p>分治法<br>
- 分解<br>
- 解决<br>
- 合并</p>
<p>主要在于合并算法MERGE(A, p, q, r)<br>
伪代码如下:</p>
<pre><code>MERGE(A, p, q, r)
n1 = q-p+1
n2 = r-q
for i = 1 to n1
do L[i] = A[p+i-1]
for j = 1 to n2
do R[j] = A[q+j]
L[n1+1] = -oo
R[n2+1] = -oo
i = j = 1
for k = p to r
do if L[i] <= R[j]
then A[k] = L[i]
i = i+1
else
A[k] = R[j]
j = j+1
</code></pre>
<p>原理</p>
<blockquote>
<p>首先将输入数组分割,然后在对分割后的数组进行一一比较,如果L的元素小,就将其输出至目的数组A,如果R小,则输出R至A.</p>
</blockquote>
<p>此时MERGE-SORT算法就清晰了,如果将一个数组通过递归不断将其分割,最终分割为每个数组只有一个元素,那么使用MERGE时,就能够将此二元素排序,然后对4元素排序,然后是8元素....<br>
伪代码如下:</p>
<pre><code>MERGE-SORT(A, p, r)
if p<r
then q = (p+r)/2
MERGE-SORT(A, p ,q)
MERGE-SORT(A, q+1, r)
MERGE(A, p , q, r)
</code></pre>
<p>冒泡排序<br>
伪代码如下:</p>
<pre><code>BUBBLE-SORT(A)
for i = 1 to length[A]
do for j = length[A] downto i+1
do if A[j] < A[j-1]
then exchange A[j], A[j-1]
</code></pre>
<p>通过不断交换相邻的两个反序元素达到排序的目的,时间复杂度为O(n^2)<br>
1----n-1<br>
2----n-2<br>
3----n-4<br>
....<br>
n-1----1<br>
即<br>
1+2+3+4+....+n = n(n+1)/2,所以时间复杂度为O(n^2)</p>
<h4>第三章 函数的增长 Done</h4>
<h4>第四章 递归 Done</h4>
<p>递归式 T(n) = aT(n/b) + f(n), 其中a>=1, b>1, f(n) 是给定函数</p>
<h5>4.1 代换法</h5>
<ul>
<li>猜测解的形式</li>
<li>用数学归纳法找出真正有效的常数</li>
</ul>
<h5>4.2 递归树方法</h5>
<p>仔细画出递归树</p>
<h5>4.3 主方法</h5>
<p>是求解递归式T(n)的食谱方法。<br>
主方法依赖于定理4.1主定理 设a>=1 和 b >1为常数,设f(n)为一函数,T(n)由递归式</p>
<blockquote>
<p>T(n) = aT(n/b) + f(n)</p>
</blockquote>
<p>对非负整数定义,其中n/b 指上顶或者下底,那么T(n)可能有如下的渐进界<br>
...<br>
主要是针对f(n),a,b等值。</p>
<h4>第五章 概率分析与随机化算法</h4>
<h5>5.1 雇用问题</h5>
<p>概率分析,随机算法。<br>
使用概率分布预测输入值的分布,以此来帮助分析算法平均情况行为的。</p>
<h5>5.2 指示器随机变量</h5>
<p>I(A) = 1,如果A发生的话;0,如果A不发生的话。</p>
<h5>5.3 随机算法</h5>
<p>无法得到输入分布,考虑使用随机算法。<br><strong>随机排列数组</strong><br>
许多随机算法通过排列给定的输入数组来使输入随机化。</p>
<ul>
<li>一个常用方法是为每个数组元素A[i]赋一个随机的优先级P[i],然后按照优先级对数组进行排序,过程PERMUTE-BY-SORT,花费O(nlgn)</li>
<li>原地排列给定的数组,过程RANDOMIZE-IN-PLACE花费O(n)</li>
</ul>
<h5>5.4 进一步使用</h5>
<ul>
<li>1 生日悖论<br>
一个房间的人数需要达到多少,使得两个人生日相同的机会达到50%?分析所有人生日互不相同的概率,其补就为至少两人生日相同。<br>
因此 Pr{Bk} = 1<em>(n-1/n)</em>(n-2/n)<em>(n-3/n)....(n-k+1/n)=1</em>(1-1/n)*(1-2/n)...(1-(k-1)/n)<br>
最后分析得出至少23人在一个房间里,概率就能够为1/2。火星为38个人</li>
<li>2 球与盒子</li>
<li>
</ul>
<hr>
<h3>第二部分 排序和顺序统计学. Done</h3>
<h4>第六章 堆排序 Done</h4>
<p>二叉堆数据结构可以被视为一颗完全二叉树。树的每一层都是被填满的,最后一层可能除外。<br>
父节点</p>
<pre><code>PARENT(i)
return [i/2]
LEFT(i)
return 2i
RIGHT(i)
return 2i+1
</code></pre>
<p>有两种:最大堆和最小堆,在这两种堆中,节点内的数组都要满足堆特性,在最大堆中,除了根以外的每个节点,有</p>
<blockquote>
<p>A[PARENT(i)] >= A[i]</p>
</blockquote>
<p>根元素为最大值。<br>
最小堆刚好相反。</p>
<p>MAX-HEAPIFY. 保持堆的性质,最大堆性质,根或者子根都是树最大元素,O(lgn)<br>
对A[i]的这颗字数执行最大堆化。<br>
算法如下:</p>
<pre><code>MAX-HEAPIFY( A, i )
l = LEFT(A[i])
r = RIGHT(A[i]
If l <= heap-size(A) and A[l] > A[i]
then largest = l
else largest = i
If r <= heap-size(A) and A[r] > A[largest]
then largest = r
If largest != i
then exchange(A[i], A[largest])
MAX-HEAPIFY(A, largest)
</code></pre>
<p>建堆 Build-MAX-HEAP, 对每一颗非叶子节点树执行最大堆化,直至根元素。<br>
自n/2 处开始递减,循环执行Max-HEAPIFY,运行时间的界为O(n)</p>
<pre><code>BUILD-MAX-HEAP(A)
heap-size[A] = length[A]
for i = length[A]/2 downto 1
do MAX-HEAPIFY(A, i)
</code></pre>
<p>堆排序 HEAPSORT 自叶节点向跟元素递减,交换跟元素和叶节点,接着调用MAX-HEAPIFY, 保持该子树的最大堆性质,即可完成节点自小到大排序,时间复杂度为O(nlgn)<br><strong>原理</strong></p>
<blockquote>
<p>每次都将最大的根元素换至末尾的叶节点,这一操作能够将最大元素调到最后,并且排除出堆,然后再一次构造最大堆,次最大元素又被调节到根元素位置,然后再一次将其换至叶节点,这样,不断循环,就能够将元素在数组中从小到大排列。</p>
</blockquote>
<p>HEAPSORT(A)</p>
<pre><code>BUILD_MAX_HEAP(A)
for i = length[A] downto 2
do exchange(A[1], A[i])
heap-size[A] = heap-size[A] - 1
MAX-HEAPIFY(A,1)
</code></pre>
<p>最大优先级队列,最小优先级队列的应用如图所示。<br><img src="http://segmentfault.com/img/bVcM8b" alt="请输入图片描述"></p>
<h4>第七章 快速排序 Done</h4>
<pre><code>QUICKSORT(A,p, r)
If p < r
Then q = PARTITION(A, p, r )
QUICKSORT(A, p, q-1)
QUICKSORT(A, q+1, r)
</code></pre>
<p>最初调用时,QUICKSORT(A, 1, length(A) )<br>
其中PARTITION(A, q, r )算法如下</p>
<pre><code>x = A[r]
i = p-1
For j = p to r-1
Do If A[j] <= x
Then i += 1
Exchange(A[i], A[j])
Exchange(A[i+1], A[r])
Return i + 1
</code></pre>
<p>分区算法 不断将数组分为四个区:<br>
- 第一区 小于 x的区间 A[p, i]<br>
- 第二区 大于x的区间 A[i+1, j]<br>
- 第三区间 尚未比较的区间 A[j+1, r-1]<br>
- 第四区间 主元 A[r]</p>
<p>每一次运行都是O(n)<br>
总共需要递归调用O(log n),因此算法时间复杂度为O(n log n)</p>
<p>有时我们需要对样本加入一些随机化数据,以便对于所有输入,都能得到较好的平均性能,因此,采用随机选择主元的快速排序随机化版本。<br>
主要是对分区操作进行修改</p>
<pre><code>RAMDOMIZED-PARTITION(A, p, r)
i = random(p, r)
Exchange(i , r)
Return PARTITION(A, p, r)
</code></pre>
<p>新的排序算法修改如下:</p>
<pre><code>RANDOMIZED-QUICKSORT(A, p, r )
If p < r
then q = RANDOMIZED-PARTITION(A, p, r)
RANDOMIZED-QUICKSORT(A, p, q-1)
RANDOMIZED-QUICKSORT(A, q+1, r)
</code></pre>
<h4>第八章 线性时间排序(即时间复杂度为O(n)) Done</h4>
<p>合并排序和堆排序在最坏情况下,达到上界O(nlogn),快速排序在平均情况下达到此上界,最坏O(n^2)<br>
比较排序,非比较排序</p>
<h5>8.1 排序算法的时间的下界</h5>
<p>比较排序可以抽象为决策树模型,比如三个值的比较决策结果又3!= 6<br>
定理8.1 任意一个比较排序的算法在最坏情况下,都需要做O(nlgn)次的比较<br>
推论8.2 堆排序和合并排序都是渐进最优的比较排序算法。</p>
<h5>8.2 计数排序**</h5>
<p>比较有趣的一个排序算法,使用三个数组空间A,B,C。<br>
统计各个值出现的次数.</p>
<blockquote>
<p>C[A[j]] = C[A[j]] +1</p>
</blockquote>
<p>可见对于C的空间要求将会比较大,只适用于数值比较小的空间。<br>
这样的排序将会导致各个值A[j]的出现次数按照A[j]值的大小在C中依次由小到大排列。然后计算出C中每个值之前出现多少个值,进而计算出该值所占的位置。</p>
<p>算法如下:</p>
<pre><code>COUNTING-SORT(A, B, k)
for i=0 to k
do C[i] = 0
// 计算A[i] 各个值出现的次数
for j=1 to lenght[A]
do C[A[j]] = C[A[j]]+1
// 计算小于或者等于C[i]值出现的次数
for i=1 to k
do C[i] = C[i]+C[i-1]
// 将A[j]的值按照出现的位置置入B中,并将该值的出现次数减一,使与之相同的值自动排在A[j]的前一个位置
for j = length[A] downto 1
do B[C[A[j]]] = A[j]
C[A[j]] = C[A[j]]-1
</code></pre>
<p>该算法很绕,但比较稳定,运行时间为O(n)</p>
<h5>8.3 基数排序</h5>
<p>基数排序是对具有多个关键字域的记录进行排序,比如年月日。</p>
<pre><code>RADIX-SORT(A,d)
for i = 1 to d
do use a stable sort to sort array A to digit i
</code></pre>
<p>可以将一个32位的字视为4个8位的数字,于是就可以将k 缩小至255,数位d变为4.<br>
同样的,利用计数排序作为中间稳定排序的基数排序不是原地排序,占用空间较大。尽管基数排序执行的遍数可能比快速排序少,但每一遍所需的时间要长得多。因此一般还是使用快排,因为快排能够有效的利用硬件缓存,同时也是个原地排序。</p>
<h5>8.3 桶排序</h5>
<p>输入需要符合均匀分布,即可以以线性时间O(n)运行。计数排序的假设输入由小范围内整数构成,而桶排序假设输入由一个随机过程产生,该过程均匀分布在区间(0,1]<br>
算法<br>
BUCKET-SORT(A)</p>
<pre><code>n = length(A)
for i = 1 to n
do insert A[i] into list B[nA[i]]
for i = 0 to n-1
do sort list B[i] with insertion sort
concatenate the lists B[0],B[1],....
B[n-1] together in order
</code></pre>
<h4>第九章 中位数和顺序统计学 Done</h4>
<p>约定<br>
取下中位数<br>
本章讨论从一个由n个不同数值构成的集合中选择第i个顺序统计量的问题。选择问题的定义如下:</p>
<blockquote>
<p><strong>输入:</strong>一个包含n个不同的数的集合A和一个数i, 1=<i<=n<br><strong>输出:</strong> 元素x属于A,它恰大于A中其他的i-1个元素。</p>
</blockquote>
<h5>最大值和最小值</h5>
<p>比如MINIMUM(A)</p>
<pre><code>min = A[1]
for i = 2 to length[A]
do if min > A[i]
min = A[i]
return min
</code></pre>
<p>通过n-1比较得出,时间复杂度为O(n)<br>
最大值也是如此。<br>
同时计算最大值和最小值,如使用独立比较需要比较2(n-1)次。<br>
可通过每次输入一对数据,然后将其中大的于最大值比较,小的与最小值比较,每个元素比较三次,但只需运行n/2次,所以总的比较次数是3(n/2)次。</p>
<h5>9.2 以期望线性时间做选择</h5>
<p>使用RANDOMIZED-SELECT 算法,以第七章快速排序算法为模型,也是对输入数组进行划分,但只处理划分的一边,该边通过与需要选择的位计算而出,期望运行时间为O(n)<br>
RANDOMIZED-SELECT算法利用RADOMIZED-PARTITION程序,返回数组A[p..r]中第i小的元素</p>
<pre><code>RANDOMIZED-SELECT(A, p, r, i)
if p = r
then return A[p]
q = RANDOMIZED-PARTITION(A, p, r)
k = q - p + 1
if i = k
then return A[q]
elseif i < k
then return RANDOMIZED-SELECT(A, p, q-1, i)
//注意i的位置将会做相应的调整,i始终只是相对位置,而q,r都是指在整个数组中的位置
//因此在比较i和k的位置时,是通过计算k的相对位置来与i比较的。
else return RANDOMIZED-SELECT(A, q+1, r, i-k)
</code></pre>
<p>按照我的理解,似乎不需要使用i的相对位置,原始位置应该就可以,感觉上没有限制。</p>
<p>结论,在平均情况下,任何顺序统计量特别是中位数,都可以在线性时间内O(n)得到。</p>
<h5>9.3 最坏情况线性时间的选择</h5>
<p>最坏情况运行时间为O(n)的SELECT算法。采用快速排序的确定性划分算法PARTITION,并做了相应的修改。<br>
- 将输入数组划分为n/5个组,每组5个<br>
- 寻找n/5个组中的每一组中位数,首先对每组中的元素进行插入排序(插入排序在元素数量较小的情况下有着O(n)的复杂度)然后从排序过的数组中选出中位数。<br>
- 在第二步中选出的中位数,递归调用SELECT选出一个中位数x<br>
- 利用修改过的PARTITION过程,按照中位数的中位数x,对输入数组进行划分,让k比划分地区的元素数目多1,所以x就是第k小元素,并且有n-k个元素在划分的高区。<br>
- 如果i = k ,则返回x,否则,如果i<k, 则在低区递归调用SELECT以找出第i小的元素,如果i>k, 则在高区递归调用寻找第(i-k)个最小元素。</p>
<h5>本章结论</h5>
<p>本章中选择算法之所以具有线性时间,是因为这些算法并没有进行排序。线程时间的行为并不是因为对输入做假设所得到的结果。在比较模型中,即使是在平均情况下,排序仍然需要O(nlgn)的时间,所以从渐进意义上来看,排序和索引方法的效率是不高的。</p>
<h3>第三部分 数据结构</h3>
<h4>第十章 基本数据结构. Done</h4>
<h4>第十一章 散列表. Done</h4>
<h4>第十二章 二叉查找树. Done</h4>
<h4>第十三章 红黑树. Undone</h4>
<h3>第八部分 附录:数学基础知识</h3>
<h4>A. 求和. Done</h4>
<h4>B. 集合等离散数学结构. Done</h4>
<ul>
<li>集合</li>
<li>关系</li>
<li>函数</li>
<li>图</li>
<li>树</li>
<li>自由树</li>
<li>有根树和有序树</li>
<li>二叉树和位置树</li>
</ul>
<h4>C. 计数和概率. Undone</h4>
<h5>C.1 计数</h5>
<ul>
<li>串</li>
</ul>
<p>在有限集合S上构造一个长度为k的串称为K串。直观上,为了在n元集上构造一个k串,有n种方法选择第一个元素,同样地,也有N种方法选择第二个元素,这样一次进行K次,从而K串的数目为n<em>n</em>n*...n = n^k.<br>
最形象的就是车牌号了。</p>
<ul>
<li>排列</li>
</ul>
<p>有限集S的排列是S中所有元素的有序序列,且每个元素仅出现一次。一个N元集的集合有n!种排列,第一个元素有n种选择,第二个有n-1种选择,第三个有n-2种选择...<br>
集合S的k排列是S中k个元素的有序排列,且每个元素出现一次。因此N元集合的K排列数目是</p>
<blockquote>
<p>n(n-1)(n-2)(n-3)(n-4)...(n-k+1) = n!/(n-k)!</p>
</blockquote>
<ul>
<li>组合</li>
</ul>
<p>n元集的K组合就是S的k子集,n元集的k组合的数目可以用排列的数目来表示,对每个k组合,它的元素恰好有k!中排列,每个都是n元集的一个不同的k排列。因此,n元集的k组合数目是其k排列的数目除以k!。这个数量为</p>
<blockquote>
<p>n!/(k!(n-k)!)</p>
</blockquote>
<p>组合与排列的区别是,组合中k元祖是无序的,而排列是有序的,意味着组合的元祖对应着k!种不同的排列元祖。<br>
因此在计算时需要除去每个元祖的其他的有序排列。</p>
<ul>
<li><p>二项式系数</p></li>
<li><p>二项式界</p></li>
</ul>
<p>下界</p>
<blockquote>
<p>= n!/k!(n-k)! 展开>= (n/k)^k</p>
</blockquote>
<p>再利用斯特林近似式得上界</p>
<blockquote>
<p><= n^k/k! <= (en/k)^k</p>
</blockquote>
<h5>C.2 概率</h5>
<p>条件概率Pr{A|B} 读作在事件B发生的条件下,事件A发生的概率</p>
<blockquote>
<p>Pr{A|B} = Pr{A交B}/ Pr{B}<br>
A交B是时间A发生,B也发生的事件,因此可以认为这是B中的一个基本事件,因此该事件占整个B事件的概率就是Pr{A交B}/ Pr{N},也即Pr{A|B}</p>
</blockquote>
<p>如果Pr{A交B} = Pr{A} Pr{B},则称连个事件独立。</p>
<p><strong>贝叶斯定理</strong></p>
<blockquote>
<p>Pr{A交B} = Pr{B} Pr{A|B} = Pr{A} Pr{B|A}</p>
</blockquote>
<p>通过交换律,可得如下贝叶斯定理</p>
<blockquote>
<p>Pr{A|B} = Pr{A} Pr{B|A}/Pr{B}</p>
</blockquote>
<h5>C.3 离散随机变量</h5>
<p>随机变量X的概率密度函数</p>
<blockquote>
<p>f(x) = Pr{X=x}</p>
</blockquote>
<p>随机变量的期望值</p>
<blockquote>
<p>E[X] = ∑xPr{X=x}</p>
</blockquote>
<p>方差</p>
<blockquote>
<p>Var[X] = E[(X-E[X]^2)]<br>
= E[X^2]-E^2[X]</p>
</blockquote>
<h5>C.4 几何分布与二项分布</h5>
<p><strong>几何分布</strong><br>
假设进行一系列的伯努利实验,每次实验成功的概率是P,失败的概率是q=1-p,在取得一次成功前要进行多少次实验?因为在取得一次成功前有k-1次失败,从而有</p>
<blockquote>
<p>Pr{X=k} = q^(k-1)p</p>
</blockquote>
<p>期望 1/p</p>
<p><strong>二项分布</strong><br>
因为有(n k) 种方法从n次实验中选取k次成功的试验,且每次发生的概率是p^k*q^(n-k)</p>
<blockquote>
<p>Pr{X=k} = (n k)p^k*q^(n-k)</p>
</blockquote>
<p>期望E[X] = np, 方差 Var[X] = npq<br>
发自我的 iPad</p>
Java Exchanger交换线程
https://segmentfault.com/a/1190000000603088
2014-07-12T22:25:23+08:00
2014-07-12T22:25:23+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<p>Exchanger 使用一个pipeline交换两个线程的数据,交换期间该Slot会被锁住,一般用于通信领域。<br>
该类提供两个方法</p>
<blockquote>
<p>// k为交换对象<br>
exchange(V k)<br>
//指定超时时间<br>
exchange(V k, int timeOut, TimeUnit unit)</p>
</blockquote>
<p>使用时,除了参数k必须外,返回值也必须使用k接收,否则会出现接收不到的数据的情况,如:</p>
<pre><code>//生产者线程
ProductBean bean= new ProductBean()
// 设置Product属性
bean.set()....
// 生产完成,开始与消费者交换
bean = Exchanger实例名.exchange(bean)
// 消费者线程
ProductBean bean = new ProductBean();
bean = Exchanger实例名.exchange(bean)
</code></pre>
<p>生产者将生产的ProductBean放入pipeline中,等待交换。消费者将空Bean放入pipeline等待交换。不论是生产者还是消费者都必须接收exchange方法的返回值,即将exchange的bean赋值给bean。当双方完成交换后,交换的bean就为exchange的返回值。<br>
必须指定返回值的bean,否则该生产者生产的bean将不会与消费者的空bean进行交换。</p>
<p>关于该类的最详细解释当属于源码中的关于算法的注释,这一部分并没有显示在doc文档中,必须看源码才能看见。该注释中说到</p>
<blockquote>
<p>The basic idea is to maintain a "slot", which is a reference to a Node containing both an Item to offer and a "hole" waiting to get filled in.</p>
</blockquote>
<blockquote>
<p>最基本的思想是维护一个Slot(槽),该Slot指向一个包含有一个用于交换的Item和一个等待填充的hole的Node。</p>
</blockquote>
<p>该hole其实是用于接收交换的Node.</p>
<p>该算法解释分为五部分,</p>
<ul>
<li>Waiting</li>
<li>Sizing</li>
<li>Hashing</li>
<li>Probing</li>
<li>Padding</li>
</ul>
<p>此外,该类的作者Doug Lea,还附上了一篇关于此思想的论文<a rel="nofollow" href="https://urresearch.rochester.edu/institutionalPublicationPublicView.action?institutionalItemId=1905&versionNumber=1">A Scalable Elimination-based Exchange Channel</a>,可免费获得。</p>
<p>有同学做了如下图,比较浅显易懂。<br><img src="/img/remote/1460000007286714?w=577&h=569" alt="Exchanger"><br>
关于此图的解释请移步至<a rel="nofollow" href="http://www.blogjava.net/xylz/archive/2010/11/22/338733.html">深入浅出 Java Concurrency (26): 并发容器 part 11 Exchanger</a></p>
Python实现的帕斯卡三角形
https://segmentfault.com/a/1190000000518360
2014-05-24T11:47:39+08:00
2014-05-24T11:47:39+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<h2>带有缓存的迭代器</h2>
<pre><code class="lang-Python"># 帕斯卡三角形
mCache = {}
def pascalWithDict(n,k):
if n==k or k==0 or n==1:
return 1
if k==1:
return n
if mCache.has_key((n,k)):
return mCache[(n,k)]
else:
mCache[n,k] = pascalWithDict(n-1,k-1)+pascalWithDict(n-1,k)
return mCache[n,k]
## 获得每行pascal列表
def generatePascal(depth):
lines = []
for row in range(depth):
line = []
for col in range(row+1):
line.append(pascal(row, col))
lines.append(line)
return lines
if __name__ == "__main__":
high = int(raw_input("pls enter the height of pascal:"))
lines = generatePascal(high)
for i in range(high):
print lines[i]
</code></pre>
<p>结果如下:</p>
<pre><code>pls enter the height of pascal:6
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
</code></pre>
<h2>使用装饰器的迭代器</h2>
<pre><code class="lang-Python"><br>from functools import wraps
def memo(func):
cache={}
@wraps(func)
def wrap(*args):
if args not in cache:
cache[args]=func(*args)
return cache[args]
return wrap
@memo
def pascal(n,k):
if n==k or k==0 or n==1:
return 1
if k==1:
return n
return pascal(n-1,k-1)+pascal(n-1,k)
</code></pre>
<h2>跳舞的数字[Just for fun]</h2>
<pre><code class="lang-Python">if __name__ == "__main__":
#depth = int(raw_input("pls enter the depth of pascal:"))
for depth in range(100):
lines = generatePascal(depth)
for i in range(depth):
print lines[i]
</code></pre>
[转载]如何练习一万个小时(原标题 How To Be The Best Entrepreneur In The World)
https://segmentfault.com/a/1190000000499843
2014-05-09T16:30:42+08:00
2014-05-09T16:30:42+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
0
<p>作者:同人于野 from <a rel="nofollow" href="http://emuch.net/bbs/viewthread.php?tid=5847367&fpage=1">小木虫的帖子</a></p>
<p>出处:<a rel="nofollow" href="http://www.geekonomics10000.com/519">http://www.geekonomics10000.com/519</a></p>
<p>随着畅销书《<strong>异类</strong>》的流行,<strong>“练习一万小时成天才”</strong>这个口号现在是尽人皆知。也许仍然有不少人相信那些不世出的天才必有天生的神秘能力,但科学家通过大量的调查研究已经达成共识,那就是所有顶级高手都是练出来的。不但如此,最近几年的科学进展,人们可能第一次拥有了一个关于怎样炼成天才的统一理论。</p>
<p>好消息是除了某些体育项目对天生的身高和体型有特殊要求之外,<strong>神秘的天生素质并不存在,也就是说人人都有可能成为顶级高手</strong>。早在20多年以前,芝加哥大学的教育学家 Benjamin Bloom 就曾经深入考察过120名从音乐到数学多个领域内的精英人物,发现他们幼年时代没有任何特别之处。<strong>后人的研究更证明,在多个领域内,就连智商都跟一个人能不能达到专家水平没关系</strong>。</p>
<p>有个匈牙利心理学家很早就相信只要方法得当,任何一个人都可以被训练成任何一个领域内的高手。为了证明这一点,他选择了一个传统上女性不擅长的项目,也就是国际象棋。结果他和妻子把自己的三个女儿都训练成了国际象棋世界大师,这就是著名的波尔加三姐妹。这个实验甚至证明哪怕你不爱好这个领域,也能被训练成这个领域的大师,因为三姐妹中的一个并不怎么喜欢国际象棋。</p>
<p>而坏消息是成为大师需要长时间的苦练。每天练三小时,完成一万小时需要十年时间,但这只是达到世界水平的最低要求。统计表明对音乐家而言,世界级水平要求的训练时间是十五到二十五年。但最关键的并不是练习的时间,而是练习的方法。</p>
<p>天才是怎样炼成的?中国传统思维比较强调一个“苦”字,冬练三九夏练三伏,甚至是头悬梁锥刺股。而近代生活条件越来越好,人们则开始强调一个“爱”字,说兴趣是最好的老师,强调寓教于乐,“哈佛女孩”的家长们纷纷写书,介绍自己的孩子如何一路玩进名校。</p>
<p>很多励志故事和流行的成功学书籍最爱强调的似乎是“顿悟”,认为一个人之所以不成功是因为他没想通,他没有认识到真正的自己!好像一旦一个人顿悟到了真正的自己,他就会非常简单地在本来应该属于自己的领域成为天才人物。一个销售员可能认为真正的自己其实是个小说家,一个医生可能认为真正的自己其实是个画家 — 唯一的问题是他们从来没有写过小说或者画过画 — 但他们认为他们距离“真正的自己”只有一步之遥,一旦尝试了就会爆发天才。</p>
<p>所有这些关于成功学的个人经验和励志故事都不科学。假设一个成功人士做过一百件事,包括参加演讲比赛,衣着有个性,听英文歌曲,最喜欢的颜色是绿色等等,他会非常自得地把这一百件事都写进自传,没准还要加上女朋友的影响。然而其中真正起到决定性作用的可能只有四件事,问题是他和读者都不知道是哪四件。</p>
<p>科学家不信励志故事,他们只相信调查研究。在过去二三十年内,心理学家们系统地调研了各行各业内的从新手,一般专家到世界级大师们的训练方法,包括运动员,音乐家,国际象棋棋手,医生,数学家,有超强记忆力者等等,试图发现其中的共性。他们的研究甚至细致到精确记录一所音乐学院的所有学生每天干的每一件小事,用多少时间做每件事,父母和家庭环境,来比较到底是什么使得那些音乐天才脱颖而出。</p>
<p>现在这项工作已经成熟了。2006年,一本900多页的书,<strong>The Cambridge Handbook of Expertise and Expert Performance</strong>, 出版。这是“<strong>怎样炼成天才</strong>”研究的一本里程碑式的学术著作,此书直接引领了后来一系列畅销书的出现,包括格拉德威尔的《异类》,Geoff Colvin 的 Talent is Overrated,和 Daniel Coyle 的 The Talent Code 等等。科学家们不但证明了高手是练出来的,而且通过考察各个领域最好的训练方法的共性,总结了一套统一的练习方法,这就是所谓“刻意练习”(deliberate practice)。</p>
<p>过去多年来,训练方法是不断进步的。比如说作曲,假设一名普通学生使用普通训练方法六年时间能达到的水平,另一个学生使用新的训练方法三年就能达到,那么我们可以说这个新训练方法的“有效指数”是200%。统计表明,莫扎特当时的训练,他的有效指数是130%。而二十世纪的天才也许没有莫扎特有名,但其训练水平都能达到300%到500%!十三世纪的哲学家培根曾经认为任何人都不可能在少于30年之内掌握数学,而现在的学生十几岁的时候已经学到多得多的数学,教学方法进步了。事实上,我们今天在所有领域都比过去做得更好,体育世界纪录被不断打破,艺术家们的技巧也是过去根本无法想象的。</p>
<p>训练方法重要性的另一个体现是“天才”的扎堆出现,比如曾经有一个时期俄罗斯对女子网球,韩国对女子曲棍球,更不必说中国对乒乓球的的绝对优势。更进一步,哪怕你这个国家传统上并不擅长这个项目,只要有一名教练摸索掌握了科学训练法,那么他就可以带出一代绝世高手,比如中国花样滑冰教练姚滨。人们经常感慨中国十多亿人居然找不到11个足球天才 — 如果天才是天生的,那么十多亿人必然足以产生很多天才,但天才是练出来的,而中国缺乏有效的练习环境,人口再多也比不上欧洲小国。</p>
<h2>刻意练习</h2>
<p>首次提出“刻意练习”这个概念的是佛罗里达大学心理学家 K. Anders Ericsson。这套练习方法的核心假设是,专家级水平是逐渐地练出来的,而有效进步的关键在于找到一系列的小任务让受训者按顺序完成。这些小任务必须是受训者正好不会做,但是又正好可以学习掌握的。完成这种练习要求受训者思想高度集中,这就与那些例行公事或者带娱乐色彩的练习完全不同。“刻意练习”的理论目前已经被广泛接受,我们可以总结一下它的特点。</p>
<h3>1. 只在“学习区”练习</h3>
<p>科学家们考察花样滑冰运动员的训练,发现在同样的练习时间内,普通的运动员更喜欢练自己早已掌握了的动作,而顶尖运动员则更多地练习各种高难度的跳。普通爱好者打高尔夫球纯粹是为了享受打球的过程,而职业运动员则集中练习在各种极端不舒服的位置打不好打的球。真正的练习不是为了完成运动量,练习的精髓是要持续地做自己做不好的事。</p>
<p>心理学家把人的知识和技能分为层层嵌套的三个圆形区域:最内一层是“舒适区”,是我们已经熟练掌握的各种技能;最外一层是“恐慌区”,是我们暂时无法学会的技能,二者中间则是“学习区”。只有在学习区里面练习,一个人才可能进步。有效的练习任务必须精确的在受训者的“学习区”内进行,具有高度的针对性。在很多情况下这要求必须要有一个好的老师或者教练,从旁观者的角度更能发现我们最需要改进的地方。</p>
<p>只在学习区练习,是一个非常强的要求。一般的学校课堂往往有几十人按照相同的进度学习知识,这种学习是没有针对性的。同样的内容,对某些同学来说是舒适区根本无需再练,而对某些学生则是恐慌区。科学教学必须因材施教,小班学习,甚至是一对一的传授。真正高手训练与其说是老师教学生,不如说是师傅带学徒。</p>
<p>一旦已经学会了某个东西,就不应该继续在上面花时间,应该立即转入下一个难度。长期使用这种方法训练必然事半功倍。2004年的一项研究表明,大学生的学习成绩和他们在学习上投入的总时间没有直接关系,关键是学习方法。</p>
<h3>2. 大量重复训练。</h3>
<p>从不会到会,秘诀是重复。美国加州有个“害羞诊所”(The Shyness Clinic),专门帮助那些比如说不敢和异性说话的人克服害羞心理。这个诊所的心理学家不相信什么心理暗示疗法,什么童年回忆之类,他们相信练习。他们认为使人害羞的并不是事情本身,而是我们对事情的观点。怎么治疗恐女症?做法是设计各种不同难度的场合进行对话训练。最初是在房间内集体对话,角色扮演。然后是到直接跑到大街上找陌生美女搭讪要求约会。最高难度是有意在公共场合做出使自己难堪的事情,比如去超市把一个西瓜掉在地上摔坏。</p>
<p>这种把不常见的高难度事件重复化的办法正是MBA课程的精髓。在商学院里一个学生每周可能要面对20个真实发生过的商业案例,学生们首先自己研究怎么决策,提出解决方案,最后老师给出实际的结果并作点评。学习商业决策的最好办法不是观察老板每个月做两次决策,而是自己每周做20次模拟的决策。军事学院的模拟战,飞行员在计算机上模拟各种罕见的空中险情,包括丘吉尔对着镜子练习演讲,都是重复训练。</p>
<p>在体育和音乐训练中,比较强调“分块”练习。首先你要把整个动作或者整首曲子过一遍,看专家是怎么做的。然后把它分解为很多小块,一块一块地学习掌握。在这种训练中一定要慢,只有慢下来才能感知技能的内部结构,注意到自己的错误。在美国一所最好的小提琴学校里,甚至有禁止学生把一支曲子连贯地演奏的要求,规定如果别人听出来你拉的是什么曲子,那就说明你没有正确练习。职业的体育训练往往是针对技术动作,而不是比赛本身。一个高水平的美式足球运动员只有1%的时间用于队内比赛,其他都是各种相关的基础训练。</p>
<p>反过来说如果没有这种事先的重复训练,一个人面对不常见的事件往往会不知所措。统计表明工作多年的医生通过读X光片诊断罕见病症的水平反而不如刚毕业的医学院学生 — 因为很少遇到这种病例,而在医学院学到的东西早就忘了。最好的办法其实是定期地让医生们拿过去的旧X光片集中训练,而不是期待在工作中碰到。</p>
<h3>3. 持续获得有效的反馈。</h3>
<p>传道,授业,解惑,老师和教练最大的用处是什么?也许对一般人来说小学老师最大的作用是激发了他学习的兴趣,教会了他什么东西,曾经有过传道授业解惑。而真正的高手都有很强的自学能力,对他们而言,老师和教练的最重要作用是提供即时的反馈。</p>
<p>一个动作做得好与不好,最好有教练随时指出,本人必须能够随时了解练习结果。看不到结果的练习等于没有练习:如果只是应付了事,你不但不会变好,而且会对好坏不再关心。在某种程度上,刻意练习是以错误为中心的练习。练习者必须建立起对错误的极度敏感,一旦发现自己错了会感到非常不舒服,一直练习到改正为止。</p>
<p>从训练的角度,一个真正好教练是什么样的?John Wooden 是美国最具传奇色彩的大学篮球教练,他曾经率领 UCLA 队在12年内10次获得 NCAA 冠军。为了获得 Wooden 的执教秘诀,两位心理学家曾经全程观察他的训练课,甚至记录下了他给球员的每一条指令。结果统计表明,在记录的2326条指令之中, 6.9%是表扬,6.6%是表示不满,而有75% 是纯粹的信息,也就是做什么动作和怎么做。他最常见的办法是三段论:演示一遍正确动作,表现一遍错误动作,再演示一遍正确动作。</p>
<p>与外行想象的不同,最好的教练从不发表什么激情演说,甚至不讲课,说话从不超过20秒。他们只给学生非常具体的即时反馈。所有训练都事先进行无比详细的计划,甚至包括教运动员怎么系鞋带。他们仿佛有一种诡异的知道学员在想什么的能力,即使是第一次见面能指出学生在技术上最需要什么。他们是绝对的因材施教,源源不断地提供高度具有针对性的具体指导。</p>
<p>获得反馈的最高境界是自己给自己当教练。高手工作的时候会以一个旁观者的角度观察自己,每天都有非常具体的小目标,对自己的错误极其敏感,并不断寻求改进。</p>
<h3>4. 精神高度集中。</h3>
<p>刻意练习没有“寓教于乐”这个概念。曾经有个著名小提琴家说过,如果你是练习手指,你可以练一整天;可是如果你是练习脑子,你每天能练两个小时就不错了。高手的练习每次最多1到1.5小时,每天最多4到5小时。没人受得了更多。一般女球迷可能认为贝克汉姆那样的球星很可爱,她们可能不知道的是很少有球员能完成贝克汉姆的训练强度,因为太苦了。</p>
<p>科学家们曾经调查研究了一个音乐学院。他们把这里的所有小提琴学生分为好(将来主要是做音乐教师),更好,和最好(将来做演奏家)三个组。这三个组的学生 在很多方面都相同,比如都是从8岁左右开始练习,甚至现在每周的总的音乐相关活动(上课,学习, 练习)时间也相同,都是51个小时。</p>
<p>研究人员发现,所有学生都了解一个道理:真正决定你水平的不是全班一起上的音乐课,而是单独练习:</p>
<ul>
<li><p>最好的两个组学生平均每周有24小时的单独练习,而第三个组只有9小时。</p></li>
<li><p>他们都认为单独练习是最困难也是最不好玩的活动。</p></li>
<li><p>最好的两个组的学生利用上午的晚些时候和下午的早些时候单独练习,这时候他们还很清醒;而第三个组利用下午的晚些时候单独练习,这时候他们已经很困了。</p></li>
<li><p>最好的两个组不仅仅练得多,而且睡眠也多。他们午睡也多。</p></li>
</ul>
<p>那么是什么因素区分了前两个组呢?是学生的历史练习总时间。到18岁,最好的组中,学会平均总共练习了7410小时,而第二组是 5301小时,第三组 3420小时。第二组的人现在跟最好的组一样努力,可是已经晚了。可见要想成为世界级高手,一定要尽早投入训练,这就是为什么天才音乐家都是从很小的时候就开始苦练了。</p>
<h2>人脑的学习原理</h2>
<p>现代神经科学和认知科学认为,几乎没有任何技能是人一出生就会的。哪怕是对简单物体的识别,把东西抓取过来这些简单的动作,也是婴儿后天学习的结果。一个人一出生的时候根本不可能预见到将来自己需要什么技能,基因不可能把一切技能都用遗传的方法事先编程,那样的话太浪费大脑的存储空间。最好的办法是不预设任何技能,只提供一个能够学习各种技能的能力,这就是人脑的巧妙之处。基因的做法是先预设一些对刺激的基本反应和感觉,比如看见好吃的东西我们会饿等等。这些基本的反应需要调动的神经较少。但对于更高级别的技能,比如演奏音乐,需要协调调动很多神经,就必须靠后天学习了。</p>
<p>人的任何一个技能,都是大脑内一系列神经纤维传递的电脉冲信号的组合。解剖表明拥有不同技能的人,其大脑的神经结构非常不同,比如出租车司机大脑内识别方向的区域就特别发达。也就是说与计算机不同,人对于技能的掌握是在大脑硬件层次实现的。</p>
<p>而最近有一派科学家认为,髓磷脂是技能训练的关键,它的作用是像胶皮把电线包起来一样,把这些神经纤维给包起来,通过防止电脉冲外泄而使得信号更强,更快,更准确。不管练习什么,我们都是在练习大脑中的髓磷脂,就好像把一堆杂乱无章的电线被排列整齐变成电缆。直到2000年新技术允许科学家直接观察活体大脑内的髓磷脂之后,髓磷脂的作用才被发现,而且一直到2006年才第一次被在学术期刊上说明。科学家认为髓磷脂是脑神经的高速公路,提高信号传递速度,并且可以把延迟时间减少30倍,总共提速3000倍,甚至可以控制速度,想慢就慢。</p>
<p>人脑之中分布着大量“自由的”髓磷脂,它们观测脑神经纤维的信号发射和组合,哪些神经纤维用的越多,它们就过去把这一段线路给包起来,使得线路中的信号传递更快,形成高速公路。这就是为什么练习是如此重要。</p>
<p>髓磷脂理论可以解释很多事情。比如为什么小孩常会犯错?他们的神经系统都在,也知道对错,只是需要时间去建立起来髓磷脂的高速网络。为什么习惯一旦养成不容易改变?因为所谓“习惯”,其实是以神经纤维电缆组合的形式“长”在大脑之中的,髓磷脂一旦把神经包起来,它不会自动散开 — 改变习惯的唯一办法是形成新习惯。为什么年轻人学东西快?因为尽管人的一生之中髓磷脂都在生长,但年轻人生长得最快。最激进的理论则认为人跟猴子的最显著区别不在于脑神经元的多少,而在于人的髓磷脂比猴子多20%!解剖表明,爱因斯坦的大脑中的神经元数量是平均水平,但他拥有更多能够产生髓磷脂的细胞。</p>
<h2>谁愿意练习一万小时?</h2>
<p>看了钢琴家朗朗的传记之后,可能很多人会怀疑是否真的应该让孩子接受这样的苦练。实际上,顶级运动员都是穷人家的孩子。不练这一万小时,一定成不了高手,但问题是考虑到机遇因素练了这一万小时也未必成功。</p>
<p>这就是兴趣的作用了。如果说有什么成功因素是目前科学家无法用后天训练解释的,那就是兴趣。有的孩子似乎天生就对某一领域感兴趣。感兴趣并不一定说明他能做好,就算不感兴趣只要愿意练,也能练成。兴趣最大的作用是让人愿意在这个领域内苦练。</p>
<p>不论如何,刻意练习是个科学方法,值得我们把它运用到日常工作中去。显然我们平时中做的绝大多数事情都不符合刻意练习的特点,这可能就是为什么大多数人都没能成为世界级高手。天才来自刻意练习。如何练习一万个小时(原标题 How To Be The Best Entrepreneur In The World)</p>
{转载}10秒内吸引HR的程序员简历
https://segmentfault.com/a/1190000000499328
2014-05-09T10:57:48+08:00
2014-05-09T10:57:48+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<p>转载自<a rel="nofollow" href="http://bbs.csdn.net/topics/390765397">CSDN的论坛</a><br>
HR看简历的顺序为:<strong>年龄,学历,所经历公司,职位,内容</strong>,熟练的每份简历不超过10秒,超过这个时间证明简历写失败了。</p>
<p>一、首先,一般情况下word版本就好,宋体,小五/五号/小四皆可,最好不要别出心裁。简历命名很重要,最好是:<strong>姓名+年龄+期望地点+所在公司或行业+职位类别</strong>,简洁清晰。</p>
<p>二、简历内容最好控制在三页之内,包括基本信息、背景总结(这个最好写一下)、工作经验、教育背景。</p>
<ol>
<li><p>基本信息:<strong>名字、性别、年龄、目前所在地、期望工作地点、电话、邮箱</strong>,这些足够了。</p></li>
<li><p>背景总结:主观性的东西没人看,<strong>突出自己工作年限、行业、擅长的职位方向</strong>,主要能力OR技术点也可以写一些,期望从事的行业或职位。注:如果你学历一般,请在此注明所经历公司取得的突出成绩和代表性的认可。</p></li>
<li><p>工作经验格式:时间段(细化到月)+公司名称(皆知的企业可以写简称,公司业务线多的可以添加部门名称)+职位名称+工作职责(如有突出的工作成就可以添加)<br>
如:2003.7——2009.6 腾讯 游戏事业部 产品总监</p></li>
<li><p>教育背景格式:时间段+学校+专业+学历<br>
如:2006.9 - 2008.7 北京航空航天大学 经济管理学院 硕士<br>
2002.9 - 2006.7 山东大学 控制科学与工程学院 本科</p></li>
</ol>
<p>亲们,不要怀着侥幸心理不写时间段、不写第一学历,或者只写MBA/硕士教育背景,这都小伎俩对求职本身并没有什么帮助。</p>
<hr>
<p>以留后用</p>
腾讯2014实习生笔试题--德梅齐里亚克砝码问题
https://segmentfault.com/a/1190000000473432
2014-04-18T10:04:04+08:00
2014-04-18T10:04:04+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<h3>问题</h3>
<p>珠宝商甲需要去鉴定一批41克以下的宝石(可能是41克以下不包括41克的任意重量),他只能携带一个天平和四个砝码去称重,请问他会携带那些重量的砝码?</p>
<h3>来源</h3>
<p>2014腾讯暑期实习生附加题第一题</p>
<h3>解决方案</h3>
<h4>第一种</h4>
<p>这道题的本质应该是这样,用4个数来表示1-40.刚开是我的思路是使用二进制,但是二进制显然是不够的</p>
<blockquote>
<p>2^4 = 16</p>
</blockquote>
<p>所以,考试的时候没有做上来,智商捉急。今天搜索了一下答案,参考了<a rel="nofollow" href="http://www.cnblogs.com/nausicaa/p/3661904.html">cnblog</a>上一篇文章下的评论撰写把本文。</p>
<p>采用三进制,即</p>
<blockquote>
<p>3^4 = 81</p>
</blockquote>
<p>所以进一步地,就是解决如何用4位3进制数来表示40这个数,即</p>
<blockquote>
<p>40/3 = 13---1<br>
13/3 = 4---1<br>
4/3 = 1---1<br>
1/3 = 0---1</p>
</blockquote>
<p>三进制表示为<strong>1111</strong>,即</p>
<blockquote>
<p>1*3^3+1*3^2+1*3^1+1*3^0 = 27+9+3+1</p>
</blockquote>
<p>所以这四个砝码的重量为27,9,3和1</p>
<p>评论下的某位大神用C++把算法写出来了。</p>
<h4>第二种</h4>
<p>在百度知道上看到的,这是德梅齐里亚克砝码问题<br>
原题是这样的,摘自<a rel="nofollow" href="http://baike.baidu.com/view/580271.htm">百度百科</a></p>
<blockquote>
<p>一位商人有一个40磅的砝码,由于跌落在地而碎成4块.后来,称得每块碎片的重量都是整磅数,而且可以用这4块来称从1至40磅之间的任意整数磅的重物.<br>
问这4块砝码碎片各重多少?</p>
</blockquote>
<p>法国数学家G.B.德·梅齐里亚克(1581一1638)在他的著作中解答了这题. 为使两砝码A与B能称出最多种重量,必须是1磅和3磅,用它们能称出1、2、3、4磅的重物。如选第三块砝码C的重量为2x4+1=9磅,则用它们可称出1至9+4=13磅间的所有整数磅重物。最后选第四块砝码D,使它重量为2x13+1=27磅,那么用这四块砝码能称出从1至27+13=40磅的重物.因此,这四块砝码的重量分别为1、3、9、27磅。</p>
<p>更详细的关于德梅齐里亚克砝码问题,还是看<a rel="nofollow" href="http://blog.csdn.net/zhu_hit/article/details/5697888">CSDN这篇博客</a>,很详细。</p>
<p>其实这就是德梅齐里亚克砝码所带来的整数最优拆分法,用1和3可以构造一切有理数。</p>
<p>公式</p>
<blockquote>
<p>X=M*2+1</p>
</blockquote>
<p>其中X为M的后一项数,M为X前所有项的和。初值M为1,那么往后推论就可以推出任意的数值,即</p>
<blockquote>
<p>M=1<br>
M = 1, X = 3<br>
M = 1 + 3, X = 9<br>
M = 1 + 3 + 9, X = 27<br>
M = 1 + 3 + 9 + 27, X = 81<br>
M = 1 + 3 + 9 + 27 + 81, X = 243</p>
</blockquote>
<p>发现这就是3的各次幂序列</p>
<blockquote>
<p>3^0, 3^1, 3^2, 3^3, 3^4, 3^5......</p>
</blockquote>
<p><a rel="nofollow" href="http://blog.sciencenet.cn/blog-5190-51860.html">世界上最完美的砝码组合---神秘的“3”重现江湖!</a>这篇文章讲的更为透彻。原文里有这么句话让我对老子,对道家,对中华文化的博大精深所折服。</p>
<blockquote>
<p>全是数字“3”的各次幂…神奇而又神秘的“3”再次出现!<br>
让人情不自禁地又一次想起“三生万物”!<br>
说句老实话,有多少人在研究老子道德经,就是无法对老子的这句话彻底理解明白:<br><strong>“道生一,一生二,二生三,三生万物”</strong></p>
</blockquote>
<h4>总结</h4>
<p><strong>道生一,一生二,二生三,三生万物</strong></p>
[草稿]小白学习Struts、Spring、Hibernate
https://segmentfault.com/a/1190000000456586
2014-04-04T11:45:55+08:00
2014-04-04T11:45:55+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<h2>申明</h2>
<p>1.这是草稿,关于近期所看资料的一个记录,以后慢慢整理成一片博文。</p>
<p>2.我是web开发小白,没接触过web开发,所以这是一篇小白的学习记录。</p>
<h3>问题</h3>
<ol>
<li>Spring MVC和Struts2MVC到底学习哪个?还是都需要学习呢?</li>
<li>直接抛弃SSH,学习国产的Nutz呢?</li>
<li>如果学习Spring 4.0 MVC,是先学习Spring 3.0的教程,还是自己去阅读Spring 4.0的Tutorial呢?</li>
</ol>
<h2>记录</h2>
<h3>1. Struts</h3>
<p>Struts是一个幵源项目,它实现了经典的Model-View-Controller (MVC)设计模式,用于开发Java企业Web应用。它最初由Craig McClanahan创立于2000年5月份并由Apache基金会赞助,之前隶属于Apache Jakarta项目下,称为JakartaStruts,在2005年成为Apache顶级项目。</p>
<p>Struts的设计目标是把模型(用于业务逻辑和数据库之间交互的数据载体)与视图(呈现给客户端的HTML页面)和控制器(在模型和视图之间传递信息的实例)分离开来。</p>
<p>Stmts提供了控制器(一个名为ActionServlet的服务程序)并使得为视图或表现层编写模板更加容易。Web应用开发人员需要编写模型代码,并在全局配置文件stmts-config.xml中配置好模型、视图和控制器之间的关系。图1-1展示了 Struts2的MVC架构。<br><img src="http://www.tutorialspoint.com/images/struts_2_architecture.gif" alt=""><br>
图1-1</p>
<h3>2. Hibernate</h3>
<p>Hibernate 是一个解决 Object-Relation Mapping (ORM,对象关系映射)的 Java语言库。它提供了一个在面向对象的领域模型与关系型数据库之间映射的框架。它通过允许开发人员使用高级对象处理函数而不是直接数据库访问的方法存取数据来解决对象关系阻抗不匹配的问题。<br>
它将对象模型表示的数据映射到SQL表示的关系模型上去。Hibernate管理java到数据库的映射,提供给了数据查询和存取的方法,减少了程序员对数据持久化层相关的编程任务。图1-2展示了 Hibernate的架构。<br><img src="http://images.51cto.com/files/uploadimg/20090925/1601401.gif" alt=""><br>
图1-2</p>
<h4>ORM对象关系映射</h4>
<p>什么是持久化?是把数据保存到数据库或者某些存储设备中。在三层架构中,持久化是和数据库打交道的层次。在jsp的web开发中,经常有许多数据库连接、删除、查询等操作,在数据库相关工作中通过jdbc过于繁琐,就催生出了ORM(Object-RelationMapping),ORM作用是在关系数据库和对象之间做一个自动映射,这样在操作数据库时不需要使用复杂的sql语句,只要操作对象即可,ORM工具会自动将对象转换为sql语句操作。这样就只需要关注业务逻辑中的对象结构,而不用关心底层复杂的sql和jdbc代码。而Hibernate框架是ORM工具的一种,是关系/对象映射的解决方案(如图1-3)。<br><img src="http://img.blog.csdn.net/20140213182907718?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbG92ZXN1bW1lcmZvcmV2ZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""><br>
图1-3</p>
<h4>优点</h4>
<p>Hibernate持久层与数据库打交道的桥梁,彻底封装了JDBC,隐藏了更多的细节,有很好的移植性。JDBC语句繁琐,赋值执行我们不用再写相关代码,提高了效率,同时hibernate是一个轻量级框架(不依赖于别的运行),没有侵入性,测试简单,提高了生产力。</p>
<h4>缺点</h4>
<p>封装的太彻底导致不灵活,是用数据特定的东西比较不容易。对大量数据库的更新有问题,当程序大量查询统计,或批量更新无法使用数据库特性机制,例如存储过程等。</p>
<h3>3. Spring</h3>
<p>Spring是一个Java平台的开源应用程序框架,以其控制反转闻名。2002年10 月 Rod Johnson 在出版 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7) 一书同时发布了 Spring 的第一版。<br>
Spring的核心是其<strong>控制反转容器</strong>,它根据开发人员在XML中配置类之间的依赖关系通过使用Java的反射机制来实现对这些对象生命周期的管理。当一个对象需要另一个类的实例时,Spring会创建它,调用其初始化函数,再把它注入到发起请求的对象中去。</p>
<p>Spring还提供自己的<strong>面向切面编程</strong>(Aspect-Oriented Programming, AOP)的框架,它把切面中的横切关注点模块化,同时也充分利用Spring的容器。Spring还提供数据库事务管理,配合AOP,开发人员可以在Spring的配置文件里指明切入点(可以利用正则表达式来创建匹配规则),Spring就会帮助开发人员把数据库事务管理代码插入刚才配置的切入点,从而降低了代码的稱合,让开发人员将主要精力放在业务逻辑上。<br>
Spring是一个全功能栈(foil-stack)的应用程序框架,包括上述的三个主要功能,Spring 一共由7个模块组成,它们构建在Spring的核心容器之上。图1-4展示了 Spring的架构。使用Spring的IOC和AOP可以使项目的逻辑清晰、稱合度低,易于完成、易于修改、易于移植。<br><img src="http://www.kuqin.com/spring2.0_doc/images/spring-overview.gif" alt=""><br>
图1-4</p>
<p>现在Spring 4.0出来了,并且还附带推出一个能够提高开发者效率的项目,该项目就是Spring Boot,内嵌了Tomcat。<br>
我按照官网的指南,<a rel="nofollow" href="http://spring.io/guides/gs/rest-service/">Building a RESTful Web Service</a>搭建了一个RESTful的WS,一点也没配置,我这种小白都会玩。。。。<br>
查询了一下最新的Spring 4.0框架图,如图1.5所示:<br><img src="http://docs.spring.io/spring/docs/4.1.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/images/spring-overview.png" alt=""><br>
图1-5</p>
<p>参考文献:<br>
1. 汤承刚. “Things-Cloud-People”: 一个 “Web of Things” 实现方案[D].华东师范大学,2012年.</p>
关于Maven3安装、测试、网络配置
https://segmentfault.com/a/1190000000453887
2014-04-02T11:28:57+08:00
2014-04-02T11:28:57+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
6
<h2>项目构建工具</h2>
<p>目前主流有三种构建工具,包括<strong>Maven</strong>,<strong>Gradle</strong>和<strong>Buildr</strong>。其中Maven比较老了的,当然也是目前使用最多的。但是Gradle和Buildr是后起之秀,已有超越Maven的存在,尤其是Spring迁移到Gradle之后。<br>
本人之前一直听说,但一直没有能够去系统学习类似于Maven这种自动化构建工具,学生党,视野严重受限。。。。<br>
写本文的原因是,起初准备学习Nutz框架,去学习Web开发,然后看到Nutz官方文档中使用Maven构建和测试Nutz。于是就索性去学习下Maven,本着实用的原则,先学习Maven,之后有时间再学习Gradle。</p>
<h2>安装</h2>
<p>本人比较懒,只对自己搜索到写的比较好的文章进行一个总结。<br>
1. <a rel="nofollow" href="http://www.blogjava.net/fancydeepin/archive/2014/03/06/maven-setup.html">Maven安装</a>,从下载到安装每一步都有图文说明,详细的不能再详细了,小白必备。<br>
2. <a rel="nofollow" href="http://www.blogjava.net/fancydeepin/archive/2014/03/06/eclipse-maven-setup.html">eclipse Maven插件安装</a>,该文与上文是同一个作者所写,任然是图文并茂,非常详细,并且详解了如何使用link去安装eclipse插件,不得不为作者的细心所佩服,不说了,仍然是小白必备。</p>
<h2>测试</h2>
<p><a rel="nofollow" href="http://my.oschina.net/u/873661/blog/195301">maven 5分钟 maven3 快速入门指南</a>,该文对于安装过程比较精简,但之所以写在这里,是因为这片文章对于如何使用Maven进行新建项目,<strong>编译、运行、测试</strong>等过程都写的非常详细。</p>
<p>主要几条命令:<br>
1. 打包</p>
<blockquote>
<p>mvn package,打包该程序为一个jar包或者war包。</p>
</blockquote>
<p>2.测试</p>
<blockquote>
<p>java -cp target/nameofjar.jar javamain</p>
</blockquote>
<p>其中<strong>-cp</strong>命令是用于运行一个jar包并指定该jar包的main方法所在类。<strong>nameofjar.jar</strong>是指项目在target目录下打包项目生成的jar包,替换为自己的jar包名即可,另外<strong>javamain</strong>是指该jar的main方法所在类,包含该类的包名和类名。<br>
例如:</p>
<pre><code>java -cp target/my-maven-test-1.0-SNAPSHOT.jar com.oschina.test.App
</code></pre>
<p>3.转换为Eclipse项目</p>
<blockquote>
<p>mvn eclipse:eclipse</p>
</blockquote>
<p>执行完成后,在Eclipse中导入。Eclipse可能提示pom.xml有错误,并且问题列表中显示如下的问题</p>
<blockquote>
<p>Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:maven-jar-plugin:maven-plugin:2.3.2:runtime Cause: error in opening zip file pom.xml /hellomaven line 1 Maven Project Build Lifecycle Mapping Problem</p>
</blockquote>
<p>此时,你只需要在项目上->右键->Maven->Upgrade Project即可消除该错误。</p>
<h2>仓库</h2>
<p><a rel="nofollow" href="http://iqeq00.iteye.com/blog/2026163">Maven仓库</a>,这篇文章同样写的比较详细,对Maven的仓库进行了详细的说明,想要进一步了解Maven的仓库的同学可以好好了解一下。<br><a rel="nofollow" href="http://wenku.baidu.com/view/3fae1ce9102de2bd960588a5.html">Maven3入门应用讲座</a>,这是一个百度文库上的PPT,该PPT从整体上对Maven框架什么的进行完整的阐述,也可以作为了解入门使用,当然最好的还是官方文档。</p>
<h2>安装过程中需要注意的问题</h2>
<h3>设置仓库目录</h3>
<p>第一次安装完后,拷贝安装目录下的conf/setting.xml文件到$HOME/.m2/目录下,并配置localRepository,maven库默认是c盘下的。所以本人<strong>强烈建议修改repo目录</strong>,因为后期可能会有大量的库文件会被下载到该目录下,直接导致C盘过大,另外也可以避免重装系统格式C盘导致的库丢失,需要重新下载等问题。配置方式为直接输入repo本地地址,比如我的仓库地址为F:\Programing\maven_repo,直接如下输入即可。</p>
<pre><code class="lang-xml"> <localRepository>F:\Programing\maven_repo</localRepository>
</code></pre>
<h3>网络配置</h3>
<p>在安装完后,使用</p>
<pre><code> mvn help:system
</code></pre>
<p>这个操作会出现Downloading 等信息,如果网络不好的比如GreatWall的原因导致的一直下载失败,这个时候就轮到强大的Goagent就登场了,顺带<a rel="nofollow" href="http://wildcat.name/goagent.html">Goagent安装教程</a>。<br>
使用Goagent后,下载速度蹭蹭的。继续配置maven网络,打开.m2/setting.xml文件,按照如下的方式修改Proxies标签。</p>
<pre><code> <proxies>
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username></username>
<password></password>
<host>127.0.0.1</host>
<port>8087</port>
<nonProxyHosts>localhost|127.0.0.1</nonProxyHosts>
</proxy>
</proxies>
</code></pre>
<p>另外Eclipse在安装maven后,也会去下载一堆东西,这个时候还是很卡。同样的方式,进入Eclipse的配置菜单Window->Preference->General->Networking connection.<br>
设置Proxy entries,配置如下两项<br>
Schema Host Port<br>
HTTP | 127.0.0.1 |8087<br>
HTTPS | 127.0.0.1 | 8087<br>
同时选中SOCKS,点击Clear,否则依旧上不了网。</p>
<h3>pacage等过程中出现的missing 某某类的问题</h3>
<p>我在package的过程中,出现ERROR,找到第一行的ERROR,定位错误信息时missing org.codehaus.plexus.util.XmpParse....神马的。于是我进入的repo找到plexus包,发现这里真没有util这个包。按理说maven应该自动更新repo的啊,于是我就去maven的中央仓库上找,然后写了一个pom包含了该依赖,还是不行。我想原因是可能是本身网络问题,导致的maven包没下载完,但maven似乎认为该包没有问题。<br>
解决方法就是删除本地的plexus包,然后重新mvn package即可,maven就会重新下载,并且能够编译通过。</p>
Android 使用fastjson找不到fastjson包问题的解决方法
https://segmentfault.com/a/1190000000380461
2014-01-07T21:09:31+08:00
2014-01-07T21:09:31+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<p>JSON解析库有很多,诸如Jackson,Json-lib,org.json,Gson和fastjson等,但是fastjson以其解析速度最快而脱颖而出。详细的测试地址如下:<br />
<a rel="nofollow" href="https://github.com/eishay/jvm-serializers/wiki/Staging-Results">https://github.com/eishay/jvm-serializers/wiki/Staging-Results</a><br />
fastjson是国内alibaba公司的wenshao开发的,项目Git地址:<br />
<a rel="nofollow" href="https://github.com/alibaba/fastjson">https://github.com/alibaba/fastjson</a></p>
<p>今天测试了下发现fastjson挺好用,比Android自带的org.json库要好用多了。当然我没有对fastjson的性能进行测试,只是因为Android自带的不太好。<br />
在普通的java项目下,只需要导入fastjson.jar就可以,无需依赖其他包,这一点相比json-lib要好多,json-lib依赖五六个包。但是将fastjson.jar导入Android工程后,在使用时会出现 java.lang.NoClassDefFoundError:can't find com.alibaba.fastjson.JSON等错误消息。</p>
<p>初步认为是与Android自带的org.json冲突。于是Build Path->Configure Build Path->Order And Export下将fastjson.jar上调至Android xx.jar上(xx为android 版本号)。再运行工程,这个错误不再出现了,奇迹般的没问题了。</p>
<p>然后现在再调整fastjson.jar和Android.jar顺序也不会出现can't not find com.alibaba.fastjson的错误,不知道为何,继续看。</p>
<p>文章<a rel="nofollow" href="http://ruby-java.iteye.com/blog/169854">Java虚拟机类加载顺序</a>讲解了java 虚拟机加载class和jar包的顺序。<br />
bootstrap classloader ->extension classloader->system classloader<br />
其中bootstrap引导类加载器;<br />
extension classloader扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包;<br />
system classloader系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径.<br />
该文中还有一句话是这么说的,应该能解决我们的疑惑:</p>
<p>“此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。”</p>
<p>我想应该是Android虚拟机中已经有了fastjson的cache了,所以导致如何更改项目的fastjson.jar和Android.jar顺序都不会有任何反应。我的理解是这样,不知道对不对,欢迎大侠指正。</p>
<p>解释到这里,也解决了我另外一个疑问,就是在Android的工程中新建一个java类,并生成main方法,然后Run->Java Application. 结果会出现如下的错误:</p>
<pre><code>#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (classFileParser.cpp:3494), pid=5940, tid=5632
# Error: ShouldNotReachHere()
</code></pre>
<p>这个问题的产生就是和bootstrap classloard 有关了,文件上右键Run As->Run Configuration选择Java Application下的这个Java类,然后选择右侧的Classpath标签页下有两个目录,分别是Bootstrap Entries 和 User Entries。<br />
Android工程中Bootsrap下默认的是Android Classpath Container,而Java则应该是JRE System Library。所以按照该文<a rel="nofollow" href="http://univasity.iteye.com/blog/1067127">Error: ShouldNotReachHere()</a>的解决方法,更改就好了。</p>
利用AXIS2传递JSON数据
https://segmentfault.com/a/1190000000346631
2013-11-28T14:37:00+08:00
2013-11-28T14:37:00+08:00
Kylin_Mountain
https://segmentfault.com/u/kylin_mountain
1
<p>Axis2是目前比较流行的WebService引擎。WebService被应用在很多不同的场景。例如,可以使用WebService来发布服务端 Java类的方法,以便使用不同的客户端进行调用。这样可以有效地集成多种不同的技术来完成应用系统。WebService还经常被使用在SOA中,用于 SOA各个部分交换数据。本文重点在于如何使用AXIS2引擎实现传递JSON数据。</p>
<p>博主本人由于项目需要,所以就查找了关于Web Service的文章。但碍于博主对Web的不熟,所以很多也就是按照网上教程做的。或许有许多地方不对。还请各位海涵了。</p>
<p>首先按照《<a rel="nofollow" href="http://wenku.baidu.com/view/6eae036d011ca300a6c390a4.html">axis2之webservice新手超详细教程</a>》这篇文档搭建了AXIS2的开发环境。接着我想让服务器传递JSON的数据,然后就又查找了如下的两篇资料:《<a rel="nofollow" href="http://blog.djakapm.com/2011/12/15/json-web-service-with-java-and-axis2/">JSON web service with Java and Axis2</a>》和《<a rel="nofollow" href="http://www.marcusschiesser.de/2009/01/building-a-json-web-service-with-java-and-axis2/">Building a JSON web service with Java and Axis2</a>》</p>
<p>这两篇资料是老外写的,内容差不多,但是不够详细,没有对具体操作进行说明。下面一个是国人写的《<a rel="nofollow" href="http://blog.csdn.net/hou_jiong/article/details/9044881">Axis2与Android的Json(Gson)通信传数据</a>》,这篇文档的方法也是按照上述两篇的文章的方法做的,具体就交代了每一步的操作。本文就在此基础上,将每步阐述如下,并且会将各部分的资料上传到百度云盘,方便各位下载。</p>
<p><strong>第一步</strong>:按照<a rel="nofollow" href="http://wenku.baidu.com/view/6eae036d011ca300a6c390a4.html">1</a>的方法,按部就班的安装 AXIS2。所需要下载的axis.war包和eclipse的两个插件都会放在百度云盘里。<br />
下载地址:<a rel="nofollow" href="http://pan.baidu.com/s/13ThAk">http://pan.baidu.com/s/13ThAk</a></p>
<p><strong>第二步</strong>:下载DynamicResponseHandler(wso2dynamic-response-1.5.mar)模块,复制到webservice项目中的WEB-INF/modules中,将wso2dynamic-response-1.5.mar加到modules.list文件中。<br />
下载地址:<a rel="nofollow" href="http://pan.baidu.com/s/1gm56z">http://pan.baidu.com/s/1gm56z</a></p>
<p><strong>第三步</strong>:下载Jettison 1.2和jettison-1.2-patched.jar,放入WEB-INF/lib中的模块,删除jettison-1.0-RC2.jar。<br />
下载地址:都在第二步下载的包中。</p>
<p><strong>第四步</strong>:找到tomcat\webapps\axis2\WEB-INF\conf\axis2.xml文件,添加xml</p>
<pre><code class="lang-xml"> <module ref="DynamicResponseHandler"/>
</code></pre>
<p><strong>第五步</strong>:在axis2.xml中添加JSON消息格式,找到<messageFormatters>标签,在这个标签里添加如下代码段:</p>
<pre><code class="lang-xml"> <messageFormatter contentType="application/json"
class="org.apache.axis2.json.JSONMessageFormatter"/>
<messageFormatter contentType="application/json/badgerfish"
class="org.apache.axis2.json.JSONBadgerfishMessageFormatter"/>
</code></pre>
<p><strong>第六步</strong>:在axis2.xml中添加消息编译,找到<messageBuilders>标签,添加如下代码:</p>
<pre><code class="lang-xml"> <messageBuilder contentType="application/json"
class="org.apache.axis2.json.JSONOMBuilder"/>
<messageBuilder contentType="application/json/badgerfish"
class="org.apache.axis2.json.JSONBadgerfishOMBuilder"/>
</code></pre>
<p>执行完以上步骤后,打开Tomcat服务器,在浏览器中输入测试url: <a rel="nofollow" href="http://localhost:8080/axis2/services/Version/getVersion?response=application/json">http://localhost:8080/axis2/services/Version/getVersion?response=application/json</a></p>
<p>浏览器将返回如下的内容:返回的值为:</p>
<pre><code class="lang-json"> {"return":"Hi - the Axis2version is 1.6.2"}
</code></pre>
<p>这样就完成了AXIS2的 JSON 传值配置。</p>