首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

阅读笔记-Hadoop入门实践

ASF:阿里软件开发平台服务框架SIP:服务集成平台BEA:应用基础结构软件公司分布式计算的应用场景:日志分析、索引建立Threshold:是一款IOS平台的应用。随时随地帮您监控行动数据使用奘况 无论是使用无限数据方案或是有限数据方案, Threshold 可以帮助您随时掌握最新数据使用状态, 提供客制化警示设定, 还可以设立一个您专属的行动数据区域, 以确保您不会被额外收费。 对于日志内容使用任务分解的多线程处理模式来分析统计,在比较简单的情况下,使用Memcache作为计数器,结合MySQL就能完成访问控制以及统计的工作,随着日志量的增加,未来可能不能满足要求。早期的多线程多任务分解日志模式,其实是分布式计算的一个单机版。将单机的工作进行分拆,变成协同工作的集群,其实就是分布式计算框架设计所涉及的。BEA和VMWare合作采用虚拟机来构建集群,就是希望使得计算机硬件能够类似于应用程序中资源池的资源,使用者无需关心资源分配情况,从而最大化了硬件资源的使用价值。分布式计算亦是如此,任务的分配和汇总都由分布式框架的Master来抉择,使用者需要提供待分析内容给分布式计算系统作为输入,等待结果。Hadoop是Apache开源组织的一个分布式计算开源框架,在很多大型网站上都已经得到了应用,比如亚马逊、Facebook和Yahoo等等。平台日志分析位常用,特别是集成平台的日志量会很大,非常适合分布式计算的适用场景。wwh学习:what? why? how? what:Hadoop框架中最核心的设计:MapReduce和HDFS。MapReduce:任务的分解与结果的汇总。HDFS:是Hadoop分布式文件系统(Hadoop Distributed File System)的缩写,为分布式计算存储提供了底层支持。MapReduce:Map&&Reduce,此处Map不适Map容器,作“展开”来讲,就是将一个任务分解为多个任务,Reduce就是将分解后多任务处理的结果汇总起来,得出最后的分析结果。类似于多线程,多任务。任务之间的关系可以分为两种:不相关的任务,可并行执行;人物之间有相互依赖,先后顺序不能够颠倒,这类任务是无法并行处理的。一般情况下,在Map前还可能会对输入的数据有Split(分割)的过程,保证任务并行效率,在Map之后,还会有Shuffer(混合)的过程,对于提高Reduce的效率以及减少数据传输的压力有很大的帮助。 HDFS:是分布式计算的存储基石。Hadoop的分布式文件系统和其他分布式文件系统有很多的类似的特质。分布式文件系统基本的几个特点。1、对于整个集群有单一的命名空间。2、数据一致性。适合一次写入多次读取的模型,客户端在文件没有被成功创建之前无法看到文件存在。3、文件会被分割成多个文件块,每个文件块被分配存储到数据节点上,而且根据配置会由赋值文件块来保证数据的安全性。 HDFS三个重要角色:NameNode、DataNode和Client。NameNode:可以看作是分布式文件系统中的管理者,主要负责管理文件系统的命名空间、集群配置信息和存储块的赋值等。NameNode会将文件系统的Meta-data存储在内存中,这些信息主要包括了文件信息、每一个文件对应的文件块的信息和每一个文件块在DataNode的信息等。DataNode:文件存储的基本单元,它将Block存储在本地文件系统中,保存了Block的Meta-data,同时周期性地将所有存在的Block信息发送给NameNode。Client:需要获取分布式文件系统文件的应用程序。三者关系样例说明:文件写入:1、Client向NameNode发起文件写入的请求。2、NameNode根据文件大小和文件块配置情况,返回给Client它所管理部分DataNode的信息。3、Client将文件划分为多个Block,根据DataNode的地址信息,按顺序写入到每一个DataNode块中。文件读取:1、Client向NameNode发起文件读取的请求。2、NameNode返回文件存储的DataNode的信息。3、Client读取文件信息。文件Bock复制:1、NameNode发现部分文件的Block不符合最小复制数或者部分DataNode失效。2、通知DataNode相互复制Block。3、DataNode开始直接相互复制。 HDFS的几个设计特点:1、Block的放置:默认不配置。一个Block会有三个备份,一份放在NameNode指定的DataNode,另一份放在与指定DataNode非同一个Rack上的DataNode,最后一份放在与指定DataNode同一Rack上的DataNode上。备份无非就是为了数据安全,考虑同一Rack的失败情况以及不同Rack之间数据拷贝性能问题就采用这种配置方式。2、心跳检测DataNode的健康状况,如果发现问题就采取数据备份的方式来保证数据的安全性。3、数据复制(场景为DataNode失败,需要平衡DataNode的存储利用率和需要平衡DataNode数据交互压力等情况):简述,使用HDFS的balancer命令,可以配置一个Threshold来平衡每一个DataNode磁盘利用率。例如设置了Threshold为10%,那么执行balancer命令的时候,首先统计所有DataNode的磁盘利用率平均值,然后判断如果某一个DataNode的磁盘利用率超过这个平均值Threshold以上,那么将会把这个DataNode的block转移到磁盘利用率低的DataNode,这对于新节点的加入来说十分有用。4、数据校验:采用CRC32作数据校验。在文件Block写入的时候除了写入数据还会写入校验信息,在读取的时候需要校验后在读入。5、NameNode是单点:如果失败的话,任务处理信息将会记录在本地文件系统和远端的文件系统中。6、数据管道性的写入:当客户端要写入文件到DataNode上,首先客户端读取一个Block然后写入到第一个DataNode上,然后由第一个DataNode传递到备份的DataNode上,一直到所有需要写入这个Block的DataNode都成功写入,客户端才会继续开始写下一个Block。7、安全模式:在分布式文件系统启动的时候,开始的时候会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统中的内容不允许修改也不允许删除,直到安全模式结束。安全模式主要是为了系统启动的时候检查各个DataNode上数据块的有效性,同时根据策略必要的复制或删除部分数据块。运行期通过命令也可以进入安全模式。生产中,系统启动时修改和删除文件也会有安全模式不允许修改的出错提示,需要等待。 MapReduce&&HDFS来看Hadoop结构:在Hadoop的系统中,会有一台Master,主要负责NameNode的工作以及JobTracker的工作。JobTracker的主要职责就是启动、跟踪和调度各个Slave的任务执行。还会有多台Slave,每一台Slave通常具有DataNode的功能并负责TaskTracker的工作。TaskTracker根据应用要求来结合本地数据执行Map任务以及Reduce任务。Moving Computation is Cheaper than Moving Data:分布式计算最重要的一个设计点。就是在分布式处理中,移动数据的代价总是高于转移计算机的代价。简单来说就是分而治之的工作,需要将数据也分而存储,本地任务处理本地数据然会归总,这样才会保证分布式计算的高效性。 why:Hadoop的有点&适用场景:1、可扩展:不论是存储的可扩展性还是计算的可扩展性都是Hadoop的设计根本。2、经济:框架可以运行在任何普通的PC上。3、可靠:分布式文件系统的备份恢复机制以及MapReduce的任务监控保证了分布式处理的可靠性。4、高效:分布式文件系统的高效数据交互实现以及MapReduce结合Local Data处理的模式,为高效处理海量的信息作了基础准备。使用场景:海量数据的分析。海量数据被分割于多个节点,然后每一个节点并行计算,将得出的结果归并到输出。同时第一阶段的输出又可以作为下一阶段计算的输入,因此可以想象到一个树状结构的分布式计算图,在不同阶段都由不同产品,同时并行和串行结合的计算也可以很好地在分布式集群的资源下得以高效的处理。 Hadoop可以单机跑,也可以配置集群跑;集群配置运行的过程:环境:机器在多而不在精,JDK必须时1.5以上的,各个机器的机器名务必不同,机器名对于MapReduce有很大的影响。部署:对于Hadoop的集群来说,可以分成两大类角色:Master和Slave,前者主要配置NameNode和JobTracker的角色,负责总管分布式数据存储以及任务的执行。一般情况下,Master和Slave不会部署在同一台机器上。 实施步骤:1、在所有的机器上都建立相同的目录,也就可以建立相同的用户,以该用户的home路径来做Hadoop的安装路径。例如在所有机器上都建立了/home/Hado2、下载Hadoop,先解压到Master上。此时Hadoop的安装路径就是/home/Hado/版本。3、解压后进入conf目录,主要修改以下文件:Hadoop-env.sh, Hadoop-site.xml, masters, slavers。Hadoop的基础配置文件时Hadoop-default.xml,看Hadoop的代码可以知道,默认建立一个Job的时候会建立Job的Config,Config首先读入Hadoop-defaulg.xml的配置,然后再读入Hadoop-site.xml的配置(这个文件初始的时候配置为空),hadoop-site.xml中主要配置你需要覆盖的Hadoop-default.xml的系统级配置,以及你需要再你的MapReduce过程中使用的自定义配置(具体一些的使用例如final等参考文档)。以下是一个简单的hadoop-site.xml的配置:<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?> fs.default.name dfs://10.2.224.46:54310/mapred.job.tracker hdfs://10.2.224.46:54311dfs.replication 1Hadoop.tmp.dir /home/Hado/Hadoop/tmp/mapred.child.java.opts -Xmx512mdfs.block.size 5120000The default block size for new files.Hadoop-env.sh文件只需要修改一个参数: The java implementation to use.Required. export JAVA_HOME=/usr/ali/jdk1.5.0_10(jdk版本)配置你的Java路径,记住一定要1.5版本以上,免得莫名其妙出现问题Masters中配置Masters的ip或者机器名,如果是机器名那么需要在/etc/hosts中有所设置。Slaves中配置的是Slaves的ip或者机器名,同样如果是机器名需要在etc/hosts中有所设置。范例如下:Masters: 10.2.224.46Slaves: 10.2.224.40 10.2.224.39 10.2.224.38 10.2.224.37 10.2.224.36 ...4、建立Masters到每一台Slave的SSH受信证书。由于Master将会通过SSH启动所有Slave的Hadoop,所以需要建立单向或者双向证书保证命令执行时不需要再输入密码。在Master和所有的Slave机器上执行:ssh-keygen -t rsa。执行此命令的时候,看到提示只需要回车。然后就会在/root/.ssh/下面产生id_rsa.pub的证书文件,通过scp将Master机器上的这个文件拷贝到Slave上(记得修改名称),例如:scp root@masterIP:/root/.ssh/id_rsa.pub /root/.ssh/46_rsa.pub,然后执行cat /root/.ssh/46_rsa.pub >>/root/.ssh/authorized_keys,建立authorized_keys文件即可,可以打开这个文件看看,也就是rsa的公钥作为key,user@IP作为value。此时可以试验一下,从master ssh到slave已经不需要密码了。由slave反向建立也是同样。为什么要反向呢?其实如果一直都是Master启动和关闭的话那就没有必要建立反向,只是如果想在Slave也可以关闭Hadoop就需要建立反向。5、将Master上的Hadoop通过scp拷贝到每一个Slave相同的目录下,根据每一个Slave的Java_HOME的不同修改器Hadoop-env.sh.6、修改Master上/etc/profile;新增一下内容:(具体的内容根据你的安装路径修改,这步只是为了方便使用)export HADOOP_HOME=/home/Hado/Hadoop_.. export PATH=$PATH:$HADOOP_HOME/bin修改完之后,执行source /etc/profile来使其生效。7、在Master上执行Hadoop namenode -format,这是第一需要做的初始化,可以看作格式化,以后除了上面提及的删除Master上的hadoop.tmp.dir目录,否则是不需要再次执行的。8、然后执行Master上的start-all.sh,这个命令可以直接执行,因为在6中已经添加到了path路径,这个命令是启动hdfs和mapreduce两部分,当然你也可以分开单独启动hdfs和mapreduce,分别是bin目录下的start-dfs.sh和start-mapred.sh.9、检查Master的logs目录,看看Namenode日志以及JobTracker日志是否正常启动。10、检查Slave的logs目录看看Datanode日志以及TaskTracker日志是否正常。11、如果需要关闭,那么就直接执行stop-all.sh即可。 感谢:岑文初!!!

优秀的个人博客,低调大师

神经网络入门实例

这里有一个灰度图像的集合,每个图像都是一个2x2的像素网格,每个像素在0(白色)和255(黑色)之间有一个强度值。我们的目标是建立一个模型,利用“阶梯”模式识别图像。 在这篇文章,我们只关心是否能够合理地匹配数据的模型。 预处理 对于每个图像,我们将像素用x1、x2、x3、x4标记,并生成一个输入向量\mathbf{x} \mathbf{x} = \begin{bmatrix}x_1 & x_2 & x_3 & x_4\end{bmatrix} 这将是我们的模型的输入。我们期望我们的模型能够预测真(图像有阶梯模式)或假(图像没有阶梯模式)。 图像ID x1 x2 x3 x4 具有阶梯模式 1 252 4 155 175 真 2 175 10 186 200 真 3 82 131 230 100 假 … … … … … … 498 36 187 43 249 假 499 1 160 169 242 真

优秀的个人博客,低调大师

git入门(6.repo)

repo是一种代码版本管理工具,它是由一系列的Python脚本组成,封装了一系列的Git命令,用来统一管理多个Git仓库。 因为Android源码引用了很多开源项目,每一个子项目都是一个Git仓库,每个Git仓库都有很多分支版本,为了方便统一管理各个子项目的Git仓库,需要一个上层工具批量进行处理,因此repo诞生。 repo也会建立一个Git仓库,用来记录当前Android版本下各个子项目的Git仓库分别处于哪一个分支,这个仓库通常叫做:manifest仓库。 repo start <topic_name> 开启一个新的主题,其实就是每个Project都新建一个分支。 repo init -u <url> [OPTIONS] 在当前目录下初始化repo,会在当前目录生生成一个.repo目录,像Git Project下的.git一样,-u指定url,可以加参数-m指定manifest文件,默认是default.xml,.repo/manifests保存manifest文件。.repo/projects下有所有的project的数据信息,repo是一系列git project的集合,每个git project下的.git目录中的refs等目录都是链接到.repo/manifests下的。 repo manifest 可以根据当前各Project的版本信息生成一个manifest文件 repo sync [PROJECT1...PROJECTN] 同步Code。 repo status 查看本地所有Project的修改,在每个修改的文件前有两个字符,第一个字符表示暂存区的状态。 每二个字符表示工作区的状态 repo prune <topic> 删除已经merge的分支 repo abandon <topic> 删除分支,无论是否merged repo branch或repo branches 查看所有分支 repo diff 查看修改 repo upload 上传本地提交至服务器 repo forall[PROJECT_LIST]-c COMMAND 对指定的Project列表或所有Project执行命令COMMAND,加上-p参数可打印出Project的路径。 repo forall -c 'git reset --hard HEAD;git clean -df;git rebase --abort' 这个命令可以撤销整个工程的本地修改。 本文转自Tenderrain 51CTO博客,原文链接:http://blog.51cto.com/tenderrain/1881971,如需转载请自行联系原作者

优秀的个人博客,低调大师

android107 指针入门

#include <stdio.h> #include <stdlib.h> //指针就是内存地址 //32为操作系统最大是4G内存,32为系统则是2的32次方, //所以只能表示2的32次方个内存地址, 也就是2的32次方个字节就是4G. main(){ int i;//i是内存空间的别名 ,i里面保存的是内存地址, i = 3; printf("%#x\n", &i); system("pause"); } #include <stdio.h> #include <stdlib.h> main(){ //野指针:没有赋值的指针 int* p; int i; double d = 3.14; p = &d; //不是野指针了, //*p = 23; printf("%#x\n", p);//打印地址,p里面存的是地址 printf("%lf\n", *p); system("pause"); } /*##*的三种用法 1. 乘法 2. int* p:定义一个指针变量p,p中存放一个内存地址,这个地址所存放的数据规定是int型 3. *p:取出p中保存的内存地址存放的数据 ##数据传递 * 所有语言所有平台,都只有值传递,引用传递传递的值是内存地址 */ #include <stdio.h> #include <stdlib.h> void swap(int* p, int* q){ int temp = *p; *p = *q; *q = temp; } main(){ int i = 3; int j = 5; printf("i=%d\n", i); printf("j=%d\n", j); swap(&i, &j); printf("i=%d\n", i); printf("j=%d\n", j); system("pause"); } #include <stdio.h> #include <stdlib.h> void function(int* p, int* q){ *p += 15; *q += 15; } main(){ int i = 10; int j = 20; function(&i, &j); printf("%d\n", i); printf("%d\n", j); system("pause"); } #include <stdio.h> #include <stdlib.h> void function(int** p){ int i = 3; printf("i的地址为%#x\n", &i); //p就是取出p内存空间的值 //*p:就是取出p内存空间值的内存空间的值, *p = &i;//修改P所指向的内存空间值的内存空间的值 } main(){ int* mainp;//mainp在栈中存的是地址,指向堆中的一个int数据 的内存空间 。 function(&mainp);//&mainp在栈中存的是地址的地址也就是 mainp的地址,也就是指向mainp的内存空间。 printf("主函数中获取i的地址为%#x\n", mainp); //数据幻影 printf("主函数中获取i的值为%d\n", *mainp);//-2因为i是局部变量已经销毁了 system("pause"); } 内存分析:0级指针,一级指针,二级指针都有, 二级指针是曾曾用名,一级指针是曾用名。 #include <stdio.h> #include <stdlib.h> main(){ //char arr[] = "hello"; int arr[5] = {1,2,3,4,5}; printf("%#x\n", &arr[0]);//0x22ff50 printf("%#x\n", &arr[1]); printf("%#x\n", &arr[2]); printf("%#x\n", &arr[3]); //arr = printf("数组名字的地址%#x\n", &arr);//也是输出第0个元素的地址 printf("数组名字的地址%#x\n", arr);//也是输出第0个元素的地址 //char* p = &arr; int* p = &arr; //+1表示向右偏移一个单位 printf("%d\n", *(p+0));// 1 printf("%d\n", *(p+1));//2 printf("%d\n", *(p+2));//3 printf("%d\n", (p+2)-p); //2 system("pause"); } #include <stdio.h> #include <stdlib.h> main(){ char* cp; int* ip; short* lizhip; int i; char c; cp = &c; ip = &i; //指针的长度都是4个字节 printf("%d\n", sizeof(cp));//4 printf("%d\n", sizeof(ip));//4 printf("%d\n", sizeof(lizhip));//4 printf("%d\n", (p+2)-p); //2,偏移量。 printf("%d\n", cp-ip);//出错, 连续内存空间相加减是可以的,非连续的内存空间相加减是没有意义的, system("pause"); } 本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/4979472.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android入门:MVC模式(中)

MVC 模式的最基本概念是分层设计,把我们的代码基于 View(视图)、Model(模型)、Controller(控制器)进行分类封装,这样做的目的是为了清晰结构,使代码更易维护和扩展。 在上一篇文章中,我们完成了计算器的界面还原,但严格来说并不是真正的 View 类,因为它还没反映视图的逻辑。在这次文章中,我们将编写计算器程序的 View 部分,Let’s Go!(注意:这次在代码的注释中写了较多的点,所以可以多看注释部分) 一,初识 Activity Activity(活动)作为 Android 四大组件之一,相当于应用中的整个界面,用前端的角度看,就像一个 web 页。而 Activity 的实质是什么呢?这次先简要描述,从 Google 大神中可知,Activity 起始继承于 Context 类,来看看它们的描述关键词: android.content.Context Interface to global information about an application environment … It allows access to application-specific resources and classes, as well as up-calls for application-level operation … 原来 Context 被定义为关于应用场景(上下文)的抽象类,具有访问应用层面资源和类的权限,并封装了一些应用级别的方法。 android.app.Activity An activity is a single, focused thing that the user can do … interact with the user … takes care of creating a window (full-screen windows or floating windows) for place UI … Activity 被定义为与用户交互(事件),负责创建加载视图的窗口等功能的功能类。可以说因 Context,使 Activity 具有强大的功能。 在开始编写前,先介绍一个重要的 Java 文件 - R.java,在前面介绍过,gen 目录会自动生成一些系统需要的文件,打开 R.java: R 类通过 attr,color,drawable,id,layout 等静态内部类,记录了所有标识。(注意:R类的标识会自动生成,不用去修改) 二,在 onCreate 中编写我们的程序 Activity 有个明显的特点,就是有生命周期。可以想象一下平时应用的使用过程,从一个界面滑入至另一界面,又从当前界面返回,伴随的就是 Activity 周期的不同阶段。打开 Calculator 项目的 MainActivity.java 文件: 在前端编程中,最重要的是获取操作对象(dom)。在 Android 中也如此,主要通过 id 标识获取操作对象。我们首先给activity_main.xml 中的 TextView 和 Button 加上以下 id 标识:(@+id/{name} 的意思是在 R 类中增加为 {name} 的 id 标识) TextView:@+id/ResultOutput Button 数字 0 ~ 9:@+id/Operand0 ~ @+id/Operand9 Button 除号:@+id/Operate0 Button 乘号:@+id/Operate1 Button 减号:@+id/Operate2 Button 加号:@+id/Operate3 Button 等号:@+id/Operate4 Button 清除:@+id/Operate5 如图所示,请务必为元素加上正确的 id。 在 Android 中,主要通过 findViewById() 方法获取操作对象,如TextView的获取: 在前端编程中,我们可以通过 getElementsByTagName() 方法获取一系列操作元素,但在 Android 中却没那么幸运了,没有这种方法。那有什么快捷点的方式不?答案是肯定的。我们知道 findViewById() 传入的是一个 int 类型的引用值,那么可否通过循环的方式找出这些引用值,然后直接获取呢?我们把 Button 元素分为两组,操作数的 id 以 Operand0 ~ 9 命名。而其余为运算符,则以 Operate0 ~ 5 命名(如上面提示的)。这样我们则可以: 这样,通过一个 TextView 和两个数组,我们就把需要的操作元素全部收集好了。 三,分离及定义 View 类的接口 从上面的代码看,一切似乎都很美好,但这种面向过程的思考方式是导致代码迅速膨胀,难以维护的原因之一。按 MVC 的设计思想,上面编写的代码应属于视图部分的逻辑,更好的办法应该封装在视图内,实现细节不被其它类所知。我们现在遵循这一思想从新组织一下代码:(注意:这里只朴素地用 MVC 思想表达意图,至于划分及编写的合理性就不探究了) 计算器将由两个 View 类组成,一个是用于显示结果的 CaOutputView 类,一个是用于用户输入的 CaInputView 类。首先建立存放 View 类的包,通过包区分不同类型文件。 然后我们建立这两个 View 类: 接着,我们打开 CaInputView.java 文件。好了,现在我们来思考一个问题,CaInputView 负责与用户的交互,自然会知道用户按了什么按钮,但怎样通知 Activity 用户的行为呢? 这个就是我们准备要接触的回调机制的概念。 就好比,CaInputView 对 Activity 说:你把“联系方式”留我,用户输入了我就通知你。而“联系方式”有多种实现的方式。这次就通过委托的方式实现,相当于 iOS 中的代理(delegate)的概念: 四,编写 View 类 现在我们继续编写 CaInputView类,把原先 Activity 类的代码逻辑归入 CaInputView 类: 继续编写CaOutputView 类,CaOutputView 类比较简单,只用于显示: 五,在 Activity 中使用 View 类 两个 View 已经创建完毕,现在可以尝试在 Activity 中使用了: 上图提示错误,是因为实例化了 CaInputView,却没有实现接口,所以提示 MainActivity 应该实现 CaInputView 声明的接口: 实现 CaInputView 声明的接口后仍然会报错,因为没实现接口声明的方法,选择“Add unimplemented methods”则自动添加了方法,如下图: 最后当 CaInputView 与用户发生交互时,我们“通知” Activity,而 Activity 则调用 CaOutputView 将结果显示出来,MainActivity 类的最终代码如下图: 运行程序,点击每个按钮,看是否显示正确的值: 通过分层设计,MainActivity 中的代码变得简洁很多,它只需知道如何使用 View 类则可,使它可以专注于自己的责任部分。 六,总结 这次说了的点比较多,主要有: MVC 的设计概念 两种方式获取操作对象 Java 的类型及转型相关概念 回调机制及接口 如何使用 View 类 如果对这些点还不清晰,可以再细看注释部分,或者留言给我,我会尽快答复。另外我也会尽量加快更新进度,下周将利用递归函数编写我们的 Model 类,敬请期待。 本文转自 一点点征服 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6693717.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

深入Android 【三】 —— 组件入门

Android组件 横看成岭侧成峰,远近高低各不同。 -- 《题西林壁》 组件( Component),在谈及所谓架构和重用的时候,是一个重要的事情。很多时候都会说基于组件的软件架构,指的是期望把程序做乐高似的,有一堆接口标准封装完整的组件放在哪里,想用的时候取上几个一搭配,整个程序就构建完成了。 在 开篇的时候就在说,Android是一个为组件化而搭建的平台,它引入所谓Mash-Up的概念,这使得你在应用的最上层,想做的不组件化都是很困难的一件事情(底层逻辑,好吧,管不了...)。具体说来,Android有四大组件四喜丸子:Activity、Service、Broadcast Receiver、Content Provider。 Activity 做一个完整的Android程序,不想用到Activity,真的是比较困难的一件事情,除非是想做绿叶想疯了。因为Activity是Android程序与用户交互的窗口,在我看来,从这个层面的视角来看,Android的Activity特像网站的 页面。 首先,一个网站,如果一张页面都没有,那...,真是一颗奇葩。而一张页面往往都有个 独立的主题和功能点,比如登录页面,注册页面,管理页面,如是。 在每个页面里面,会放一些链接,已实现功能点的串联,有的链接点了,刷,跑到同一站点的另一个页面去了;有的链接点了,啾,可能跳到其他网站的页面去;还有的链接点了,恩...,这次没跑,但当前页面的样子可能有所变化了。这些模式,和Activity给人的感觉很像,只不过实现策略不同罢了,毕竟Android这套架构的核心思想,本身就来自源于Web的Mash-Up概念,视为页面的客户端化,也未尝不可。 Activity,在四大组件中,无疑是最复杂的,这年头,一样东西和界面挂上了勾,都简化不了,想一想,独立做一个应用有多少时间沦落在了界面上,就能琢磨清楚了。从视觉效果来看,一个Activity占据当前的窗口,响应所有窗口事件,具备有控件,菜单等界面元素。从内部逻辑来看,Activity需要为了保持各个界面状态,需要做很多持久化的事情,还需要妥善管理生命周期,和一些转跳逻辑。对于开发者而言,就需要派生一个Activity的子类,然后埋头苦干上述事情。对于Activity的更多细节,先可以参见: reference/android/app/Activity.html。后续,会献上更为详尽的剖析。 Service 服务,从最直白的视角来看,就是剥离了界面的Activity,它们在很多Android的概念方面比较接近,都是封装有一个 完整的功能逻辑实现,只不过Service不抛头露脸,只是默默无声的做坚实的后盾。 但其实,换个角度来看,Android中的服务,和我们通常说的Windows服务,Web的后台服务又有一些相近,它们通常都是 后台长时间运行,接受上层指令,完成相关事务的模块。用运行模式来看,Activity是跳,从一个跳到一个,呃...,这有点像模态对话框(或者还像web页面好了...),给一个输入(抑或没有...),然后不管不顾的让它运行,离开时返回输出(同抑或没有...)。 而Service不是,它是等,等着上层连接上它,然后产生一段持久而缠绵的通信,这就像一个用了Ajax页面,看着没啥变化,偷偷摸摸的和Service不知眉来眼去多少回了。 但和一般的Service还是有所不同,Android的Service和所有四大组件一样,其 进程模型都是可以配置的,调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下,还是不同的进程下。这句话,可以拿把指甲刀刻进脑海中去,它凸显了Android的运行特征。如果一个Service,是有期望运行在于调用方不同进程的时候,就需要利用Android提供的 RPC机制,为其部署一套进程间通信的策略。 Android的RPC实现,如上图所示(好吧,也是从SDK中拿来主义的...),无甚稀奇,基于代理模式的一个实现,在调用端和服务端都去生成一个代理类,做一些序列化和反序列化的事情,使得调用端和服务器端都可以像调用一个本地接口一样使用RPC接口。 Android中用来做数据序列化的类是 Parcel,参见: /reference/android/os/Parcel.html,封装了序列化的细节,向外提供了足够对象化的访问接口,Android号称实现非常高效。 还有就是 AIDL (Android Interface Definition Language),一种接口定义的语言,服务的RPC接口,可以用AIDL来描述,这样,ADT就可以帮助你自动生成一整套的代理模式需要用到的类,都是想起来很乏力写起来很苦力的那种。更多内容,可以再看看: guide/developing/tools/aidl.html,如果有兴致,可以找些其他PRC实现的资料lou几眼。 关于Service的实现,还强推参看 API Demos这个Sample里面的 RemoteService实现。它完整的展示了实现一个Service需要做的事情:那就是定义好需要接受的Intent,提供 同步或异步的接口,在上层绑定了它后,通过这些接口(很多时候都是RPC的...)进行通信。在RPC接口中使用的数据、回调接口对象,如果不是标准的系统实现(系统可序列化的),则需要自定义aidl,所有一切,在这个Sample里都有表达,强荐。 Service从实现角度看,最特别的就是这些RPC的实现了,其他内容,都会接近于Activity的一些实现,也许不再会详述了。 Broadcast Receiver 在实际应用中,我们常需要等,等待系统抑或其他应用发出一道指令,为自己的应用擦亮明灯指明方向。而这种等待,在很多的平台上,都会需要付出不小的代价。 比如,在Symbian中,你要等待一个来电消息,显示归属地之类的,必须让自己的应用忍辱负重偷偷摸摸的开机启动,消隐图标隐藏任务项,潜伏在后台,监控着相关事件,等待转瞬即逝的出手机会。这是一件很发指的事情,不但白白耗费了系统资源,还留了个流氓软件的骂名,这真是卖力不讨好的正面典型。 在Android中,充分考虑了广泛的这类需求,于是就有了 Broadcast Receiver这样的一个组件。每个Broadcast Receiver都可以接收一种或若干种Intent作为触发事件(有不知道Intent的么,后面会知道了...),当发生这样事件的时候,系统会负责唤醒或传递消息到该Broadcast Receiver,任其处置。在此之前和这以后,Broadcast Receiver是否在运行都变得不重要了,及其绿色环保。 这个实现机制,显然是基于一种 注册方式的,Broadcast Receiver将其特征描述并注册在系统中,根据注册时机,可以分为两类,被我冠名为 冷热插拔。所谓 冷插拔,就是Broadcast Receiver的相关信息写在配置文件中(求配置文件详情?稍安,后续奉上...),系统会负责在相关事件发生的时候及时通知到该Broadcast Receiver,这种模式适合于这样的场景。 某事件方式 -> 通知Broadcast -> 启动相关处理应用。比如,监听来电、邮件、短信之类的,都隶属于这种模式。而 热插拔,顾名思义,插拔这样的事情,都是由应用自己来处理的,通常是在OnResume事件中通过 registerReceiver进行注册,在OnPause等事件中反注册,通过这种方式使其能够在运行期间保持对相关事件的关注。比如,一款优秀的词典软件(比如, 有道词典...),可能会有在运行期间关注网络状况变化的需求,使其可以在有廉价网络的时候优先使用网络查询词汇,在其他情况下,首先通过本地词库来查词,从而兼顾腰包和体验,一举两得一石二鸟一箭双雕(注,真实在有道词典中有这样的能力,但不是通过Broadcast Receiver实现的,仅以为例...)。而这样的监听,只需要在其工作状态下保持就好,不运行的时候,管你是天大的网路变化,与我何干。其模式可以归结为: 启动应用 -> 监听事件 -> 发生时进行处理。 除了接受消息的一方有多种模式,发送者也有很重要的选择权。通常,发送这有两类,一个就是系统本身,我们称之为系统Broadcast消息,在 reference/android/content/Intent.html的 Standard Broadcast Actions,可以求到相关消息的详情。除了系统,自定义的应用可以放出Broadcast消息,通过的接口可以是 Context.sendBroadcast,抑或是 Context.sendOrderedBroadcast。前者发出的称为 Normal broadcast,所有关注该消息的Receiver,都有机会获得并进行处理;后者放出的称作 Ordered broadcasts,顾名思义,接受者需要按资排辈,排在后面的只能吃前面吃剩下的,前面的心情不好私吞了,后面的只能喝西北风了。 当Broadcast Receiver接收到相关的消息,它们通常做一些简单的处理,然后转化称为一条Notification,一次振铃,一次震动,抑或是启动一个Activity进行进一步的交互和处理。所以,虽然Broadcast整个逻辑不复杂,却是足够有用和好用,它统一了Android的事件广播模型,让很多平台都相形见绌了。更多Broadcast Receiver相关内容,参见: /reference/android/content/BroadcastReceiver.html。 Content Provider Content Provider,听着就和数据相关,没错,这就是Android提供的第三方应用数据的访问方案。在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件、等等内容,都是不允许其他直接访问的,但有时候,沟通是必要的,不仅对第三方很重要,对应用自己也很重要。 比如,一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查,整个应用就失去了可扩展力,必将被其他应用抛弃,然后另立门户,自个玩自个的去了。 Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是 Content Provider。应用想对外提供的数据,可以通过派生 ContentProvider类,封装成一枚Content Provider,每个Content Provider都用一个 uri作为独立的标识,形如: content://com.xxxxx。所有东西看着像REST的样子,但实际上,它比REST更为灵活。和REST类似,uri也可以有两种类型,一种是带id的,另一种是列表的,但实现者不需要按照这个模式来做,给你id的uri你也可以返回列表类型的数据,只要调用者明白,就无妨,不用苛求所谓的REST。 另外,Content Provider不和REST一样只有uri可用,还可以接受 Projection, Selection, OrderBy等参数,这样,就可以像数据库那样进行投影,选择和排序。查询到的结果,以 Cursor(参见: reference/android/database/Cursor.html)的形式进行返回,调用者可以移动Cursor来访问各列的数据。 Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型,这样的抽象层次,大大简化了上层应用的书写,也对数据的整合提供了更方便的途径。Content Provider内部,常用数据库来实现,Android提供了强大的 Sqlite支持,但很多时候,你也可以封装文件或其他混合的数据。 在Android中, ContentResolver是用来发起Content Provider的定位和访问的。不过它仅提供了同步访问的Content Provider的接口。但通常,Content Provider需要访问的可能是数据库等大数据源,效率上不足够快,会导致调用线程的拥塞。因此Android提供了一个 AsyncQueryHandler(参见: reference/android/content/AsyncQueryHandler.html),帮助进行异步访问Content Provider。 在各大组件中,Service和Content Provider都是那种需要持续访问的。Service如果是一个耗时的场景,往往会提供 异步访问的接口,而Content Provider不论效率如何,都提供的是约定的 同步访问接口。我想这遵循的就是 场景导向设计的原则,因为Content Provider仅是提供数据访问的,它不能确信具体的使用场景如何,会怎样使用它的数据;而相比之下,Service包含的逻辑更复杂更完整,可以抉择大部分时候使用某接口的场景,从而确定最贴切的接口是同步还是异步,简化了上层调用的逻辑。 配置 四大组件说完了,四大组件幕后的英雄也该出场了,那就是每个应用都会有一份的配置文件,名称是 AndroidManifest.xml,在工程的根目录下。在这个配置文件中,不仅会描述一些应用相关的信息,很重要的,会包含一个应用中所有组件的信息。如果你派生Activity或者Service实现了一个相关的类,这只是把它组件化的第一步,你需要把这个类的相关信息写到配置文件中,它才会作为一个组件被应用到,否则只能默默无闻的黯淡度过余生。 摆了一幅图出来,这次不是偷来的,是敝帚自珍原创,所以没有意外的画的很丑,但基本还是可以体现出一些意思。在 In Others的部分,这里是一般平台应用之间通信和交互的模型,每个应用都有很强烈的应用边界(往往表现为进程边界...),App 1的还是App 2的,分得很是清楚。每个应用内部,都有自己的逻辑去切分功能组件,这样的切分通常没有什么标准,率性而为。应用间的交互逻辑也比较零散,App 1与App 2交互,往往需要明确知道对方应用的具体信息,比如进程ID,进程名称之类的,这样使得应用和应用之间的联系,变得很生硬。而上层应用和系统应用的通信,往往有很多特定的模式,这种模式,很可能是无法直接应用在普通应用之间的,换而言之,系统应用是有一定特殊性的。 重点,在图的下半部,描述的是 Android的应用情形。在Android中, 应用的边界,在组件这个层面,是极度模糊,什么进程、什么应用,都可以不必感知到。举个例子,App 1,实现了A和B两个组件,App 2,实现了C这个组件。A和C,都想使用B这个组件,那么它们的使用方式是 完全一致的,都需要通过系统核心的组件识别和通信机制,找到和使用组件B。A,虽说和B是一个娘胎里蹦出来的,很不好意思,没有任何特殊的后面和捷径,还是要跑规矩的途径才能用到,一片和谐社会的景象油然而生。 在Android中, 所有组件的识别和消息传递逻辑都必须依赖底层核心来进行(通信可以没有底层核心的参与,比如一旦Service找到了,就可以和它产生持久的通信...),没有底层核心的牵线搭桥,任何两个组件都无法产生联系。比如一个Activity,跳到另一个Activity,必须要向底层核心发起一个Intent,有底层解析并认可后,会找到另一个Activity,把相关消息和数据传给它。一个Activity想使用Content Provider中的数据,必须通过底层核心解析相关的uri,定位到这个Content Provider,把参数传递给它,然后返回Activity需要的Cursor。这样的设计,保证了底层核心对所有组件的绝对掌控权和认知权,使得搭积木似的开发变成可能。 为了,使得核心系统能够完整的掌握每个组件的信息,这就需要配置文件了。配置文件,就是将组件插到底层核心上的这个 插头。只有通过这个插头插在底层核心的插座上(不要乱想,非十八禁...),组件才能够发光发热,闪耀光芒。 组件的配置信息在我看来主要包含两个方面,一部分是描述 如何认知。比如,Activity、Service、Broadcast Receiver都会有名字信息,和希望能够把握的Intent信息(姑且看成消息好了...),Content Provider会有一个描述其身份的uri。当其他组件通过这样的名字或者Intent,就可以找到它。 另一部分是 运行相关的信息。这个组件,期望怎么来运行,放在单独的进程,还是和调用者一个进程,还是找相关的其他组件挤在同一个进程里面,这些内容,都可以在配置的时候来决定(调用者在这个约束范围内,有进一步的选择权...)。更多配置项,请参见: guide/topics/manifest/manifest-intro.html。 通过前续内容,也许可以帮助大家对Android组件有个初略的了解。但这些了解都还停留在静态层面,程序是个动态的概念,关于各个组件具体是怎么联系在一起的,如何手拉手运行起来完成一项功能的,这便是后话了。 本文转自 duguguiyu 51CTO博客,原文链接:http://blog.51cto.com/duguguiyu/362223,如需转载请自行联系原作者

资源下载

更多资源
Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册