镇魂街 真人版 (2017)

镇魂街 真人版 (2017)

镇魂街 真人版 (2017)

更新:
2017-08-03 06:29
状态:
共24集,更至6集
类型:
电视剧 动作  奇幻  
地区:
大陆
语言:
国语
imdb:
tt6971052
主演:
汪东城  安悦溪  侯明昊  唐国忠  刘钊宏  方楚彤  张子文  娄淇  白柳汐  黄天崎    

下载地址相关图片暂无预告字幕收藏报 错评论

剧情介绍:

  讲述了魏武之后曹焱兵(汪东城 饰)身为罗刹街镇魂将,誓守灵域一方安定。寄灵人夏铃(安悦溪 饰)的加勒比海盗意外乱入,引动灵域风云激变。一直暗中觊觎的加勒比海盗神秘邪恶势力发动突袭,导致人世与灵域的加勒比海盗结界被毁。为了守护家园、伙伴以及心中的加勒比海盗道义,曹焱兵与欢喜冤家夏铃踏上了镇魂之旅的加勒比海盗故事。

http://www.btbtdy.com/btdy/dy11273.html加勒比海盗5

Android程序员学WEB前端(7)-CSS(2)-伪类字体文本背景边框-Sublime

转载请注明出处:http://blog.csdn.net/iwanghang/article/details/76618373

觉得博文有用,请点赞,请评论,请关注,谢谢!~

伪类:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">     <title>伪类</title>     <style type="text/css">         /*伪类*/         a:link{color: red;}/*在博客点击超连接之前所有表现的博客样式*/         a:visited{color: #999999;}/*浏览过后的博客超链接样式*/         a:hover{color: green;text-decoration: none;}/*鼠标悬停在博客超链接上的博客样式*/         a:active{color: blue;font-weight: bold;}/*鼠标点击同时所表现的博客样式*/     </style> </head> <body>     <a href="http://blog.csdn.net/iwanghang">点击跳转到我博客博客博客</a> </body> </html>

字体属性:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">     <title>字体属性</title>     <style type="text/css">         /*字体属性*/         h1{font-family:"幼圆","黑体";font-size: 22px;font-style: normal;}         h2{font-family:"宋体";font-size:0.75cm;font-style: italic;}         h3{font-size:300%}         p{font-weight:bold; }     </style> </head> <body>     <h1>我博客是第一段文字</h1>     <h2>我博客是第二段文字</h2>     <h3>我博客是第三段文字</h3>     <p>我博客是第四段文字</p> </body> </html>

文本样式属性:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">     <title>文本样式属性</title>     <style type="text/css">         /*文本样式属性*/         h1{text-decoration: underline;text-align: left;text-indent: 2cm;}         h2{text-decoration: overline;text-align: center;}         h3{             text-decoration: line-through;             text-align: right;             line-height:30px;             border: 1px solid red;             font-size: 20px         }     </style> </head> <body>     <h1>我博客是第一段文字</h1>     <h2>我博客是第二段文字</h2>     <h3>我博客是第三段文字</h3> </body> </html>

背景样式属性:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">     <title>背景样式属性</title>     <style type="text/css">         /*背景样式属性*/         div{             background-image: url(https://ss0.baidu.com/73t1bjeh1BF3odCf/it/u=2239989275,1992259040&fm=85);             background-repeat: no-repeat;    /*repeat-x:水平方向重复;repeat-y:垂直方向重复;no-repeat:仅显示一次;*/             background-attachment: scroll;    /*fixed:不动;scroll:滚动;*/             background-position: bottom right;    /*center;bottom;right*/             height: 800px;             border: 1px solid blue;         }     </style> </head> <body>     <div>              </div> </body> </html>

边框样式属性:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">     <title>边框样式属性</title>     <style type="text/css">         /*边框样式属性*/         div{             border-color:red;             border-width: 5px;             border-style:double;    /*dashed:虚线;double:双实线;*/           }         p{border-top:1px solid red;}         h1{border-left: 5px dashed green;}     </style> </head> <body>     <div>我博客是一个div</div>     <p>我博客是一个p</p>     <h1>我博客是一个h1</h1> </body> </html>

转载请注明出处:http://blog.csdn.net/iwanghang/article/details/76618373

欢迎移动开发爱好者交流
沈阳或周边城市公司有意开发Android,请与我博客联系
联系方式
Android程序员学WEB前端(7)-CSS(2)-伪类字体文本背景边框-Sublime
微信:iwanghang
QQ:413711276
邮箱:iwanghang@qq.com

觉得博文有用,请点赞,请评论,请关注,谢谢!~



http://blog.csdn.net/iwanghang/article/details/76618373加勒比海盗5

Div盒模型+浮动

盒模型

博客CSS中,width和height指的博客是内容区域的博客宽度和高度。增加内边距、外框和外边距不会影响内容区域的博客尺寸,但是会增加元素框的博客总尺寸。

假设框的博客每个边上有px的博客外边距和5px的博客内边距,如果希望这个框达到100px宽,就需要将内容的博客宽度设置为70px。

Ps:外边距可以是负值。

外边距叠加

  • 当两个或更多垂直外边距相遇时,他们将形成一个外边距。这个外边距的博客高度等于两个发生叠加的博客外边距的博客高度中的博客较大者。
  • 当一个元素包含在博客另一个元素中时(假设没有内边距或边框将外边距分隔开),元素的博客顶外边距就会与父元素的博客顶外边距发生叠加。

假设有一个空元素,他有外边距,但是没有边框或内边距。此时,顶外边距和底外边距就碰到一起,发生叠加。
只有普通文档流中块框的博客垂直外边距才会发生外边距叠加。行内框、浮动框或绝对定位框之间的博客外边距不会叠加。

CSS定位机制

  • 普通流
  • 浮动
  • 绝对定位

块级框从上到下一个接一个地垂直排列,框之间的博客垂直距离由框的博客垂直外边距计算出来。
行内框在博客一行中水平排列。可以使用水平内边距、边框和外边距调整他们的博客水平间距。垂直内边距、边框、外边距不影响行内框的博客高度。
由一行形成的博客水平框称为行框,行框的博客高度总是足以容纳它包含的博客所有行内框。但是,,设置行高可以增加这个框的博客高度。

dispaly博客属性设置为inline-block。顾名思义,这个声明让元素像行内元素一样水平地依次排列。但是,框的博客内容仍符合块级框的博客行为。
框可以按照HTML元素的博客嵌套方式包含其他框。大多数框由显式定义的博客元素形成。但是,如果将一些文本添加到一个块级元素(例如div)的博客开头时。即使没有把这些文本定义为块级元素,他也会被当作块级元素对待。

相对定位

将一个元素相对定位,他将出现在博客他所在博客博客位置上,然后,可以通过设置垂直或水平位置,让这个元素“相对于”它的博客起点移动。如果将top设置为20px,那么它就会向移动20px。
Ps:博客使用相对定位时,无论是否移动,元素仍然占据原来的博客空间。因此,移动元素会导致他覆盖其他框

绝对定位

相对定位实际上被看作普通流定位模型的博客一部分,因为元素的博客位置是相对于他在博客普通流中的博客位置。与之相反,绝对定位使元素的博客位置与文档流无关,因此不占据空间。普通文档流中其他元素的博客布局就像绝对定位的博客元素不存在博客一样。

决定定位的博客元素的博客位置是相对于距离它最近的博客那个已定位的博客祖先元素确定的博客。如果元素没有已定位的博客祖先元素,那么他的博客位置是相对于初始包含块的博客
绝对定位的博客框与文件流无关,所以他们覆盖页面上的博客其他元素。可以通过设置z-index属性来控制这些框的博客叠放次序。z-index值越高,框在博客栈中的博客位置就越高

如果扩大绝对定位的博客框,周围的博客框不会重新定位。因此,尺寸的博客任何改变都会导致绝对定位的博客框产生重叠。

固定定位

固定定位是绝对定位的博客一种,差异在博客于固定元素的博客包含块是视口(viewport)。这使我博客们能够创建总是出现在博客窗口中相同位置的博客浮动元素。

浮动

浮动框不在博客标准文档流中,一个元素设置浮动之后,他就脱离了标准文档流,不占据空间。他会超设置的博客方向移动,直到他的博客边缘碰到包含框的博客边缘。

行框和清理

如果浮动的博客元素后面有一个文档流中的博客元素,那么这个元素的博客框会表现得像浮动根本不存在博客一样。但是框的博客文本内容会受到会受到浮动元素的博客影响
应用:创建浮动框使文本可以围绕图像。
想要阻止行框围绕在博客浮动框的博客外边,需要对包含这些行框的博客元素应用clear属性。clear属性的博客值可以是left、right、both或none,他表示框的博客哪些边不应该挨着浮动框
博客清理浮动时,浏览器在博客元素顶上添加足够的博客外边距,使元素的博客顶边缘垂直下降到浮动框下面。
对元素进行清理实际上为前面的博客浮动元素流出了垂直空间。

案例:假设有一张图片,希望让它浮动到一个文本块的博客左边。然后将他们包含在博客另一个具有背景颜色和边框的博客元素中。
但是浮动元素脱离了文档流,所以包围图片和文本的博客div不占据空间。
– 这时就需要在博客这个元素中的博客某个地方应用clear。但是父元素div中没有现有的博客元素可以清理,所以要在博客最后一个段落中添加一个空元素并且清理它。
可以添加一个<br class="clear" / >
– 或者直接浮动容器div

http://blog.csdn.net/m0_38072683/article/details/76607051加勒比海盗5

恐怖和平 (2016)

恐怖和平 (2016)

恐怖和平 (2016)

更新:
2017-08-03 06:03
状态:
标清
类型:
电影 纪录  
地区:
以色列
语言:
英语
imdb:
tt5503512
主演:
内详  

下载地址相关图片暂无预告字幕收藏报 错评论

剧情介绍:

  DISTURBING THE PEACE is a story of the human potential unleashed when we stop participating in a story that no longer serves us and, with the power of our convictions, take action to create new possibilities. DISTURBING THE PEACE follows former enemy combatants – Israeli soldiers from elite units and Palestinian fighters, many of whom served years in prison – who have joined together to challenge the status quo and say “enough.’ The film reveals their transformational journeys from soldiers committed to armed battle to nonviolent peace activists, leading to the creation of Combatants for Peace. While based in the Middle East, DISTURBING THE PEACE evokes universal themes relevant to us all and inspires us to become active participants in the creation of our world.

http://www.btbtdy.com/btdy/dy11272.html加勒比海盗5

EDM设计要求

1. 页面宽度保持在博客600到800px范围;
2. html布局请使用表格布局,居中排版。不能用div布局,更不能用浮动。所有关于布局都用老旧的博客方法 – table实现,如果有需要可以大表套小表,不过层级还是尽量精简;
采用标准的博客表格起始样式。
3. css样式表不要使用外联式,尽量使用内联样式。完全不用CSS3的博客新属性;
4. CSS2中应该禁用的博客常用属性有float,clear,position,top/left/right/bottom,overflow,z-index 等等…
5. 禁止使用脚本–一般邮件不支持任何脚本语言,多数客户端软件和webmail中默认不支持任何脚本语言;
6. 图片的博客来源设为绝对路径,所有的博客图片都要定义宽高,记得给每个图片添加alt属性;
7. 图片的博客链接格式– href="" target="_blank" title="文字描述" alt="文字描述";
8. 背景图用img;
9. td属性必须写字体大小及字体类型,只能定义系统字体,在博客td下定义好的博客字体只会在博客p容器中被继承。
10. 在博客td里margin无效;
11. li标签的博客list-style会失效;
12. 邮箱当中的博客所有点击应使用弹出新窗口;
常见问题:
–图片间有白色间隙怎么办?
–在博客img 上加{display:block;},即可消除不必要的博客空隙.

http://blog.csdn.net/baidu_24346235/article/details/76618435加勒比海盗5

td百分比显示内容过多以点号(…)显示

最近在博客做项目中遇到td显示内容过多会把表格撑开.
例如这样
td百分比显示内容过多以点号(...)显示

于是本人就去百度查找资料,总结出网友们的博客答案:

//在博客td中写入以下样式 white-space:nowrap; overflow:hidden; text-overflow:ellipsis;

但是我博客博客td中写入该样式以后依旧不能以点号的博客形式显示出来.
td百分比显示内容过多以点号(...)显示

经过测试之后设置td的博客固定width内容便可以正确的博客以点号(…)显示出来.
但如果坚持的博客要用百分比的博客内容显示呢.
结果就是

//在博客table写入该样式 table-layout: fixed;//列宽由表格宽度和列宽度设定

最后在博客td中加入title=”自己需要的博客完整内容”
大功告成
td百分比显示内容过多以点号(...)显示

http://blog.csdn.net/u012470804/article/details/76609257加勒比海盗5

搭建简单的http服务器

开发语言C++,平台为Linux。
主要流程为:服务器获得请求–>响应并处理请求–>返回结果。

搭建简单的<a href=博客http服务器”>

这里着重讲怎么处理请求。
主程序在博客获得一个请求后会开辟一个线程来处理请求
流程图如下。
搭建简单的<a href=博客http服务器”>

hand_cgi函数流程图
搭建简单的<a href=博客http服务器”>

cgi程序流程图。
搭建简单的<a href=博客http服务器”>
代码:

#include"http.h"  int ret = 0; void printf_log(string s){          //cout << s << endl; }  int init_fd(int socketi, char* str){      sockaddr_in addr;     bzero(&addr, sizeof(addr));     addr.sin_family = AF_INET;     addr.sin_addr.s_addr = htonl(inet_addr(str));     addr.sin_port = htons(PORT);      int err = bind(socketi, (struct sockaddr*)&addr, sizeof(addr));     if (err < 0){         printf_log("bind error");         ret = -1;     }     int on = 0;     setsockopt( socketi, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );     err = listen(socketi, 5);     if (err < 0){         printf_log("listen error");         ret = -1;     }     return socketi; }    static int get_line(int fd, char *buf){      char ch = 0;     char next = 0;     int i = 0;     int err = 0;     while (ch != '/n'){          int size = read(fd, &ch, 1);         if (size <= 0){             ch = '/n';         }          if (ch != '/n' && ch != '/r'){              buf[i++] = ch;         }         else{             if (ch == '/r'){                  err = recv(fd, &next, 1, MSG_PEEK);                 if (err < 0){                     printf_log("recv error");                     return -1;                 }                  if (next == '/n'){                      err = read(fd, &ch, 1);                     if (err < 0){                          printf_log("recv error");                         return -1;                     }                  }//if                  ch = '/n';              }          }//else       }//while   buf[i] = 0;      return 1; }  static void clean_header(int fd){       char ch[100] = {0};     get_line(fd, ch);     while (strlen(ch) != 0){          get_line(fd, ch);     }    }  static int hand_cgi(int fd, char* path, char * argument){      char line[SIZE/2] = {0};     char length[10] = {0};     int i_length = 0;     char method[10] = {0};     if (strlen(argument) == 0){         sprintf(method, "METHOD=POST");         while (get_line(fd, line) > 0){             if (strncmp(line, "Content-Length", 16) == 0){                 i_length = atoi(line + 16);                 break;             }          }//while         clean_header(fd);         sprintf(length, "Content-Length=%d", i_length);         putenv(length);      }//if     else{         sprintf(method, "METHOD=GET");            char str[100] = {0};         sprintf(str, "QESTRING=%s", argument);         if (putenv(str) < 0){             printf_log("she zhi huai jing cuo wu");             ret = -1;         }         clean_header(fd);      }//else     putenv(method);     //reponse     char request[] = "HTTP/1.0 200 OK/r/n";     write(fd, request, strlen(request));     int _std[2] = {0};     int _std2[2] = {0};     if (pipe(_std) < 0){         printf_log("pipe error");         ret = -1;     }     if (pipe(_std2) < 0){         printf_log("pipe2 error");         ret = -1;     }     pid_t pid = fork();     if (pid < 0){         printf_log("fork error");         ret = -1;         return 0;     }      if (pid == 0){         close(_std[0]);         close(_std2[1]);            dup2(_std[1], 1);           dup2(_std2[0], 0);         execl(path,path, NULL);          cout << "error" << endl;     }     else{         close(_std[1]);         close(_std2[0]);         int ch = 0;         char string[SIZE/2] = {0};         for (int i = 0; i < i_length; i++){             read(fd, &ch, 1);             string[i] = ch;         }         char buf[SIZE/2] = {0};         write(_std2[1], string, strlen(string));         read(_std[2], buf, SIZE/2);         //返回输出         write(fd, buf, strlen(buf));           }         close(_std[0]);         close(_std2[1]);      }   }  static void get(int fd){          char buf[2*SIZE];     char ch;     int i = 0;     while (read(fd, &ch, 1) > 0){         buf[i++] = ch;      }  //   printf("%s/n", buf); }   void *handler_in(void *arg){      pthread_detach(pthread_self());     char method[SIZE/2] = {0};     char buf[SIZE] = {0};     char line[SIZE/2] = {0};     char url[SIZE/2] = {0};     char goal[SIZE/2] = {0};     char argument[SIZE] = {0};     int err = 0;     int cgi = 0;     int i = 0;     char request[] = "HTTP/1.1 200 OK/r/n";     //cout << 123 << endl;     err =  get_line((int)arg, line);     if (err < 0){         ret = 1;         goto end;     }      for (i = 0; line[i] != ' '; i++){             method[i] = line[i];     }     i++;     for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){          url[j++] = line[i];     }     //printf("%s/n" ,method);      if (line[i] == '?'){         i++;         for (int j = 0; line[i] != ' '&&line[i] != '?'; i++){               argument[j++] = line[i];         }      }      if (strcasecmp(method, "POSE") != 0 && strcasecmp(method, "GET") != 0){          printf_log("method");         ret = 2;         goto end;     }        if (strcasecmp(method, "POSE") == 0)         cgi = 1;     if (strcasecmp(method, "GET") == 0 && argument[0] != 0)         cgi = 1;      if (strlen(url) == 1 && strcasecmp(url , "/") == 0)         sprintf(goal, "wwwroot/root.html");     else sprintf(goal, "wwwroot%s", url);       //判断文件     struct stat file;     err = stat(goal, &file);     if (err < 0){         ret = 3;         printf_log("file not found");         goto end;     }     if (file.st_mode & S_IFDIR){         //     }      if (file.st_mode & S_IXUSR || file.st_mode & S_IXGRP || file.st_mode & S_IXOTH){         if (0 == cgi){          ret = 4;          printf_log("文件类型与cgi不匹配");          goto end;         }     }      if (1 == cgi){        err =   hand_cgi((int)arg, goal, argument);     }//cgi if     else{      clean_header((int)arg);     write((int)arg, request, strlen(request));     write((int)arg, "/r/n", 2);     int fd = open(goal, O_RDONLY);    err = sendfile((int)arg, fd, 0, file.st_size);         if (err < 0){             ret = 5;             printf_log("sendfile error");             printf("%d, %s/n", errno, strerror(errno));         }      }//cgi else  end: close((int)arg);   }   

cgi程序示例(2数相加):

#include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h>  #define SIZE 1024 using namespace std;  int hand(char *q){      char *num = NULL;   char *num_2 = NULL;   int num1 = 0;   int num2 = 0;   while (*q != '=')     q++;     q++;   num = q;   while (*q != '&')     q++;   *q = 0;   num1 = atoi(num);    while (*q != '=')       q++;     q++;   num_2 = q;   num2 = atoi(num_2);      printf("<html>/n");     printf("<head><h2>%d + %d = %d</h2></head>/r/n", num1, num2, num1+num2);     printf("</html>/n");   }  int main(){      printf("Content-Type:text/html;charset=ISO-8859-1/n/n");                char *p = NULL;       int i_length = 0;     char query_string[SIZE/2] = {0};     char *length = NULL;    p = getenv("METHOD");    if (strcmp(p, "GET") == 0){             char *s = getenv("QESTRING");            memcpy(query_string, s, strlen(s));     }//if    else{        length = getenv("Content-Length");        i_length = atoi(length);        char ch = 0;        for (int i = 0; i < i_length; i++){            ch = getc(0);            query_string[i] = ch;        }    }     hand(query_string);     printf("/n");     return 0; } 

运行:
发送请求
搭建简单的<a href=博客http服务器”>
返回结果:
搭建简单的<a href=博客http服务器”>

前端知识知之甚少,勿怪啊。。
博客这里一个简单的博客http服务器就搭建完成了,但是我博客们还可以做很多事。比如,当请求过多时效率太低怎么办?我博客们还可以写一个cgi程序连接数据库,再者,我博客们可以写一个爬虫程序来提高。除此之外,我博客们可以在博客深入调研nginx服务器。
源码地址:https://github.com/fengasdf/-http-(由于整合了协程,python爬虫,以及其他程序,可能有点乱)

http://blog.csdn.net/fengasdfgh/article/details/76602163加勒比海盗5

Head First:Meeting the Media

本章将重点介绍元素<img>的博客具体用法,知识点如下:

  1. JPEG,PNG,GIF的博客区别;
  2. 2.

博客区别”>1.JPEG,PNG,GIF的博客区别

attribute JPEG PNG GIF
work 适合于照片的博客格式,色彩连续型较好 适合于有明朗线条颜色对比大的博客图片 适合于有明朗线条颜色对比大的博客图片
color 可以展示160万中不同色彩 可以展示上百万种色彩 只能展示256种色彩
loss lossy:当图片大小改变时,图片的博客质量也有所下降 lossless:图片大小改变不影响图片质量 lossless
transparency 不支持透明度 所有色彩支持透明度 仅支持一种颜色实现透明度
file quality small medium large
animation 不支持 不支持 支持

不管在博客本地还是服务器上,我博客们都可以使用相对路径来展示图片
img src="images/drinks.gif"

但是当我博客们使用全路径来展示时,服务器的博客路径有所不同
img src="http://www.starbuzzcoffee.com/images/corporate/ceo.jpg"

continue….

http://blog.csdn.net/zyh_haha/article/details/76602214加勒比海盗5

H5面试—Doctype作用


Doctype作用

1)<!Doctype>声明位于文档中的博客最前面,处于<html>标签之前。告知浏览器的博客解析器,用什么文档类型规范来解析这个文档。

2)严格模式的博客排版和js运作模式是以该浏览器支持的博客最高标准运行。
3)在博客混杂模式中,页面以宽松的博客向后兼容的博客方式显示。模拟老式浏览器的博客行为以防止站点无法工作。
4)DOCTYPE不存在博客或格式不正确会导致文档以混杂模式呈现。
http://blog.csdn.net/songchunmin_/article/details/76606861加勒比海盗5

优雅设计封装基于Okhttp3的网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换

前5篇博文完成了此框架的博客一大模块—–多线程下载,而这两篇文章实现另一大模块——Http基本框架封装,在博客上一篇博文中完成了HttpHeader的博客接口定义和实现、状态码定义及response、request接口封装和实现,定义了许多接口和抽象类,在博客接下来编码过程中会体现出程序的博客扩展性重要性。

博客此篇博文中将添加新功能——原生请求的博客类库支持,你会发现在博客此基础上只需增加3个类即可,充分体现出了程序的博客扩展性。新增功能如下:

  • 原生HttpUrlConnction请求和响应
  • 业务层多线程分发处理
  • 移除请求
  • 请求成功类型转换包装处理

(建议阅读此篇文章之前,需理解前两篇文章的博客讲解,此系列文章是环环相扣,不可缺一,链接如下:)
优雅设计封装基于Okhttp3的博客网络框架(一):Http网络协议与Okhttp3解析
优雅设计封装基于Okhttp3的博客网络框架(二):多线程下载功能原理设计 及 简单实现
优雅设计封装基于Okhttp3的博客网络框架(三):多线程下载功能核心实现 及 线程池、队列机制解析
优雅设计封装基于Okhttp3的博客网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
优雅设计封装基于Okhttp3的博客网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
优雅设计封装基于Okhttp3的博客网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现


一. 原生HttpConnection方式请求和响应

以下的博客封装是为了增强此网络框架的博客功能扩展性,在博客除了使用Okhttp方式请求外,在博客此基础上增加最少的博客类使网络框架可以支持别的博客类库请求,例如原生的博客UrlConnction请求。此时前期所封装的博客接口扩展性就显得很重要了,所以在博客上一篇博文中我博客们定义了大量的博客接口与抽象类,看似复杂冗余,其实都是在博客为代码扩展性考虑,而此点中将完成原生请求的博客封装。

1. OriginHttpRequest 原生请求实现类

此类与OkHttpRequest 类相似,都继承于BufferHttpRequest 接口,区别在博客于一个是原生(HttpURLConnection对象)请求实现类,一个是Okhttp(OkhttpClient对象)请求实现类。所以两者大体实现类似,只是底层执行对象、操作API不同(区别主要体现在博客executeInternal方法实现上)。组成如下:

  • 定义成员变量HttpURLConnection及参数HttpMethodUrl来实现Okhttp的博客请求过程。
  • 提供构造方法初始化以上3个成员变量。
  • 实现抽象方法getMethod()getUri()。(这两个抽象方法实现简单,只需返回成员变量即可)
  • 实现抽象方法executeInternal(HttpHeader header, byte[] data)
    • header进行处理,循环该参数将所有请求头封装至HttpURLConnection
    • 判断data即传输参数是否为空,写入到HttpURLConnection输出流中。
    • 最后封装完毕,创建原生的博客响应实现类OriginHttpResponse,将HttpURLConnection传入其构造方法,最后将原生响应实现类OriginHttpResponse返回出去即可。
/**  * @function OriginHttpRequest 原生请求实现类(继承BufferHttpRequest接口)  * @author lemon guo  */  public class OriginHttpRequest extends BufferHttpRequest {      private HttpURLConnection mConnection;      private String mUrl;      private HttpMethod mMethod;      public OriginHttpRequest(HttpURLConnection connection, HttpMethod method, String url) {         this.mConnection = connection;         this.mUrl = url;         this.mMethod = method;     }      @Override     protected HttpResponse executeInternal(HttpHeader header, byte[] data) throws IOException {          for (Map.Entry<String, String> entry : header.entrySet()) {             mConnection.addRequestProperty(entry.getKey(), entry.getValue());         }         mConnection.setDoOutput(true);         mConnection.setDoInput(true);         mConnection.setRequestMethod(mMethod.name());         mConnection.connect();         if (data != null && data.length > 0) {             OutputStream out = mConnection.getOutputStream();             out.write(data,0,data.length);             out.close();         }         OriginHttpResponse response = new OriginHttpResponse(mConnection);         return response;     }       @Override     public HttpMethod getMethod() {         return mMethod;     }      @Override     public URI getUri() {         return URI.create(mUrl);     } }

2. 原生工厂类 OriginHttpRequestFactory

OkHttpRequest后续设计实现类似,需要在博客实现类的博客基础上对HttpRequest对象进行封装,提供方法供上层接口调用,那具体的博客网络请求是调用HttpURLConnection 来完成。

  • 定义成员变量HttpURLConnection
  • 为此类提供构造方法初始化成员变量
  • 实现接口中的博客createHttpRequest方法,即创建OriginHttpRequest 对象并返回。
  • 再提供一些基本方法setConnectionTimeOut设置请求超时时间,setReadTimeOutsetWriteTimeOut设置读写时间。(若有其他需求,此处可继续增加)
/**  * @function 实现类 OriginHttpRequestFactory(返回HttpRequest对象)  * @author lemon guo  */  public class OriginHttpRequestFactory implements HttpRequestFactory {      private HttpURLConnection mConnection;      public OriginHttpRequestFactory() {      }      public void setReadTimeOut(int readTimeOut) {         mConnection.setReadTimeout(readTimeOut);     }      public void setConnectionTimeOut(int connectionTimeOut) {         mConnection.setConnectTimeout(connectionTimeOut);     }      @Override     public HttpRequest createHttpRequest(URI uri, HttpMethod method) throws IOException {         mConnection = (HttpURLConnection) uri.toURL().openConnection();         return new OriginHttpRequest(mConnection, method, uri.toString());     } }

3. 原生响应实现类 OriginHttpResponse

相对应的博客,同Okhttp中的博客响应实现类OkHttpResponse类似,继承抽象类AbstractHttpResponse,实现父类的博客方法:

  • 实现类内部定义重要成员变量:HttpURLConnection
  • 为实现类提供构造方法,参数为HttpURLConnection
  • 实现类内部待实现的博客方法具体编码都依赖于HttpURLConnection成员变量。

代码量虽然不少,主要是实现方法,但是编码简单,查看即可理解,代码如下:

/**  * @function 响应实现类 OriginHttpResponse  * @author lemon Guo  */  public class OriginHttpResponse extends AbstractHttpResponse {      private HttpURLConnection mConnection;      public OriginHttpResponse(HttpURLConnection connection) {         this.mConnection = connection;     }      @Override     public HttpStatus getStatus() {         try {             return HttpStatus.getValue(mConnection.getResponseCode());         } catch (IOException e) {             e.printStackTrace();         }         return null;     }      @Override     public String getStatusMsg() {         try {             return mConnection.getResponseMessage();         } catch (IOException e) {             e.printStackTrace();         }         return null;     }      @Override     public long getContentLength() {         return mConnection.getContentLength();     }       @Override     protected InputStream getBodyInternal() throws IOException {         return mConnection.getInputStream();     }      @Override     protected void closeInternal() {         mConnection.disconnect();      }      @Override     public HttpHeader getHeaders() {          HttpHeader header = new HttpHeader();         for (Map.Entry<String, List<String>> entry : mConnection.getHeaderFields().entrySet()) {             header.set(entry.getKey(), entry.getValue().get(0));         }         return header;     } }

4. 供开发者调用类HttpRequestProvider ☆☆☆☆☆

以上原生请求方式封装完毕后,可以发现总共新增了**OriginHttpRequest、OriginHttpRequestFactory、OriginHttpResponse**3个类而已,这说明此网络框架代码的博客扩展性还是可行的博客,在博客后续想要添加别的博客请求类库,只要新增此3种代码即可。

博客新增完代码后,最后需要在博客HttpRequestProvider进行判断调用,这是一个供开发者调用的博客

    public HttpRequestProvider() {         if (OKHTTP_REQUEST) {             mHttpRequestFactory = new OkHttpRequestFactory();         } else {             mHttpRequestFactory = new OriginHttpRequestFactory();         }     }

博客其构造方法中进行判断更改,可以直接改变网络请求所使用的博客依赖类库!




二. 业务层多线程分发处理

上一点已经完成此网络框架对原生UrlConnction请求的博客支持,但是还有一个重点没有完成——多线程处理请求,大家都知道在博客主线程进行网络请求会出现异常,此点就是为了完成异步请求。

博客请求对象-multithreadrequest”>1. 队列中的博客请求对象 MultiThreadRequest

博客请求队列中需要定义业务层的博客相关接口,用于上层开发人员调用,上层只关注请求成功success还是失败fail,对于底层具体试下并不关心。

博客代码实现之前再次强调此网络框架中“队列”的博客概念,因为在博客处理多线程请求时,不可能无限制的博客创建多个线程来处理,而一个队列中存储的博客是一个Request对象,存储着请求Url、请求方式、数据等相关信息,再提供对应的博客getset方法。

/**  * @funtion 业务层多线程分发处理,队列中的博客Request对象MultiThreadRequest  * @author lemon Guo  */  public class MultiThreadRequest {      private String mUrl;      private HttpMethod mMethod;      private byte[] mData;      private MultiThreadResponse mResponse;      private String mContentType;      public String getUrl() {         return mUrl;     }      public void setUrl(String url) {         mUrl = url;     }      //相对应的博客get/set方法     ...... }

2. 响应对象 MultiThreadRequest

根据上一点所讲,上层只关心请求结果成功还是失败,所以响应接口只有以下两个方法。

/**  * @funtion 响应抽象类MultiThreadResponse  * @author lemon Guo  */  public abstract class MultiThreadResponse<T> {      public abstract void success(MultiThreadRequest request, T data);      public abstract void fail(int errorCode, String errorMsg); }

3. 工作站WorkStation

接下来需要一个类来处理多线程中的博客请求,属于服务的博客一种,用于处理多线程的博客控制和队列的博客管理。

  • 内部维护一个线程池成员变量,这里为了能够快速响应多个线程的博客同时请求数据,将线程数量最大值设置为Integer.MAX_VALUE再引入两个队列,一个队列存储着请求request,另一个存储着cache,即待执行的博客请求request队列(考虑到处理线程数量超过最大限制时)。
  • 提供构造方法初始化成员变量HttpRequestProvider,经过前期封装后,获取请求request对象由专门供上层调用的博客HttpRequestProvider完成。
  • 提供add 方法将请求任务添加到队列中。注意在博客这里需要做一个开启线程最大数判断,例如最多同时开启60个线程处理请求:
    • 若超过则将request添加cache队列中,等待执行。
    • 若未超过,则通过HttpRequestProvider获取请求request对象,最后由线程池执行。注意,既然是由线程池执行,这里还需要一个Runnable,后续编写。
      -提供finish 方法,在博客线程池执行Runnable时,即请求结束会调用此方法,将完成的博客Request移除队列。
/**  * @funtion 业务层多线程分发处理:用于处理多线程的博客控制和队列的博客管理 MultiThreadRequest  * @author lemon Guo  */  public class WorkStation {      private static final int MAX_REQUEST_SIZE = 60;      private static final ThreadPoolExecutor sThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {          private AtomicInteger index = new AtomicInteger();          @Override         public Thread newThread(Runnable r) {             Thread thread = new Thread(r);             thread.setName("http thread name is " + index.getAndIncrement());             return thread;         }     });       private Deque<MultiThreadRequest> mRunning = new ArrayDeque<>();      private Deque<MultiThreadRequest> mCache = new ArrayDeque<>();      private HttpRequestProvider mRequestProvider;      public WorkStation() {         mRequestProvider = new HttpRequestProvider();     }      public void add(MultiThreadRequest request) {          if (mRunning.size() > MAX_REQUEST_SIZE) {             mCache.add(request);         } else {             doHttpRequest(request);         }     }       public void doHttpRequest(MultiThreadRequest request) {         HttpRequest httpRequest = null;         try {             httpRequest = mRequestProvider.getHttpRequest(URI.create(request.getUrl()), request.getMethod());         } catch (IOException e) {             e.printStackTrace();         }         sThreadPool.execute(new HttpRunnable(httpRequest, request, this));     }       public void finish(MultiThreadRequest request) {         mRunning.remove(request);         if (mRunning.size() > MAX_REQUEST_SIZE) {             return;         }          if (mCache.size() == 0) {             return;         }          Iterator<MultiThreadRequest> iterator = mCache.iterator();          while (iterator.hasNext()) {             MultiThreadRequest next = iterator.next();             mRunning.add(next);             iterator.remove();             doHttpRequest(next);         }     }  }

4. HttpRunnable

博客专门用于处理多线程的博客控制和队列的博客管理类WorkStation中维护了一个线程池,用来执行网络请求,所以需要对应的博客Runnable来执行下载任务。

  • 成员变量有基本Http封装的博客接口HttpRequest、多线程请求的博客接口MultiThreadRequest、管理多线程和队列类WorkStation
    • WorkStation主要用于run方法执行完后调用此对象中的博客方法,来移除队列中已执行完的博客request。
    • HttpRequest中的博客重要请求数据获取并封装到MultiThreadRequest中来执行请求。
/**  * @funtion 业务层多线程分发处理:用于处理下载任务  * @author lemon Guo  */  public class HttpRunnable implements Runnable {      private HttpRequest mHttpRequest;      private MultiThreadRequest mRequest;      private WorkStation mWorkStation;      public HttpRunnable(HttpRequest httpRequest, MultiThreadRequest request, WorkStation workStation) {         this.mHttpRequest = httpRequest;         this.mRequest = request;         this.mWorkStation = workStation;      }      @Override     public void run() {         try {             mHttpRequest.getBody().write(mRequest.getData());             HttpResponse response = mHttpRequest.execute();             String contentType = response.getHeaders().getContentType();             mRequest.setContentType(contentType);             if (response.getStatus().isSuccess()) {                 if (mRequest.getResponse() != null) {                     mRequest.getResponse().success(mRequest, new String(getData(response)));                 }              }         } catch (IOException e) {             e.printStackTrace();         } finally {             mWorkStation.finish(mRequest);         }       }      public byte[] getData(HttpResponse response) {         ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) response.getContentLength());         int len;         byte[] data = new byte[512];         try {             while ((len = response.getBody().read(data)) != -1) {                 outputStream.write(data, 0, len);             }         } catch (IOException e) {             e.printStackTrace();         }         return outputStream.toByteArray();     } }

5. HttpApiProvider上层调用API

以上代码基本完成,但是为了方便上层调用,需要在博客此基础上封装一个接口的博客API,类似之前专门提供Request对象的博客HttpRequestProvider,此类名为HttpApiProvider

public class HttpApiProvider {      private static final String ENCODING = "utf-8";      private static WorkStation sWorkStation = new WorkStation();      /*     *   对请求参数进行编码处理     * */     public static byte[] encodeParam(Map<String, String> value) {         if (value == null || value.size() == 0) {             return null;         }         StringBuffer buffer = new StringBuffer();         int count = 0;         try {             for (Map.Entry<String, String> entry : value.entrySet()) {             buffer.append(URLEncoder.encode(entry.getKey(), ENCODING)).append("=").                         append(URLEncoder.encode(entry.getValue(), ENCODING));                 if (count != value.size() - 1) {                     buffer.append("&");                 }                 count++;             }         } catch (UnsupportedEncodingException e) {             e.printStackTrace();         }         return buffer.toString().getBytes();     }      public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) {         MultiThreadRequest request = new MultiThreadRequest();         request.setUrl(ul);         request.setMethod(HttpMethod.POST);         request.setData(encodeParam(value));         request.setResponse(response);         sWorkStation.add(request);     } }

6. 测试

以上封装功能已经完成,接下来以一个POST请求来测试这一系列HTTP封装请求,代码如下:

Map<String, String> map = new HashMap<>();         map.put("username", "nate");         map.put("userage", "12");         MoocApiProvider.helloWorld("http:....../web/HelloServlet", map, new MoocResponse<Person>() {              @Override             public void success(MoocRequest request, Person data) {                 Logger.debug("nate", data.toString());             }              @Override             public void fail(int errorCode, String errorMsg) {             }         });

结果显示:

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>

可以看出日志打印,代表POST请求成功,以上代码封装无误。




三. 数据类型自动转换

现在博客还剩下一个需求需要完善,即响应请求到的博客数据更加简单直接,是对象类型而并非xml、json数据,所以这涉及到了数据类型转换,为了整个程序的博客扩展性考虑,首要的博客还是来封装接口。

1. 数据类型转换接口Convert

  • parse方法中进行类型转换。
  • isCanParse方法判断此数据是否可以进行转换
/**  * @funtion 数据类型转换接口  * @author lemon Guo  */  public interface Convert {      Object parse(HttpResponse response, Type type) throws IOException;      boolean isCanParse(String contentType);      Object parse(String content, Type type) throws IOException; }

2. 转换实现类JsonConvert

如上在博客实现接口之后,可以定义不同类型转换的博客实现类来实现此接口,此项目中我博客只定义了JsonConvert,用来进行Json数据转换,相应的博客,还可以定义XmlConvert实现类等等。

这里方法的博客具体实现都是借助开源库Gson来解析数据,比较常用的博客方法,实现不难,代码如下:

/**  * @funtion 转换实现类JsonConvert  * @author lemon Guo  */  public class JsonConvert implements Convert {      private Gson gson = new Gson();      private static final String CONTENT_TYPE = "application/json;charset=UTF-8";      @Override     public Object parse(HttpResponse response, Type type) throws IOException {          Reader reader = new InputStreamReader(response.getBody());         return gson.fromJson(reader, type);      }      @Override     public boolean isCanParse(String contentType) {         return CONTENT_TYPE.equals(contentType);     }      @Override     public Object parse(String content, Type type) throws IOException {         return gson.fromJson(content, type);     } }

3. 解析Response数据WrapperResponse

以上封装好类型转换后,需要将此结合到网络请求中,在博客MultiThreadResponse做一个上层封装,相当于一层过滤,将获取到的博客响应数据通过类型转换后再返回。

WrapperResponse 继承于MultiThreadResponse实现其抽象方法success,在博客成功响应方法中对数据进行解析类型转换操作。

/**  * @funtion WrapperResponse类型转换封装 Response  * @author lemon Guo  */  public class WrapperResponse extends MultiThreadResponse<String> {      private MultiThreadResponse mMoocResponse;      private List<Convert> mConvert;      public WrapperResponse(MultiThreadResponse moocResponse, List<Convert> converts) {         this.mMoocResponse = moocResponse;         this.mConvert = converts;     }      @Override     public void success(MultiThreadRequest request, String data) {          for (Convert convert : mConvert) {             if (convert.isCanParse(request.getContentType())) {                 try {                     Object object = convert.parse(data, getType());                     mMoocResponse.success(request, object);                 } catch (IOException e) {                     e.printStackTrace();                 }                 return;             }         }     }      public Type getType() {         Type type = mMoocResponse.getClass().getGenericSuperclass();         Type[] paramType = ((ParameterizedType) type).getActualTypeArguments();         return paramType[0];     }      @Override     public void fail(int errorCode, String errorMsg) {     } }

4. 修改API调用类HttpApiProvider

    public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) {         MultiThreadRequest request = new MultiThreadRequest();         request.setUrl(ul);         request.setMethod(HttpMethod.POST);         request.setData(encodeParam(value));         request.setResponse(response);         sWorkStation.add(request);     }

如上,这是未解析响应数据时Api暴露网络请求接口中的博客实现,其中使用的博客响应数据是MultiThreadResponse ,在博客博客们封装好可自动解析的博客数据后,修改使用WrapperResponse ,代码如下:

    public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) {         MultiThreadRequest request = new MultiThreadRequest();         WrapperResponse wrapperResponse = new WrapperResponse(response, sConverts);         request.setUrl(ul);         request.setMethod(HttpMethod.POST);         request.setData(encodeParam(value)); //        request.setResponse(response);         request.setResponse(wrapperResponse);         sWorkStation.add(request);     }

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>

再次测试,结果正确,以上类型转换封装无误,此网络框架封装完成。




四. 网络框架总结

此系列文章旨于:基于okhttp3原始框架来设计封装一个满足业务需求、扩展性强、耦合度低的博客网络框架。具体框架功能为:

  • 封装基本的博客网络请求
  • 扩展其对数据库的博客支持
  • 对多文件上传、多线程文件下载的博客支持
  • 对Json数据解析等功能的博客支持

1.整体代码

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>

以上是这个网络框架EasyOkhttp封装的博客全部代码,看似代码量并不少,但是其中定义了大量的博客接口和抽象类,注重扩展性和解耦性,所以读者可在博客博客封装的博客基础上继续拓展,根据自身需求添加代码。


2. 架构设计

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>

如上图所示,此框架可以分为三个层次

  • 第一层:便于框架扩展,第一层即最底层是Http InterfaceAbstact,例如Http中的博客Headers、Request、Response等通用的博客原生接口。

  • 第二层:有了第一层请求接口定义,便于第二层对接口的博客实现,此框架采用两种方式对接口进行实现,分别是Okhttp和原生的博客HttpURLConnection。通过这两个相关的博客API去实现整个Http请求和响应的博客过程,若还想要做相应的博客拓展,采用别的博客第三方http请求库,在博客此处可增加。(已经预先在博客第一层定义了足够多的博客接口实现网络请求的博客回调,第一层可无需修改)对于整个上层业务来说,无需直接接触到底层Okhttp、HttpURLConnection具体实现,所以提供二次封装的博客 HttpProvider ,暴露接口给上层调用。(具体底层是调用Http还是HttpURLConnection取决于配置,首先判断Okhttp依赖在博客项目中是否存在博客,若有则主要采用Okhttp来进行网络请求,否则采用HttpURLConnection)

  • 第三层:即最上层由 WorkstationConvert组成。Workstation博客中文意思是工作站,用来处理一些线程的博客调度分发和任务的博客队列,之所以将它设计在博客最上层,因为整个多线程、队列机制是与业务层紧密相关的博客Convert是为上层开发者提供了更好的博客接口封装,用于接口返回类型转换、数据解析,例如json、xml等。


3. 文件多线程下载和Http设计封装

整个系列的博客文章可以分成两个部分,即前5篇博文在博客重点讲解多线程下载有关设计与编码实现,而后两篇博文则是重点讲解Http请求、响应接口封装,两部分的博客思维导图如下,讲解顺序也是按此进行:

文件多线程下载思维导图:

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>


Http设计封装思维导图:

优雅设计封装基于Okhttp3的<a href=博客网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换”>


4. 小结

源码

此系列所完成的博客网络框架封装编码工作暂告一段落,有些功能可能完成的博客不是很全面,编写此系列过程中收益最大的博客应当是整体规划封装思想。多线程下载模块多涉及的博客是Java线程、Http字段有关知识,而后半部分——Http网络框架实现过程中充分体现出了接口、抽象类、实现类这之间的博客封装思想,而大量的博客接口和抽象类也体现出整个程序的博客扩展性、解耦性,这两点从一开始封装网络框架就被视为重点,同时也是我博客们需要学习的博客

此框架可能只算一个简单封装demo,些许部分完成的博客不是很好,但是这整个封装过程便是精华所在博客,从一开始的博客框架架构设计,到功能设计实现、编码优化、bug程序调试等等。这不仅仅只是编码,只涉及到Java单一的博客内容,同时融合了 Okhttp相关内容、Http协议、接口设计、代码隔离、架构设计、解决思路等综合考虑,此乃重中之重。

编程,或不只是编程。

共勉~



若有错误,虚心指教~

http://blog.csdn.net/itermeng/article/details/76602237加勒比海盗5