精华内容
下载资源
问答
  • html的<head>中加入以下代码 <meta name="referrer" content="never"> 一定要强制刷新

    html的<head>中加入以下代码 

    <meta name="referrer" content="never">

    一定要强制刷新 

     

    展开全文
  • 1. 随便找个公众号地址我们来抓一下代码 测试的地址:https://mp.weixin.qq.com/s?__biz=MjM5MDk4NTg2MA==&mid=2652650152&idx=1&sn=93e4aeb524ee94cb64d1e9dbd5cf0266&chksm=bd54e1438a236855e93...

    1. 随便找个公众号地址我们来抓一下代码

       测试的地址:https://mp.weixin.qq.com/s?__biz=MjM5MDk4NTg2MA==&mid=2652650152&idx=1&sn=93e4aeb524ee94cb64d1e9dbd5cf0266&chksm=bd54e1438a236855e93c27f0565b91380277ab484f8deda25745986b295b58046d75e52a68e2&scene=21#wechat_redirect

    (随便找的)

    2.我们正常的抓

         /// <summary>
            /// 根据网址的URL,获取源代码HTML
            /// </summary>
            /// <param name="url"></param>
            /// <returns></returns>
            public static string GetHtmlByUrl(string url)
            {
                using (WebClient wc = new WebClient())
                {
                    try
                    {
                        wc.UseDefaultCredentials = true;
                        wc.Proxy = new WebProxy();
                        wc.Proxy.Credentials = CredentialCache.DefaultCredentials;
                        wc.Credentials = System.Net.CredentialCache.DefaultCredentials;
                        byte[] bt = wc.DownloadData(url);
                        string txt = System.Text.Encoding.GetEncoding("GB2312").GetString(bt);
                        switch (GetCharset(txt).ToUpper())
                        {
                            case "UTF-8":
                                txt = System.Text.Encoding.UTF8.GetString(bt);
                                break;
                            case "UNICODE":
                                txt = System.Text.Encoding.Unicode.GetString(bt);
                                break;
                            default:
                                break;
                        }
                        return txt;
                    }
                    catch (Exception ex)
                    {
                        return null;
                    }
                }
            }
    
    
      /// <summary>
            /// 从HTML中获取获取charset
            /// </summary>
            /// <param name="html"></param>
            /// <returns></returns>
            public static string GetCharset(string html)
            {
                string charset = "";
                Regex regCharset = new Regex(@"content=[""'].*\s*charset\b\s*=\s*""?(?<charset>[^""']*)", RegexOptions.IgnoreCase);
                if (regCharset.IsMatch(html))
                {
                    charset = regCharset.Match(html).Groups["charset"].Value;
                }
                if (charset.Equals(""))
                {
                    regCharset = new Regex(@"<\s*meta\s*charset\s*=\s*[""']?(?<charset>[^""']*)", RegexOptions.IgnoreCase);
                    if (regCharset.IsMatch(html))
                    {
                        charset = regCharset.Match(html).Groups["charset"].Value;
                    }
                }
                return charset;
            }
    
    

    3. 我们将爬出来的网页保存一哈瞅瞅看

       

      /// <summary>
            /// 保存Html
            /// </summary>
            /// <param name="html"></param>
            /// <returns></returns>
            public static bool SaveHtml(string html)
            {
                try
                {
                    using (StreamWriter sw = new StreamWriter("MyHtml.html", false, Encoding.UTF8))
                    {
                        sw.WriteLine(html);//将字符串写入到文本中
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
    
    

     这样调用

      var url = "https://mp.weixin.qq.com/s?__biz=MjM5MDk4NTg2MA==&mid=2652650152&idx=1&sn=93e4aeb524ee94cb64d1e9dbd5cf0266&chksm=bd54e1438a236855e93c27f0565b91380277ab484f8deda25745986b295b58046d75e52a68e2&scene=21#wechat_redirect";
                var backHtml = GetHtmlByUrl(url);
                var saveResult = SaveHtml(backHtml);

    这是原网页

    这是我们爬到本地的网页

    此时我们F12打开调试可以看到,很多错误信息

    我们可以看到,js用的是本地的地址,我们这里需要把原来Html的代码给改了(location.protocol,location.href,location.host) 采用绝对路径 

    4.我们采用第三方的 AngleSharp 来改Html代码 (以前都写正则,现在忘了怎么写了。。),直接Nuget安装就行了

     

         /// <summary>
            /// 处理html
            /// </summary>
            /// <param name="html"></param>
            /// <param name="url">请求地址</param>
            /// <returns></returns>
            public static string HandleHtml(string html, string url)
            {
                var parser = new HtmlParser();
                var document = parser.ParseDocument(html);
                Uri myurl = new Uri(url);
                //追加自定义节点
                var addNode = document.CreateElement("script");
                addNode.TextContent = $"var newhref=\"{url}\";var newhost=\"{myurl.Host}\";var newprotocol=\"{myurl.Scheme}:\";";
                document.Head.Append(addNode);
                var newOuterHtml = document.DocumentElement.OuterHtml;
                //全局替换 "//res
                return newOuterHtml.Replace("\"//res", "\"https://res").Replace("location.protocol", "newprotocol").Replace("location.href", "newhref").Replace("location.host", "newhost");
            }

      调用方式我们也改一下

      

              if (!string.IsNullOrWhiteSpace(backHtml))
                {
                    var newHtml = HandleHtml(backHtml, url);
                    var saveResult = SaveHtml(newHtml);
                }

    然后我们在看看什么效果

      图片出来了,视频没有出来。。。

     继续打开调试,发现是有些js没有加载,

    还是用的本地的路径,因为html引用的js还是用的网络地址,除非把js下载下来,于是,我们把爬下面的网页挂在IIS下试试

    一个是图片未授权,一个是图片跨域 。。

     未授权好办,直接html加一段代码 ,跨域我想到的办法是自己服务器替换一次(就是说图片地址改为我们服务器的地址)直接上处理过的代码

            [HttpGet]
            [Route("GetImgStream")]
            public HttpResponseMessage GetImgStream([FromUri]string imgUrl)
            {
                var ss=HttpContext.Current.Request.QueryString;
                var imageBuffer = GetDownloadStream(imgUrl);
                var respimg = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new System.Net.Http.ByteArrayContent(imageBuffer)
                };
                respimg.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
                return respimg;
            }
    
    
            /// <summary>
            /// 获取网络文件的二进制流
            /// </summary>
            /// <param name="url">腾讯云地址(腾讯云文件的完整地址)</param>
            /// <returns></returns>
            public  byte[] GetDownloadStream(string url)
            {
                try
                {
                    // 设置参数
                    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
                    request.Method = "get";
                    //发送请求并获取相应回应数据
                    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                    Stream responseStream = response.GetResponseStream();
                    WebHeaderCollection header = response.Headers;
                    string lastmodify = header["Last-Modified"];
                    byte[] bArr = null;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        int b;
                        while ((b = responseStream.ReadByte()) != -1)
                        {
                            ms.WriteByte((byte)b);
                        }
                        bArr = ms.ToArray();
                    }
                    responseStream.Close();
                    responseStream.Dispose();
                    return bArr;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    

     我们写一个webapi,然后把html所有的图片地址都换成我们的api地址动态获取图片,于是我们的代码改成了这样

          /// <summary>
            /// 处理html
            /// </summary>
            /// <param name="html"></param>
            /// <param name="url">请求地址</param>
            /// <returns></returns>
            public static string HandleHtml(string html, string url)
            {
                var parser = new HtmlParser();
                var document = parser.ParseDocument(html);
                Uri myurl = new Uri(url);
    
                //增加跨域节点
                var addMetaDom = document.CreateElement("meta");
                addMetaDom.SetAttribute("name", "referrer");
                addMetaDom.SetAttribute("content", "never");
                document.Head.Append(addMetaDom);
    
    
                //追加自定义节点
                var addNode = document.CreateElement("script");
                addNode.TextContent = $"var newhref=\"{url}\";var newhost=\"{myurl.Host}\";var newprotocol=\"{myurl.Scheme}:\";";
                document.Head.Append(addNode);
    
                //解析link标签
                var blueListItemsLinq = document.All.Where(p => p.LocalName == "link");
                foreach (var item in blueListItemsLinq)
                {
                    var oldHtml = item.OuterHtml;
                    var href = item.GetAttribute("href");
                    if (!string.IsNullOrWhiteSpace(href))
                    {
                        if (href.Length > 2)
                        {
                            if (href[0] == '/' && href[1] == '/')
                            {
                                var newHref = "https:" + href;
                                var replaceHtml = oldHtml.Replace(href, newHref);
                                item.OuterHtml = replaceHtml;
                            }
                        }
                    }
                }
                //解析iframe
                var videoItemLinq = document.All.Where(p => p.LocalName == "iframe" && p.ClassName == "video_iframe rich_pages");
                foreach (var item in videoItemLinq)
                {
                    var vid = item.GetAttribute("data-mpvid");
                    if (string.IsNullOrWhiteSpace(vid))
                    {
                        continue;
                    }
                    var realUrl = GetRealVideo(vid);
                    //替换当前iframe
                    if (!string.IsNullOrWhiteSpace(realUrl))
                    {
                        var addvideoNode = document.CreateElement("video");
                        addvideoNode.SetAttribute("src", realUrl);
                        addvideoNode.SetAttribute("controls", "controls");
                        item.Parent.AppendChild(addvideoNode);
                        item.Parent.RemoveChild(item);
                    }
                }
                var newOuterHtml = document.DocumentElement.OuterHtml;
                //全局替换 "//res
                return newOuterHtml.Replace("\"//res", "\"https://res").Replace("location.protocol", "newprotocol").Replace("location.href", "newhref").Replace("location.host", "newhost")
                    .Replace("src=\"https://mmbiz.qpic.cn/", "src=\"http://自己的webapi地址/GetImgStream?imgUrl=http://mmbiz.qpic.cn/")
                    .Replace("src=\"/mp/videoplayer?", "src=\"https://mp.weixin.qq.com/mp/videoplayer?");
            }

    好了,图片出来了,视频也出来了,本以为大功告成,突然发现视频还有一种格式的

    打开F12,我们可以看到这个视频是iframe套了一层,而且路径不能直接打开

    这就是视频的加密机制,于是我们需要把视频给解密出来 ,还是看请求来分析

    我们注意到这个请求,url就是真实的视频地址,而入参的id就是上面那个iframe的id,于是,我们需要把视频先解密出来,然后把Iframe替换成video标签

            /// <summary>
            /// 处理html
            /// </summary>
            /// <param name="html"></param>
            /// <param name="url">请求地址</param>
            /// <returns></returns>
            public static string HandleHtml(string html, string url)
            {
                var parser = new HtmlParser();
                var document = parser.ParseDocument(html);
                Uri myurl = new Uri(url);
    
                //增加跨域节点
                var addMetaDom = document.CreateElement("meta");
                addMetaDom.SetAttribute("name", "referrer");
                addMetaDom.SetAttribute("content", "never");
                document.Head.Append(addMetaDom);
    
    
                //追加自定义节点
                var addNode = document.CreateElement("script");
                addNode.TextContent = $"var newhref=\"{url}\";var newhost=\"{myurl.Host}\";var newprotocol=\"{myurl.Scheme}:\";";
                document.Head.Append(addNode);
    
                //解析link标签
                var blueListItemsLinq = document.All.Where(p => p.LocalName == "link");
                foreach (var item in blueListItemsLinq)
                {
                    var oldHtml = item.OuterHtml;
                    var href = item.GetAttribute("href");
                    if (!string.IsNullOrWhiteSpace(href))
                    {
                        if (href.Length > 2)
                        {
                            if (href[0] == '/' && href[1] == '/')
                            {
                                var newHref = "https:" + href;
                                var replaceHtml = oldHtml.Replace(href, newHref);
                                item.OuterHtml = replaceHtml;
                            }
                        }
                    }
                }
                //解析iframe
                var videoItemLinq = document.All.Where(p => p.LocalName == "iframe" && p.ClassName == "video_iframe rich_pages");
                foreach (var item in videoItemLinq)
                {
                    var vid = item.GetAttribute("data-mpvid");
                    if (string.IsNullOrWhiteSpace(vid))
                    {
                        continue;
                    }
                    var realUrl = GetRealVideo(vid);
                    //替换当前iframe
                    if (!string.IsNullOrWhiteSpace(realUrl))
                    {
                        var addvideoNode = document.CreateElement("video");
                        addvideoNode.SetAttribute("src", realUrl);
                        addvideoNode.SetAttribute("controls", "controls");
                        item.Parent.AppendChild(addvideoNode);
                        item.Parent.RemoveChild(item);
                    }
                }
                var newOuterHtml = document.DocumentElement.OuterHtml;
                //全局替换 "//res
                return newOuterHtml.Replace("\"//res", "\"https://res").Replace("location.protocol", "newprotocol").Replace("location.href", "newhref").Replace("location.host", "newhost")
                    .Replace("src=\"https://mmbiz.qpic.cn/", "src=\"http://自己的webapi地址/GetImgStream?imgUrl=http://mmbiz.qpic.cn/")
                    .Replace("src=\"/mp/videoplayer?", "src=\"https://mp.weixin.qq.com/mp/videoplayer?");
            }
    
    
    
    
    
    
    
    
            /// <summary>
            ///  根据vid获取公众号视频的真实地址
            /// </summary>
            /// <param name="vid"></param>
            /// <returns></returns>
            public static string GetRealVideo(string vid)
            {
                string txUrl = $"https://mp.weixin.qq.com/mp/videoplayer?action=get_mp_video_play_url&preview=0&__biz=MzI4NDk4OTQxMg==&mid=2247485200&idx=1&vid={vid}&uin=&key=&pass_ticket=&wxtoken=777&appmsg_token=&x5=0&f=json";
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(txUrl);
                request.Method = "GET";
                var backJson = GetBackHtml(request, "1");
                var realUrl = JsonConvert.DeserializeObject<VideoSerializeModel>(backJson);
                if (realUrl == null || realUrl.videoinfos.Count() == 0)
                {
                    throw new Exception("视频解析错误");
                }
                return realUrl.videoinfos[0].url;
            }
    
      public class VideoSerializeModel
        {
            /// <summary>
            /// 视频标题
            /// </summary>
            public string title { get; set; }
    
            [JsonProperty(PropertyName = "url_info")]
            public List<VideoModel> videoinfos = new List<VideoModel>();
        }
    
    
        public class VideoModel
        {
            public string duration_ms { get; set; }
            public string filesize { get; set; }
            public string format_id { get; set; }
            public string height { get; set; }
            public string url { get; set; }
            public string width { get; set; }
        }

    然后大功告成,我们就可以根据公众号网页的url  直接放到我们自己的网站上了

    展开全文
  • 微信公众号排版方法及相关素材网站

    万次阅读 多人点赞 2018-05-31 15:41:04
    公众号会话:矩形。 • 历史消息:矩形。 • 转发消息:正方形。 • 分享朋友圈:正方形。 上图是@王如飞 研究的封面图裁剪情况这张图太复杂,不用记住。只要记住封面图的尺寸是 900 X 500,次条封面图的尺寸是 ...
    作者:杨坤龙
    链接:https://www.zhihu.com/question/23640203/answer/375921114
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    关于排版

    前段时间我发了条朋友圈:有哪些东西是你一旦知道,生活就从此回不去了的?

    审美。

    现在无论是微信文章还是其他场景,面对没有排版的文字,我都会瞬间失去阅读兴趣。下面将从文字、留白、图片、引导几部分来写。先来说说排版的作用。


    001 排版的作用

    首先要明确,排版是为了内容服务。好的排版,一定都是顺应人性的排版。

    人天生缺乏耐心,如果没有经过排版处理,冗长的文章会给读者带来很大的信息获取压力。

    排版的作用有三点:


    正文排版,包括三部分:

    1.文字:字号、颜色、标点符号、排版规范
    2.留白:字间距、行间距、段间距、页边距
    3.配图:封面图、配图、GIF


    002 字号

    • 标题:推荐16px-18px
    • 正文:推荐14px-16px
    • 标注:推荐12px-14px


    003 颜色

    文章中除了图片,正文全部颜色不要超过三种,颜色太多显得杂乱。另外,不要选择饱和度太高的颜色,就是日常所说鲜艳的颜色。

    这些颜色看起来比较廉价,因为会对我们的情绪造成影响。


    ▍主题色

    一种颜色作为主题色,可以用在标题、重点内容、二维码、头像、顶部和底部的引导等。

    ▍正文颜色

    一种作为正文颜色,不建议用纯黑(#000000),手机端会比较刺眼,灰色会温和一点。常用的有:#595959、#3f3f3f

    ▍标注色

    一种作为标注颜色,用作引用内容、注释、声明等。常用的颜色有:#888888、#a5a5a5


    004 对齐

    对齐方式主要有两种:左对齐、居中。

    ▍左对齐

    左对齐型排版,首行不需要缩进。在中文书籍中,因为段落间没有间隙,所以需要用两个字符的缩进加以辨识。但在网页端和手机端,书本式的排版会让读者感到压力,大家改用空行作了段落区分。


    ▍两端对齐

    没有了首行缩进之后,文章左侧像刀削斧砍一样工整。与之相对,右侧容易出现锯齿一样粗糙的边缘,英文排版中尤甚。

    针对这种情况,可以在编辑器中,使用两端对齐功能,将右侧也对齐,显得更加精致。


    ▍居中
    居中型排版
    一句话一行
    视觉在中央
    所以呢
    每行不要多
    否则就像这句话突然让眼球扫一整行好费劲

    另外,不同的对齐方式不要混淆在一起。否则就打破了整体上的视觉统一,不够舒适。


    005 标点符号

    标点的乱用,也会导致排版会乱。一些文字编辑对标点乱用是非常反感的。

    我之前在朋友圈看到简书主编一鸣发文章说这个事,整篇文章用的都是咆哮体!一天审稿那么多,可以体会一下他的心情。

    中文标点符号应全部使用全角符号,下面举出几个常用的标点符号。


    ▍引号

    引号推荐使用直角引号「」,这个符号是「我的最爱」。引号中再用引号使用双直角引号『』。表示讽刺、反讽暗示、强调语气、人物对话时,使用弯引号“ ”。

    直角引号可以在中文输入法中「特殊符号 — 标点符号」中找到,或者是用直接日文输入法输入,键盘上的括号键即是。

    直角引号源自日文,弯引号源自英文。

    相比弯引号,直角引号在汉字排版中更为优雅和美观,也避免了全角半角不分的同学乱用弯引号导致的格式错乱。


    ▍省略号

    千万不要用六个句号「。。。。。。」作省略号,也不要用三个点「...」,三个点是英文中的省略号。中文省略号「……」在中文输入法下,用 shift + 6 打出来。


    ▍破折号

    破折号占两个汉字空间,不能用连字符代替,即「--」,除了书本里,我现在很少看到有人打出标准的破折号了。

    在中文输入法下,用 shift + - (键盘 0 右侧的键)打出来,即「——」。


    ▍项目符号

    有时候文章中会有一些并列关系的条目和句子,可以用项目符号区分开。各种编辑器中都有列表功能,分为有序列表和无序列表两种。

    有序列表:

    1. 数字序列是最常见的有序列表。
    2. 数字之外还有英文、罗马文序列等。
    3. 数字后面加的是圆点而不是顿号。

    无序列表:

    ● 小黑点是最常用的。
    ■ 小方块跟中文搭配也很优美。
    ○ 小圆圈多用于注释内容。

    ▍三角符

    微信文章中经常可以看到各类三角符号,有各式各样的用处:
    ▲正三角经常居中放在照片下方,注释内容前面,起指示作用。
    ▼倒三角经常单独居中成行,用于分隔两部分内容,引出下文。


    ▍分隔符

    除倒三角外,还有很多符号,都可以用作分隔符。

    分割线也是最常用的一种,各种编辑器中都有不同样式的分割线,是排版中很重要的元素。除了用在文中,也可以用在文末。


    ▍进度符

    这是 i排版在一篇文章中提出的方法,我觉得也很有意思:

    选择同一个小符号,用类似进度条的方式来标注,更换其中某一个或几个小符号的颜色,让读者知道文章读到哪一部分,起到提示进度的作用。

    下面我用一个小符号来示范下:

    第一部分
    ❖ ❖
    第二部分
    ❖ ❖
    第三部分
    ❖ ❖ ❖

    PS:改不了颜色,我就先用斜体代替了。


    006 排版规范

    ▍强调

    在文章中,经常需要强调某一部分,通过设计在视觉上凸显出来。

    引用 justfont 中的一段话:

    借由底色、边框与不同字体的变化,告诉读者这一段很重要,也帮助快速浏览掌握重点。除了要特别讨论的上色之外,最常用的强调法不外乎「加粗」、「变斜」或是「加底线」。

    加粗、变斜是透过墨色浓度或形状变化与正文对比,这发源自西方数百年的排版规则;加底线则是打字机盛行之后留下的遗产。

    强调方式很多,但是千万不能乱用,更不能同时用在一个段落里,否则效果会让人很尴尬。下面进行一一说明。


    ▍下划线

    下划线是打字机时代的产物,如今已经过时。如果用下划线进行强调,反而会喧宾夺主,对文字的阅读形成干扰。除超链接外,其他句子尽量不要使用下划线。


    ▍斜体
    英文从文艺复兴时期,就已经有了用斜体进行强调的排版规则。但中文不同,中文没有斜体,只有计算机程序变形的斜体效果。中文斜体后失去了中文字形原来的结构和美感,所以不要在中文中使用斜体。


    ▍加粗
    加粗是最理想的强调方式,也是最通用的做法。强调的时候除了加粗外,还可以改变字号或改变颜色。


    ▍引用
    开头的摘要、别人的资料、人物对话、重点语句,都可以使用引用功能。

    ▲引用功能

    引用的内容要独立成段,如果来自别处,请记得加上引用说明。用了引动功能的句子,就是多了这句话前面那道竖杠。

    引用格式中的句子,推荐使用标注色,即 #a5a5a5 或者#888888


    ▍中西文混排

    中英文之间加空格:

    我用 Typora 进行写作。

    中文与数字之间加空格:

    现在已经到了 2 月份。

    中文夹杂英文单词使用全角标点:

    这是我给爸妈新买的 iPhone 7,你拿去吧。

    对于完整的英语句子,则使用半角标点:

    我最喜欢的英文名言是「Each man is the architect of his own fate.」

    以上几点是通行的做法,不是标准规范。我查了很多资料,中西文混排目前还没有一套标准的官方规则,其中最主要的争论之一是关于空格的用法。

    有一些同学的文章中字间距很大,这种情况就不需要再加空格。

    关于这个问题,我在方宏章的文章《关于文字的可阅读性》中找到一段话,很有参考意义:

    每个字母之间必须有一定合理空间(Kerning),如果字母之间的距离过于狭小,就像两个挨着很近的鸡蛋随时有可能相碰撞的危险,如果字母间距过大,那字母就会失去张力和紧张感,使得文字阅读变得乏味。

    所以字母之间的距离应该合适,使得在很紧凑的句子和印刷很差的情况下,不会和其他的字母混淆在一起而影响阅读性。


    留白

    如果用黑白打印机,把微信文章打印出来,你会发现各种颜色间区别,其实只是灰度不同。
    纸上只有黑与白两部分。

    上面说的文字和符号,就是排版设计中「黑」的部分,而设计「白」的部分,也是非常重要的。

    字间距、行间距、段间距、页边距等,这些是「白」的部分。就像阴阳一样,黑白得当,才有了和谐的美感,给阅读留下了呼吸的空间。

    007 字间距

    针对一般的文本设计来说,字号越小,字母之间的间距就要越大,以使文字易于辨认。相反,如果将字号调大的话,紧致一些的间距以至于字符之间不会那么松垮,而更易阅读。

    可以在编辑器中对字间距进行调节,0.5 —1.5 都很常见。


    008 行间距

    文字太过密集,容易让读者失去阅读兴趣,很多好内容因此被埋没。而相反过于稀疏,视觉效果也并不理想。

    根据行业经验,行间距一般为字号的 1.5 倍左右。14px — 16px的字号,推荐 1.5 或者 1.75 的行间距。


    009 段间距

    段间距一般用回车空行代替,当然也可以进行手动设置,正文中设置 15 的段间距,在微信上看起来就非常美观。

    段间距也有不同层次的划分,可以参考一下知乎用户 @Hindy 的习惯:

    我将段落间距分为四档:空三回车、空两回车、空一回车与换行不空。
    • 空三回车:用在小节与小节结束的时候。比如一个标题小节结束,下文是另一个标题开始。
    • 空两回车:用在二级标题下的分隔。
    • 空一回车:用在段落与段落间,内容相较接近。
    • 换行不空:用在两段文字是讲述的一个内容,只是在阅读时需稍作停顿的情况。

    手动设置的话,标题和小节之间的行间距也有所差别。最好的方法,还是去参考喜欢的大号,模仿他们的习惯,然后在试图创新。


    010 段落长度

    正文段落的长度,是影响留白很大的元素。

    段落长,留白少。
    段落短,留白多。

    如果都是长段落,黑压压的大段文字会让人觉得呼吸不过来;如果是一连串的短段落,大片的留白会打断排版的连续性。

    而如果都是中等长度的段落,则就像唱片机卡碟一样,絮絮叨叨,缺乏丰富的节奏。

    控制段落长度的节奏,是非常重要的排版技巧,这点很多人都不知道。长、中、短段落结合,能让读者在大段文字中迅速找到落脚点。

    如此,排版真正成为文字情绪的一部分。

    最后,段落最长也千万不要超过一屏。下图这种神人行为,我们一般人千万不要尝试,简直喘不过气。


    011 页边距

    页边距是阅读时,正文与左右两边的距离。微信默认比较窄,可以用编辑器进行调整,给读者更多的阅读空间。

    有一些比较文艺风的文章,会设置很大的页边距来增加留白。根据自己情况来调整。


    012 图片的作用

    图片是为内容服务,我把图片分为两种类型:有意义的、无意义的。


    ▍有意义的图片
    有意义的图片有 4 种作用:分享、娱乐、销售、解决问题。

    1.分享

    分享某种情感,或者文章中提到的某个物品、人物、场景、行为等等。


    2.娱乐

    一些表情包、GIF 等都是此类,跟读者进行情感上的娱乐互动。


    3.销售

    跟文章内容结合,对读者进行销售。或者是销售某一种观点,或者是说服读者做某件事,如一些商品使用场景的图片。


    4.解决问题

    有些信息只靠文字很难直观表达,或者过于复杂,需要图片来进行解释说明,辅助理解,如一些数据图片、趋势图等等。


    ▍无意义的图片
    无意义的图片有 3 种作用:美化排版、阅读奖励、凸显调性

    1.美化排版

    图像能够打破视觉的单调性,如果一篇文章全是字,会让人兴趣索然,而通过配图来调节视线,让人不容易轻易疲劳。


    2.阅读奖励

    图片相比文字更容易理解,调动的读者认知成本比较少,读者在看图片的时间,可以让大脑进行适当休息,作为阅读中的奖励。

    尤其是一些有趣好笑的或者是性感美女图片,都是阅读奖励。


    3.凸显调性

    统一风格的图片能够塑造文章的调性,而高逼格的配图,会提升文章的质感。

    013 图片的选择标准

    简单来讲,图片有两大选择标准:清晰、统一。

    ▍清晰
    图片高清不模糊,是排版的基本要求。除了原始图片是否高清以外,不同的格式也会影响最终效果,图片的格式部分下文会讲。

    另外要注意图片上不要有水印,除非是经过特殊设计的水印,否则真的很土。

    ▍统一
    统一包括主题、风格、尺寸上的统一。图片要与文章主题统一,不同图片之间的风格要统一,不要上面都是漫画表情,下面一下子都变成了人物风景。另外不同类型的图片,最好将尺寸统一。


    014 封面图尺寸

    封面图是一篇微信文章的第一印象。封面图总共出现在 4 个场景,尺寸会变形,都有裁剪,而且在 Android 和 IOS 系统中,具体也会有所区别:

    • 公众号会话:矩形。
    • 历史消息:矩形。
    • 转发消息:正方形。
    • 分享朋友圈:正方形。

    上图是@王如飞 研究的封面图裁剪情况这张图太复杂,不用记住。只要记住封面图的尺寸是 900 X 500,次条封面图的尺寸是 200 X 200 就好。按照这个尺寸来配图,效果是最佳的。

    另外要把重要内容放在中间 400 X 400 的正方形内,这部分的内容一定不会被裁掉。


    015 制作

    ▍独家风格

    可以用美图秀秀或者 PS 等一些制图软件,加上一些文字、图形、logo 等固定元素,形成自己独特的风格。当然更厉害的就是请专业的插画师定制插画。


    ▍尺寸

    修改尺寸,可以用美图秀秀进行修改,或者直接用 PPT 也行。把图片放入 PPT 中,选中图片之后「点击格式 — 裁剪 — 纵横比 — 16:9」。



    ▍在线生成

    如果担心自己审美不达标的话,创客贴这个网站可以帮你一分钟制作出高大上的封面图。登录网站后,点击「开启设计 — 公众号文章首图」即可。

    创客贴:chuangkit.com


    ▍获取封面图

    关于如何获取别人的封面图,IT云课堂 提供了一个很实用的技巧:

    在电脑打开文章,右击鼠标查看源代码。
    然后按 Ctrl + F,出现一个搜索框。
    输入 var msg,出现黄色背景代码。
    复制 msg_cdn_url 后面的地址,在浏览器打开。
    点击「另存为」即可下载。

    016 图库

    ▍导航

    相信大家的文件夹里都收藏了一大堆图片网站,其实只要用好设计师导航网站就好了。绝大多数常用的图库,在这些导航中都有收录,并且已经按照免费 / 付费分好类,也有相应的介绍说明。

    这里推荐三个:
    设计导航:hao.shejidaren.com
    HiPPTer:hippter.com
    优庆设计:ubuuk.com

    导航网站中除了图片以外,还包含各种素材和神器,可以去探索一下。


    ▍常用图片网站

    下面这些是大家常用的图库网站,在上面三个导航中都有收录。

    Behance:behance.net
    Dribbble:dribbble.com
    Pinterest:pinterest.com
    Pixabay:pixabay.com
    Unsplash:unsplash.com
    500px:500px.com
    花瓣网:huaban.com
    站酷网:zcool.com.cn


    ▍特殊网站介绍
    工欲善其事,必先利其器。下面再介绍几个比较特殊的。

    文字云

    这个网站可以在线生成文字云效果的图片。网站是英文的,可以借助谷歌浏览器自带的翻译功能。具体步骤是「上传字体 — 添加文字 — 上传图片」。

    文字云网站:tagul.com


    表情

    很多文章配图里有大量的表情,可以在百度搜索「斗图」,有很多丰富的资源。也可以在「暴走漫画」网站,或者下载「花熊」App 进行制作。

    暴走漫画:baozoumanhua.com
    斗图啦:doutula.com


    Mockup

    Mockup是指图片展示素材模型,什么意思呢,就像这张图。

    你可以找一张手机截图,替换图中的手机图片,手机截图就可以瞬间变得高大上了。就像这样:

    推荐三个Mockup网站:
    smartmockups.com
    mockplus.cn
    freedesignresources.net

    第一个网站只有台式机、笔记本、平板、手机、手表五种模板,但是好处是只要选好模板,可以上传图片进行自动替换。其他两个网站有各式各样的其他素材,不过需要下载 PSD 图层进行手动替换。


    017 找图

    ▍关键词
    用关键词有两个搜索方向:纵向、横向。

    1.纵向

    纵向就是缩小或者扩大关键词的范围。

    比如说要搜索法律相关的图片,搜索「法律」没有满意的图片,则可以缩小范围,试试「律师」或者「法官」。

    同样也可以反过来扩大范围,比如需要一个小蛋糕的图片,可以试试搜索「下午茶」。

    2.横向

    横向就是搜索相近的词汇,很多时候找的图片是可以用其他图片代替的。

    比如需要一张表现时间图片,可以去搜索「手表」,也可以试试「钟表」、「watch」等等。另外也可以用英文或者其他语言去检索,出来的图片也会不一样。


    ▍以图识图

    有时候看到一些喜欢的图片,但是尺寸太小、不够高清,或者是有水印,这个时候可以用百度和 Google 的「以图识图」功能,让搜索引擎帮忙找这张图片的其他尺寸、格式。

    在百度图片中选择好一张图片后,点击「按图片搜索」:

    点击右下角那个放大镜,或者是在百度页面点击那个照相机,上传图片:

    搜索引擎会自动帮你找到不同尺寸的相似图片。

    另外也可以直接在谷歌浏览器的程序商店里下载安装 Tineye 插件。看到一张图后右键选择「Search Image on TinEye」,大多数图片都可以找到相似图片。

    ▍下载

    有的时候好不容易找到了一张图片,但是下载起来可能又很麻烦。有很多插件都支持一键下载功能,可以在任何网站一键下载图片。

    依旧推荐三个插件:
    gentleman
    OK记
    花瓣采集插件

    安装插件以后,在图片上直接 alt + 鼠标左键即可一键下载。除了插件,360浏览器、UC浏览器也有一键下载图片的功能。

    不过提醒大家一定要注意版权问题,不要盗用有版权的图片。

    018 图片处理

    ▍水印

    非原创图片不要打水印。对于原创图片,除非水印经过特殊设计,否则都不会好看,另外加了水印也不方便读者保存。

    有时候网上找来的一些图片需要去水印,这个时候可以用美图秀秀或者是 PS:

    美图秀秀:消除笔功能 / 祛痘功能
    PS:修复画笔功能 / 修补工具

    另外还有一款常用的去水印工具:Inpaint,可以傻瓜式一键去水印。

    ▍截图美化

    讲一个用 PPT 美化截图的小技巧。有时候文章中经常需要放一些手机截图,但是直接放在文章里并不好看,需要进行处理一下。

    这个时候可以借助 PPT 图片样式中的「居中矩形阴影」,为截图加上阴影。

    ▍版权声明

    要小心图片侵权,使用了别人的图片,一定要注明来源。实际上绝大多数公众号在这方面做的都不好,可以参考一下专业媒体号的做法。

    确定图源的,可以在图片下方,小字标明来自某部电影、某位摄影师、还是某个商业图库。

    如果不确定图片源,一定要注明「图片来自互联网」,通常写在文末左下方。如果是转载的文章,不确定图源,写上「图源:原作者配图」。

    019 GIF 图库

    1.Giphy

    最大的 Gif 搜索引擎,找 Gif 用好 Giphy 基本就够了。

    Giphy:giphy.com

    2.Tumblr

    俗称「汤不热」,国外非常好玩的社交平台,有大量精美的 Gif,可以关注那些网上推荐的 tumblr 账号。例如专门恶搞名画的 Scorpiondagger

    Tumblr:tumblr.com

    3.SOOGIF

    国内的动图网站,专门给公众号提供素材的。除此以外,还集合了GIF压缩、剪裁、制作的功能。

    SOOGIF:soogif.com

    4.Gif 喵

    也是斗图常用的国内网站,每天都会更新专辑,内容全面且动图质量比堆糖、花瓣上的普遍要高。此外也自带 Gif 制作和压缩功能。

    Gif 喵:gifmiao.com

    5.9gag

    暴走漫画最初就是模仿9gag,这个网站的 GIF 质量非常高,注册后右键另存为可以下载。但是需要视频格式将转换 GIF 格式。

    9gag:9gag.com

    6.小猪动图

    国内很专业的动图网站,素材更新快,并且把GIF动图划分了上百个子类。同时在网站上还可以对 GIF 压缩、制作、加水印、裁剪,以及将视频转换成 GIF 格式。

    小猪动图:piggif.com

    7.搜索引擎
    也可以在百度、必应、Google 等搜索引擎上直接搜索 GIF 图片,关键词 + gif,比如「小猫 gif」。

    8.其他

    叶子猪:xx.yzz.cn/
    NBAGIF:nba-80s-90sgifs.lofter.com
    Rafael-varona:rafael-varona.com
    GIFBIN:gifbin.com
    GIFSON:gifson.net
    Golden Wolf:goldenwolf.tv
    Julian Glander:glander.co
    &amp;lt;img src=&quot;https://pic3.zhimg.com/50/v2-5e4e1039b98367c9f38db3195cbd5d70_hd.jpg&quot; data-caption=&quot;&quot; data-size=&quot;normal&quot; data-rawwidth=&quot;540&quot; data-rawheight=&quot;304&quot; class=&quot;origin_image zh-lightbox-thumb&quot; width=&quot;540&quot; data-original=&quot;https://pic3.zhimg.com/v2-5e4e1039b98367c9f38db3195cbd5d70_r.jpg&quot;&amp;gt;


    020 GIF 处理

    ▍压缩

    在微信上传 GIF 的大小不能超过 2M,因此经常需要进行压缩。可以直接在上面提到的 SOOGIF、Gif 喵、小猪动图 这几个网站上面进行压缩,这里再推荐一个压缩常用的网站。

    ezgif.com/optimize

    ▍录制

    有时候需要自己录制一些 GIF,例如一些软件的教程。这里推荐几款口碑比较好、易上手的 GIF 录制软件。

    GIFCam(Win 系统)
    LICEcap(Mac 系统)
    Screen To Gif


    ▍转换

    有时候需要把一些视频转化为 GIF,上面提到小猪动图的网站就具备这个功能。另外可以用迅雷播放器直接从视频中截取 GIF 图。用迅雷播放器打开视频,点击右键「GIF 图截取」即可。

    021 顶部内容

    顶部是阅读文章时的第一眼,决定了读者对文章的初始印象。顶部的内容,一定要经过设计,否则就干脆保持空白。

    良好的设计会增加内容的可信任感,而那些烂大街的模板,不仅难看没有效果,反而会降低读者对公众号的评价。

    顶部通常放置的元素有:

    标题、slogan、头图、引导语、作者信息、声明信息、转载信息、音频等

    顶部内容大概有三个方面的用途:

    • 引导读者关注、置顶,通常是 logo + slogan。
    • 对文章信息进行补充说明,例如阅读时间、作者信息、声明信息、转载信息等。
    • 引出下文,通过一张图片,或者是一段引导语。

    下图是行动派的顶部内容,包含了:slogan、日历、授权信息。其中第二张图的日历每天都会变,是行动派的标志一直,也是与读者互动的一部分。

    weixin.qq.com/r/d3UdBWT (二维码自动识别)

    顶部的内容不要占用太大版面,要让读者快速切入文章。读者被标题吸引进来是为了看内容的,这个时候心情是急切的。如果顶部内容太多,半天看不到正文,反而起了反作用。

    022 底部的作用

    底部的元素有:

    底图、投票、作者信息、版权声明、合作说明、引导(话题互动)、关注语、往期推荐、二维码、赞赏、阅读原文。

    底部与顶部不同,读者已经看完了正文,整个人的状态比较放松,所以底部内容多一些也没有关系。这个时候应该尽量对上面缺失的要素进行补充,顶部没有放的信息,可以放在这里,一些重要信息可以选择重复。

    如领英的底部,包括了:话题互动、广告、互推、作者信息、合作说明、二维码:


    linkedin-event.com/triv (二维码自动识别)


    023 二维码

    二维码底部放置二维码是为了方便读者直接关注,不用再翻页到文章开头。

    一个经过用心设计的二维码,能够提高不少转化率。在各大编辑器中,都有各种二维码的样式,可以挑选合适的。

    有时候需要自己生成一些二维码,这个时候要借助两个工具:短网址、二维码生成器。

    网址越长,生成的二维码就越复杂难看,而微信自己的链接都太长了,下面就是一个长网址生成的二维码。

    mp.weixin.qq.com/s? (二维码自动识别)

    这时候需要借助短网址工具,将长网址进行缩短,用缩短后的网址去生成二维码。

    百度短网址:dwz.cn

    二维码生成器基本上都带有美化功能,生成后可以进行美化。这里推荐两个二维码生成器:

    草料二维码:cli.im
    第九工厂:9thws.com

    总结

    这篇文章是我个人经验的一些总结,讲了一些很琐碎的东西,可以收藏下来作为一篇工具文。

    同时,我最希望你记住「黑与白」的排版理念,这是排版的道。如果你忘记了,可以翻看留白那小节。

    ---------------------------------下面推荐20个排版好看的公众号--------------------------------

    weixin.qq.com/r/5ExUTJz (二维码自动识别)

    | 未读

    推荐之处:未读是做荐书的,经常做合集清单的同学推荐学习一下。

    | 拾遗

    推荐之处:我非常喜欢拾遗的配图,很有感觉。另外一节文字+一张图的排版样式也很值得学习。

    | 物道

    推荐之处:箱子有开箱体验,物道的落地头图也有一种开屏体验,很爽快。另外小标题会随着物品变动而改变,很有趣。

    | 誰最中國

    推荐之处:誰最中國的标题+配图+文章,尤其是配图特别有中国风,非常有辨识度。


    | 顾爷

    推荐之处:顾爷做的都是长图,因此也不受微信编辑器本身的限制。如果你在排版上非常有创意的话,可以模仿顾爷做成全图片。


    | 好色派沙拉

    推荐之处:好色派沙拉的配图都来自设计师之手,非常的有感觉。另外最后的图片链接做成推送的样子也很有意思。


    | voicer

    推荐之处:voicer是分享生活美学和设计的杂志,整体审美非常棒。


    weixin.qq.com/r/IEw9Jdv (二维码自动识别)

    | 丹尼尔先生

    推荐之处:这个号只有每周六是原创,作者很有品位。把(244,244,244)的底纹用到了极致。


    | JZsolve

    推荐之处:黑底白字很惊艳,而这只是 JZ 风格的一种,这个号除了干货什么都没有,连排版也是干货。


    | 可能吧

    推荐之处:可能吧的排版一直被人津津乐道,它并不花哨,但足够优雅和有呼吸感,对文字节奏的控制值得每个人学习。


    weixin.qq.com/r/KDgUDCf (二维码自动识别)

    | 正好有空

    推荐之处:正好有空的头图当时花了5千多块,历时半个月修改了几十次才定下来。可惜知乎上不能看动图,推荐关注一下看看效果。


    weixin.qq.com/r/4EOXj9b (二维码自动识别)

    | 行动派

    推荐之处:行动派的排版也经常被大家推崇,头图和尾图很好看,正文基本没有图片,但用小符号控制的很好。

    | 24hours

    推荐之处:24hours 的头图也很好看,值得学习,多图之间的节奏控制也很好。


    | AppSo

    推荐之处:AppSo 的排版比较适合那些“正经”一些的公众号学习参考。


    | 姜胡说

    推荐之处:这是我一个学员的公众号,在排版上也有很多值得借鉴和学习的地方。


    | 那一座城

    推荐之处:大家不要笑,那一座城的排版牛逼之处在于跟阅读情绪配合的十分到位,点名学习。


    | 企鹅吃喝指南

    推荐之处:很喜欢他们的动态头图,整体排版的感觉也很不错。


    | 阿司匹林博物馆

    推荐之处:封面图都是统一的插画,很有感觉,排版也很有呼吸感。


    | 附中人Further

    推荐之处:我不说你绝对猜不到这是一群高中生做的号,排版很赞,点名学习。


    | 坤龙老师

    推荐之处:最后给我的个人号打个广告,你没意见吧 ^_^


    ---------------------------------------------我是分割线--------------------------------

    我是坤龙,一家6个人的文化公司的创始人,90后新媒体老司机,在行超1000单行家。从业4年,前前后后参与了近50个公众号运营。


    往期精选:

    怎么在今日头条上赚钱?

    微信公众号互推如何操作?


    PS:其他干货请看微信公众号「坤龙老师团队」(ID:ikunlong) ,回复“涨粉”,送你9个涨粉方法。

    展开全文
  • 1.0 关于强引用引用的场景 1.1 强引用介绍 1.2 强引用的特点 1.3 注意相互引用情况 2.软引用 2.0 关于SoftReference软引用 2.1 软引用应用场景 2.2 软引用的简单使用 2.3 软引用的特点 2.4 实际应用案例 ...

    目录介绍

    • 0.关于四种引用
      • 0.1 引用说明
      • 0.2 关于Java下ref包和Android下ref包
    • 1.强引用
      • 1.0 关于强引用引用的场景
      • 1.1 强引用介绍
      • 1.2 强引用的特点
      • 1.3 注意相互引用情况
    • 2.软引用
      • 2.0 关于SoftReference软引用
      • 2.1 软引用应用场景
      • 2.2 软引用的简单使用
      • 2.3 软引用的特点
      • 2.4 实际应用案例
      • 2.5 注意避免软引用获取对象为null
    • 3.弱引用
      • 3.0 关于WeakReference弱引用
      • 3.1 WeakReference:防止内存泄漏,要保证内存被虚拟机回收
        • 3.1.1 先看一个handler小案例
        • 3.1.2 为什么这样会造成内存泄漏
        • 3.1.3 根本原因
      • 3.2 弱引用解决办法
      • 3.3 弱引用实际应用案例
    • 4.虚引用
      • 4.0 关于PhantomReference类虚引用
      • 4.1 Android实际开发中没有用到过
    • 5.四种引用其他介绍
      • 5.1 弱引用和软引用区别
      • 5.2 使用软引用或者弱引用防止内存泄漏
      • 5.3 到底什么时候使用软引用,什么时候使用弱引用呢?
      • 5.4 四种引用用一张表总结[摘自网络]
    • 6.源码分析
      • 6.1 首先看看如何通过弱引用加载图片
      • 6.2 看看Reference的源代码
      • 6.3 看看ReferenceQueue的enqueue函数
      • 6.4 看看ReferenceQueue的enqueueLocked(Reference)函数
      • 6.5 接着看看ReferenceQueue.isEnqueued()代码
      • 6.6 那么enqueueLocked(Reference)函数中的Cleaner是做什么的
      • 6.7 软引用SoftReference源码
      • 6.8 弱引用WeakReference源码
      • 6.9 虚引用PhantomReference源码
    • 7.关于其他
      • 7.1 关于参考案例
      • 7.2 关于版本说明
      • 7.3 关于我的博客

    好消息

    • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
    • 链接地址:https://github.com/yangchong211/YCBlogs
    • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

    特此说明

    0.关于四种引用

    0.1 引用说明

    • java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。

    0.2 关于Java下ref包和Android下ref包

    • 在Android下的ref包结构
      image
    • 在java下的ref包
      image

    1.强引用

    1.0 关于强引用引用的场景

    • 直接new出来的对象
    • String str = new String(“yc”);

    1.1 强引用介绍

    • 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
    • 通过引用,可以对堆中的对象进行操作。在某个函数中,当创建了一个对象,该对象被分配在堆中,通过这个对象的引用才能对这个对象进行操作。

    1.2 强引用的特点

    • 强引用可以直接访问目标对象。
    • 强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。
    • 强引用可能导致内存泄露。

    1.3 注意相互引用情况

    2.软引用

    2.0 关于SoftReference软引用

    • SoftReference:软引用–>当虚拟机内存不足时,将会回收它指向的对象;需要获取对象时,可以调用get方法。
    • 可以通过java.lang.ref.SoftReference使用软引用。一个持有软引用的对象,不会被JVM很快回收,JVM会根据当前堆的使用情况来判断何时回收。当堆的使用率临近阈值时,才会回收软引用的对象。

    2.1 软引用应用场景

    • 例如从网络上获取图片,然后将获取的图片显示的同时,通过软引用缓存起来。当下次再去网络上获取图片时,首先会检查要获取的图片缓存中是否存在,若存在,直接取出来,不需要再去网络上获取。

    2.2 软引用的简单使用

    • 用法如下
    MyObject aRef = new  MyObject();
    SoftReference aSoftRef = new SoftReference(aRef);
    MyObject anotherRef = (MyObject)aSoftRef.get();
    

    2.3 软引用的特点

    • 2.3.1 特点:
    • 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
    • 2.3.2 代码如下
    ReferenceQueue queue = new  ReferenceQueue();
    SoftReference  ref = new  SoftReference(aMyObject, queue);
    
    • 2.3.3 如何回收:
    • 那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
    • 在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。
    • 常用的方式为
    SoftReference ref = null;
    while ((ref = (EmployeeRef) q.poll()) != null) {
        // 清除ref
    }
    

    2.4 实际应用案例

    • 2.4.1 正常是用来处理图片这种占用内存大的情况
    • 代码如下所示
    View view = findViewById(R.id.button);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
    Drawable drawable = new BitmapDrawable(bitmap);
    SoftReference<Drawable> drawableSoftReference = new SoftReference<Drawable>(drawable);
    if(drawableSoftReference != null) {
        view.setBackground(drawableSoftReference.get());
    }
    
    • 2.4.2 这样使用软引用好处
    • 通过软引用的get()方法,取得drawable对象实例的强引用,发现对象被未回收。在GC在内存充足的情况下,不会回收软引用对象。此时view的背景显示
    • 实际情况中,我们会获取很多图片.然后可能给很多个view展示, 这种情况下很容易内存吃紧导致oom,内存吃紧,系统开始会GC。这次GC后,drawables.get()不再返回Drawable对象,而是返回null,这时屏幕上背景图不显示,说明在系统内存紧张的情况下,软引用被回收。
    • 使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

    2.5 注意避免软引用获取对象为null

    • 在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

    3.弱引用

    3.0 关于WeakReference弱引用

    • 3.0.1 WeakReference
    • 弱引用–>随时可能会被垃圾回收器回收,不一定要等到虚拟机内存不足时才强制回收。要获取对象时,同样可以调用get方法。
    • 3.0.2 特点
    • 如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
    • 弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    3.1 WeakReference:防止内存泄漏,要保证内存被虚拟机回收

    • 3.1.1 先看一个handler小案例【千万不要忽视淡黄色警告】
      image

    • 3.1.2 为什么这样会造成内存泄漏

    • 这种情况就是由于android的特殊机制造成的:当一个android主线程被创建的时候,同时会有一个Looper对象被创建,而这个Looper对象会实现一个MessageQueue(消息队列),当我们创建一个handler对象时,而handler的作用就是放入和取出消息从这个消息队列中,每当我们通过handler将一个msg放入消息队列时,这个msg就会持有一个handler对象的引用。因此当Activity被结束后,这个msg在被取出来之前,这msg会继续存活,但是这个msg持有handler的引用,而handler在Activity中创建,会持有Activity的引用,因而当Activity结束后,Activity对象并不能够被gc回收,因而出现内存泄漏。

    • 3.1.3 根本原因

    • Activity在被结束之后,MessageQueue并不会随之被结束,如果这个消息队列中存在msg,则导致持有handler的引用,但是又由于Activity被结束了,msg无法被处理,从而导致永久持有handler对象,handler永久持有Activity对象,于是发生内存泄漏。但是为什么为static类型就会解决这个问题呢?因为在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。声明为静态后,handler将会持有一个Activity的弱引用,而弱引用会很容易被gc回收,这样就能解决Activity结束后,gc却无法回收的情况。

    3.2 弱引用解决办法

    • 代码如下所示
    private MyHandler handler = new MyHandler(this);
    private static class MyHandler extends Handler{
        WeakReference<FirstActivity> weakReference;
        MyHandler(FirstActivity activity) {
            weakReference = new WeakReference<>(activity);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
            }
        }
    }
    

    3.3 弱引用实际应用案例

    4.虚引用

    4.0 关于PhantomReference类虚引用

    • 虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,奖这个虚引用加入引用队列。

    4.1 Android实际开发中没有用到过

    • 貌似开发中没有接触过虚引用

    5.四种引用其他介绍

    5.1 弱引用和软引用区别

    • 弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。

    5.2 使用软引用或者弱引用防止内存泄漏

    • 在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。
    • 软引用,弱引用都非常适合来保存那些可有可无的缓存数据。如果这样做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间。

    5.3 到底什么时候使用软引用,什么时候使用弱引用呢?

    • 个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
    • 还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。
    • 另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。

    5.4 四种引用用一张表总结[摘自网络]

    image

    6.源码分析

    6.1 首先看看如何通过弱引用加载图片

    image

    6.2 看看Reference的源代码

    • 6.2.1 源码说明:

    • 看到Reference除了带有对象引用referent的构造函数,还有一个带有ReferenceQueue参数的构造函数。那么这个ReferenceQueue用来做什么呢?

    • 需要我们从enqueue这个函数来开始分析。当系统要回收Reference持有的对象引用referent的时候,Reference的enqueue函数会被调用,而在这个函数中调用了ReferenceQueue的enqueue函数。

    • 那么我们来看看ReferenceQueue的enqueue函数做了什么?

    • 6.2.2 看看这段源代码

    public abstract class Reference<T> {
    
        private static boolean disableIntrinsic = false;
        private static boolean slowPathEnabled = false;
        volatile T referent;         /* Treated specially by GC */
        final ReferenceQueue<? super T> queue;
        Reference queueNext;
        Reference<?> pendingNext;
    
        //返回此引用对象的引用。如果这个引用对象有由程序或垃圾收集器清除,然后此方法返回
        public T get() {
            return getReferent();
        }
    
        private final native T getReferent();
    
        //清除此引用对象。调用此方法不会将对象加入队列
        public void clear() {
            this.referent = null;
        }
    
        //是否引用对象已进入队列,由程序或垃圾收集器。
        //如果该引用对象在创建队列时没有注册,则该方法将始终返回
        public boolean isEnqueued() {
            return queue != null && queue.isEnqueued(this);
        }
    
        //添加引用对象到其注册的队列,如果他的方法是通过java代码调用
        public boolean enqueue() {
           return queue != null && queue.enqueue(this);
        }
    
        Reference(T referent) {
            this(referent, null);
        }
    
        Reference(T referent, ReferenceQueue<? super T> queue) {
            this.referent = referent;
            this.queue = queue;
        }
    }
    

    6.3 看看ReferenceQueue的enqueue函数

    • 6.3.1 源码说明
    • 可以看到首先获取同步锁,然后调用了enqueueLocked(Reference)函数
    • 6.3.2 看看这段代码
      image

    6.4 看看ReferenceQueue的enqueueLocked(Reference)函数

    • 6.4.1 源码说明
    • 通过 enqueueLocked函数可以看到ReferenceQueue维护了一个队列(链表结构),而enqueue这一系列函数就是将reference添加到这个队列(链表)中
    • 6.4.2 看看这段代码
      image

    6.5 接着看看ReferenceQueue.isEnqueued()代码

    • 6.5.1 让我们回到Reference源码中
    • 可以看到除了enqueue这个函数还有一个isEnqueued函数,同样这个函数调用了ReferenceQueue的同名函数,源码如下:
    boolean isEnqueued(Reference<? extends T> reference) {
        synchronized (lock) {
            return reference.queueNext != null && reference.queueNext != sQueueNextUnenqueued;
        }
    }
    
    • 6.5.2 源码分析说明
    • 可以看到先获取同步锁,然后判断该reference是否在队列(链表)中。由于enqueue和isEnqueue函数都要申请同步锁,所以这是线程安全的。
    • 这里要注意“reference.queueNext != sQueueNextUnenqueued”用于判断该Reference是否是一个Cleaner类,在上面ReferenceQueue的enqueueLocked函数中我们可以看到如果一个Reference是一个Cleaner,则调用它的clean方法,同时并不加入链表,并且将其queueNext设置为sQueueNextUnequeued,这是一个空的虚引用

    6.6 那么enqueueLocked(Reference)函数中的Cleaner是做什么的

    • 在stackoverflow网站中找到这个解释
      • sun.misc.Cleaner是JDK内部提供的用来释放非堆内存资源的API。JVM只会帮我们自动释放堆内存资源,但是它提供了回调机制,通过这个类能方便的释放系统的其他资源。
      • 可以看到Cleaner是用于释放非堆内存的,所以做特殊处理。
      • 通过enqueue和isEnqueue两个函数的分析,ReferenceQueue队列维护了那些被回收对象referent的Reference的引用,这样通过isEnqueue就可以判断对象referent是否已经被回收,用于一些情况的处理。

    6.7 软引用SoftReference源码

    • 6.7.1 关于这段源码分析

    • 可以看到SoftReference有一个类变量clock和一个变量timestamp,这两个参数对于SoftReference至关重要。

      • clock:记录了上一次GC的时间。这个变量由GC(garbage collector)来改变。
      • timestamp:记录对象被访问(get函数)时最近一次GC的时间。
    • 那么这两个参数有什么用?

      • 我们知道软引用是当内存不足时可以回收的。但是这只是大致情况,实际上软应用的回收有一个条件:
      • clock - timestamp <= free_heap * ms_per_mb
      • free_heap是JVM Heap的空闲大小,单位是MB
      • ms_per_mb单位是毫秒,是每MB空闲允许保留软引用的时间。Sun JVM可以通过参数-XX:SoftRefLRUPolicyMSPerMB进行设置
    • 举个栗子:

      • 目前有3MB的空闲,ms_per_mb为1000,这时如果clock和timestamp分别为5000和2000,那么
      • 5000 - 2000 <= 3 * 1000
      • 条件成立,则该次GC不对该软引用进行回收。
      • 所以每次GC时,通过上面的条件去判断软应用是否可以回收并进行回收,即我们通常说的内存不足时被回收。
    • 6.7.2 源码如下所示

    public class SoftReference<T> extends Reference<T> { 
        static private long clock; 
        private long timestamp; 
        public SoftReference(T referent) { 
            super(referent); 
            this.timestamp = clock; 
        } 
        public SoftReference(T referent, ReferenceQueue<? super T> q) { 
            super(referent, q); 
            this.timestamp = clock; 
        } 
        public T get() { 
            T o = super.get(); 
            if (o != null && this.timestamp != clock) 
                this.timestamp = clock; 
            return o; 
        } 
    } 
    

    6.8 弱引用WeakReference源码

    • 6.8.1 源码分析说明
    • 没有其他代码,GC时被回收掉。
    • 6.8.2 源码如下所示
    public class WeakReference<T> extends Reference<T> { 
        public WeakReference(T referent) { 
            super(referent); 
        } 
        public WeakReference(T referent, ReferenceQueue<? super T> q) { 
            super(referent, q); 
        } 
    } 
    

    6.9 虚引用PhantomReference源码

    • 6.9.1 源码分析说明

    • 可以看到get函数返回null,正如前面说得虚引用无法获取对象引用。(注意网上有些文章说虚引用不持有对象的引用,这是有误的,通过构造函数可以看到虚引用是持有对象引用的,但是无法获取该引用

    • 同时可以看到虚引用只有一个构造函数,所以必须传入ReferenceQueue对象。

    • 前面提到虚引用的作用是判断对象是否被回收,这个功能正是通过ReferenceQueue实现的。

    • 这里注意:不仅仅是虚引用可以判断回收,弱引用和软引用同样实现了带有ReferenceQueue的构造函数,如果创建时传入了一个ReferenceQueue对象,同样也可以判断。

    • 6.9.2 源码如下所示

    public class PhantomReference<T> extends Reference<T> { 
        public T get() { 
            return null; 
        } 
        public PhantomReference(T referent, ReferenceQueue<? super T> q) { 
            super(referent, q); 
        } 
    }
    

    7.关于其他

    7.1 关于参考案例

    7.2 关于版本说明

    • v1.0 16年6月18日
    • v1.1 17年11月18日,简单的分析了源码
    • v1.2 18年重新整理了笔记

    7.3 关于我的博客

    展开全文
  • 下午ytkah在自己小博客搜索时看到有几篇文章图片显示不了,再访问一些网站时发现有些图片无法显示出来,显示"此图片来自微信公众号平台未经允许不可引用",如下图所示,这个应该是最近微信团队对有原创保护能力的...
  • Python微信公众号

    2017-11-27 14:39:03
    尽管很多人吐槽王者荣耀里的小学生太坑爹,但不得不承认,近年来腾讯的...微信公众号是个人或者企业的一个宣传平台,通过开发微信公众号,可以给关注公众号的用户提供更多定制化的服务,进一步可以将服务转化为效益。
  • 微信公众号主要有以下几个步骤 微信公众号的通讯机制 微信公众号简介 1.注册微信公众号 2.注册测试公众号 3.搭建微信本地调试环境 1)下载客户端natapp: 2)安装natapp: 4.微信公众号接入(校验签名) 第1步中...
  • thinkphp5微信公众号支付

    千次阅读 2018-07-17 16:34:42
    用户通过微信公众号打开网页, 进入到如下界面,选择或输入相应金额为网站中用户账号充值。 二、开发步骤 这里就不再介绍商户如何接入微信支付,官方文档中已经有了详细介绍,具体请参考微信公众号支付官方文档...
  • 关于我 Base:山东烟台,“美丽的海滨城市,最幸福的时光莫过于闲暇时间,牵...个人网站地址:https://www.lovebetterworld.com/ CSDN:https://blog.csdn.net/an1090239782 博客园:https://www.cnblogs.com/ai
  • 微信公众号开发入门

    2020-07-17 09:29:59
    ​ 本文是主要是针对了解微信公众号开发或者进行过一些简单的开发,但是不成体系的开发者。前后端在参与公众号开发期间,主要承担的是各自的开发工作,前后端逻辑隔离较大。本文将从申请测试账号开始,选择常用的...
  • 公众号内卷

    2021-01-10 16:30:00
    曾几何时公众号文章的标题单纯且没有套路3年前的我就是这样仅仅把公众号当做一个文章记录平台《Linux - 环境变量和可执行属性》《R语言 - 热图绘制 (heatmap)》《关于编程学习...
  • 微信公众号开发点滴

    2017-04-02 15:55:00
    任何从微信客户端发起对第三方网站访问时,如果希望实现微信第三方登陆,必须使用公众号网页授权这个接口来开发实现。 或者对于PC端的网站使用PC浏览器访问网站时,希望使用微信来作第三方登录,这时也可以说是微信...
  • 微信公众号内调用微信支付接口wx.chooseWXPay,需要引用JSSDK,配置config,这个步骤在我的另一篇调用地图接口博客有详细说明,这里就不介绍了,然后微信公众号里还提供了wx.onBridgeReady,这个就不需要配置直接调用...
  • 主要介绍了微信小程序如何访问公众号文章,随着小程序不断的发展,现在个人的小程序也开放了很多功能了,个人小程序直接打开公众号链接。在群里看到的一款小程序,点击可以直接阅读文章了,需要的朋友可以参考下
  • 微信公众号运营学习

    2020-12-23 23:57:12
    微信公众号运营学习概要公众号的目的和定位功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右...
  • 这里是修真院前端小课堂,每篇分享文从 【背景介绍】【知识剖析】【常见问题】【解决方案】【编码实战】...复盘项目并不是所有的项目都是我们在任务中做的普通的WEB网站,有的项目是安卓APP,有的项目是需要在...
  • 公众号有两个access_token。 一个是基础支持中的access_token 通过下面的接口获取,这个接口有调用次数限制 https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&ap....
  • 微信公众号文章跨域展示

    千次阅读 2019-05-16 15:45:43
    帮朋友做了个整站,更新新闻的时候他用不惯我写的后台,老是发微信公众号,让我帮忙发到网站上,我觉得太麻烦了,就写了个调用的方法。 微信公众号自带保护,不让跨域访问。 只能用...
  • 1.首先进入微外链官方网站:https://uom.cn/a/ 点击右上角的登录注册按钮,先注册一个账号(一般都会自动永久保存) 2.再将外链网址复制粘贴到下方输入框中: 3.点击提交按钮(前提是登录了账号)即可获取...
  • 微信公众号自动回复

    2019-05-08 09:38:00
    入口文件 用于验证token (对应微信公众号服务器配置) <?phptraceHttp();//1、将token、timestamp、nonce三个参数进行字典序排序$token = 'yangzhenhua';$timestamp = $_GET['timestamp'];$nonce = $_GET['...
  • 关于微信公众号《云爬虫技术研究笔记》可以看到更多哦! 背景   最近发现搜狗微信在2019.10.29号的时候悄然下线了一个功能,也就是不能在搜狗搜索中指定公众号的名称,如下图   很多媒体以及...
  • 现在很多博客网站、微信公众号都可以发布文章,每天看着别人发布的排版布局精美的文章时,自己就特别羡慕,特别是在自己也想要开始写微信公众号文章的时候就特别难受。那时就在想:人家是怎样写出来的?用word写好后...
  • 前言 早前进入it这个行业就有写博客的... 附录 jsdelivr 网站地址:https://www.jsdelivr.com/?docs=gh markdown-nice官网:https://www.mdnice.com/ mark解析器flexmark地址:https://github.com/vsch/flexmark-java
  • 直接返回官方图片链接,方便引用; 全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 / v1.3); 全面兼容 Apple ATS; 全国多节点 CDN 部署; 接口极速响应,多台服务器构建 API 接口负载均衡。 2. API 文档 接口地址: ...
  • 公众号招新总结

    2021-03-16 19:25:36
    这里推荐一个前端jquery插件库:http://www.jq22.com/jqueryUI-1-jq 关于前端部分如何快速上手,以招新页面使用的上传文件插件为例:Webuploader 在需要使用的html上引用: 在对应的js文件里初始化方法: 具体可阅读...
  • 1、支付js引用添加 <script src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script> 2、具体的实现流程: 1.首先获取到支付订单号 2.根据支付订单号获取支付参数 3.通过JavaScript调用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,742
精华内容 4,296
关键字:

网站引用公众号