首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

马哥运维学习作业(三)

1、列出当前系统上所有已经登录的用户的用户名,注意:同一个用户登录多次,则只显示一次即可。 1 2 3 4 5 6 7 8 [root@c6-1~] #who rootpts /0 2016-08-2201:40(10.3.20.100) zdwpts /1 2016-08-2201:45(10.3.20.100) zdwpts /2 2016-08-2201:45(10.3.20.100) rootpts /3 2016-08-2201:46(10.3.20.100) [root@c6-1~] #who|cut-d''-f1|sort-u root zdw 2、取出最后登录到当前系统的用户的相关信息。 1 2 [root@c6-1~] #last|head-1 rootpts /3 10.3.20.100MonAug2201:46stilllogged in 3、取出当前系统上被用户当作其默认shell的最多的那个shell。 1 2 [root@c6-1~] #cat/etc/passwd|cut-d:-f7|sort|uniq-c|sort-n|tail-1 15 /sbin/nologin 4、将/etc/passwd 中的第三个字段数值最大的后10个用户的信息全部改为大写后保存至/tmp/maxusers.txt文件中。 1 2 3 4 5 6 7 8 9 10 11 12 [root@c6-1~] #sort-t:-k3-n/etc/passwd|tail-10|tr"a-z""A-Z">/tmp/maxusers.txt [root@c6-1~] #cat/tmp/maxusers.txt OPERATOR:X:11:0:OPERATOR: /ROOT : /SBIN/NOLOGIN GAMES:X:12:100:GAMES: /USR/GAMES : /SBIN/NOLOGIN GOPHER:X:13:30:GOPHER: /VAR/GOPHER : /SBIN/NOLOGIN FTP:X:14:50:FTPUSER: /VAR/FTP : /SBIN/NOLOGIN VCSA:X:69:69:VIRTUALCONSOLEMEMORYOWNER: /DEV : /SBIN/NOLOGIN SSHD:X:74:74:PRIVILEGE-SEPARATEDSSH: /VAR/EMPTY/SSHD : /SBIN/NOLOGIN POSTFIX:X:89:89:: /VAR/SPOOL/POSTFIX : /SBIN/NOLOGIN NOBODY:X:99:99:NOBODY:/: /SBIN/NOLOGIN SASLAUTH:X:499:76: "SASLAUTHDUSER" : /VAR/EMPTY/SASLAUTH : /SBIN/NOLOGIN ZDW:X:500:500:: /HOME/ZDW : /BIN/BASH 5、取出当前主机的IP地址,提示:对ifconfig命令的结果进行切分。 1 2 [root@c6-1~] #ifconfig|grepinet|cut-d:-f2|cut-d''-f1|head-1 10.3.20.80 6、列出/etc目录下所有以.conf结尾的文件的文件名,并将其名字转换为大写后保存至/tmp/etc.conf文件中。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@c6-1~] #ls/etc/*.conf|tr"a-z""A-Z">/tmp/etc.conf [root@c6-1~] #cat/tmp/etc.conf /ETC/DRACUT .CONF /ETC/GAI .CONF /ETC/GRUB .CONF /ETC/HOST .CONF /ETC/KRB5 .CONF /ETC/LD .SO.CONF /ETC/LIBAUDIT .CONF /ETC/LIBUSER .CONF /ETC/LOGROTATE .CONF /ETC/MKE2FS .CONF /ETC/NSSWITCH .CONF /ETC/RESOLV .CONF /ETC/RSYSLOG .CONF /ETC/SESTATUS .CONF /ETC/SUDO .CONF /ETC/SUDO-LDAP .CONF /ETC/SYSCTL .CONF /ETC/YUM .CONF 7、显示/var目录下一级子目录或文件的总个数。 1 2 [root@c6-1~] #ls/var/|wc-l 16 8、取出/etc/group文件中第三个字段数值最小的10个组的名字。 1 2 3 4 5 6 7 8 9 10 11 [root@c6-1~] #sort-t:-k3-n/etc/group|head-10|cut-d:-f1 root bin daemon sys adm tty disk lp mem kmem 9、将/etc/fstab和/etc/issue文件的内容合并为同一个内容后保存至/tmp/etc.test文件中。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@c6-1~] #cat/etc/{fstab,issue}>/tmp/etc.test [root@c6-1~] #cat/tmp/etc.test # #/etc/fstab #CreatedbyanacondaonMonFeb2901:31:342016 # #Accessiblefilesystems,byreference,aremaintainedunder'/dev/disk' #Seemanpagesfstab(5),findfs(8),mount(8)and/orblkid(8)formoreinfo # /dev/mapper/VolGroup-lv_root /ext4defaults11 UUID=e880d9c0-5a7e-4e59-a49b-06488a6e4208 /boot ext4defaults12 /dev/mapper/VolGroup-lv_swap swapswapdefaults00 tmpfs /dev/shm tmpfsdefaults00 devpts /dev/pts devptsgid=5,mode=62000 sysfs /sys sysfsdefaults00 proc /proc procdefaults00 CentOSrelease6.5(Final) Kernel\ronan\m 10、请总结描述用户和组管理类命令的使用方法并完成以下练习: (1)、创建组distro,其GID为2016; 1 2 3 [root@c6-1~] #groupadd-g2016distro [root@c6-1~] #tail-1/etc/group distro:x:2016: (2)、创建用户mandriva, 其ID号为1005;基本组为distro; 1 2 3 [root@c6-1~] #useradd-u1005-gdistromandriva [root@c6-1~] #idmandriva uid=1005(mandriva)gid=2016(distro) groups =2016(distro) (3)、创建用户mageia,其ID号为1100,家目录为/home/linux; 1 2 3 [root@c6-1~] #useradd-u1100-d/home/linuxmageia [root@c6-1~] #tail-1/etc/passwd mageia:x:1100:1100:: /home/linux : /bin/bash (4)、给用户mageia添加密码,密码为mageedu; 1 2 3 [root@c6-1~] #echo"password"|passwd--stdinmageia Changingpassword for usermageia. passwd :allauthenticationtokensupdatedsuccessfully. (5)、删除mandriva,但保留其家目录; 1 2 3 4 5 [root@c6-1~] #userdelmandriva [root@c6-1~] #ls/home/ linuxmandrivazdw [root@c6-1~] #idmandriva id :mandriva:Nosuchuser (6)、创建用户slackware,其ID号为2002,基本组为distro,附加组peguin; 1 2 3 4 [root@c6-1~] #groupaddpeguin [root@c6-1~] #useradd-u2002-gdistro-Gpeguinslackware [root@c6-1~] #idslackware uid=2002(slackware)gid=2016(distro) groups =2016(distro),2017(peguin) (7)、修改slackware的默认shell为/bin/tcsh; 1 2 3 [root@c6-1~] #usermod-s/bin/tcshslackware [root@c6-1~] #grep'slackware'/etc/passwd slackware:x:2002:2016:: /home/slackware : /bin/tcsh (8)、为用户slackware新增附加组admins; 1 2 3 4 [root@c6-1~] #groupaddadmins [root@c6-1~] #usermod-a-Gadminsslackware [root@c6-1~] #idslackware uid=2002(slackware)gid=2016(distro) groups =2016(distro),2017(peguin),2018(admins) (9)、为slackware添加密码,且要求密码最短使用期限为3天,最长为180天,警告为3天; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@c6-1~] #echo"123456"|passwd--stdinslackware Changingpassword for userslackware. passwd :allauthenticationtokensupdatedsuccessfully. [root@c6-1~] #passwd-n3-x180-w3slackware Adjustingagingdata for userslackware. passwd :Success [root@c6-1~] #chage-lslackware Lastpasswordchange:Aug21,2016 Passwordexpires:Feb17,2017 Passwordinactive:never Accountexpires:never Minimumnumberofdaysbetweenpasswordchange:3 Maximumnumberofdaysbetweenpasswordchange:180 Numberofdaysofwarningbeforepasswordexpires:3 (10)、添加用户openstack,其ID号为3003, 基本组为clouds,附加组为peguin和nova; 1 2 3 4 5 [root@c6-1~] #groupaddclouds [root@c6-1~] #groupaddnova [root@c6-1~] #useradd-u3003-gclouds-Gpeguin,novaopenstack [root@c6-1~] #idopenstack uid=3003(openstack)gid=2019(clouds) groups =2019(clouds),2017(peguin),2020(nova) (11)、添加系统用户mysql,要求其shell为/sbin/nologin; 1 2 3 [root@c6-1~] #useradd-s/sbin/nologinmysql [root@c6-1~] #grep'mysql'/etc/passwd mysql:x:3004:3004:: /home/mysql : /sbin/nologin (12)、使用echo命令,非交互式为openstack添加密码。 1 2 3 [root@c6-1~] #echo"password"|passwd--stdinopenstack Changingpassword for useropenstack. passwd :allauthenticationtokensupdatedsuccessfully. 本文转自cix123 51CTO博客,原文链接:http://blog.51cto.com/zhaodongwei/1840876,如需转载请自行联系原作者

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

马哥运维学习作业(四)

1、复制/etc/skel目录为/home/tuser1,要求/home/tuser1及其内部文件的属组和其它用户均没有任何访问权限。 1 2 3 4 5 6 7 8 9 10 11 12 [root@c6-1~] #cp-r/etc/skel//home/tuser1#使用cp-r递归复制文件夹内容到/home/tuser1下 [root@c6-1~] #ls-ld/home/tuser1/#查看/home下已有tuser1文件夹并看到默认权限为755 drwxr-xr-x.2rootroot4096Aug2517:47 /home/tuser1/ [root@c6-1~] #chmod-R700/home/tuser1/#使用chmod-R参数把/home/tuser1及其内部文件的权限都改为700。不使用-R递归参数,则只能改tuser1的权限 [root@c6-1~] #ll-d/home/tuser1/&&ll-a/home/tuser1/#同时验证tuser1和其内部文件权限都已改为属组和其它用户没有任何访问权限 drwx------.2rootroot4096Aug2517:47 /home/tuser1/ #文件夹权限 total20 drwx------.2rootroot4096Aug2517:47. drwxr-xr-x.5rootroot4096Aug2517:47.. -rwx------.1rootroot18Aug2517:47.bash_logout #下面是其内部文件的权限 -rwx------.1rootroot176Aug2517:47.bash_profile -rwx------.1rootroot124Aug2517:47.bashrc 图示: 2、编辑/etc/group文件,添加组hadoop。 方法一:手动vim编辑 1 2 3 4 5 [root@c6-1~] #vim/etc/group#使用vim编辑器打开/etc/group后,在末尾插入如下内容 ..........以上显示略 hadoop:x:2016: [root@c6-1~] #tail-1/etc/group#查看/etc/group最后一行已插入hadoop:x:2016: hadoop:x:2016: 注:使用vim编辑器打开/etc/group文件,按G(跳到行尾)-按yy(复制该行)-按p(粘帖该行到下一行)-按i(进入编辑状态),最后把第1,3列内容改为hadoop:x:2016: 注:也可直接打开vim编辑器到末尾输入hadoop:x:2016:保存退出,上面的操作是个人习惯,替换字符可减少错误的概率。 图示: 方法二:非交互式插入 1 2 3 [root@c6-2~] #echo"hadoop:x:2018">>/etc/group#非交互式插入内容 [root@c6-2~] #tail-1/etc/group#查看/etc/group最后一行已插入 hadoop:x:2018 图示: 3、手动编辑/etc/passwd文件新增一行,添加用户hadoop,其基本组ID为hadoop组的id号;其家目录为/home/hadoop。 1 2 3 4 5 [root@c6-1~] #vim/etc/passwd#按第2题的操作方法插入如下内容 ........以上显示略 hadoop:x:2016:2016:: /home/hadoop : /bin/bash [root@c6-1~] #idhadoop#idhadoop查看用户信息 uid=2016(hadoop)gid=2016(hadoop) groups =2016(hadoop) 图示: 4、复制/etc/skel目录为/home/hadoop,要求修改hadoop目录的属组和其它用户没有任何访问权限。 1 2 3 4 5 6 7 8 9 10 [root@c6-2~] #cp-r/etc/skel/home/hadoop#复制/etc/skel目录为/home/hadoop [root@c6-2~] #ll/home/#已复制完成,hadoop默认权限为755 total8 drwxr-xr-x.2rootroot4096Aug2421:06hadoop drwx------.4zdwzdw4096Jul2023:33zdw [root@c6-2~] #chmod700/home/hadoop/#按要求把hadoop目录改为属组和其它用户没有任何访问权限 [root@c6-2~] #ll/home/#再次查看,hadoop权限已设置好 total8 drwx------.2rootroot4096Aug2421:06hadoop drwx------.4zdwzdw4096Jul2023:33zdw 图示: 5、修改/home/hadoop目录及其内部所有文件的属主为hadoop,属组为hadoop。 1 2 3 4 5 [root@c6-2~] #chown-Rhadoop:hadoop/home/hadoop#使用chown-R命令把/home/hadoop目录及其内部所有文件属主、属组改为hadoop [root@c6-2~] #ll/home/#属主、属组已从原来的root,改为hadoop total8 drwx------.2hadoophadoop4096Aug2421:06hadoop drwx------.4zdwzdw4096Jul2023:33zdw 图示: 6、显示/proc/meminfo文件中以大写或小写S开头的行;用两种方式; 方法一: 1 2 3 4 5 6 7 8 [root@C7-1~] #grep^[sS]/proc/meminfo#^匹配以sS开头,[]设置范围 SwapCached:0kB SwapTotal:2097148kB SwapFree:2097148kB Shmem:8628kB Slab:55796kB SReclaimable:23544kB SUnreclaim:32252kB 方法二: 1 2 3 4 5 6 7 8 [root@C7-1~] #grep-i'^s'/proc/meminfo#使用-i参数忽略大小写 SwapCached:0kB SwapTotal:2097148kB SwapFree:2097148kB Shmem:8628kB Slab:55796kB SReclaimable:23544kB SUnreclaim:32252kB 图示: 7、显示/etc/passw d文件中其默认shell为非/sbin/nologin的用户; 1 [root@C7-1~] #grep-v"/sbin/nologin"/etc/passwd|cut-d:-f1 注:使用-v参数反向查找,cut -d: -f1指定分隔符为冒号,显示第一列用户名列 图示: 8、显示/etc/passw d文件中其默认shell为/bin/bash的用户; 方法一: 1 2 3 4 [root@C7-1~] #grep"/bin/bash$"/etc/passwd|cut-d:-f1 root zdw hadoop 注:$代表以/bin/bash结尾,适用于要搜索末尾的关键字后面有空格的,/bin/bash后面没有空格,直接使用下面的方法二即可。 方法二: 1 2 3 4 [root@C7-1~] #grep"/bin/bash"/etc/passwd|cut-d:-f1 root zdw hadoop 注:接上,cut -d: -f1指定分隔符为冒号,显示第一列用户名列,如果要看得更清楚,使用-f1,7把bash也显示出来 图示: 9、找出/etc/passwd文件中的一位数或两位数; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@C7-1~] #grep"\<[0-9]\{1,2\}\>"/etc/passwd root:x:0:0:root: /root : /bin/bash bin:x:1:1:bin: /bin : /sbin/nologin daemon:x:2:2:daemon: /sbin : /sbin/nologin adm:x:3:4:adm: /var/adm : /sbin/nologin lp:x:4:7:lp: /var/spool/lpd : /sbin/nologin sync :x:5:0: sync : /sbin : /bin/sync shutdown :x:6:0: shutdown : /sbin : /sbin/shutdown halt:x:7:0:halt: /sbin : /sbin/halt mail:x:8:12:mail: /var/spool/mail : /sbin/nologin operator:x:11:0:operator: /root : /sbin/nologin games:x:12:100:games: /usr/games : /sbin/nologin ftp :x:14:50:FTPUser: /var/ftp : /sbin/nologin nobody:x:99:99:Nobody:/: /sbin/nologin dbus:x:81:81:Systemmessagebus:/: /sbin/nologin tss:x:59:59:Accountusedbythetrouserspackagetosandboxthetcsddaemon: /dev/null : /sbin/nologin postfix:x:89:89:: /var/spool/postfix : /sbin/nologin sshd:x:74:74:Privilege-separatedSSH: /var/empty/sshd : /sbin/nologin 注:\<[0-9]\{1,2\}\>指的是第一位是数字,\{1,2\}前面的数字出现至少1次,最多2次,\<,\>是词首和词尾锚定,限定了数字只能是1位数或二位数,就不会出现3位或4位的数字。如果不加上锚定,就会把3位、4位数字也显示出来。注:[0-9]和[[:digit:]]代表的意思一样。 图示: 10、显示/boot/grub/grub.conf中以至少一个空白字符开头的行; 方法一: 1 2 3 4 [root@c6-1~] #grep"^[[:space:]]\{1,\}"/boot/grub/grub.conf#\{1,\}是代表前面的字符至少出现一次 root(hd0,0) kernel /vmlinuz-2 .6.32-431.el6.x86_64roroot= /dev/mapper/VolGroup-lv_root rd_NO_LUKSLANG=en_US.UTF-8rd_NO_MDrd_LVM_LV=VolGroup /lv_swap SYSFONT=latarcyrheb-sun16crashkernel=autord_LVM_LV=VolGroup /lv_root KEYBOARDTYPE=pcKEYTABLE=usrd_NO_DMrhgbquiet initrd /initramfs-2 .6.32-431.el6.x86_64.img 方法二: 1 2 3 4 [root@c6-1~] #grep-E"^[[:space:]]+"/boot/grub/grub.conf#\+也是代表前面的字符至少出现一次,所以二个显示出的结果都一样 root(hd0,0) kernel /vmlinuz-2 .6.32-431.el6.x86_64roroot= /dev/mapper/VolGroup-lv_root rd_NO_LUKSLANG=en_US.UTF-8rd_NO_MDrd_LVM_LV=VolGroup /lv_swap SYSFONT=latarcyrheb-sun16crashkernel=autord_LVM_LV=VolGroup /lv_root KEYBOARDTYPE=pcKEYTABLE=usrd_NO_DMrhgbquiet initrd /initramfs-2 .6.32-431.el6.x86_64.img 图示: 11、显示/etc/rc.d/rc.sysinit文件中以#开头,后面跟至少一个空白字符,而后又有至少一个非空白字符的行; 方法一: 1 2 3 4 5 6 7 [root@ cat ~] #grep"^#[[:space:]]\+[^[:space:]]\+"/etc/rc.d/rc.sysinit #/etc/rc.d/rc.sysinit-runonceatboottime #TakeninpartfromMiquelvanSmoorenburg'sbcheckrc. #CheckSELinuxstatus #Printatextbanner. #Onlyreadthisonce. ....以下显示略 注:[[:space:]]+表示至少一个空白字符,[^[:space:]]+表示至少一个非空白字符 方法二: 1 2 3 4 5 6 7 8 [root@ cat ~] #grep-E^#[[:space:]]\{1\}[^[:space:]]\{1\}/etc/rc.d/rc.sysinit #/etc/rc.d/rc.sysinit-runonceatboottime #TakeninpartfromMiquelvanSmoorenburg'sbcheckrc. #CheckSELinuxstatus #Printatextbanner. #Onlyreadthisonce. #Initializehardware ....以下显示略 注:使用-E支持扩展的正则表达式,[[:space:]]\{1\}表示至少一个空白字符,[^[:space:]]\{1\}表示至少一个非空白字符 图示: 12、打出netstat -tan命令执行结果中以‘LISTEN’,后或跟空白字符结尾的行; 方法一: 1 2 3 4 5 [root@C7-1~] #netstat-tan|grep'LISTEN[[:space:]]*$'#*$以空白字符任意次为结尾 tcp000.0.0.0:220.0.0.0:*LISTEN tcp00127.0.0.1:250.0.0.0:*LISTEN tcp600:::22:::*LISTEN tcp600::1:25:::*LISTEN 方法二: 1 2 3 4 5 [root@C7-1~] #netstat-tan|grep"\<LISTEN\>[[:space:]]*$"#\<LISTEN\>表示词首词尾锚定 tcp000.0.0.0:220.0.0.0:*LISTEN tcp00127.0.0.1:250.0.0.0:*LISTEN tcp600:::22:::*LISTEN tcp600::1:25:::*LISTEN 方法三: 1 2 3 4 5 [root@C7-1~] #netstat-tan|grep'LISTEN[[:space:]]\+$'#\+$以空白字符至少1次为结尾 tcp000.0.0.0:220.0.0.0:*LISTEN tcp00127.0.0.1:250.0.0.0:*LISTEN tcp600:::22:::*LISTEN tcp600::1:25:::*LISTEN 图示: 13、添加用户bash, testbash, basher, nologin (此一个用户的shell为/sbin/nologin),而后找出当前系统上其用户名和默认shell相同的用户的信息; 1 2 3 4 5 6 7 8 9 10 [root@c6-1~] #useraddbash#新建用户bash [root@c6-1~] #useraddtestbash#新建用户testbash [root@c6-1~] #useraddbasher#新建用户basher [root@c6-1~] #useradd-s/sbin/nologinnologin#新建用户nologin使用-s指定其shell为/sbin/nologin [root@c6-1~] #grep"^\([[:alnum:]]\+\>\).*\1$"/etc/passwd#注释在最后有详细说明 sync :x:5:0: sync : /sbin : /bin/sync shutdown :x:6:0: shutdown : /sbin : /sbin/shutdown halt:x:7:0:halt: /sbin : /sbin/halt bash :x:2017:2017:: /home/bash : /bin/bash nologin:x:2020:2020:: /home/nologin : /sbin/nologin 注:"^\([[:alnum:]]\+\>\).*\1$"的意思是做分组中间可出现任意内容,结尾匹配行首。 详细解答:\(和\)把里面的多个字符分成一组,当作一个整体进行处理,[[:alnum:]]\+代表以一个数字或字母开头后面可以跟任意位数的字符,\>表示这个字符的结束。.*表示中间可出现任意长度字符。\1$表示结尾匹配行首。 图示: 本文转自cix123 51CTO博客,原文链接:http://blog.51cto.com/zhaodongwei/1841892,如需转载请自行联系原作者

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

PHP开源项目《phpbeginner_gmanage》学习日记

项目链接:https://github.com/udoless/phpbeginner_gmanage 2016.10.10 common.inc.php |--global.func.php(公共函数:检查登录状态) |--mysql.func.php (数据库操作函数) 基类,数据库连接及初始化,网站设置的初始化(如表的分页信息,由表gm_system来控制) 页面布局架构 title_admin.inc.php (标题-共用,同时动态引入了各个可变页面的css样式,以及公共的js文件) header_admin.inc.php(头-共用) 具体页面(不同的页面不一样) footer_admin.inc.php(脚-共用) 本文转自屠夫章哥 51CTO博客,原文链接:http://blog.51cto.com/4259297/1860246,如需转载请自行联系原作者

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

Docker学习系列 之etcd(一)etcd简介

Etcd按照官方介绍 Etcd is a distributed, consistentkey-valuestore forshared configurationandservice discovery etcd 是一个分布式一致性键值存储,用于共享配置和服务发现,专注于: 简单: 良好定义的,面向用户的API (gRPC) 安全: 带有可选客户端证书认证的自动 TLS 快速: 测试验证,每秒 10000 写入 可靠: 使用Raft适当分布 etcd是Go编写,并使用 Raft 一致性算法来管理高可用复制日志。 为什么需要 Etcd 所有的分布式系统,都面临的一个问题是多个节点之间的数据共享问题,这个和团队协作的道理是一样的,成员可以分头干活,但总是需要共享一些必须的信息,比如谁是 leader, 都有哪些成员,依赖任务之间的顺序协调等。所以分布式系统要么自己实现一个可靠的共享存储来同步信息(比如 Elasticsearch ),要么依赖一个可靠的共享存储服务,而 Etcd 就是这样一个服务。 Etcd 提供什么能力 Etcd 主要提供以下能力,已经熟悉 Etcd 的读者可以略过本段。 提供存储以及获取数据的接口,它通过协议保证 Etcd 集群中的多个节点数据的强一致性。用于存储元信息以及共享配置。 提供监听机制,客户端可以监听某个key或者某些key的变更。用于监听和推送变更。 提供key的过期以及续约机制,客户端通过定时刷新来实现续约。用于集群监控以及服务注册发现。 提供原子的CAS(Compare-and-Swap)和 CAD(Compare-and-Delete)支持(v2通过接口参数实现,v3通过批量事务实现)。用于分布式锁以及leader选举。 Etcd 如何实现一致性的 Etcd使用Raft协议来维护集群内各个节点状态的一致性。简单说,Etcd集群是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证每个节点维护的数据是一致的。 如图所示,每个Etcd节点都维护了一个状态机,并且,任意时刻至多存在一个有效的主节点。主节点处理所有来自客户端写操作,通过Raft协议保证写操作对状态机的改动会可靠的同步到其他节点。 Etcd工作原理核心部分在于Raft协议。本节接下来将简要介绍Raft协议,具体细节请参考其[论文]。 Raft协议正如论文所述,确实方便理解。主要分为三个部分:选主,日志复制,安全性。 1) 选主 Raft协议是用于维护一组服务节点数据一致性的协议。这一组服务节点构成一个集群,并且有一个主节点来对外提供服务。当集群初始化,或者主节点挂掉后,面临一个选主问题。集群中每个节点,任意时刻处于Leader, Follower, Candidate这三个角色之一。选举特点如下: 当集群初始化时候,每个节点都是Follower角色; 集群中存在至多1个有效的主节点,通过心跳与其他节点同步数据; 当Follower在一定时间内没有收到来自主节点的心跳,会将自己角色改变为Candidate,并发起一次选主投票;当收到包括自己在内超过半数节点赞成后,选举成功;当收到票数不足半数选举失败,或者选举超时。若本轮未选出主节点,将进行下一轮选举(出现这种情况,是由于多个节点同时选举,所有节点均为获得过半选票)。 Candidate节点收到来自主节点的信息后,会立即终止选举过程,进入Follower角色。 为了避免陷入选主失败循环,每个节点未收到心跳发起选举的时间是一定范围内的随机值,这样能够避免2个节点同时发起选主。 2) 日志复制 所谓日志复制,是指主节点将每次操作形成日志条目,并持久化到本地磁盘,然后通过网络IO发送给其他节点。其他节点根据日志的逻辑时钟(TERM)和日志编号(INDEX)来判断是否将该日志记录持久化到本地。当主节点收到包括自己在内超过半数节点成功返回,那么认为该日志是可提交的(committed),并将日志输入到状态机,将结果返回给客户端。 这里需要注意的是,每次选主都会形成一个唯一的TERM编号,相当于逻辑时钟。每一条日志都有全局唯一的编号。 主节点通过网络IO向其他节点追加日志。若某节点收到日志追加的消息,首先判断该日志的TERM是否过期,以及该日志条目的INDEX是否比当前以及提交的日志的INDEX跟早。若已过期,或者比提交的日志更早,那么就拒绝追加,并返回该节点当前的已提交的日志的编号。否则,将日志追加,并返回成功。 当主节点收到其他节点关于日志追加的回复后,若发现有拒绝,则根据该节点返回的已提交日志编号,发生其编号下一条日志。 主节点像其他节点同步日志,还作了拥塞控制。具体地说,主节点发现日志复制的目标节点拒绝了某次日志追加消息,将进入日志探测阶段,一条一条发送日志,直到目标节点接受日志,然后进入快速复制阶段,可进行批量日志追加。 按照日志复制的逻辑,我们可以看到,集群中慢节点不影响整个集群的性能。另外一个特点是,数据只从主节点复制到Follower节点,这样大大简化了逻辑流程。 3) 安全性 截止此刻,选主以及日志复制并不能保证节点间数据一致。试想,当一个某个节点挂掉了,一段时间后再次重启,并当选为主节点。而在其挂掉这段时间内,集群若有超过半数节点存活,集群会正常工作,那么会有日志提交。这些提交的日志无法传递给挂掉的节点。当挂掉的节点再次当选主节点,它将缺失部分已提交的日志。在这样场景下,按Raft协议,它将自己日志复制给其他节点,会将集群已经提交的日志给覆盖掉。 这显然是不可接受的。 其他协议解决这个问题的办法是,新当选的主节点会询问其他节点,和自己数据对比,确定出集群已提交数据,然后将缺失的数据同步过来。这个方案有明显缺陷,增加了集群恢复服务的时间(集群在选举阶段不可服务),并且增加了协议的复杂度。 Raft解决的办法是,在选主逻辑中,对能够成为主的节点加以限制,确保选出的节点已定包含了集群已经提交的所有日志。如果新选出的主节点已经包含了集群所有提交的日志,那就不需要从和其他节点比对数据了。简化了流程,缩短了集群恢复服务的时间。 这里存在一个问题,加以这样限制之后,还能否选出主呢?答案是:只要仍然有超过半数节点存活,这样的主一定能够选出。因为已经提交的日志必然被集群中超过半数节点持久化,显然前一个主节点提交的最后一条日志也被集群中大部分节点持久化。当主节点挂掉后,集群中仍有大部分节点存活,那这存活的节点中一定存在一个节点包含了已经提交的日志了。 至此,关于Raft协议的简介就全部结束了。 Etcd的使用场景 和ZK类似,Etcd有很多使用场景,包括: 配置管理 服务注册于发现 选主 应用调度 分布式队列 分布式锁 Etcd,Zookeeper,Consul 比较 这三个产品是经常被人拿来做选型比较的。 Etcd 和 Zookeeper 提供的能力非常相似,都是通用的一致性元信息存储,都提供watch机制用于变更通知和分发,也都被分布式系统用来作为共享信息存储,在软件生态中所处的位置也几乎是一样的,可以互相替代的。二者除了实现细节,语言,一致性协议上的区别,最大的区别在周边生态圈。Zookeeper 是apache下的,用java写的,提供rpc接口,最早从hadoop项目中孵化出来,在分布式系统中得到广泛使用(hadoop, solr, kafka, mesos 等)。Etcd 是coreos公司旗下的开源产品,比较新,以其简单好用的rest接口以及活跃的社区俘获了一批用户,在新的一些集群中得到使用(比如kubernetes)。虽然v3为了性能也改成二进制rpc接口了,但其易用性上比 Zookeeper 还是好一些。 而 Consul 的目标则更为具体一些,Etcd 和 Zookeeper 提供的是分布式一致性存储能力,具体的业务场景需要用户自己实现,比如服务发现,比如配置变更。而Consul 则以服务发现和配置变更为主要目标,同时附带了kv存储。 本文转自 ponpon_ 51CTO博客,原文链接:http://blog.51cto.com/liuxp0827/1898320,如需转载请自行联系原作者

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

Android开发学习笔记:Service的远程调用

在Andorid平台中,各个组件运行在自己的进程中,他们之间是不能相互访问的,但是在程序之间是不可避免的要传递一些对象,在进程之间相互通信。为了实现进程之间的相互通信,Andorid采用了一种轻量级的实现方式RPC(Remote Procedure Call远程进程调用)来完成进程之间的通信,并且Android通过接口定义语言(Andorid Interface Definition Language ,AIDL)来生成两个进程之间相互访问的代码,例如,你在Activity里的代码需要访问Service中的一个方法,那么就可以通过这种方式来实现了。 AIDL是Android的一种接口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象。 AIDL RPC机制是通过接口来实现的,类似Windows中的COM或者Corba,但他是轻量级的,客户端和被调用实现之间是通过代理模式实现的,代理类和被代理类实现同一个接口Ibinder接口。 下面是实现Activity访问Service例子的步骤: 一.创建.aidl文件 AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。重要的是必须导入导入除了内建类型(例如:int,boolean等)外的任何其他类型,哪怕是这些类型是在与接口相同的包中。具体的要求如下: JAVA基本数据类型不需要导入 String,List,Map和CharSequence不需要导入 使用Eclipse的ADT插件创建一个BookInfo.aidl文件,该文件有4个方法: setName(String name)设置图书的书名,setPrice(int price)设置图书的价格,setPublish(String pname)设置图书的出版社和String display()显示图书的信息. BookInfo.aidl文件 packagecom.android.aidl; //BookInfo接口 interfaceBookInfo{ voidsetName(Stringname); voidsetPrice(intprice); voidssetPublish(Stringpname); //显示图书的信息 Stringdisplay(); } 创建好BookInfo.aidl文件,系统会自动在gen目录下生成Java接口文件BookInfo.java 二.实现AIDL文件生成的JAVA接口 AIDL会生成一个和.aidl文件同名的JAVA接口文件,该接口中有一个静态抽象内部类Stub,该类中声明了AIDL文件中定义的所有方法,其中有一个重要的方法是asInterface(),该方法通过代理模式返回JAVA接口的实现我们可以定义一个实现类,BookImpl,该类继承Stub类,实现我们定义的4个方法 packagecom.android.aidl; importandroid.os.RemoteException; publicclassBookInfoImplextendsBookInfo.Stub{ //声明三个个变量 privateintprice; privateStringname,pname; //显示书名,价格,出版社 publicStringdisplay()throwsRemoteException{ return"书名:"+name+";价格:"+price+";出版社:"+price; } @Override //设置书名 publicvoidsetName(Stringname)throwsRemoteException{ //TODOAuto this.name=name; } @Override //设置价格 publicvoidsetPrice(intprice)throwsRemoteException{ //TODOAuto-generatedmethodstub this.price=price; } @Override //设置出版社 publicvoidsetPublish(Stringpname)throwsRemoteException{ //TODOAuto this.pname=pname; } } 三.向客户端暴露接口 现在已经实现了BookInfo接口,接下来要将该接口暴露给客户端调用。一般通过定义一个Service来实现,在Service的onBind()方法中返回该接口,当我们绑定该接口时调用该方法。 packagecom.android.aidl; importcom.android.aidl.BookInfo.Stub; importandroid.app.Service; importandroid.content.Intent; importandroid.os.IBinder; publicclassRemoteServiceextendsService{ //声明BookInfo接口 privateStubbookifo=newBookInfoImpl(); publicIBinderonBind(Intentintent){ returnbookifo; } } 四.在客户端调用 定义一个Activity来绑定远程Service,获得BookInfo接口,通过RPC机制调用接口中的方法。 packagecom.android.aidl; importandroid.app.Activity; importandroid.app.Service; importandroid.content.ComponentName; importandroid.content.Intent; importandroid.content.ServiceConnection; importandroid.os.Bundle; importandroid.os.IBinder; importandroid.os.RemoteException; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; importandroid.widget.Toast; publicclassMainActivityextendsActivity{ //声明IPerson接口 privateBookInfobookInfo; //声明Button privateButtonbtn; //实例化ServiceConnection privateServiceConnectionconn=newServiceConnection(){ @Override synchronizedpublicvoidonServiceConnected(ComponentNamename,IBinderservice){ //获得IPerson接口 bookInfo=BookInfo.Stub.asInterface(service); if(bookInfo!=null) try{ //RPC方法调用 bookInfo.setName("GoogleAndroidSDK开发范例大全"); bookInfo.setPrice(55); bookInfo.setPublish("人民邮电出版社"); Stringmsg=bookInfo.display(); //显示方法调用返回值 Toast.makeText(MainActivity.this,msg,Toast.LENGTH_LONG) .show(); }catch(RemoteExceptione){ e.printStackTrace(); } } @Override publicvoidonServiceDisconnected(ComponentNamename){ } }; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); //设置当前视图布局 setContentView(R.layout.main); //实例化Button btn=(Button)findViewById(R.id.Button1); //为Button添加单击事件监听器 btn.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ //实例化Intent Intentintent=newIntent(); //设置IntentAction属性 intent.setAction("com.android.aidl.action.MY_REMOTE_SERVICE"); //绑定服务 bindService(intent,conn,Service.BIND_AUTO_CREATE); } }); } } 五.main.xml和AndroidManifest.xml文件 main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:text="远程调用Service" android:id="@+id/Button1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> 在AndroidManifest.xml文件16~20声明Service <?xmlversion="1.0"encoding="utf-8"?> <manifestxmlns:android="http://schemas.android.com/apk/res/android" package="com.android.aidl" android:versionCode="1" android:versionName="1.0"> <uses-sdkandroid:minSdkVersion="10"/> <applicationandroid:icon="@drawable/icon"android:label="@string/app_name"> <activityandroid:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <serviceandroid:name="RemoteService"> <intent-filter> <actionandroid:name="com.android.aidl.action.MY_REMOTE_SERVICE"/> </intent-filter> </service> </application> </manifest> 效果图: 本文转自 lingdududu 51CTO博客,原文链接:http://blog.51cto.com/liangruijun/653344

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

Android开发学习笔记:对话框浅析

对话框式程序运行中弹出的窗口。Android系统中有四种默认的对话框:警告对话框AlertDialog、进度对话框ProgressDialog、日期选择对话框DatePickerDialog以及时间选择对话框TimePickerDialog。除此之外,我们自定义自已的dialog。 一.警告对话框(AlertDialog) Android系统中最常用的对话框是AlertDialog,它是一个提示窗口,需要用户作出选择的。一般会有几个按钮、标题信息、提示信息等。 在程序中创建AlertDialog的步骤: 1.获得AlertDialog的静态内部类Builder对象,由该类来创建对话框,Builder所提供的方法如下: setTitle():给对话框设置title. setIcon():给对话框设置图标。 setMessage():设置对话框的提示信息 setItems():设置对话框要显示的一个list,一般用于要显示几个命令时 setSingleChoiceItems():设置对话框显示一个单选的List setMultiChoiceItems():用来设置对话框显示一系列的复选框。 setPositiveButton():给对话框添加”Yes”按钮。 setNegativeButton():给对话框添加”No”按钮。 2.调用Builder的create( )方法 3.调用AlertDialog的show( )方法显示对话框 下面是一个提示信息对话框的实例: AlertDialogActivity.java package com.android.dialog.activity; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class AlertDialogActivity extends Activity { private TextView tv; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView)findViewById(R.id.TextView_1); btn = (Button)findViewById(R.id.Button_1); //实例化AlertDialog.Builder对象 final AlertDialog.Builder builder = new AlertDialog.Builder(this); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { //设置提示信息,确定按钮 builder.setMessage("真的要删除该文件吗?").setPositiveButton("是", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { tv.setText("成功删除"); } //设置取消按钮 }).setNegativeButton("否", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { tv.setText("取消删除"); } }); //创建对话框 AlertDialog ad = builder.create(); //显示对话框 ad.show(); } } ); } } main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/TextView_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试AlertDialog" /> <Button android:id="@+id/Button_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除文件" /> </LinearLayout> 效果图: 二.进度对话框(ProgressDialog) 在程序中创建ProgressDialog的步骤: 1.覆盖Activity的onCreateDialog( )方法,并在其中创建对话框 2.调用Activity的showDialog( )方法,显示进度对话框 下面是一个提示进度对话框的实例: ProgressDialogActivity.java packagecom.android.progress.activity; importandroid.app.Activity; importandroid.app.Dialog; importandroid.app.ProgressDialog; importandroid.content.DialogInterface; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; publicclassProgressDialogActivityextendsActivity{ privateButtonbtn; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); btn=(Button)findViewById(R.id.Button_1); btn.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ //调用Activity的showDialog()方法,显示进度对话框 showDialog(0); } }); } @Override protectedDialogonCreateDialog(intid){ //对进度对话框进行实例化 ProgressDialogdialog=newProgressDialog(this); //设置显示的标题 dialog.setTitle("测试ProgressDialog"); dialog.setIndeterminate(true); dialog.setMessage("程序正在Loading..."); dialog.setCancelable(true); dialog.setButton(Dialog.BUTTON_POSITIVE,"确定", newDialogInterface.OnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ dialog.cancel(); } } ); returndialog; } } main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/Button_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试ProgressDialog" /> </LinearLayout> 效果图: 三.日期,时间选择对话框(DatePickerDialog、TimePickerDialog) 在程序中创建日期,时间选择对话框的步骤: 1.覆盖Activity的onCreateDialog( )方法,并在其中创建对话框 2.分别在OnDateSetListener的onDateSet( )方法和OnTimeSetListener( )的onTimeSet( )事件方法中更改日期,时间 3.调用Activity的showDialog( )方法,显示进度对话框 MainActivity.java packagecom.android.datatime.activity; importjava.util.Calendar; importandroid.app.Activity; importandroid.app.DatePickerDialog; importandroid.app.Dialog; importandroid.app.TimePickerDialog; importandroid.app.DatePickerDialog.OnDateSetListener; importandroid.app.TimePickerDialog.OnTimeSetListener; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; importandroid.widget.DatePicker; importandroid.widget.TextView; importandroid.widget.TimePicker; publicclassMainActivityextendsActivity{ privateButtonbtn1,btn2; privateTextViewtv_1,tv_2; privateCalendarc;//获得日历实例 privateintm_year,m_month,m_day; privateintm_hour,m_minute; publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); btn1=(Button)findViewById(R.id.Button_1); btn2=(Button)findViewById(R.id.Button_2); c=Calendar.getInstance(); m_year=c.get(Calendar.YEAR); m_month=c.get(Calendar.MONTH); m_day=c.get(Calendar.DAY_OF_MONTH); m_hour=c.get(Calendar.HOUR); m_minute=c.get(Calendar.MINUTE); tv_1=(TextView)findViewById(R.id.TextView_1); tv_1.setText(m_year+":"+(m_month+1)+":"+m_day);//设置TextView里的内容为日期 tv_2=(TextView)findViewById(R.id.TextView_2); tv_2.setText(m_hour+":"+m_minute);//设置TextView里的内容为时间 btn1.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ showDialog(0);//显示日期对话框 } }); btn2.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ showDialog(1);//显示时间对话框 } }); } //调用Activity的showDialog()方法显示进对话框 protectedDialogonCreateDialog(intid){ if(id==0) returnnewDatePickerDialog(this,l1,m_year,m_month,m_day); else returnnewTimePickerDialog(this,l2,m_hour,m_minute,false); } //设置日期监听器 privateOnDateSetListenerl1=newOnDateSetListener(){ publicvoidonDateSet(DatePickerview,intyear,intmonthOfYear, intdayOfMonth){ m_year=year; m_month=monthOfYear; m_day=dayOfMonth; tv_1.setText(m_year+":"+(m_month+1)+":"+m_day);//为TextView设置文本内容,重新显示日期 } }; //设置时间监听器 privateOnTimeSetListenerl2=newOnTimeSetListener(){ publicvoidonTimeSet(TimePickerview,inthourOfDay,intminute){ m_hour=hourOfDay; m_minute=minute; tv_2.setText(m_hour+":"+m_minute);//为TextView设置文本内容,重新显示时间 } }; } main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:text="" android:id="@+id/TextView_1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="" android:id="@+id/TextView_2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:text="修改日期" android:id="@+id/Button_1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:text="修改时间" android:id="@+id/Button_2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> 效果图: 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/641365

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

Android开发学习笔记:EditText的属性介绍

EditText继承TextView,所以EditText具有TextView的属性特点,下面主要介绍一些EditText的特有的输入法的属性特点 android:layout_gravity="center_vertical":设置控件显示的位置:默认top,这里居中显示,还有bottom android:hin :Text为空时显示的文字提示信息,可通过textColorHint设置提示信息的颜色。 android:singleLine :设置单行输入,一旦设置为 true ,则文字不会自动换行。 android:gray="top" :多行中指针在第一行第一位置 et.setSelection(et.length()); :调整光标到最后一行 android:autoText :自动拼写帮助。这里单独设置是没有效果的,可能需要其他输入法辅助才行 android:capitalize :设置英文字母大写类型。设置如下值:sentences仅第一个字母大写;words每一个单词首字母大小,用空格区分单词;characters每一个英文字母都大写。 android:digits :设置允许输入哪些字符。如“1234567890.+-*/%\n()” android:singleLine :是否单行或者多行,回车是离开文本框还是文本框增加新行 android:numeric :如果被设置,该TextView接收数字输入。有如下值设置:integer正整数、signed带符号整数、decimal带小数点浮点数。 android:inputType:设置文本的类型 android:password :密码,以小点”.”显示文本 android:phoneNumber :设置为电话号码的输入方式。 android:editable :设置是否可编辑。仍然可以获取光标,但是无法输入。 android:autoLink=”all” :设置文本超链接样式当点击网址时,跳向该网址 android:textColor = "#ff8c00" :字体颜色 android:textStyle="bold" :字体, bold, italic, bolditalic android:textAlign="center" : EditText 没有这个属性,但 TextView 有 android:textColorHighlight="#cccccc" :被选中文字的底色,默认为蓝色 android:textColorHint="#ffff00" :设置提示信息文字的颜色,默认为灰色 android:textScaleX="1.5" :控制字与字之间的间距 android:typeface="monospace" :字型, normal, sans, serif, monospace android:background="@null" :空间背景,这里没有,指透明 android:layout_weight="1" :权重在控制控件显示的大小时蛮有用的。 android:textAppearance="?android:attr/textAppearanceLargeInverse" :文字外观,这里引用的是系统自带的一个外观,?表示系统是否有这种外观,否则使用默认的外观。 本文转自 lingdududu 51CTO博客,原文链接:http://blog.51cto.com/liangruijun/627350

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

[Android Pro] Java进阶学习:jar打包详解

jar文件听说过吗,没有?或者陌生!好,没关系,这就是我们的第一站:打包发布。 为什么会有这个玩意呢,首先,这是jar的全称:JavaTM Archive (JAR) file,是的,就是java存档文件。这有点类似zip文件,想一想它是干什么的用的呢,压缩!?没错就是要压缩,将我们原先零散的东西放到一下,重新 组织,所有这些目的只有一个:方便!好了,不用管他是怎么压缩的,我们的重点是哪些是我们要压缩的(输入),还有压缩成了什么(输出),进而将它发布(部 署)。 那我们的输入(要压缩的东西)主要是class文件,还有辅助的资源(这其中可能有图片,jsp文件,html文件等等)。 Jar技术在jdk1.1版本中就已存在,在1.2中又有了增强。接下来说说jar的好处吧,这是官方的描述:安全,快速下载,压缩,猎取包,版本化包, 可携。 说了这么多,我们现在开始实施。 先打开命令提示符(win2000或在运行框里执行cmd命令,win98为DOS提示符),输入jar Chelp,然后回车(如果你盘上已经有了jdk1.1或以上版本),看到什么: 用法:jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... 选项: -c 创建新的存档 -t 列出存档内容的列表 -x 展开存档中的命名的(或所有的〕文件 -u 更新已存在的存档 -v 生成详细输出到标准输出上 -f 指定存档文件名 -m 包含来自标明文件的标明信息 -0 只存储方式;未用zip压缩格式 -M 不产生所有项的清单(manifest〕文件 -i 为指定的jar文件产生索引信息 -C 改变到指定的目录,并且包含下列文件: 如果一个文件名是一个目录,它将被递归处理。 清单(manifest〕文件名和存档文件名都需要被指定,按'm' 和 'f'标志指定的相同顺序。 示例1:将两个class文件存档到一个名为 'classes.jar' 的存档文件中: jar cvf classes.jar Foo.class Bar.class 示例2:用一个存在的清单(manifest)文件 'mymanifest' 将 foo/ 目录下的所有文件存档到一个名为 'classes.jar' 的存档文件中: jar cvfm classes.jar mymanifest -C foo/A.txt -C foo/B.txt jar cvf ${baseDir}/plugin.jar -C ${baseDir} com -C ${baseDir} assets jar基本操作: 1. 创建jar文件 jar cf jar-file input-file(s) c---want to Create a JAR file. f---want the output to go to a file rather than to stdout. eg: 1)jar cf myjar.jar query_maintain_insert.htm 2)jar cvf myjar.jar query_maintain_insert.htm v---Produces verbose(详细的) output. 3)jar cvf myjar.jar query_maintain_insert.htm mydirectory 4)jar cv0f myjar.jar query_maintain_insert.htm mydirectory 0---don't want the JAR file to be compressed. 5)jar cmf MANIFEST.MF myjar.jar yahh.txt m---Used to include manifest information from an existing manifest file. 6)jar cMf MANIFEST.MF myjar.jar yahh.txt M---the default manifest file should not be produced. 7)jar cvf myjar.jar * *---create all contents in current directory. 2. 察看jar文件 jar tf jar-file t---want to view the Table of contents of the JAR file. eg: 1)jar vft yahh.jar v---Produces verbose(详细的) output. 3. 提取jar文件 jar xf jar-file [archived-file(s)] x---want to extract files from the JAR archive. eg: 1)jar xf yahh.jar yahh.txt(仅提取文件yahh.txt) 2)jar xf yahh.jar alex/yahhalex.txt(仅提取目录alex下的文件yahhalex.txt) 3)jar xf yahh.jar(提取该jar包中的所有文件或目录) 4. 修改Manifest文件 jar cmf manifest-addition jar-file input-file(s) m---Used to include manifest information from an existing manifest file. 5. 更新jar文件 jar uf jar-file input-file(s) u---want to update an existing JAR file. 分类: Android Pro, Java基础 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/p/5648438.html如需转载请自行联系原作者 demoblog

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

Android学习笔记(二)基础知识(1)

更改文字颜色 文字色: TextView.setTextColor(Color.***); 背景色: Resources resources = getBaseContext().getResources(); Drawable HippoDrawable = resources.getDrawable(R.drawable.white); TextView.setBackgroundDrawable(HippoDrawable); 获取手机分辨率 import android.util.DisplayMetrics DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); String x,y; x=dm.widthPixels; y=dm.heightPixels; 页面转换 在Android应用中,是通过setContentView来实现页面的转换处理的,也就是在不同的layout之间进行切换。 setContentView(R.layout.****); 例子: package com.example.test4; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button b1=(Button)findViewById(R.id.button1); b1.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub jumpToMylayout(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public void jumpToMylayout(){ setContentView(R.layout.mylayout); Button b2 = (Button)findViewById(R.id.button1); b2.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub jumpToLayout(); } }); } public void jumpToLayout(){ setContentView(R.layout.activity_main); Button b1=(Button)findViewById(R.id.button1); b1.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub jumpToMylayout(); } }); } } 调用其他Activity 与上面在不同layout之间转换不同,这里我们需要移交主权到另外一个Activity。 //一个Activity Intent intent=new Intent(); intent.setClass(MainActivity.this,replace.class); startActivity(intent); MainActivity.this.finish(); //另外一个Activity Intent intent=new Intent(); intent.setClass(replace.this, MainActivity.class); startActivity(intent); replace.this.finish(); 不同Activity之间的数据转换 利用Android.os.Bundle对象封装数据的能力,将欲传递的数据或参数,通过Bundle来传递不同Intent之间的数据。 发送: Intent intent=new Intent(); intent.setClass(MainActivity.this,display.class); Bundle bundle = new Bundle(); bundle.putString("num",et1.getText().toString()); bundle.putString("name",et2.getText().toString()); intent.putExtras(bundle); startActivity(intent); 接收: Bundle bundle = this.getIntent().getExtras(); String num = bundle.getString("num"); String name = bundle.getString("name"); 返回数据到前一个Activity 唤起一个Activity: startActivityForResult(intent,0) 重载onActivityResult: protected voidonActivityResult(int requestCode,int resultCode,Intent data){ switch(resultCode){ case RESULT_OK: Bundle bundle=data.getExtras(); //code there break; default: break; } } 返回数据: input.this.setResult(RESULT_OK, intent); 部分代码: //main_activity package com.example.test7; import android.os.Bundle; import android.app.Activity; import android.content.Intent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { TextView tx; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bt=(Button)findViewById(R.id.button1); tx=(TextView)findViewById(R.id.textView1); bt.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub Intent intent = new Intent(); intent.setClass(MainActivity.this, input.class); startActivityForResult(intent,0); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected void onActivityResult(int requestCode,int resultCode,Intent data){ switch(resultCode){ case RESULT_OK: Bundle bundle=data.getExtras(); String str=bundle.getString("str").toString(); tx.setText(str); break; default: break; } } } /** * input.java */ package com.example.test7; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; public class input extends Activity { Intent intent; Bundle bundle; EditText et; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.input); Button bt=(Button)findViewById(R.id.button1); et=(EditText)findViewById(R.id.editText1); intent = this.getIntent(); bundle= new Bundle(); bt.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub bundle.putString("str", et.getText().toString()); intent.putExtras(bundle); input.this.setResult(RESULT_OK, intent); input.this.finish(); } }); } } 本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/archive/2013/05/13/3075259.html,如需转载请自行联系原作者

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

Qt学习之路(38): model-view架构

从这一节开始,我们进入model-view阶段。这一阶段主要还是依据 C++ GUI Programming with Qt4, 2nd Edition。 我们的系统有很多数据显示的需求,比如从数据库中把数据取出,然后以自己的方式显示在我们自己的应用程序的界面中。进行这一操作的典型方式是使用Qt的Item View类。 在早期的Qt版本中,要实现这个功能,我们需要定义一个widget,然后在这个widget中保存一个数据对象,比如是个list,然后我们对这个list进行查找、插入等的操作,或者把修改的地方写回这个list,然后刷新widget进行显示。这个思路很简单,也很清晰,但是对于大型程序,这种设计就显得苍白无力。比如,在一个大型系统中,你的数据可能很大,如果全部存入一个widget的数据对象中,效率会很低,并且这样的设计也很难在widgets之间共享变量,也就是说,如果你要几个组件共享一个数据对象,要么你就要用getter函数公开这个数据对象,要么你就必须把这个数据对象放进不同的组件分别进行维护。 Smalltalk语言发明了一种崭新的实现,用来解决这个问题,这就是著名的MVC模型。对这个模型无需多言,简单来说,这是一个model-view-controller模型,即模型-视图-控制器。在MVC中,模型负责获取需要显示的数据,并且能够存储这些数据的修改。每种数据类型都有它自己对应的模型,但是这些模型提供一个相同的API,用于隐藏内部实现。视图用于将模型数据显示给用户。对于很大的数据,或许只显示一小部分,这样就能很好的提高性能。控制器是模型和视图之间的媒介,将用户的动作解析成对数据的操作,比如查找数据或者修改数据,然后转发给模型执行,最后再将模型中需要被显示的数据直接转发给视图进行显示。 对于Qt而言,它使用的是一个类似于MVC模型的model-view架构。其中,model就相当于MVC架构中的model,而对于控制器部分,Qt使用的是另外的一种抽象,代理delegate。代理被用来提供对item渲染和编辑的控制。对于每种视图,Qt都提供了一个默认的代理,对于大多数应用来说,我们只需要使用这个默认的代理即可。这其中的类关系如下图所示(出自C++ GUI Programming with Qt 4, 2nd Edition) 使用Qt的model-view架构,我们可以让model是取回view所要展示的数据,这样就可以在不降低性能的情形下处理大量数据。并且你可以把一个model注册给多个view,让这些view能够显示同样的数据,也就是为同一个数据提供不同的显示方式。Qt会自动地对这些view保持同步,自动刷新所有的view以显示最新的数据。这样,我们就可以只对model进行修改,view会自动更新。 在少量数据的情形下,我们不需要动用model这样重量级的组件。Qt为了方便起见也提供了item view类,分别是QListWidget,QTableWidget和QTreeWidget,使用这些类可以直接对item进行操作。这种实现很像Qt早期版本,组件中包含了相应的item,例如QTableWidget中包含有QTableWidgetItem等。但是对于很大的数据,我们则需要使用Qt的view类,比如QListView,QTabelView和QTreeView,同时需要提供一个model,可以是自定义model,也可以是Qt预置的model。例如,如果数据来自数据库,那么你可以使用QTabelView和QSqlTableModel这两个类。 今天就说这些,下次我们将开始进入对model-view架构的具体介绍。 本文转自 FinderCheng 51CTO博客,原文链接: http://blog.51cto.com/devbean/250566

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

[Android学习笔记一] ContentProvider组件开发详解

Android四大组件中ContentProvider组件相对Activity,BroadcastReceiver, Service而言比较独立,而且多数使用的时候都是在用Android系统提供的关于邮件,媒体,短信,联系人等ContentProvider。通常开发的应用较少提供ContentProvider组件,一般会在同家公司的产品或则内容适配方面会用到ContentProvider组件。 本文主要讲述开发和使用ContentProvider组件的通用方式。其中包含:代码模板,权限设置,ContentResolver 等。 1 . 开发ContentProvider基本思路 1.1.ContentProvider组件需要对外开放的内容 授权字符串(Authoritis),内容类型,数据字段名称, 访问权限说明等 1.2. 编写ContentProvider组件类继承ContentProvider类,实现其中的CRUD操作方法 1.3. AndroidMainifest.xml中配置<provider>元素,指定控制属性,其中包含是否对外部应用可用,读写权限,Authorities字符串等 1.4. 应用本身或者外部应用使用ContentProvider组件 2. 开发一个基于SQLite数据库提供Note表信息的ContentProvider 2.1 开发一个独立的ContentProvider组件代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 package secondriver.xprovider; import android.content.*; import android.content.pm.ProviderInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; import java.text.SimpleDateFormat; import java.util.Date; /** *Author:secondriver *Date:2015/11/4 */ public class XProvider extends ContentProvider{ private static final StringTAG= "XProvider" ; //授权字符串 //授权Uri //Note公开信息 public static final class Note implements BaseColumns{ //Note表名 //Note表的内容Uri //内容类型 //Note表字段 //Uri匹配码 } //Uri匹配器 public static UriMatcheruriMatcher; static { uriMatcher= new UriMatcher(UriMatcher.NO_MATCH); //添加Uri匹配内容 } private XSQLiteHelperhelper; @Override public boolean onCreate(){ //ContentProvider组件创建时做的工作 return true ; } @Override public Cursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){ return cursor; } @Override public StringgetType(Uriuri){ return null ; } @Override public Uriinsert(Uriuri,ContentValuesvalues){ return null ; } @Override public int delete(Uriuri,Stringselection,String[]selectionArgs){ return effectRows; } @Override public int update(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){ return effectRows; } public static class XSQLiteHelper extends SQLiteOpenHelper{ public static final StringDATA_BASE_NAME= "xprovider.sqlite" ; public static final int DATA_BASE_VERSION= 1 ; public static volatile XSQLiteHelperxsqLiteHelper; public static XSQLiteHelpergetXsqLiteHelper(Contextcontext){ //实例化XSQLiteHelp对象 return xsqLiteHelper; } public XSQLiteHelper(Contextcontext,Stringname,SQLiteDatabase.CursorFactoryfactory, int version){ super (context,name,factory,version); } @Override public void onCreate(SQLiteDatabasedb){ //数据库初始化 } @Override public void onUpgrade(SQLiteDatabasedb, int oldVersion, int newVersion){ } } } 上面代码提供了一个SQLiteOpenHelper的实现,实际开发中可个会更具具体应用来提供给ContentProvider组件。 中文注释部分可能需要填充具体代码,比如权限可以更具实际情况来定义,表字段根据要提供的内容信息来公开字段名并且需要在文档中说明。 2.1 准备Note表信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 //Note公开信息 public static final class Note implements BaseColumns{ //Note表名 public static final StringTABLE_NAME= "note" ; //Note表的内容Uri public static final UriCONTENT_URI=Uri.withAppendedPath(AUTHORITY_URI, "note" ); //建议使用格式如:type/subtype=>vnd.android.cursor.dir/vnd.<name>.<type>name=packagenametype=tablename public static final StringCONTENT_DIR_TYPE= "vnd.android.cursor.dir/vnd.secondriver.xprovider.note" ; public static final StringCONTENT_ITEM_TYPE= "vnd.android.cursor.item/vnd.secondriver.xprovider.note" ; //ID主键 public static final StringID_COLUMN=_ID; //内容列 public static final StringCONTENT_COLUMN= "CONTENT" ; //创建时间列表 public static final StringCREATED_COLUMN= "CREATED" ; //标识列 public static final StringFLAG_COLUMN= "FLAG" ; //状态列 public static final StringSTATUS_COLUMN= "STATUS" ; //Uri匹配码 private static final int NOTE_ITEM= 0x21 ; private static final int NOTE_DIR= 0x22 ; } 说明:Uri匹配码声明为Note类的常量这样可以方便对照,一个ContentProvider组件中声明多个表公开类,比如User类,这样就比较容易区分操作的是那个表的内容。 2.2 Note类外其它的具体代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 package secondriver.xprovider; import android.content.*; import android.content.pm.ProviderInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; import java.text.SimpleDateFormat; import java.util.Date; /** *Author:secondriver *Date:2015/11/4 */ public class XProvider extends ContentProvider{ private static final StringTAG= "XProvider" ; //授权字符串 public static final StringAUTHORITY= "secondriver.xprovider.X_PROVIDER" ; //授权Uri public static final UriAUTHORITY_URI=Uri.parse( "content://" +AUTHORITY); //Uri匹配器 public static UriMatcheruriMatcher; static { uriMatcher= new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, "note/#" ,Note.NOTE_ITEM); uriMatcher.addURI(AUTHORITY, "note" ,Note.NOTE_DIR); } private XSQLiteHelperhelper; @Override public boolean onCreate(){ helper=XSQLiteHelper.getXsqLiteHelper(getContext()); return true ; } @Override public Cursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder){ SQLiteQueryBuilderbuilder= new SQLiteQueryBuilder(); switch (uriMatcher.match(uri)){ case Note.NOTE_DIR: break ; case Note.NOTE_ITEM: builder.appendWhere(Note.ID_COLUMN+ "=" +uri.getPathSegments().get( 1 )); break ; default : throw new IllegalArgumentException( "UnsupportedURI:" +uri); } builder.setTables(Note.TABLE_NAME); Cursorcursor=builder.query(helper.getReadableDatabase(),projection,selection,selectionArgs, null , null ,sortOrder); return cursor; } @Override public StringgetType(Uriuri){ switch (uriMatcher.match(uri)){ case Note.NOTE_ITEM: return Note.CONTENT_ITEM_TYPE; case Note.NOTE_DIR: return Note.CONTENT_DIR_TYPE; default : throw new IllegalArgumentException( "UnsupportedURI:" +uri); } } @Override public Uriinsert(Uriuri,ContentValuesvalues){ SQLiteDatabasedb=helper.getWritableDatabase(); switch (uriMatcher.match(uri)){ case Note.NOTE_DIR: long noteId=db.insert(Note.TABLE_NAME, null ,values); if (noteId> 0 ){ //插入成功 UrinewUri=ContentUris.withAppendedId(uri,noteId); getContext().getContentResolver().notifyChange(newUri, null ); return newUri; } else { //插入失败 return null ; } default : throw new IllegalArgumentException( "UnsupportedURI:" +uri); } } @Override public int delete(Uriuri,Stringselection,String[]selectionArgs){ SQLiteDatabasedb=helper.getWritableDatabase(); int effectRows= 0 ; switch (uriMatcher.match(uri)){ case Note.NOTE_DIR: effectRows=db.delete(Note.TABLE_NAME,selection,selectionArgs); break ; case Note.NOTE_ITEM: long id=ContentUris.parseId(uri); StringwhereClause=Note.ID_COLUMN+ "=" +String.valueOf(id); if (!TextUtils.isEmpty(selection)){ whereClause=whereClause+ "AND" +selection; } effectRows=db.delete(Note.TABLE_NAME,whereClause,selectionArgs); break ; default : throw new IllegalArgumentException( "UnsupportedURI:" +uri); } getContext().getContentResolver().notifyChange(uri, null ); return effectRows; } @Override public int update(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs){ SQLiteDatabasedb=helper.getWritableDatabase(); int effectRows= 0 ; switch (uriMatcher.match(uri)){ case Note.NOTE_DIR: effectRows=db.update(Note.TABLE_NAME,values,selection,selectionArgs); break ; case Note.NOTE_ITEM: long nodeId=ContentUris.parseId(uri); StringwhereClause=Note.ID_COLUMN+ "=" +String.valueOf(nodeId); if (!TextUtils.isEmpty(selection)){ whereClause=whereClause+ "AND" +selection; } effectRows=db.update(Note.TABLE_NAME,values,whereClause,selectionArgs); break ; default : throw new IllegalArgumentException( "UnsupportedURI:" +uri); } return effectRows; } public static class XSQLiteHelper extends SQLiteOpenHelper{ public static final StringDATA_BASE_NAME= "xprovider.sqlite" ; public static final int DATA_BASE_VERSION= 1 ; public static volatile XSQLiteHelperxsqLiteHelper; public static XSQLiteHelpergetXsqLiteHelper(Contextcontext){ if ( null ==xsqLiteHelper){ synchronized (XSQLiteHelper. class ){ if ( null ==xsqLiteHelper){ xsqLiteHelper= new XSQLiteHelper(context,DATA_BASE_NAME, null ,DATA_BASE_VERSION); } } } return xsqLiteHelper; } public XSQLiteHelper(Contextcontext,Stringname,SQLiteDatabase.CursorFactoryfactory, int version){ super (context,name,factory,version); } @Override public void onCreate(SQLiteDatabasedb){ db.beginTransaction(); try { db.execSQL( new StringBuilder( "createtableifnotexists" ) .append(Note.TABLE_NAME) .append( "(" ) .append(Note.ID_COLUMN) .append( "integerprimarykeyautoincrement," ) .append(Note.CONTENT_COLUMN) .append( "varchar," ) .append(Note.CREATED_COLUMN) .append( "varchar," ) .append(Note.FLAG_COLUMN) .append( "varchar," ) .append(Note.STATUS_COLUMN) .append( "varchar" ) .append( ")" ) .toString() ); SimpleDateFormatsimpleDateFormat= new SimpleDateFormat( "yyyy-MM-ddHH:mm:ss" ); for ( int i= 0 ;i< 50 ;i++){ ContentValuescv= new ContentValues(); cv.put(Note.CONTENT_COLUMN, "Note内容" +i); cv.put(Note.CREATED_COLUMN,simpleDateFormat.format( new Date())); cv.put(Note.FLAG_COLUMN,i); cv.put(Note.STATUS_COLUMN,i); db.insert(Note.TABLE_NAME, null ,cv); } db.setTransactionSuccessful(); } catch (SQLiteExceptione){ Log.e(TAG,e.getMessage()); } finally { db.endTransaction(); } } @Override public void onUpgrade(SQLiteDatabasedb, int oldVersion, int newVersion){ } } } 3. 在AndroidMainfest.xml清单文件中声明ContentProvider组件 3.1 声明XProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!-- 按如下顺序验证权限 android:grantUriPermssions:Temporarypermissionflag. android:permission:Singleprovider-wideread/writepermission. android:readPermission:Provider-widereadpermission. android:writePermission:Provider-widewritepermission. --> < provider android:permission = "secondriver.xprovider.permission.X_PROVIDER" android:authorities = "secondriver.xprovider.X_PROVIDER" android:name = ".XProvider" android:exported = "true" > <!-- 子元素: grant-uri-permission:Uri临时访问授权 path-permission:Uri细粒度控制读写权限 --> </ provider > 3.2 定义权限 清单文件中声明XProvider的时候使用到了内容读写权限 “secondriver.xprovider.permission.X_PROVIDER” 因此该权限需要清单文件中定义。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 < permission android:name = "secondriver.xprovider.permission.X_PROVIDER" android:label = "xProvider的读写权限" android:description = "@string/permission_x_provider_desc" android:protectionLevel = "normal" /> <!-- <permission android:name="secondriver.xprovider.permission.READ_X_PROVIDER" android:label="xProvider的读权限" android:description="@string/permission_read_x_provider_desc" android:protectionLevel="normal"/> <permission android:name="secondriver.xprovider.permission.WRITE_X_PROVIDER" android:label="xProvider的写权限" android:description="@string/permission_write_x_provider_desc" android:protectionLevel="normal"/> --> 权限定义时label属性的值通常为”XXX的权限“,description属性的值通常是”允许该应用干什么,授权有XXX的危害“。比如:label =发送持久广播权限 description=允许该应用发送持久广播,此类消息在广播结束后仍会保留。过多使用会占用手机过多内容,从而降低其速度或稳定性。 3.3 权限说明 默认情况下Provider是没有权限控制的,因此一旦exported=true,那么外部其它应用都可以访问到Provider提供的内容,为了更加安全,有效,范围合适的控制需要添加权限控制。 Provider权限分为: 读写的Provider层权限 读写分开的Provider层权限 Path层权限 临时授权 四种权限从上往下优先级越高。 Path层权限:是对于Uri的更具细粒度的权限控制,provider元素的子元素中可以配置grant-ui-permission和path-permission 。 临时授权:provider元素属性grantUriPermissions=true时系统将授予应用临时权限访问完整的Provider,覆盖掉Provider和Path层的权限;grantUriPermissions=false时需要在provider元素的子元素中配置一个或者多个grant-uri-permission元素来为指定的Uri的临时访问授权。 另外应用在使用临时授权访问Provider时Provider应用会在返回的Intent中通过的setFlags方法指定FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSION标识,携带一个具有临时授权的Uri供外部应用完成本次内容访问操作。 4. 在本应用和其它应用中使用ContentProvider提供的内容 这里提供在其它应用中使用ContentProvider。在使用外部提供的ContentProvider通常需要了解的内容便是文字1.1部分提到。 4.1 下面是通过一个ListView来展示Note中的”CONTENT“和”CREATED“字段信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 package secondriver.oapp; import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** *Author:secondriver *Date:2015/11/5 */ public class XResolverActivity extends Activity{ private SimpleAdaptermAdapter; private ListViewmListView; private List<Map<String,String>>mData; private final int xproviderLoad= 0x01 ; private LoaderManagerloaderManager; private LoaderManager.LoaderCallbacks<Cursor>callbacks= new LoaderManager.LoaderCallbacks<Cursor>(){ @Override public Loader<Cursor>onCreateLoader( int id,Bundleargs){ CursorLoadercursorLoader= new CursorLoader(getApplicationContext(), Uri.parse( "content://secondriver.xprovider.X_PROVIDER/note" ), new String[]{ "_id" , "CONTENT" , "CREATED" }, null , null , null ); return cursorLoader; } @Override public void onLoadFinished(Loader<Cursor>loader,Cursordata){ int idIndex=data.getColumnIndexOrThrow( "_id" ); int contentIndex=data.getColumnIndexOrThrow( "CONTENT" ); int createdIndex=data.getColumnIndexOrThrow( "CREATED" ); mData.clear(); while (data.moveToNext()){ HashMap<String,String>m= new HashMap<>(); m.put( "CONTENT" ,data.getString(contentIndex)); m.put( "CREATED" ,data.getString(createdIndex)); m.put( "_id" ,data.getString(idIndex)); mData.add(m); } mAdapter.notifyDataSetChanged(); } @Override public void onLoaderReset(Loader<Cursor>loader){ } }; @Override protected void onCreate(BundlesavedInstanceState){ super .onCreate(savedInstanceState); setContentView(R.layout.activity_xresolver); mListView=(ListView)findViewById(android.R.id.list); mData= new ArrayList<>(); mAdapter= new SimpleAdapter( this ,mData,android.R.layout.simple_list_item_2, new String[]{ "CONTENT" , "CREATED" }, new int []{ android.R.id.text1, android.R.id.text2 }); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener( new AdapterView.OnItemClickListener(){ @Override public void onItemClick(AdapterView<?>parent,Viewview, int position, long id){ HashMapm=(HashMap)mData.get(position); StringnoteId=(String)m.get( "_id" ); StringnoteContent=(String)m.get( "CONTENT" ); getContentResolver().delete(Uri.withAppendedPath(Uri.parse( "content://secondriver.xprovider.X_PROVIDER/note" ),noteId), null , null ); mData.remove(position); mAdapter.notifyDataSetChanged(); Toast.makeText(XResolverActivity. this , "Delete:" +noteContent,Toast.LENGTH_LONG).show(); } }); loaderManager=getLoaderManager(); loaderManager.initLoader(xproviderLoad, new Bundle(),callbacks); } @Override protected void onResume(){ super .onResume(); if ( null !=loaderManager){ loaderManager.restartLoader(xproviderLoad, new Bundle(),callbacks); } } @Override protected void onDestroy(){ super .onDestroy(); if ( null !=loaderManager){ loaderManager.destroyLoader(xproviderLoad); } } } 说明:需要额外注意的是代码中的硬编码字符串内容,这些内容正是XProvider类和Note类公开的信息,这些内容通常在ContentProvider组件的使用文档中公开说明的。如果是在应用内部使用XProvider的话,那么就可以直接使用变量名而避免硬编码。如下代码片段所示: 1 2 3 4 5 6 7 8 9 mAdapter= new SimpleAdapter( this ,mData,android.R.layout.simple_list_item_2, new String[]{ XProvider.Note.CONTENT_COLUMN, XProvider.Note.CREATED_COLUMN }, new int []{ android.R.id.text1, android.R.id.text2 }); 由于XProvider的访问需要读写权限,因此需要在清单文件中声明使用的权限。 1 < uses-permission android:name = "secondriver.xprovider.permission.X_PROVIDER" /> 5. ContentProvider组件小结 在开发ContentProvider时尽可能使其具备以下特点: 提供恰当的内容访问范围;ContentProvider组件独立封装;详细的权限,Uri,提供内容的使用说明。 本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1710151,如需转载请自行联系原作者

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

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

用户登录
用户注册