Introduction  众所周知,web请求(HTTP请求)是根据请求/响应机制工作的。通过这种方式,作为客户机的浏览器使用GET或POST向服务器发送请求,服务器根据客户机对它的请求准备适当的响应,然后关闭它们之间的连接。因此,这个过程是一种单向通信,客户端是初学者,因此服务器只是一个响应,它不能发送一个请求给客户端,并通知他们自己的状态。对于开发人员来说,这种请求/响应机制似乎是一个麻烦的限制。为什么我们被强制发送一个请求来告知当前的服务器状态?我们如何摆脱这个响应/请求问题?这个问题促使开发人员尝试许多方法、技巧和技术来克服这个限制。其中的一些方法是: 刷新页面在一个周期时间:这个方法是最糟糕的,因为它刷新所有元素的指定页面没有必要性refreshmentRefreshing页面在一个周期的一部分时间使用Iframe 彗星Programming  TechniquesWeb Socket : HTML5和IIS7 在本文中,我们主要讨论Web套接字、Comet技术和SignalR,它是一个很好的工具,可以帮助在ASP中使用Comet技术。网络开发者。 彗星Programming  Techniques  我引用以下维基百科的描述: Comet是一种web应用程序模型,在该模型中,一个长期存在的HTTP 请求允许web服务器将数据推送到浏览器,而不需要浏览器明确地请求它。所有这些方法都依赖于浏览器默认包含的特性,比如JavaScript,而不是非默认插件。Comet方法不同于web的原始模型,在原始模型中,浏览器一次请求一个完整的web页面 Comet技术在web开发中的使用早于Comet一词作为集体技术的新词的使用。Comet还有其他几个名字,包括Ajax Push,[4][5] Reverse Ajax,[6]双向web,[7] HTTP流,[7]和HTTP服务器Push[8]等等。 Comet有一些用于实现的技术,它们是两大类的子集:流和长轮询 流媒体包括以下: 依次向服务器(传统的AJAX)隐藏框架发送AJAX请求 长轮询的AJAX:“以上所有流传输都不能在所有现代浏览器上正常工作而不产生负面的副作用。这迫使Comet开发人员实现几个复杂的流传输,并根据浏览器在它们之间切换。因此,许多Comet应用程序使用长轮询,这更容易在浏览器端实现,并且至少可以在支持XHR的每个浏览器中工作。顾名思义,长轮询要求客户机向服务器轮询一个事件(或一组事件)。浏览器向服务器发出ajax风格的请求,该请求一直保持打开状态,直到服务器有新数据要发送给浏览器,该数据将以完整的响应发送给浏览器。浏览器启动一个新的长轮询请求,以获取后续事件。完成长轮询的具体技术包括: XMLHttpRequest长轮询脚本标记长轮询 SignalR 使用comet技术进行编程是很复杂的,如果您想这样做的话,它将是一项困难而耗时的工作。SignalR是一个用于ASP的库。NET在web应用程序中添加实时功能。使用SignalR并不困难,但更重要的是,它根据浏览器的功能使用一组comet技术。如果你的浏览器不支持HTML5和web套接字技术,SignalR将回到以前的框架或长池作为comet优先级(例如:IE8)。使用SignalR需要有丰富的背景对客户端和服务器之间的通信,但是这个库是一个简单的方法使用彗星,这可能就像一把双刃剑肤浅的程序员(程序员所使用客户端框架的豪言壮语jQuery和JavaScript编程没有经验,因为他们大多没有深度了解客户端编程),这可能会导致缺乏很多不适。此外,你应该了解。net 4中的动态类型。除此之外,SignalR是一个很棒的库,它提供了许多用于创建实时web应用程序的高级功能。 如何安装信号 进入Nuget: PM>安装包Microsoft.AspNet.SignalR 和 PM>安装包SignalR版本0.6.1 为了得到它的例子,在Nuget上键入这一行: PM>安装包Microsoft.AspNet.SignalR.Sample安装后,一些dll将添加到bin目录中,一些JavaScript文件将添加到Scripts文件夹中。 对于IE8,你应该安装JSON2.js如下: PM>安装包json2 如果这个文件没有安装到你的脚本文件夹,你会遇到这个错误: 隐藏,复制Code

No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file

如何使用信号 有两种使用SignalR的框架,包括Hub和persistence connection。Hub提供了一个比持久连接更高级的框架。PersistentConnection给了你很多控制权。如果您想让SignalR为您做所有繁重的工作,并将困难的工作分配给SignalR来自动完成,您可以使用Hub框架。在本文中,我们使用Hub。 首先将以下代码写入全局变量。在ApplicationStart()方法中的asax: 隐藏,复制Code

RouteTable.Routes.MapHubs();  

与低级的持久连接不同,不需要为hub指定路由,因为它们可以通过一个特殊的URL自动访问。 创建一个从Hub类继承的类。 隐藏,复制Code

public class CommentHub : Hub {......}

客户机调用服务器 要展示一个可从所有客户端调用的方法,只需声明一个公共方法: 隐藏,复制Code

public class MyHub : Hub
{
public string Send(string data)
{
return data;
}
}

此方法可从客户端访问。客户端使用指定的数据作为参数调用此方法,然后此方法可以对输入数据执行任何操作,然后将其作为输出返回。 隐藏,复制Code

public string get(string data)
{
return data;
}

服务器调用客户端 从服务器调用客户端方法使用客户端属性: 隐藏,复制Code

public string get(string data)
{
return Clients.All.broadcastMessage(data);
}

现在我们扩展我们的描述,关注细节并仔细阅读项目代码。 在这个项目中,主要文件有: CommentHub。cs:根据注释成功或失败的过程,将添加的注释的XML文档(已在客户机中生成)转换为适当的html。发表评论。注释的实体及其属性注释。xslt Comments.aspx CommentHub类 隐藏,收缩,复制Code

public class CommentHub : Hub
{
public void Send(string name, string message, int status, Guid hubId,
string thisCommentId,string contentId,string currentUserID)
{
XmlDocument XmlMessage = new XmlDocument();
XmlMessage.LoadXml(message);
string CommentId=XmlMessage.SelectSingleNode("comment").Attributes["id"].Value;
string messageBody = XmlMessage.SelectSingleNode("comment/message").InnerText;
XSLTransformer XSL = new XSLTransformer();
XsltArgumentList argsList = new XsltArgumentList();
try
{
if (messageBody != string.Empty && messageBody != null)
{
if (message.Contains("fail"))
{
argsList.AddParam("ResultSet", "", 0);
Clients.All.broadcastMessage(name, XSL.TransformToHtml(XmlMessage,
"Comments.xslt", argsList), 0, hubId, CommentId);
} //Adding parameters to XSLT
//If ResultSet set to 1,it means success
else
{
argsList.AddParam("ResultSet", "", 1);
argsList.AddParam("CommentingDate", "", DateTime.Now);
Clients.All.broadcastMessage(name, XSL.TransformToHtml(XmlMessage,
"Comments.xslt", argsList).Replace(
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
""), 1, hubId, CommentId);
}
}
}
catch (Exception exp)
{
//Adding parameters to XSLT
//If ResultSet set to 0,it means fail
argsList.AddParam("ResultSet", "", 0);
switch (exp.GetType().ToString())
{
case "SqlException":
Clients.All.broadcastMessage(name, XSL.TransformToHtml(XmlMessage,
"Comments.xslt", argsList), -2, hubId,CommentId);
break;
default:
Clients.All.broadcastMessage(name, XSL.TransformToHtml(XmlMessage,
"Comments.xslt", argsList), 0, hubId, CommentId);
break;
}
}
}
}

从客户端调用Send()方法,客户端将所需的参数传递给该方法。其中一个参数是message。实际上message是由客户机生成的XML,它包含当前评论的一些属性和属性,如:用户名、CommentID和message。这个参数被加载到一个XmlDocument中,然后XSLTransformer将这个XML文档转换为将广播给用户的适当的HTML文档。在这个项目中,没有数据库中的存储注释或其他功能的代码,但您可以做任何您想做的事情。例如,假设我们的代码将注释存储到数据库中,通过插入过程,代码遇到了异常,比如连接失败等等。在这种情况下,我们想通知用户他的评论没有被发送,并给他再一次机会重新发送评论。为了实现此操作,我们将ResultSet参数添加到XSLT Transformer的参数列表中,以便对html输出进行决策。如果结果集被设置为“0”,则表示失败,而“1”表示成功。正如我写的,客户,所有人。broadcastMessage意味着我们在客户端有一个名为“broadcastMessage”的函数,服务器可以调用它。 下面的代码模拟了失败状态。如果你写了一个包含“fail”的句子,你会看到失败状态的结果。 隐藏,复制Code

//Lines 38 to 42 simulate failed status
if (message.Contains("fail"))
{
argsList.AddParam("ResultSet", "", 0);
Clients.All.broadcastMessage(name, XSL.TransformToHtml(XmlMessage,
"Comments.xslt", argsList), 0, hubId, CommentId);
}

SignalR JS客户端hub: 在你的页面上包括以下脚本: 隐藏,复制Code

<script src="http://www.codeproject.com/Scripts/json2.js"></script>
<script src="http://www.codeproject.com/Scripts/jquery-1.7.1.min.js"></script>
<script src="http://www.codeproject.com/Scripts/jquery.signalR-1.0.0-rc2.js"></script>
<script src="http://www.codeproject.com/signalr/hubs"></script>

编程模型 .connection.hub美元 所有集线器的连接(URL指向/signalr)。 返回a 连接 .connection.hub.id美元 hub连接的客户端id。 .connection.hub.logging美元 设置为true则启用日志记录。默认的是假的 $ .connection.hub.start () 启动所有集线器的连接。 查看其他重载,开始()返回jQuery延迟 .connection美元。{hubname}, 从生成的代理访问客户端中心。 返回一个中心。服务器上集线器的名称。注意:集线器的名称是驼峰式大小写(例如,如果服务器集线器是MyHub,连接上的属性将是MyHub) stateChanged是一个函数,每次连接状态改变时执行。 隐藏,复制Code

$.connection.hub.error(function (error) {
$.connection.hub.stop();
}); $.connection.hub.stateChanged(function (change) {
if (change.newState === $.signalR.connectionState.reconnecting) {
//console.log('Re-connecting');
}
else if (change.newState === $.signalR.connectionState.connected) {
//console.log('The server is online');
}
});

首先,我们声明一个变量如下: 隐藏,复制Code

var commentList = $.connection.commentHub; 

根据以上描述,其功能如下: 隐藏,收缩,复制Code

//-------functions------
$(function () {
commentList.client.broadcastMessage = function (name, message, status, hubId, thisCommentId) {
var messageResonse = message;
var oo = thisCommentId.toString();
var GUID = null;
if (writerHubId == hubId) {
GUID = comments.generateGuid();
messageResonse = message + "<tr><td align='left' id='delete_" + GUID
+ "' width='200px' align='left' style='color:red;'>" +
"<a href='javascript:void(0)' " +
"onclick='javascript:comments.deleteComment(\""
+ oo + "\");return false;'><img title='delete' " +
"style='width:20px;height:20px;border:0px;cursor:pointer' " +
"src='Images/trash_green.png' /></a></td></tr>;
}
messageResonse = "<table width='100%' id='PT_" + thisCommentId
+ "' style='background-color:#F1F1F1;" +
"border:1px;border-style:solid;border-color:#C4C4C4'>"
+ comments.setRealBreakLine(messageResonse)
+ "</table><table id='HDT_" + thisCommentId +
"' width='100%'><tr><td " +
"height='15px'></td></tr></table>";
if (status == 1)
$('#Comments').append(messageResonse);
else if ((writerHubId == hubId)
&&
(status == 0 || status == -2))
$('#Comments').append(comments.setRealBreakLineForInnerTextOnTextArea(message));
}; $('#message').focus();
$.connection.hub.start({ waitForPageLoad: false }, function () {
}).done(function () {
$('#sendmessage').click(function () {
sendOverHub();
});
}); $.connection.hub.error(function (error) {
$.connection.hub.stop();
}); $.connection.hub.stateChanged(function (change) {
if (change.newState === $.signalR.connectionState.reconnecting) {
//console.log('Re-connecting');
}
else if (change.newState === $.signalR.connectionState.connected) {
//console.log('The server is online');
}
});
});

broadcastMessage函数由服务器调用,并为客户端显示最后的注释。为了检查用户对自己的评论进行删除评论等行为的可访问性,我们需要检查当前用户的hubId是否等于当前评论已发送的hubId。 隐藏,复制Code

var GUID = null;
if (writerHubId == hubId) {
GUID = comments.generateGuid();
messageResonse = message + "<tr><td align='left' id='delete_" + GUID
+ "' width='200px' align='left' style='color:red;'><a "
+ "href='javascript:void(0)' onclick='javascript:comments.deleteComment(\""
+ oo + "\");return false;'><img title='delete' "
+ "style='width:20px;height:20px;border:0px;cursor:pointer' " +
"src='Images/trash_green.png' /></a></td></tr>";
}

函数通过CommentHub类的Send方法发送注释。在本文中,我没有为用户使用不同的名称,而是设置了相同的nam因为代码简单,所以被称为“测试用户”。 隐藏,复制Code

function send() {
writerHubId = $.connection.hub.id;
commentList.server.send(
$('test').val()
,comments.ToXml(comments.setFakeBreakline("message"),
"Test User",comments.generateGuid())
, -1
, $.connection.hub.id
, null
, ""
, $('').val());
$('#message').val('').focus();
}

如果评论出现错误,你之前的评论会再次出现,并且会显示错误信息,在再次尝试之前还会给你一次编辑评论的机会。当你点击Try again链接时,tryToSendMessageAgain 被调用: 隐藏,复制Code

tryToSendMessageAgain: function (Id) {
var messageBody = comments.getLastFailedCommentText(Id);
$("#div_" + comments.getPureId(Id)).animate({ height: 'hide', opacity: 'hide' }, 500);
window.setTimeout(function () { $("#div_" + comments.getPureId(Id)).remove() },600);
writerHubId = $.connection.hub.id;
commentList.server.send(
"Test User"
, comments.ToXml
(
comments._setFakeBreaklineForInnerText(messageBody)
, "Test User"
, comments.generateGuid()
)
, -1
, writerHubId
, ""
, ""
, "");
$('#message').val('').focus();
}

thetrytosendmessageagain和send 函数可以在一个集成的函数,但我没有花更多的时间来实现这个。 ToXML()函数将注释转换为XML格式,以便通过XSLT进行转换,而generateGuid函数为每个注释生成一个惟一的ID。 隐藏,复制Code

ToXml: function (message, user, previousID) {
return (previousID == null) ? "<comment id='"
+ this.generateGuid() + "'><message>"
+ message + "</message><username>"
+ user + "</username></comment>" : "<comment id='"
+ previousID + "'><message>"
+ message + "</message><username>"
+ user + "</username></comment>";
}

隐藏,复制Code

generateGuid: function () {
var result, i, j;
result = '';
for (j = 0; j < 32; j++) {
if (j == 8 || j == 12 || j == 16 || j == 20)
result = result + '-';
i = Math.floor(Math.random() * 16).toString(16).toUpperCase();
result = result + i;
}
return result;
},

XSLTransformer类 这个类根据传递给它的参数将每个注释的xmldocument转换为HTML文档。它包含两个重载方法。如果您想要将一些参数发送到指定的XSLT,那么应该使用第二个参数,它接受参数并将它们传递给XSLT。 隐藏,复制Code

public string TransformToHtml(XmlDocument xmlDocument,
string XmlTransformer,XsltArgumentList Argument)
{
try
{
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(HttpContext.Current.Server.MapPath(
"~/XSLT/" + XmlTransformer + ""));
StringBuilder htmlStrb = new StringBuilder();
StringReader stringReader = new StringReader(xmlDocument.InnerXml);
StringWriter stringWriter = new StringWriter(htmlStrb);
transform.Transform(new XPathDocument(stringReader), Argument, stringWriter);
return htmlStrb.ToString();
}
catch (Exception exp)
{
throw new Exception(exp.InnerException.Message);
}
}

XSLT XSLT (可扩展样式表语言转换)是一个语言转换XML 文档转换为其他XML文档,[1],或其他对象(as  HTML  for 网页,,纯text 或into  XSL格式化Objects 然后可以用转换PDF, PostScript 以及PNG。[2] 通常,输入文档是XML文件,但是可以使用处理器可以从中构建XQuery和XPath数据模型的任何文件,例如关系数据库表或地理信息系统 原始文件未发生变化;相反,新文档是基于现有文档的内容创建的 XSLT是一种图尔-完整的语言,这意味着它可以执行现代计算机程序所能执行的任何计算。 我发现XSLT是一种非常有用的语言,可以处理各种情况,例如:错误处理、基于服务器或客户端生成的XML导出适当的HTML以及基于用户请求处理各种外观。它有一些巨大的好处,如: 干净,简洁的模板一种简单的方法处理XML数据到HTML合理的快速一种伟大的方法有干净的代码在UI层 在本文中,我使用XSLT处理评论的用户界面和处理诸如连接错误之类的错误。XSLT中有两个模板。第一个调用在服务器端作业完成且没有任何错误时调用,第二个调用在发生错误时调用。 showNewComment:当服务器端作业完成且没有任何错误时调用第一个。 隐藏,复制Code

<xsl:templatematch="/">
<xsl:iftest="$ResultSet='1'">
<xsl:call-templatename="showNewComment"></xsl:call-template>
</xsl:if>
<xsl:iftest="$ResultSet='0'">
<xsl:call-templatename="showFailedComment"></xsl:call-template>
</xsl:if>
</xsl:template>

对于基于成功/失败调用正确的调用,我们使用xsl:call-template。 如果在服务器端,ResultSet参数被初始化为1,那么showNewComment 模板被调用;如果它被初始化为0,那么showFailedComment 被调用。 在这个XSLT中,我不关心HTML设计,它看起来可能设计得很糟糕,但这并不重要。这只是一个样本。 根据以上描述,XSLT结构如下: 隐藏,收缩,复制Code

<xsl:stylesheetversion="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:msxsl="urn:schemas-microsoft-com:xslt"exclude-result-prefixes="msxsl">
<xsl:outputmethod="xml"indent="yes"/>
<xsl:paramname="ResultSet"></xsl:param>
<xsl:paramname="CurrentUser"></xsl:param>
<xsl:paramname="CommentingDate"></xsl:param>
<xsl:templatematch="/">
<xsl:iftest="$ResultSet='1'">
<xsl:call-templatename="showNewComment"></xsl:call-template>
</xsl:if>
<xsl:iftest="$ResultSet='0'">
<xsl:call-templatename="showFailedComment"></xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:templatename="showNewComment">
<xsl:for-eachselect="comment">
<xsl:variablename="elementID"select="@id"/>
<xsl:variablename="userName"select="username"></xsl:variable>
.......
</xsl:for-each>
</xsl:template> <xsl:templatename="showFailedComment">
.......... </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

的兴趣点 这一点,对我来说是很有趣的和有趣的是这一现实,一个杰出的程序员没有丰富的背景是客户机/服务器的概念和缺乏当他们想要程序出现在comet技术之一,显然有些JavaScript 框架类似jQuery,加强这种短缺的程序员没有优秀的背景与JavaScript编程和使用它来处理AJAX请求。这组程序员在理解SignalR是如何工作的时候会混淆。所以我建议他们更深入地学习JavaScript和客户端/服务器的概念。 References  Comet编程: http://en.wikipedia.org/wiki/Comet_(编程)SignalR wiki: https://github.com/SignalR/SignalR/wiki  History  2013年3月1日(星期五):1.0版本。 本文转载于:http://www.diyabc.com/frontweb/news18852.html

使用SignalR和XSLT进行实时注释的更多相关文章

  1. 使用SignalR+Asp.net创建实时聊天应用程序

    一.概述: 使用 ASP.NET 那么 SignalR 2 创建一个实时聊天应用程序.将 SignalR 添加 MVC 5 应用程序中,并创建聊天视图发送并显示消息. 在Demo中,将学习Signal ...

  2. WPF+SignalR实现用户列表实时刷新

    原文:WPF+SignalR实现用户列表实时刷新 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/lordwish/article/details/5 ...

  3. 使用SignalR实现比特币价格实时刷新

    ASP.NET SignalR是微软支持的一个运行在 Dot NET 平台上的 HTML Websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重 ...

  4. Vue 结合 SignalR 实现前后端实时消息同步

    最近业务中需要实现服务器端与客户端的实时通信功能,对Signalr做了一点总结和整理. SignalR 作为  ASP.NET 的一个库,能够简单方便地为应用提供实时的服务器端与客户端双向通信功能. ...

  5. Signalr Vue Echarts绘制实时CPU使用率

    后端基于Asp.net webapi,前端Vue,前后端分离,该demo仅做演示,实现的细节可以自己优化 Echarts:4.2.1  可参考 官网 Jquery:3.4.1 Signalr:2.4. ...

  6. XAMARIN.ANDROID SIGNALR 实时消息接收发送示例

    SignalR 是一个开发实时 Web 应用的 .NET 类库,使用 SignalR 可以很容易的构建基于 ASP.NET 的实时 Web 应用.SignalR 支持多种服务器和客户端,可以 Host ...

  7. 常见的Web实时消息交互方式和SignalR

    标签: WebSocket SignalR 前言 1. Web消息交互技术 1.1 常见技术 1.2 WebSocket介绍 1.3 WebSocket示例 2. Signal 2.1 SignalR ...

  8. 【转】常见的Web实时消息交互方式和SignalR

    https://www.cnblogs.com/Wddpct/p/5650015.html 前言 1. Web消息交互技术1.1 常见技术1.2 WebSocket介绍1.3 WebSocket示例 ...

  9. SignalR 2.0入门

    下载已完成的项目 本教程展示如何使用那么 SignalR 创建一个实时聊天应用程序.你会那么 SignalR 添加一个空的 ASP.NET web 应用程序,创建一个 HTML 页面发送并显示消息. ...

随机推荐

  1. Dos简易基础及常用Dos命令

    Dos简易基础及常用Dos命令 什么是cmd? cmd是command的缩写,意指操作系统中的命令行程序,一般说的都是Windows中的Dos系统. 如何打开cmd? 键盘操作:Win + R 输入c ...

  2. about blog

    前言 今天无意中发现了一个小姐姐自己设计的的博客,感觉非常的nice,就随手copy一下,完了感觉效果还蛮好的 end 附上小姐姐的博客以及教程

  3. if __name__ == ‘__main__‘

    if __name__ == '__main__': def_test() 作为程序的入口,当函数被调用时会从此处开始运行 如被导入的模块内没写 if __name__ == '__main__',则 ...

  4. Docker学习笔记二(linux下安装Docker)

    Docker学习笔记二(linux下安装Docker) 1.在线安装linux Docker 这种方式首先要保证linux 环境下可以上网,当然,小编是在自己的电脑上安装了虚拟机,在虚拟机上安装了,l ...

  5. [转载] 微软发布 SURFACE DUO ANDROID SDK 和模拟器

    模拟器截图 微软今天发布了双屏折叠设备 Surface Duo Android 开发工具(SDK 和模拟器),Windows 10X 开发工具和模拟器之后 2 月 11 日发布,并宣布了新的针对双屏体 ...

  6. 理解Spring AOP的实现方式与思想

    Spring AOP简介 如果说IOC是Spring的核心,那么面向切面编程就是Spring最核心的功能之一了,在数据库事务中,面向切面编程被广泛应用. AOP能够将那些与业务无关,却为业务模块所共同 ...

  7. PicGo软件搭配gitee实现图床

    1.安装PicGo软件,并配置gitee 1.1安装picGo picGo 安装gitee-uploader 插件 官网下载地址如下:最新版本 可以自行选择版本进行下载,这里我选择了最新的版本进行下载 ...

  8. Openstack 一直在调度中解决

    查看日志/var/log/nova/nova-scheduler.log,/var/log/nova/nova-compute.log ,均无报错 查看/var/log/nova/nova-condu ...

  9. 移动web开发之rem适配布局

    移动web开发之rem适配布局 方案: 页面布局文字能否随着屏幕大小变化而变化 流式布局和flex布局主要针对于宽度布局,那高度如何布局? 怎样让屏幕发生变化的时候元素高度和宽度等比例缩放? 1. r ...

  10. flutter权限管理permission_handler

    flutter权限管理permission_handler 添加依赖 #权限 permission_handler: ^3.0.0 使用 在android的mainfest中添加权限: <use ...