Linux 设置定时任务crontab命令

 一、crontab命令概念

  crontab命令用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。

cron 系统调度进程。 可以使用它在每天的非高峰负荷时间段运行作业,或在一周或一月中的不同时段运行。cron是系统主要的调度进程,可以在无需人工干预的情况下运行作业。

  crontab命令允许用户提交、编辑或删除相应的作业。每一个用户都可以有一个crontab文件来保存调度信息。系统管理员可以通过cron.deny 和 cron.allow 这两个文件来禁止或允许用户拥有自己的crontab文件。

二、检查是否安装了crontab

rpm -qa | grep crontab

三、crontab服务启动与关闭

四、全局配置文件

crontab在/etc目录下面存在cron.hourly,cron.daily,cron.weekly,cron.monthly,cron.d五个目录和crontab,cron.deny二个文件

cron.daily是每天执行一次的job

cron.weekly是每个星期执行一次的job

cron.monthly是每月执行一次的job

cron.hourly是每个小时执行一次的job

cron.d是系统自动定期需要做的任务

crontab是设定定时任务执行文件

cron.deny文件就是用于控制不让哪些用户使用Crontab的功能

五、用户配置文件

  每个用户都有自己的cron配置文件,通过crontab -e 就可以编辑,一般情况下我们编辑好用户的cron配置文件保存退出后,系统会自动就存放于/var/spool/cron/目录中,文件以用户名命名.linux的cron服务是每隔一分钟去读取一次/var/spool/cron,/etc/crontab,/etc/cron.d下面所有的内容.

六、crontab文件格式

  *           *        *        *        *           command

minute   hour    day   month   week      command

分          时         天      月        星期       命令

minute: 表示分钟,可以是从0到59之间的任何整数。

hour:表示小时,可以是从0到23之间的任何整数。

day:表示日期,可以是从1到31之间的任何整数。

month:表示月份,可以是从1到12之间的任何整数。

week:表示星期几,可以是从0到7之间的任何整数,这里的0或7代表星期日。

command:要执行的命令,可以是系统命令,也可以是自己编写的脚本文件

七、特殊字符

星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。

逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”。

中杠(-):可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6”。

正斜线(/):可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。

八、查询当前用户定时任务或删除当前用户定时任务

九、测试

在home目录下编写一个test.sh脚本,脚本功能是把/home下ifcfg-eth0这个文本复制到/mnt目录下。

运行crontab –e 编写一条定时任务 */5 * * * * /home/test.sh 在每5分钟执行一次test.sh脚本。

十、其它

设置crond开机自动启动。

实例

crontab还可以支持系统重启自动执行任务     @reboot /home/test.sh

PHP服务端 苹果支付(IAP)处理

 公司做的app需要做IAP订阅支付,开始觉得和微信的支付流程差不多,做起来还是有点麻烦,主要是网上的文章很少,不能拿来主义。自己做完总结一下,希望对小伙伴们有帮助我就很欣慰了。代码写的不好 不要喷我。。。

首先讲一下我的业务逻辑,也就是php服务端需要做什么事情。

先上图:

下面我详细的讲一下每一步做些什么,贴出来相应的代码供大家参考。

 第一步: app 调用创建订单接口,创建订单信息,保存下来。

 第二步: app 调用sdk 发起支付,传苹果给的 receipt(票据)和 订单号 给服务端,服务端通过验证receipt 获取订单信息保存

                数据库。至于票据长什么样子,怎么验证,后面贴代码出来。

 第三步: 订阅模式支付,首次支付苹果服务器会异步发送两次通知到服务端,之后你在app端操作的时候也会有通知,比如更                     改套餐,取消订阅等操作都会有通知,这个就比较坑了,如果异步处理的时候没弄清楚。

                很容易出问题,不能每次接收到通知就处理,沙盒模式下通知来的很奇怪,通知有很多状态,要区别开来处理。

  接下来是代码处理流程:php框架+tp 5.02

  首先是同步完成订单时候的验证:

/** * @title 验证支付票据 完成订单接口 */public function verifyReceipt(){    $receipt = Request::instance()->param('receipt');  //票据   $orderSn = Request::instance()->param('orderSn');  /订单号     //判断订单是否存在 检查状态     //写入日志  查看票据格式  记录日志的方法 这个方法不贴出来了    Tool::callAddLog('request-param',json_encode(['receipt'=>$receipt,'orderSn'=>$orderSn]));    //password 是验证秘钥   $jsonItem = json_encode(['receipt-data'=>$receipt, 'password'=>'XXXXXXXXXXXXXXXX']);       //$url= 'https://buy.itunes.apple.com/verifyReceipt';      //正式     $url= 'https://sandbox.itunes.apple.com/verifyReceipt';  //测试      //模拟post提交   下面会贴出来    $result =  Tool::http_post_json($jsonItem,$url);    if($result['status'] !== 0){      //验证失败 返回app错误状态   }    //验证完成加入日志   Tool::callAddLog('verifyReceipt_finish',json_encode($result));    //找出时间最大的数组   $receiptitem = $result['latest_receipt_info'];    //这个是排序的方法  下面回帖出来 苹果会把这个会员往期的订单信息全部返回,需要找出来最近的那一组信息   $item = Tool::arraySort($receiptitem,'purchase_date','desc');    //判断一下过期时间 延长会员时间    $orderThird = $item['transaction_id'];                //本次订阅的订单号    $orderThirdFirst = $item['original_transaction_id'];  //这个是原始订单号    if($orderThird == $orderThirdFirst){                  //首次订阅 两个相等     }else{         //判断过期时间和当前时间比较   expires_date_ms是毫秒单位         if($item['expires_date_ms']/1000 > time()){         }else{           //过期处理  票据失效          }    }   //接下来处理订单业务逻辑,处理订单,修改订单信息,original_transaction_id 把这个单号和存入订单信息, 在异步回调的时候根据这个单号来查找订单信息来判断是哪个用户,苹果票据里面也有产品product_id,是app_store的产品id,不是自己业务的产品id,可以在后台把自己的产品ID和 product_id 关联,处理业务逻辑。 }
//模拟post提交public static function http_post_json($json,$url) {     $ch = curl_init($url);    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);    curl_setopt($ch, CURLOPT_POST, true);    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //这两行一定要加,不加会报SSL 错误    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);    $response = curl_exec($ch);    $errno = curl_errno($ch);    $errmsg = curl_error($ch);    curl_close($ch);     $data = json_decode($response, true);    return $data;}
//查找最新数据的方法 public static function arraySort($arr,$key,$type='asc'){     $keyArr = []; // 初始化存放数组将要排序的字段值    foreach ($arr as $k=>$v){        $keyArr[$k] = $v[$key]; // 循环获取到将要排序的字段值    }    if($type == 'asc'){        asort($keyArr); // 排序方式,将一维数组进行相应排序    }else{        arsort($keyArr);    }    foreach ($keyArr as $k=>$v){        $newArray[$k] = $arr[$k]; // 循环将配置的值放入响应的下标下    }    $newArray = array_merge($newArray); // 重置下标    return $newArray[0]; // 数据返回}   
public static function http_post_json($json,$url) {     $ch = curl_init($url);    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);    curl_setopt($ch, CURLOPT_POST, true);    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //这两行一定要加,不加会报SSL 错误    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);    $response = curl_exec($ch);    $errno = curl_errno($ch);    $errmsg = curl_error($ch);    curl_close($ch);     $data = json_decode($response, true);     return $data;}

————————————————————————————————————————————————————————

验证之后返回的状态

验证之前的票据是一个字符串:就是一个字符串

验证之后的信息:这个是重点

到了 这同步主动验证这边就结束了。

下面是异步通知这块:

异步通知的票据可以验证发送到苹果服务器验证一下有效性,也可以不要验证,它本身的信息就够用了。

  通知类型: notification_type 有很多种情况,有些通知接收存入日志,不用处理,

  需要处理的类型:RENEWAL           DID_CHANGE_RENEWAL_STATUS            

  但是 DID_CHANGE_RENEWAL_STATUS 这种通知 在首次订阅的时候 也会发一个过来,这个时候不处理,也就是当参数       auto_renew_status  等于 true 的时候 不处理,其余的时候都是需要处理的通知。

public function applePayReceive(){      $str = file_get_contents('php://input');     //写入通知日志     $jsonItem = json_decode($str,true);         if($jsonItem['notification_type'] == 'RENEWAL' || $jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){              if($jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){            if($jsonItem['auto_renew_status'] == 'true'){                //第一次够买的时候 不处理通知              die;        }         $expires_date = $jsonItem['latest_receipt_info']['expires_date'];        $expires_date = $expires_date/1000;         Tool::asynAddLog('request-param',$expires_date.'----'.time());         if($expires_date > time()){         }else{            //时间不对 不处理        }    }      //处理订阅产品的业务逻辑          echo '完成!';die;}

好了,上面是我写的苹果支付所有流程和代码,业务代码部分我去掉了,大家自己写自己的业务逻辑就可以。

看到最后的同志们,我上面有个地方写错了 ,苹果正式环境,第一次购买的时候,异步的两个通知,notification_type 等于DID_CHANGE_RENEWAL_STATUS 这个的时候,auto_renew_status 值有可能不是ture,我开始没弄懂意思,现在我的理解是

等于true是自动续费的意思,就是说用户选择的是连续订阅,而等于false的时候是用户买了产品取消了订阅模式。才会出现这种情况。不是苹果坑,而是自己没理解,

 

其实根本不能用通知的类型来判断是否给用户重置,正确保险的方案是在主动通知的时候把苹果的原始订单号和本次交易的订单号存入订单,异步的时候判断订单是否完成。然后再写业务逻辑。

重点:异步通知有可能收不到,这种情况下,需要把用户首次订阅的凭证存下来,等到用户到期的时候 再去验证一下这个凭证,然后取出订阅相关信息,找到最近的一条,更新会员时间,会员时间用苹果提供的过期时间就可以。

沙盒模式下苹果的通知是乱的,请大家注意,不一定按照文档上面说的时间有规则的发送通知,所以异步通知不能保证业务的完整性,处理业务时一定要把用户首次支付的凭证持久化,以便后续通知收不到的情况,会员到期服务端主动去查询会员是否续费 情况。

PHP implode():数组转字符串

implode() 函数可以将一个一维数组转化为字符串,其语法格式如下:

implode($glue, $array) 或者 implode($array)其中,$glue 用来设置一个字符串,表示使用 $glue 将数组每个元素连接在一起,默认情况下 $glue 为空字符串;$array 为需要转换的数组。

提示:implode() 函数的 $glue 参数是可选的,可以省略。【示例】使用 implode() 函数将数组转换为字符串。

纯文本复制
<?php    $arr = ['C语言中文网','PHP教程','http://c.biancheng.net/php/','implode()函数','数组转字符串'];    $str = implode($arr);    echo $str.'<br>';    $str = implode(' ',$arr);    echo $str.'<br>';    $str = implode(',',$arr);    echo $str.'<br>';    $str = implode('--',$arr);    echo $str.'<br>';?>

运行结果如下:

C语言中文网PHP教程http://c.biancheng.net/php/implode()函数数组转字符串
C语言中文网 PHP教程 http://c.biancheng.net/php/ implode()函数 数组转字符串
C语言中文网,PHP教程,http://c.biancheng.net/php/,implode()函数,数组转字符串
C语言中文网–PHP教程–http://c.biancheng.net/php/–implode()函数–数组转字符串

zend studio在线安装svn的插件

这个,其实真的很简单 ,但是让我纠结了很久。
安装步骤:
1.确保电脑能够上网;
2.打开zend studio,然后选择help->welcome会弹出一个欢迎页面;
3.等待几秒后可以看到右面一栏出现了一排列表,找到svn然后勾选上然后点击上面的”apply changes”按钮,svn开始安装了。

顺便说一句本人用的zend studio9.0做的测试,不知道低版本的行不行。   
原文地址:http://534603172-qq-com.iteye.com/blog/1699658

HBuilderX安卓离线打包教程

HBuilderX是一款不错的前端编辑器,它为使用者提供了云打包和离线打包两种形式,但云打包有着次数限制(可付费解锁)和服务器繁忙时需要排队等候,不利于开发者的正常调试,从另一方面来看,尽管官方对于隐私保护极为重视,但还是无法让使用者打消代码上传泄露的疑虑,所以离线打包作为第二条打包途径理当重视。

HBuilderX安卓离线打包基于自身的SDK和AndroidStudio软件,AndroidStudio对于初学者来说较难掌握,而官方文档也过于简洁,初期打包可能各种报错,我在社区里看到不少离线打包失败的网友,本着知识共享的原则将HBuilderX安卓离线打包步骤一一演示,希望能帮助到大家。

第一步,AndroidStudio环境测试

首先要下载AndroidStudio软件,下面是官方网址:

https://developer.android.google.cn/studio/index.html

我们可以看到目前最新的版本是3.63,大家根据自己的电脑配置按照要求下载安装即可,如果想了解更多而自己的英文水平不是那么的好,可以私下里使用谷歌浏览器翻译了解。

AndroidStudio下载安装完成后打开,如下图所示选中Empty Activity,点击Next创建。

编辑应用名称、包名和保存位置,包名一般为com.***.*** 样式。我们注意到下方Minimum SDK有下卡列表框选项,这里简单解释一下,这份是用来选择API的,注意到下面的蓝色标记“Your app will run on approximately 92.3% of device”了没,代表安卓用户中有92.3%的用户手机版本大于等于这个数,也就是安卓5.1及以上版本的Android手机可以正常使用这个应用,而低于此版本的即使安装安装成功也会闪退黑屏等无法打开。

这就需要你做一个权衡,怎么才能让应用覆盖更多的用户,太低不利于发展,太高用户基数少,官方文档的建议是大于等于19,我这里选的是22,大家自行斟酌。点击Finish完成创建。

看到下图的红色箭头了没,这个是创建模拟器的选项,在最初没有模拟器时如红色下划线状态,这在官方文档里最后运行描述时一笔带过,至于我为什么详细描述,在后面我会给大家解释。

点击红色箭头选项。

点击Create Virtual Device创建虚拟设备

在Phone栏里随意选择各种配置的模拟设备,如果你的电脑配置并不是多么好,建议挑选简单的比较容易加载,在这里我挑选了一个320×480的。点击Next。

系统镜像,自己选择。点击Next。

这个默认就行,直接点击Finish。

我们可以看到虚拟设备已经创建完成,点击右上角×直接退出此界面。

顺着最左边的箭头,可以看到刚刚创建的虚拟设备标识,点击右边箭头指向的选项,运行虚拟设备(也可直接在上图中直接点击Actions栏中最左边的三角符号运行。)

等待片刻,模拟器成功打开。

此时点击红色下划线”Run app”选项。

发现刚刚创建的Empty Activity项目成功跑起,这也就证明了你的AndroidStudio已经配置好了。相对于H5APP来说,AndroidStudio上手难,成效低,不少个人开发者转向移动APP开发时都没有接触过它,在初涉AndroidStudio时,好多人在安装配置方面往往报错,我第一次也是如此。

在写这篇教程时我是默认你SDK、API等全配置好了的,如果你在这一步报错,模拟器没能打开或者没能成功显示下图界面,就说明哪里仍存在问题,后续运行和调试自己的项目时很有可能会报错,建议大家配置完毕再往下进行。AndroidStudio的配置我就不在赘述,如果有需要后面会考虑附加一篇配置教程。

第二步,APP离线SDK下载

下载安卓离线SDK包,网址如下:

https://nativesupport.dcloud.net.cn/AppDocs/download/android

这里要吐槽的一点是需要通过百度网盘下载…

下载好后在目录

2.6.16\Android-SDK@2.6.16.80137_20200426\SDK\libs

下找到

lib.5plus.base-release.aar

android-gif-drawable-release@1.2.17.aar

miit_mdid_1.0.10.aar

三个文件,复制到自定义的新文件夹方便使用

在目录

2.6.16\Android-SDK@2.6.16.80137_20200426\SDK\assets

下找到data文件夹,打开可以发现下图几个文件。

返回上一级,复制data文件夹如上操作,为了方便与三个文件放到一起。

第三步,HBuilderX生成本地打包资源

如图,在发行选项→原生APP-本地打包(L)中选择生成本地打包App资源(R)。

显示导出成功,顺着路径将自己项目id名的文件夹拷贝,放到上一步自定义的文件夹下,方便使用。这里我的项目id是H525CFE5C,就把它放到与上一步复制的文件一起。

所需资源整理完毕,我们开始进行下一步操作。

第四步,安卓离线打包环境配置

切换到AndroidStudio,如果你第一步操作像我一样只是测试用,那也可以在里面再创建一个Empty Activity项目或者No Activity,在最后我会提到No Activity,这里采用官方文档的方式创建Empty Activity即可。

我们可以看到之前默认的是“Android”展示形式,而官方文档里是Project,为了契合我们把它换成下拉列表选项中最上面Project样式。

如图。

以5+APP举例,如下图将

lib.5plus.base-release.aar

android-gif-drawable-release@1.2.17.aar

miit_mdid_1.0.10.aar

三个文件复制粘贴到libs目录下

点击build.grade,下滑到底部资源引用页面

如下图添加引用资源,

implementation fileTree(dir: ‘libs’, include: [‘*.aar’, ‘*.jar’], exclude: [])

implementation ‘com.github.bumptech.glide:glide:4.9.0’ // 基座依赖

implementation ‘com.android.support:support-v4:28.0.0’

implementation ‘com.alibaba:fastjson:1.1.46.android’

点击红色圈中的任一选项进行同步处理。

同步完成(这跟后面在AndroidManifest.xml中application节点添加内容相联系,如果没有添加依赖会报红)。

在原页面最上方配置app版本号。

applicationId为创建时的包名,compileSdkVersion为编译版本,minSdkVersion为兼容最小的版本号,targetSdkVersion为目标版本,有兴趣的可以百度一下三者之间的区别和联系。注意,官方文档中标注“App离线SDK minSdkVersion最低支持19,小于19在部分4.4以下机型上将无法正常使用。”

versionCode需要设定一个数值,一般为1,每次更新版本时versionCode的值都要比前一个设置的值大,否则无法正常安装,versionName一般填写主版本号次版本号和修正号,如图中的“1.0”为最初版本号,其余的可以自行查阅。

下面配置资源,你可能会奇怪我为什么跟官方文档顺序不一样,官方文档这一步是开始在strings.xml里修改应用名。因为资源导入的先后会影响使用和观感,之前在HBuilderX中生成本地打包的app资源还没引入怎么跟strings.xml里的应用名比较呢。

我们在main文件夹下创建assets文件夹。

把刚刚转移到自定义文件夹下的data文件夹拷贝到assets文件夹下。

官方文档中说dcloud1.dat、dcloud2.dat为uni-app所需资源(2.7.0之后已不在需要,升级时需要删除,可以减少apk大小),我演示的是5+App,所以不需要这两个文件。

继续在刚刚创建的assets文件夹下创建apps文件夹,把第三步中的文件(我的是H525CFE5C)拷贝到apps文件夹下。

自此资源引入完成,进行下一步strings.xml操作。

在app→src→main→res→ values配置strings.xml文件,修改应用名称,与

刚刚引入本地打包资源的里的manifest.json文件(assets>apps>“应用id名文件夹”>www下)比较,

发现不一致,遂将string.xml里的“Test2020”改为Mood。

在app→src→main下配置AndroidManifest.xml文件,

在将内容添加到application节点之前,不知道大家发现没,Application标签下红色圈中内部也有activity,它和即将添加的activity之间会不会有冲突呢。

实际上使用AndroidStudio开发软件时,一个activity的使用要在AndroidManifest.xml中声明,我们在第一步末尾运行时发现最后输出了”Hello World!”字样的界面。

仔细观察图中圈出的activity,作为”Run App”跑出来的页面,它在AndroidManifest.xml文件中<action android:name=”android.intent.action.MAIN” />做出了主页声明,如果你不考虑这点直接忽略它的话,在模拟器上跑时也许会生成两个相同图标的app,分别打开后一个是“Hello World!”界面,一个是你项目自定义的首页如index.html,也有一定可能无法正常运行,所以最好的办法是直接注释掉。

然后继续下一步,添加内容到application节点(建议复制官方文档里的,下面的复制粘贴后排版会比较乱)。

代码

 <activity      

android:name=”io.dcloud.PandoraEntry”                    android:configChanges=”orientation|keyboardHidden|keyboard|navigation”      

android:label=”@string/app_name”      android:launchMode=”singleTask”      android:hardwareAccelerated=”true”      android:theme=”@style/TranslucentTheme”      android:screenOrientation=”user”     android:windowSoftInputMode=”adjustResize” >      

<intent-filter>          

<action android:name=”android.intent.action.MAIN” />          

<category android:name=”android.intent.category.LAUNCHER” />     </intent-filter>  

</activity>  

<activity      

android:name=”io.dcloud.PandoraEntryActivity”     android:launchMode=”singleTask”      android:configChanges=”orientation|keyboardHidden|screenSize|mcc|mnc|fontScale|keyboard”      

android:hardwareAccelerated=”true”      android:permission=”com.miui.securitycenter.permission.AppPermissionsEditor”      

android:screenOrientation=”user”      android:theme=”@style/DCloudTheme”      android:windowSoftInputMode=”adjustResize”>      

<intent-filter>          

<category android:name=”android.intent.category.DEFAULT” />      <category android:name=”android.intent.category.BROWSABLE” />

<action android:name=”android.intent.action.VIEW” />          

<data android:scheme=”h56131bcf” />      

</intent-filter>  

</activity>

如下图。

下面是应用图标和启动页面的配置,官方文档说的比较清楚,我就不多此一举了,我举另外的例子,通过AndroidStudio软件修改应用图标。

在应用目录内部右键调出菜单(New→Image Asset),点击。

出来应用图标设置界面。

修改背景。

修改前景。

点击Finish。

这样一个简单的应用图标就完成啦,有需求的可以网上详细了解哦。

下面配置资源环境。

将assets下apps文件夹中的manifest.json文件和data文件夹中的dcloud_control.xml文件打开,确保manifest.json中的id和dcloud_control.xml中的appid一致。

自定义基座暂不考虑。

然后就可以愉快地调试程序啦,

Run~

图标

打开。

效果。

最后一步,离线打包

在菜单栏中选择Build,点击Generate Signed Bundle /APK…项,进入打包页面。

选中APK,点击Next。

打包需要签名认证,点击Create new…创建。

如图,第一行创建自定义jks文件,并确定路径,我把它设置为Mood(这种其实是不符合jks格式的,最后生成时可能会有警告,一般为a-b-c.jks,平时还是要多注意规范)。

填写密钥库密码和确定密钥库密码,因为是演示,我把密码设置为123456。

Alias为密钥名称,在这里把它设置mood,Password为密钥密码,仍设置为123456,Validity(years)为密钥有效时间,按年算起,直接默认。

下面First and Last Name为名字和姓氏 ,Organizational Unit为组织单位 ,Organization 为组织,City or Locality为城市或地区,State or Province为州或者省份,Country Code(XX)为国家 ,至少选填一种,这里我们直接填上国家China。

点击OK。

跳转到签名界面,因为都帮我们填好了,所以点击Next即可。

这里要说一下V1和V2的选择问题,V1属于旧的验证方式,V2是在安卓版本7.0之后新的验证方式,只勾选V1在Android7.0以上不会使用更安全的验证方式,如果只勾选V2那么安卓版本7.0以下的手机将无法正常安装,所以建议V1和V2同时勾选。

debug和release不用多说,一个是测试版一个是正式版,这里我直接选择release,点击Finish,等待打包完成。

完成打包,点击locate或者直接进入到AndroidStudio项目文件夹app>release下,

找到app-release.apk包,安装。

打开。

教程结束。

总结

不知道大家发现没有,上面都是在创建Empty Activity的基础上进行打包配置的,Empty Activity在app>src>main>java>自定义包名a.b.c的目录下默认生成MainActivity文件,这个文件又绑定了一个layout文件,默认是在app>src>main>res>layout目录下的activity_main.xml文件,在AndroidManifest.xml文件中做出activity声明,最后我们还要把这个activity注释或者删除掉,这也就代表MainActivity文件和activity_main.xml文件没有效用了,蚊子再小也是肉,删掉还可以缩小点app体积。

其实没必要后期删除那么麻烦,大家只需在第一步创建Empty Activity项目用来测试AndroidStudio开发环境即可,在第四步开始时,New>New Project创建No Activity用来打包自己的项目,点击Next,下一页同第一步一样自定义,然后点击Finish,这样一个NoActivity项目就创建完毕啦。

在AndroidManifest.xml页我们可以看到如下页面:

只需把它修改为这样。

再添加内容。

后续步骤都是一样的,这样可以避免在AndroidManifest.xml中注释或删除默认activity,也省去了删除默认文件的麻烦。

官方文档中附加的内容

安卓模块及第三方SDK配置:

https://nativesupport.dcloud.net.cn/AppDocs/usemodule/android

Android注意事项:

https://nativesupport.dcloud.net.cn/AppDocs/FAQ/android

Java十大常用框架介绍

转载:https://www.toutiao.com/a6591434508417892871/?tt_from=copy_link&utm_campaign=client_share&timestamp=1535816836&app=news_article&utm_source=copy_link&iid=42275378268&utm_medium=toutiao_ios&group_id=6591434508417892871

一、SpringMVC

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。

模型(Model )封装了应用程序的数据和一般他们会组成的POJO。

视图(View)是负责呈现模型数据和一般它生成的HTML输出,客户端的浏览器能够解释。

控制器(Controller )负责处理用户的请求,并建立适当的模型,并把它传递给视图渲染。

Spring的web模型 – 视图 – 控制器(MVC)框架是围绕着处理所有的HTTP请求和响应的DispatcherServlet的设计。

Spring Web MVC处理请求的流程

Java十大常用框架介绍(spring系+dubbo+RabbitMQ+Ehcache+redis)

具体执行步骤如下

1、 首先用户发送请求————>前端控制器,前端控制器根据请求信息(如URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;图2-1中的1、2步骤;

2、 页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在Spring Web MVC中叫命令对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个ModelAndView(模型数据和逻辑视图名);图2-1中的3、4、5步骤;

3、 前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;图2-1中的步骤6、7;

4、 前端控制器再次收回控制权,将响应返回给用户,图2-1中的步骤8;至此整个结束。

二、Spring

2.1、IOC容器:

IOC容器就是具有依赖注入功能的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。

2.2、AOP:

简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系

AOP用来封装横切关注点,具体可以在下面的场景中使用:

  • Authentication 权限
  • Caching 缓存
  • Context passing 内容传递
  • Error handling 错误处理
  • Lazy loading 懒加载
  • Debugging 调试
  • logging, tracing, profiling and monitoring 记录跟踪 优化 校准
  • Performance optimization 性能优化
  • Persistence 持久化
  • Resource pooling 资源池
  • Synchronization 同步
  • Transactions 事务

三、Mybatis

MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

总体流程:

(1)加载配置并初始化

触发条件:加载配置文件

将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

(2)接收调用请求

触发条件:调用Mybatis提供的API

传入参数:为SQL的ID和传入参数对象

处理过程:将请求传递给下层的请求处理层进行处理。

(3)处理操作请求

触发条件:API接口层传递请求过来

传入参数:为SQL的ID和传入参数对象

处理过程:

(A)根据SQL的ID查找对应的MappedStatement对象。

(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。

(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。

(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。

(E)释放连接资源。

(4)返回处理结果将最终的处理结果返回

MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用JDBC或者类似框架的经历,您就会明白把SQL语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在columns列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。

四、Dubbo

Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC(远程过程调用协议)远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbo这样的分布式服务框架的需求,并且本质上是个服务调用的东东,说白了就是个远程服务调用的分布式框架。

1、透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。

2、软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。

3、 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

节点角色说明:

  • Provider: 暴露服务的服务提供方。
  • Consumer: 调用远程服务的服务消费方。
  • Registry: 服务注册与发现的注册中心。
  • Monitor: 统计服务的调用次调和调用时间的监控中心。
  • Container: 服务运行容器。

五、Maven

Maven这个个项目管理和构建自动化工具,越来越多的开发人员使用它来管理项目中的jar包。但是对于我们程序员来说,我们最关心的是它的项目构建功能。

六、RabbitMQ

消息队列一般是在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。

RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器。

Erlang是一门动态类型的函数式编程语言。对应到Erlang里,每个Actor对应着一个Erlang进程,进程之间通过消息传递进行通信。相比共享内存,进程间通过消息传递来通信带来的直接好处就是消除了直接的锁开销(不考虑Erlang虚拟机底层实现中的锁应用)。

AMQP(Advanced Message Queue Protocol)定义了一种消息系统规范。这个规范描述了在一个分布式的系统中各个子系统如何通过消息交互。

七、Log4j

日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。

八、Ehcache

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

优点:

1、 快速

2、 简单

3、 多种缓存策略

4、缓存数据有两级:内存和磁盘,因此无需担心容量问题

5、 缓存数据会在虚拟机重启的过程中写入磁盘

6、可以通过RMI、可插入API等方式进行分布式缓存

7、 具有缓存和缓存管理器的侦听接口

8、支持多缓存管理器实例,以及一个实例的多个缓存区域

9、提供Hibernate的缓存实现

缺点:

1、使用磁盘Cache的时候非常占用磁盘空间:这是因为DiskCache的算法简单,该算法简单也导致Cache的效率非常高。它只是对元素直接追加存储。因此搜索元素的时候非常的快。如果使用DiskCache的,在很频繁的应用中,很快磁盘会满。

2、不能保证数据的安全:当突然kill掉java的时候,可能会产生冲突,EhCache的解决方法是如果文件冲突了,则重建cache。这对于Cache数据需要保存的时候可能不利。当然,Cache只是简单的加速,而不能保证数据的安全。如果想保证数据的存储安全,可以使用Bekeley DB Java Edition版本。这是个嵌入式数据库。可以确保存储安全和空间的利用率。

九、Redis

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis数据库完全在内存中,使用磁盘仅用于持久性。相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。Redis可以将数据复制到任意数量的从服务器。

1.2、Redis优点:

(1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。

(2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。

(3)操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。

(4)多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

1.3、Redis缺点:

(1)单线程

(2)耗内存

十、Shiro

Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。Shiro的具体功能点如下:

(1)身份认证/登录,验证用户是不是拥有相应的身份;

(2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

(3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

(4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

(5)Web支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

(6)shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

(7)提供测试支持;

(8)允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

(9)记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

文字描述可能并不能让猿友们完全理解具体功能的意思。下面我们以登录验证为例,向猿友们介绍Shiro的使用。至于其他功能点,猿友们用到的时候再去深究其用法也不迟。

十一、设计模式

这个算不上框架,可自行忽略,不过博主认为设计模式的思想很有必要了解一下。

思想:

开闭原则:

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码。

针对接口编程,针对接口编程,依赖于抽象而不依赖于具体。

尽量使用合成/聚合的方式,而不是使用继承。

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

使用多个隔离的接口,比使用单个接口要好。

里氏代换原则:

(1)子类的能力必须大于等于父类,即父类可以使用的方法,子类都可以使用。

(2)返回值也是同样的道理。假设一个父类方法返回一个List,子类返回一个ArrayList,这当然可以。如果父类方法返回一个ArrayList,子类返回一个List,就说不通了。这里子类返回值的能力是比父类小的。

(3)还有抛出异常的情况。任何子类方法可以声明抛出父类方法声明异常的子类。 而不能声明抛出父类没有声明的异常。

在vue-cli项目中添加页面启动页

<template> <div id=”app”> // 启动页内容开始 <div class=”wrap” v-if=”show”> <div class=”pic1″> <img src=”@/assets/images/pic1.png” /> <img src=”@/assets/images/pic2.png” /> <img src=”@/assets/images/pic3.png” /> </div> </div> // 启动页内容结束 <keep-alive> <router-view v-if=”$route.meta.keepAlive” /> </keep-alive> <router-view v-if=”!$route.meta.keepAlive” /> </div> </template> <script> export default { name: ‘App’, data() { return { show: false } }, mounted() { // 判断页面是首次加载还是刷新 if (window.performance.navigation.type === 1) { console.log(‘页面被刷新’) } else { // 首次加载的话显示启动页内容,设置延时器再取消显示 console.log(‘首次加载’) this.show = true setTimeout(() => { this.show = false }, 4000) } } } </script> <style lang=”scss” scoped> #app { height: 100%; } // 启动页样式 .wrap { width: 100vw; height: 100vh; background: url(‘~@/assets/images/bg.png’) no-repeat center top #fff; background-size: cover; box-sizing: border-box; overflow: hidden; position: fixed; z-index: 2; } .wrap .pic1 { position: relative; width: 100%; top: 48px; } .pic1 img { opacity: 0; width: 21%; position: absolute; left: 48px; animation-name: fadeIn; /*动画名称*/ animation-duration: 1s; /*动画持续时间*/ animation-iteration-count: 1; /*动画次数*/ animation-delay: 0s; /*延迟时间*/ animation-fill-mode: forwards; } .pic1 img:nth-child(2) { left: 128px; top: 48px; animation-name: fadeIn; /*动画名称*/ animation-duration: 1s; /*动画持续时间*/ animation-iteration-count: 1; /*动画次数*/ animation-delay: 1s; /*延迟时间*/ animation-fill-mode: forwards; } .pic1 img:nth-child(3) { left: 208px; top: 48px; animation-name: fadeIn; /*动画名称*/ animation-duration: 1s; /*动画持续时间*/ animation-iteration-count: 1; /*动画次数*/ animation-delay: 2s; /*延迟时间*/ animation-fill-mode: forwards; } @keyframes fadeIn { 0% { opacity: 0; /*初始状态 透明度为0*/ } 50% { opacity: 0; /*中间状态 透明度为0*/ } 100% { opacity: 1; /*结尾状态 透明度为1*/ } } </style>

vue video全屏播放

需求:

1、视频为长方形,页面初始化打开为横屏全屏播放视频。

2、微信不支持自动播放,故自动播放需求删除。

方法:

1、vue-video-player插件

因需求较简单,仅要求播放本地一个视频,故未选择使用插件。

2、video

<div id="video_box" style="z-index: 999;" :class="{video_box_rotate: isIos}">
                <video
                    @click="videoPlay"
                    class="index_video"
                    poster="../assets/images/poster.jpg"
                    id="video_content"
            style="object-fit:fill"  /*加这个style会让 Android / web 的视频在微信里的视频全屏,如果是在手机上预览,会让视频的封面同视频一样大小
                    webkit-playsinline='true'
                    playsinline="true"
                    x5-playsinline="true"
                    x-webkit-airplay="true"
                    x5-video-player-type="h5"
                    x5-video-player-fullscreen="true" /*全屏播放*/
                    x5-video-ignore-metadata="true"
                    x5-video-orientation="landscape" /*播放器的方向,landscape横屏,portraint竖屏,默认值为竖屏*/
           preload="preload"> <source src="../../static/video.mp4" type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"' > </video> </div>

具体属性解释可参考 https://blog.csdn.net/qq_16494241/article/details/62046891

同层H5播放器官网 https://x5.tencent.com/tbs/guide/video.html

注意:

x5-video-orientation="landscape" /*播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏*/
landscape属性ios不支持
为兼容ios横屏将视频旋转90度
mounted() {
        if (是否为ios) {
            this.videoFullScreen();
        }
    }
methods: {
    // 视频宽高设置为手机宽高 videoFullScreen() { let width = document.documentElement.clientWidth; let height = document.documentElement.clientHeight; document.getElementById('video_content').style.height = width + 'px'; document.getElementById('video_content').style.width = height + 'px'; }, }
/*视频旋转*/
.video_box_rotate{
   transform rotate(90deg) }

视频监听播放结束、进入全屏、退出全屏事件

mounted() {this.videoEnd()},
methods: {
    videoEnd(){

      let video = document.getElementById(‘video_content’);      video.addEventListener(‘ended’, () => {        alert(‘video end’)      });


  }; // 视频播放结束 }
// 全屏事件 x5videoenterfullscreen
// 退出全屏 x5videoexitfullscreen

监听手机横竖屏

window.addEventListener('orientationchange', function() {
                    // alert(window.orientation); // 这里可以根据orientation做相应的处理
                    if (window.orientation === -90) {
                        self.iphoneScreenShow = true;
                    } else {
                        self.iphoneScreenShow = false;
                    }
                }, false);

视频初始化黑屏
可以在视频上加个div浮层(可以一个假的视频第一帧),然后用timeupdate方法监听,视屏播放及有画面的时候再移除浮层。https://segmentfault.com/a/1190000009395289
视频进入全屏,退出全屏监听
https://segmentfault.com/a/1190000013232870
监听视频播放完成
https://blog.csdn.net/mondy592/article/details/81219167

参考资料 https://blog.csdn.net/xcy1193068639/article/details/80242111#commentsedit

安卓去掉控制按钮 参考http://www.xyhtml5.com/3252.html

欢迎大家指点,谢谢

转载于:https://www.cnblogs.com/1032473245jing/p/10222448.html

在vue中写一段自适应屏幕代码,并在移动端判断横竖屏

在这里插入图片描述
在这里插入图片描述

效果如下:
1.自适应

2.判断移动端横竖屏

只需在app.vue中写入如下代码

<template>
  <div id="app">
    // 注意! src的图片自己找一张gif图
    <div class="app-hint"
         v-if="isScreen"><img src="./assets/image/screen.gif"
           alt=""></div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      screenWidth: document.body.clientWidth, // 网页可见区域宽
      screenHW: window.orientation, // 判断横竖屏
      isScreen: false // 横竖屏
    };
  },
  watch: {
    screenWidth(val) {
      // 为了避免频繁触发resize函数导致页面卡顿,使用定时器
      if (!this.timer) {
        // 一旦监听到的screenWidth值改变,就将其重新赋给data里的screenWidth
        this.screenWidth = val;
        this.timer = true;
        let that = this;
        setTimeout(function() {
          // 执行自适应代码
          that.bodyScale()
          // 打印screenWidth变化的值
          console.log(that.screenWidth);
          that.timer = false;
        }, 400);
      }
    },
    screenHW () {
      this.rotate()
    }
  },
  methods: {
    // 自适应代码
    bodyScale() {
      var devicewidth = document.documentElement.clientWidth; //获取当前分辨率下的可是区域宽度
      var scale = devicewidth / 1960; // 分母——设计稿的尺寸
      document.body.style.zoom = scale; //放大缩小相应倍数
    },
     // 判断横竖屏
    rotate () {
      if (this.screenHW == 180 || this.screenHW == 0) {
        console.log('竖屏')
        this.isScreen = true
      } else if (this.screenHW == 90 || this.screenHW == -90) {
        console.log('横屏')
        this.isScreen = false
      }
    }
  },
  created() {
    // 执行自适应代码
    this.bodyScale()
  },
  mounted () {
    // 监听窗口大小
    window.onresize = () => {
      return (() => {
        this.screenWidth = document.body.clientWidth;
        // 把横竖屏的变化赋值
        that.screenHW = window.orientation
      })()
    }
  }
};
</script>