代码:

 /** function getDeviceBrowser* @return {Object} device设备系统,browser浏览器类型*/function _getDeviceBrowser () {var ua = window.navigator.userAgent;//device & browser/** isIos 是否为ios* isAndroid 是否为android* isMobile 是否为mobile*/var device = {isIos: false,isAndroid: false,isMobile: false,//isIpad: false,//isIphone: false,iosVersion: ""}/** isWX 是否为微信* isQQ 是否为QQ* isUC 是否为UC*/var browser = {isWX: false,isQQ: false,isUC: false,isWeiBo: false,isSafari: false,isChrome: false}//设备系统判断device.isIos = (/iPhone os/i.test(ua));device.isAndroid = (/android/i.test(ua));device.isMobile = !!window.navigator.userAgent.match(/AppleWebKit.*Mobile.*/);//ios版本判断//iphone:/iphone os ([0-9]+)_///ipad:/cpu os ([0-9]+)_/if (ua.toLowerCase().indexOf("like mac os x") > 0) {var regStr_saf = /os [\d._]*/gi ;var verinfo = ua.toLowerCase().match(regStr_saf) ;device.iosVersion = (verinfo+"").replace(/[^0-9|_.]/ig,"").replace(/_/ig,".");}//浏览器browser.isWX = (/micromessenger/i.test(ua));browser.isQQ = (/mqqbrowser|qq/i.test(ua));browser.isUC = (/ucbrowser/i.test(ua));browser.isWeiBo = (/weibo/i.test(ua));browser.isSafari = (/safari/i.test(ua) && !/mqqbrowser/i.test(ua));browser.isChrome = (/chrome/i.test(ua) && !/mqqbrowser/i.test(ua));return {device: device,browser: browser,}}
/*** Created by sxj on 16/12/28.*/angular.module('h5_angularjs');
app.factory("$evokeapp", ['$window', 'FW.utility',function ($w, utility) {var db = utility.getDeviceBrowser();var _URL = {"ios": {openUrl: "yiqianbao://",downloadUrl: "https://itunes.apple.com/app/id745097904"},"android": {openUrl: "eqianbao://",downloadUrl: "//d.1qianbao.com/youqian/app/1qb_88888.apk"},baseUrl: "//d.1qianbao.com/youqian/"}//alert("isIos: " + db.device.isIos);//检查app是否打开function openApp(openUrl, callback) {function checkOpen(cb){var _clickTime = +(new Date());function check(elsTime) {if ( elsTime > 3000 || document.hidden || document.webkitHidden) {cb(1);} else {cb(0);}}//启动间隔20ms运行的定时器,并检测累计消耗时间是否超过3000ms,超过则结束var _count = 0, intHandle;intHandle = setInterval(function(){_count++;var elsTime = +(new Date()) - _clickTime;if (_count>=100 || elsTime > 3000 ) {clearInterval(intHandle);check(elsTime);}}, 20);}//在iframe 中打开APPvar ifr = document.createElement('iframe');ifr.src = openUrl;ifr.style.display = 'none';if (callback) {checkOpen(function(opened){callback && callback(opened);});}document.body.appendChild(ifr);setTimeout(function() {document.body.removeChild(ifr);}, 2000);}//唤起函数function evokeUp() {if (db.browser.isWX) {//微信走应用宝流程window.location.href = 'http://a.app.qq.com/o/simple.jsp?pkgname=com.paic.zhifu.wallet.activity';} else {//if (db.device.isIos) {//ios9以下alert(db.device.iosVersion);if (+db.device.iosVersion >= 9) {} else {openApp(_URL.ios.openUrl, function(isOpen) {//alert(isOpen);//唤起之后的回调!isOpen && (window.location.href = _URL.ios.downloadUrl);});}} else if (db.device.isAndroid) {openApp(_URL.android.openUrl, function(isOpen) {//alert(isOpen);//唤起之后的回调!isOpen && (window.location.href = _URL.android.downloadUrl);});} else {//走base 流程window.location.href = _URL.baseUrl;}}}// 部分浏览器只支持 vendor-prefixed// 根据浏览器支持情况设置隐藏属性和可见状态改变事件//var hidden, state, visibilityChange;//if (typeof document.hidden !== "undefined") {//    hidden = "hidden";//    visibilityChange = "visibilitychange";//    state = "visibilityState";//} else if (typeof document.mozHidden !== "undefined") {//    hidden = "mozHidden";//    visibilityChange = "mozvisibilitychange";//    state = "mozVisibilityState";//} else if (typeof document.msHidden !== "undefined") {//    hidden = "msHidden";//    visibilityChange = "msvisibilitychange";//    state = "msVisibilityState";//} else if (typeof document.webkitHidden !== "undefined") {//    hidden = "webkitHidden";//    visibilityChange = "webkitvisibilitychange";//    state = "webkitVisibilityState";//}// 添加一个时间来实时改变页面的标题//document.addEventListener(visibilityChange, function(e) {//    // Start or stop processing depending on state//    if (document.webkitHidden) {//       alert("离开当前窗口啦");//    } else {//        document.title = "选中当前窗口啦";//    }//////}, false);return {evoke: function(url) {evokeUp();}}}
]);

网易新闻实现代码

window.NRUM = window.NRUM || {};
window.NRUM.config = {key:'27e86c0843344caca7ba9ea652d7948d',clientStart: +new Date()
};
(function() {var n = document.getElementsByTagName('script')[0],s = document.createElement('script');s.type = 'text/javascript';s.async = true;s.src = '//nos.netease.com/apmsdk/napm-web-min-1.1.3.js';n.parentNode.insertBefore(s, n);
})();;
(function(window,doc){// http://apm.netease.com/manual?api=webNRUM.mark && NRUM.mark('pageload', true)var list = []var config = null// jsonpfunction jsonp(a, b, c) {var d;d = document.createElement('script');d.src = a;c && (d.charset = c);d.onload = function() {this.onload = this.onerror = null;this.parentNode.removeChild(this);b && b(!0);};d.onerror = function() {this.onload = this.onerror = null;this.parentNode.removeChild(this);b && b(!1);};document.head.appendChild(d);};function localParam(search,hash){search = search || window.location.search;hash = hash || window.location.hash;var fn = function(str,reg){if(str){var data = {};str.replace(reg,function( $0, $1, $2, $3 ){data[ $1 ] = $3;});return data;}}return {search: fn(search,new RegExp( "([^?=&]+)(=([^&]*))?", "g" ))||{},hash: fn(hash,new RegExp( "([^#=&]+)(=([^&]*))?", "g" ))||{}};}jsonp('http://active.163.com/service/form/v1/5847/view/1047.jsonp')window.search = localParam().searchwindow._callback = function(data) {window._callback = nulllist = data.listif(search.s && !!search.s.match(/^wap/i)) {config = list.filter(function(item){return item.type === 'wap'})[0]return}config = list.filter(function(item){return item.type === search.s})[0]}var isAndroid = !!navigator.userAgent.match(/android/ig),isIos = !!navigator.userAgent.match(/iphone|ipod/ig),isIpad = !!navigator.userAgent.match(/ipad/ig),isIos9 = !!navigator.userAgent.match(/OS 9/ig),isYx = !!navigator.userAgent.match(/MailMaster_Android/i),isNewsapp = !!navigator.userAgent.match(/newsapp/i),isWeixin = (/MicroMessenger/ig).test(navigator.userAgent),isYixin = (/yixin/ig).test(navigator.userAgent),isQQ = (/qq/ig).test(navigator.userAgent),params = localParam().search,url = 'newsapp://',iframe = document.getElementById('iframe');var isIDevicePhone = (/iphone|ipod/gi).test(navigator.platform);var isIDeviceIpad = !isIDevicePhone && (/ipad/gi).test(navigator.platform);var isIDevice = isIDevicePhone || isIDeviceIpad;var isandroid2_x = !isIDevice && (/android\s?2\./gi).test(navigator.userAgent);var isIEMobile = !isIDevice && !isAndroid && (/MSIE/gi).test(navigator.userAgent);var android_url = (!isandroid2_x) ? "http://3g.163.com/links/4304" : "http://3g.163.com/links/6264";var ios_url = "http://3g.163.com/links/3615";var wphone_url = "http://3g.163.com/links/3614";var channel = params.s || 'newsapp'// 判断在不同环境下app的urlif(params.docid){if(params['boardid'] && params['title']){url = url + 'comment/' + params.boardid + '/' + params.docid + '/' + params.title}else{url = url + 'doc/' + params.docid}}else if(params.sid){url = url + 'topic/' + params.sid}else if(params.pid){var pid = params.pid.split('_')url = url + 'photo/' + pid[0] + '/' + pid[1]}else if(params.vid){url = url + 'video/' + params.vid}else if(params.liveRoomid){url = url + 'live/' + params.liveRoomid}else if(params.url){url = url + 'web/' + decodeURIComponent(params.url)}else if(params.expertid){url = url + 'expert/' + params.expertid}else if(params.subjectid){url = url + 'subject/' + params.subjectid}else if(params.readerid){url = url + 'reader/' + params.readerid}else{url += 'startup'}if(url.indexOf('?') >= 0){url += '&s=' + (params.s || 'sps')}else{url += '?s=' + (params.s || 'sps')}// ios && 易信  用iframe 打开if((isIos||isIpad) && navigator.userAgent.match(/yixin/i)) {document.getElementById('iframe').src = url;}var height = document.documentElement.clientHeight;// 通常情况下先尝试使用iframe打开document.getElementById('iframe').src = url;// 移动端浏览器中,将下载页面显示出来if(!isWeixin && !isQQ && !isYixin && !isYx){document.querySelector('.main-body').style.display = 'block'if(isIos9){document.querySelector('.main-body').classList.add('showtip')}setTimeout(function(){document.body.scrollTop = 0},200)}else{document.getElementById('guide').style.display = 'block'}// Forward To Redirect Url// Add by zhanzhixiang 12/28/2015if (params.redirect) {var redirectUrl = decodeURIComponent(params.redirect);if ( typeof(URL) === 'function' && new URL(redirectUrl).hostname.search("163.com") !== -1) {window.location.href = redirectUrl;} else if (redirectUrl.search("163.com") !== -1){window.location.href = redirectUrl;};}// Forward To Redirect Url Endif ((isWeixin || isQQ) && isAndroid) {window.location.href = 'http://a.app.qq.com/o/simple.jsp?pkgname=com.netease.newsreader.activity&ckey=CK1331205846719&android_schema=' + url.match(/(.*)\?/)[1]}if(isIos||isIpad){document.getElementById("guide").classList.add('iosguideopen')}else if (isAndroid){document.getElementById("guide").classList.add('androidguideopen')}else{// window.location.href = 'http://www.163.com/newsapp'}document.getElementById('link').addEventListener('click', function(){// 统计neteaseTracker && neteaseTracker(false,'http://sps.163.com/func/?func=downloadapp&modelid='+modelid+'&spst='+spst+'&spsf&spss=' + channel,'', 'sps' )if (config) {android_url = config.android}if (config && config.iOS) {ios_url = config.iOS}if(isWeixin || isQQ){return}var msg = isIDeviceIpad ? "检测到您正在使用iPad, 是否直接前往AppStore下载?" : "检测到您正在使用iPhone, 是否直接前往AppStore下载?";if (isIDevice){window.location = ios_url;return;}else if(isAndroid){// uc浏览器用iframe唤醒if(navigator.userAgent.match(/ucbrowser|yixin|MailMaster/i)){document.getElementById('iframe').src = url;} else {window.location.href = url;}setTimeout(function(){if(document.webkitHidden) {return}if (confirm("检测到您正在使用Android 手机,是否直接下载程序安装包?")) {neteaseTracker && neteaseTracker(false,'http://sps.163.com/func/?func=downloadapp_pass&modelid='+modelid+'&spst='+spst+'&spsf&spss=' + channel,'', 'sps' )window.location.href = android_url;} else {neteaseTracker && neteaseTracker(false,'http://sps.163.com/func/?func=downloadapp_cancel&modelid='+modelid+'&spst='+spst+'&spsf&spss=' + channel,'', 'sps' )}},200)return;}else if(isIEMobile){window.location = wphone_url;return;}else{window.open('http://www.163.com/special/00774IQ6/newsapp_download.html');return;}}, false)setTimeout(function(){if(isIDevice && params.notdownload != 1 && !isNewsapp && !isIos9){document.getElementById('link').click()}}, 1000)})(window,document);

在移动互联网,链接是比较重要的传播媒质,但很多时候我们又希望用户能够回到APP中,这就要求APP可以通过浏览器或在微信中被方便地唤起。

这是一个既直观又很好的用户体验,但在实现过程中会遇到各种问题:

  1. 如何解决未安装APP时的做好引导页
  2. 如何在微信中唤醒APP
  3. 在iOS9中如何处理universal link被用户误关的情况
  4. 如何解决Android各种机型、各种第三方浏览器导致的兼容问题等
  5. 在APP未安装情况下,引导用户下载后打开APP后,如何进入之前唤起时指定的页面或内容,即如何实现场景还原
  6. 在微信中唤醒APP时,如何进入指定的页面或内容

下面是我一些个人的经验分享。

浏览器中打开

iOS/Android APP配置

这块内容其实比较简单,在网上都有很多资料可供查阅,就不再赘述。

原理说明

首先需要说明,不管iOS还是Android,浏览器都不可能预知本地是否安装了某个APP的。或者更严谨地说,我们不能通过浏览器来预知本地是否安装。因为就算浏览器可以读取本地应用的安装列表,但是目前也没任何一家浏览器提供查询的API,所以这条路是走不通的。

本质上浏览器是通过URL scheme打开APP,一个APP可以设置一个或多个打开自己的URL scheme。比如,Twitter就注册自己能被「twitter://」打开。

其实,如果是做APP间相互跳转是比较简单的。iOS就可以使用 UIApplication 的 canOpenUrl 方法来检测URL scheme 是否能打开对应的APP。比如,如果「twitter://」检测能被打开,也就说明本地安装了 Twitter 。再用 UIApplication 的 openURL 方法,就能打开Twitter了。Android 中的做法类似。

实现方案

因为iOS9和之前的iOS系统有区别,所以这里我们也要区别对待。

iOS7/iOS8

iOS中默认通过Safari打开URL scheme,方法一般如下两种:

  1. 直跳方式:点击链接、修改 window.location 等。
  2. iframe 方式:在 body 上添加 iframe,设置src属性为跳转的URL scheme。

第一种情况:

<ahref="schemeUrl">唤醒你的APP</a>

或者

window.location.href = schemeUrl;

但在第一种情况,如果APP唤醒失败,或者APP未安装的话,很多时候都会跳到错误页,这很影响用户体验,而我们的要求可能是跳转到其他页面或者下载APP。

后一种方法不会引起页面可见的变化(例如页面内容变成一个新页面),不会导致浏览器历史记录的变化,大致实现如下:

<a href="APP下载地址">下载或打开APP</a><script>$('a').click(function(){var ifr = document.createElement('iframe');ifr.src = '自定义 URL scheme';ifr.style.display = 'none';document.body.appendChild(ifr);setTimeout(function(){document.body.removeChild(ifr);}, 3000);});</script>

过程是这样:点击 a 标签时,首先会尝试打开URL scheme,如果成功,就唤起APP;如果失败,则跳转到 href 属性,即下载页。

Android

但这个方案在很多安卓机型上有问题,为保证可用,改用第一种方案:

$('a').click(function(){location.href = '自定义 URL scheme';t = Date.now();setTimeout(function(){if (Date.now() - t < 1200) {location.href = 'Android 下载地址';}}, 1000);return false;
}

理想过程是这样:浏览器尝试打开 URL scheme,在1秒计时后,检查当前时间,如果实际时间已过 1200 毫秒,说明唤起APP 成功(唤起 APP 会让浏览器的定时器变慢);如果没超过 1200 毫秒,很可能是没有安装应用,就跳到下载地址。

或者换种方式:

var ifr = document.createElement('iframe');
ifr.src = 'com.baidu.tieba://';
ifr.style.display = 'none';
document.body.appendChild(ifr);
var openTime = +new Date();
window.setTimeout(function(){document.body.removeChild(ifr);if( (+new Date()) - openTime > 2500 ){window.location = 'http://exam.com/xxxx.apk';}
},2000)

但原理都是一样,利用setTimeout。但这其实不稳定,因为Android是基于Linux的分时多任务的,setTimeout的基准偏差可能会没那么大。

但如果设置比较小的运行间隔(<30ms),在浏览器或者webview中,应用切换到后台,setInterval会被很明显的延迟执行,比如设置一个运行间隔20ms,总计运行100次的定时器,如果页面一直处于前台,则100次跑完,总耗时与 100x20=2000ms不会有太大差异,但页面在后台运行时,此时间会明显超过2000ms。可以利用这一点来实现是否成功打开APP检测及回调。

functionopenApp(openUrl,appUrl,action,callback){//检查app是否打开functioncheckOpen(cb){var _clickTime = +(new Date());functioncheck(elsTime){if ( elsTime > 3000 || document.hidden || document.webkitHidden) {cb(1);} else {cb(0);}}//启动间隔20ms运行的定时器,并检测累计消耗时间是否超过3000ms,超过则结束var _count = 0, intHandle;intHandle = setInterval(function(){_count++;        var elsTime = +(new Date()) - _clickTime;if (_count>=100 || elsTime > 3000 ) {clearInterval(intHandle);check(elsTime);}}, 20);}//在iframe 中打开APPvar ifr = document.createElement('iframe');ifr.src = openUrl;ifr.style.display = 'none';if (callback) {checkOpen(function(opened){callback && callback(opened);});}document.body.appendChild(ifr);      setTimeout(function(){document.body.removeChild(ifr);}, 2000);
}

另外,可以通过 document.hidden 或 document.[webkit|moz|ms]Hidden 来判断页面是否被置入后台(即应用被唤起),或visibilitychange事件,但对于Android 4.4版本一下则不支持。

iOS9

在 iOS 9 上,iframe 方案变得不可用。

按不能使用之前Android的代码,因为在打开自定义 URL scheme 时,会弹出对话框,询问是否用 xx 应用来打开。往往用户还没来得及点击打开,定时器又触发了,导致跳到 App Store。

可以在尝试打开URL scheme 后,再加一个页面跳转,这样对话框会被覆盖,再刷新页面,就能无需确认唤起APP:

$('a').click(function(){location.href = '自定义 URL scheme';location.href = '下载页';location.reload();
}

这里,下载页延时 2 秒跳转到 App Store。

APP已安装这是没问题的,但如果APP未安装,跳 App Store 的请求会失败。

这时可以使用两个定时器:

$('a').click(function(){location.href = '自定义 URL scheme';setTimeout(function(){location.href = '下载页';}, 250);setTimeout(function(){location.reload();}, 1000);
}

不过在iOS9中其实是支持universal link的,就是一个http域名形式,在微信中都可以唤起APP。如果未安装的话,可以直接引导用户去APP store下载。

可以参考这篇文章

http://www.magicwindow.cn/doc/#universal-link-info

没有完美的解决方案

主要是在安卓上,总归会有各种兼容问题,知乎的解决办法是,提供两个按钮,一个下载,一个打开APP,让用户自己选。

微信中打开

因为微信将唤起本地APP的接口给禁了,所以微信中是不能直接唤起APP的,一般做法是提示用户在浏览器中打开,之后的流程还是我们上面讲的内容。

但是,在iOS9中,这个限制是可以突破的,也就是说可以直接唤起APP。方法就是使用我们上文提到的universal link。

在Android和iOS8及其以下系统中,我们可以利用腾讯的亲儿子:应用宝。简单讲,就是把你的唤起地址配置成你APP的应用宝地址,微信中跳转到这个地址后,如果用户已经安装了APP,则可直接唤起,如果没有安装,则可直接点击下载,如下图示:

但这里有坑需要注意。

对于使用universal link来说,如下图所示用户在微信中打开APP之后,可能不小心点击右上角的链接(比方说点几分享,却不小心点击了"mlinks.cc"),导致跳到外部浏览器中,如下图所示:

这时候再在微信中就打不开APP了,因为universal link已被关闭,这是iOS9的机制,没法改变,这时候用户再在微信中打开,就得需要一个中间页来引导用户在外部浏览器中打开APP,如下图所示:

另外,在微信中唤醒APP默认只能到达首页,即不能到达指定页面或内容,如果想要做,则需要额外的处理。

拿来主义

从以上内容可以总结出:要做一个兼容性很好的方案,就需要考虑各种情况,在不同的情况适配不同的方案,比方说用户是在手机浏览器打开还是微信中打开,或者是在pc中打开,universal link是否被关闭等,这就使代码实现变得复杂,且容易出错,且还有安卓平台机型众多、浏览器众多等导致的兼容问题。

如果觉得实现难度或者成本太高,你可以考虑使用魔窗的mLink。只要你加了魔窗的sdk,就可以通过类似“https://s.mlinks.cc/AA01”的链接,在任何环境下打开你的APP(如果在pc机上打开,浏览器中将会出现APP下载地址的二维码),上面提到的问题都不复存在,并且魔窗已经兼容超过600台以上安卓机型的第三方主流浏览器。而且关键的是,不管是在手机浏览器中,还是在微信中打开,你可以指定唤起APP后直达APP中的某个页面或内容(某个促销商品等),就算用户没安装APP,点击下载安装之后,再打开,还是跳转到指定的页面,这就是场景还原,或者叫做Deffered Deep Linking。

h5浏览器,唤起app相关推荐

  1. h5页面唤起app(iOS和Android),没有安装则跳转下载页面

    由于研究了之后,和同事沟通,找到一个地址进入,分别测试不同手机,机型,安卓进入安卓应用商店,ios进入app store :所以直接贴了一个链接,即可. 下面方法,暂时没用到,分享给需要的盆友. CO ...

  2. uniapp h5在浏览器唤起app

    需求 当用户打开h5 链接时候 ,点击打开app, 若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装 这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒.这 ...

  3. H5 如何实现唤起 APP

    前言 写过hybrid的同学,想必都会遇到这样的需求,如果用户安装了自己的APP,就打开APP或跳转到APP内某个页面,如果没安装则引导用户到对应页面或应用商店下载.这里就涉及到了H5与Native之 ...

  4. H5 如何实现唤起 APP兼容版

    在移动端开发中,H5页面唤起本地APP是一个非常常见的需求.本文将介绍如何在H5页面中实现唤起本地APP的具体操作. 常见的唤起APP方式 URL Scheme URL Scheme是一种特殊的URL ...

  5. H5唤起APP进行分享的尝试

    H5唤起APP进行分享 最近很久没有写blog和note,倒是过家家的开发日志简单草草写了一点.这次记录下这个学习过程 由来 我们的 "通达有你",web h5页面的分享功能体验太 ...

  6. h5唤起app,清除timeout

    本文转载出处:https://www.cnblogs.com/chaoyuehedy/p/9118365.html 很多代码博主亲测有效!欢迎讨论! 第一种方法: <meta name=&quo ...

  7. app能不能跳转外部h5_APP内部H5页面跳转 H5唤起APP 怎么做?

    H5唤起APP某页面 做的H5页面,在APP中放了入口,在APP中访问别的页面 场景 我们做的H5页面,希望从浏览器跳转至APP的某个特定页面,如在H5中设置购买按钮,希望点击跳转至APP内部的购买页 ...

  8. web前端逃坑指南------H5及微信中JavaScript唤起app的解决方案callapp-lib

    最近遇到一个需求:点击一个按钮,如果本机装有则唤起app,没有的话则跳下载页. 刚一接到需求,觉得很熟悉,实际上这个功能也确实很常见, 尤其是在微信里面更是常见,尝试着自己写,但是却发现总是很难做到1 ...

  9. 如何用xinstall实现h5唤起app

    移动端主流的H5页面如何实现唤起APP的? 写过hybrid的同学,想必都会遇到这样的需求,如果用户安装了自己的APP,就打开APP或跳转到APP内某个页面,如果没安装则引导用户到对应页面或应用商店下 ...

最新文章

  1. 【BZOJ 2351】Matrix(Hash)
  2. 《APUE》读书笔记—第十三章守护进程
  3. 机器学习(二)——鸢尾花案例
  4. java单引号转义_Java基础入门——Java语言基础(上)
  5. gitlab更新配置无效_Gitlab 快速的搭建
  6. LeetCode-Letter Combinations of a Phone Number-电话号码字母组合-DFS
  7. 宇宙中的最大天体有多大
  8. 苹果plus html,iPhoneX 苹果8 8PLus区别:所有功能参数 最详细全面对比
  9. MATLAB如何打开chm文件
  10. 如何搭建短信中心号码服务器,短信中心号码怎么设置?设置短信中心号码两种方法介绍...
  11. 蓝桥java练习记录
  12. VMware网络问题排查思路
  13. 【vue 3】blob文件流 导出 excel 表格
  14. java计算机毕业设计线上教育教学系统源码+数据库+lw文档+系统+部署
  15. C#【必备技能篇】使用批处理脚本(.bat文件)打开控制台应用程序
  16. SAAS(软件即服务) 离我们还有多远?
  17. 拨号服务器应用场景有哪些?
  18. 两个进程同时访问同一个文件
  19. 打印html java 清晰度_java 利用jsp打印html页面
  20. 折腾了好久的输入法显示已禁用

热门文章

  1. 智慧路灯杆集中供电控制系统方案
  2. 最小的Linux系统制作过程详解
  3. Fedora 安装 Apache HTTP Server
  4. 首旅如家2019上半年营收近40亿元,净利润约3.4亿元
  5. Vcard 标准格式
  6. 作坊离工厂究竟有多远 (二)
  7. 玩音箱的“老男孩”们
  8. 同一工作组无法访问另一计算机,WinXP同一工作组无法访问的解决方法
  9. 微信小程序 | 微信公众平台SpringBoot开发之收到消息后进行简单回复
  10. 【影音工具】如何合并中英文字幕到一个文件