Gradle使用总括,餐厅CEO要累疯了No

Laravel 是在 App\Console\Kernel 类中的 schedule 方法去定义全部的调度职分。

自身是小蕉。

(一)Quartz单机

转发请标明出处:
本文来源【赵彦军的博客】

iBrand
产品作为一个电商类产品,自个儿业务要求尤其庞大和复杂,全体概念在 schedule 方法中会极度臃肿。而且
iBrand 产品的每种模块都以1个 Package,具有惊人的复用性。

往昔有座山,山里有座庙,庙里有个老和尚,阿不,有个饭馆的老总娘,在每一日午餐此前,都要和谐亲力亲为为顺序小伙伴分配职分,大喊一声开饭呀,咱们就屁颠屁颠跑过来领任务。炒菜嘛,就付出A来做,他的厨艺好。搬桌子嘛,交给B来做,他健康。煮饭嘛,交给C来做,他相比较细致。陪客人唠嗑嘛,就提交小蕉,终究他嘴皮子可决定了。每一日都如此,纵然很累,但业主倒也乐此不疲。直到有一天。

1.Quartz简介

其他

Gradle使用总括,餐厅CEO要累疯了No。Groovy 使用完全解析
http://blog.csdn.net/zhaoyanjun6/article/details/70313790

由此大家付出了此包,用来把各类模块的调度职分放在本身的包里,而不是漫天放在 App\Console\Kernel 类的 schedule 方法中。

餐厅发展进一步好,职员和工人更是多,总COO的职工达到了壹玖捌柒6名,COO明显已经黔驴技穷了,常常记错职员和工人的名字,也时不时记错要给职工布署的职务,又3回如故把作者真是隔壁老王,让自身去扫厕所T_T。那也太委屈了,该怎么帮帮组长,顺便帮帮作者自身吗?

  Quartz是三个完全由java编写的开源作业调度框架,能兑现业务的定时调度。Quartz首要有多少个主旨调度器、职分和触发器:

Android Gradle

Android项目采取 Gradle 作为创设框架,Gradle
又是以Groovy为脚本语言。所以读书Gradle在此之前供给先熟稔Groovy脚本语言。

Groovy是基于Java语言的脚本语言,所以它的语法和Java非凡相像,可是全部比java更好的面面俱圆。上边就罗列部分和Java的重中之重差距。

github: ibrandcc/laravel-scheduling

在业主累死在此之前,笔者说了算做一些业务,来消除这么些死循环。

①任务-JobDetail:Quartz在职分调度的时候,需求创制三个职分实例,JobDetail正是承担那一个剧中人物。在Quartz2.0在先的本子中,创制3个职务通过new
JobDetail(String jobName, String gruop, Class
jobCLass)这一个办法来制造,在2.0过后的本子中经过JobBuilder来创立职责。Job接口中只有贰个主意void
execute(JobExecutionContext context) throws
JobExecutionException,由此在使命调度时,只要求贯彻execute方法就可以了,使用起来很有益于。

Android Gradle 的 Project 和 Tasks

其一Gradle中最关键的三个概念。每一回塑造(build)至少由二个project构成,三个project
由一到三个task构成。项目布局中的每一个build.gradle文件表示多少个project,在那编写翻译脚本文件中能够定义一多级的task;task
本质上又是由一组被每种执行的Action`目的构成,Action其实是一段代码块,类似于Java中的方法。

 

大家换二个思路,各类职工在入职的时候都会拿走一份”工作中央“,然后主管快开饭的时候大喊一声,开饭呀。各种职员和工人就考察总首席营业官的面色,就分别行动去啊。A呢就和好去炒菜,搬桌子的业务就付出B啦,C自从没被派去扫地饭也煮得进一步鲜美,一切都好起来了。

②触发器-Trigger:定义Job的实践时间、次数等音信,有SimpleTrigger和CronTrigger三种档次。当你需求的是二回性的调度,或然您需求在钦命的时辰激活某些职务并进行N次,设置每回职责履行的区间时间T。那此时选择SimpleTrigger将是可怜便于的。假设你想在周周的周天7:00或许每月的第贰,3天完成职责调度,那时候就须求用到CronTrigger,CronTrigger能够提供复杂的触发器表达式满意大家的急需。同时必要提到的少数是Quartz还提供俯拾便是日子的兑现类DateBuilder等,在实际上利用中很便利。

Android Gradle 创设生命周期

历次创设的实践本质上实施一多重的Task。某个Task可能借助别的Task。哪些没有借助的Task总会被第二执行,而且各个Task只会被实践一回。每回创设的借助关系是在构建的配置阶段分明的。每一趟营造分为三个级次:

  • Initialization: 起先化阶段

那是开创Project阶段,营造筑工程具依照种种build.gradle文件创设出五个Project实例。开首化阶段会实施项目根目录下的settings.gradle文件,来分析哪些项目加入营造。

因此这些文件之中的情节日常是:

include ':app'
include ':libraries:someProject'

这是报告Gradle这个种类须要编译,所以大家引入一些开源的花色的时候,需求在此处填上相应的项目名称,来报告Gradle那一个种类必要出席创设。

  • Configuration:配置阶段

那个等级,通过履行营造脚本来为每种project创设并安插Task。配置阶段会去加载全数参加营造的类其余build.gradle文件,会将每一种build.gradle文件实例化为多个Gradle的project对象。然后分析project之间的注重关系,下载注重文件,分析project下的task之间的借助关系。

  • Execution:执行阶段

那是Task真正被实践的阶段,Gradle会根据注重关系决定哪些Task须要被实施,以及实施的先后顺序。
task是Gradle中的最小执行单元,大家富有的创设,编写翻译,打包,debug,test等都以实施了某一个task,二个project能够有多少个task,task之间能够互相正视。例如小编有多个task,taskA和taskB,钦赐taskA注重taskB,然后实施taskA,那时会先去履行taskB,taskB执行落成后在执行taskA。

说到那大概会有失水准,作者翻遍了build.gradle也没看见2个task长啥样,有一种被欺诈的赶脚!

事实上不是,你点击AndroidStudio左边的3个Gradle按钮,会打开二个面板,内容基本上是这么的:

澳门葡京备用网址 1

其间的每2个条款都以2个task,那这些task是哪来的吧?

一个是根目录下的 build.gradle 中的

dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
    }

1个是 app 目录下的 build.gradle 中的

apply plugin: 'com.android.application'

这两段代码决定的。也正是说,Gradle提供了二个框架,那一个框架有一些周转的机制得以让您完结编写翻译,可是关于怎么编写翻译是由插件决定的。辛亏谷歌已经给我们写好了Android对应的Gradle工具,大家利用就足以了。

根目录下的build.gradle中dependencies {classpath
‘com.android.tools.build:gradle:2.2.2’}是Android Gradle编写翻译插件的本子。

app目录下的build.gradle中的apply plugin:
‘com.android.application’是引入了Android的行使营造项目,还有com.android.library和com.android.test用来创设library和测试。

持有Android构建供给执行的task都封装在工具里,假如您有一对优良必要的话,也能够协调写一些task。那么对于开发3个Android应用来说,最要害的有的正是什么来用AndroidGradle的插件了。

Installation

$ composer require ibrand/laravel-scheduling -vvv

If your Laravel version below 5.5, you need add the follow line to the section providers of config/app.php:

iBrand\Scheduling\ServiceProvider::class,

依据那样布置吧,那样前面正是新进二个职工,也只是多一发一份工作中央的事。

③调度器-Scheduler:Quartz框架的主干是调度器。调度器负责管理Quartz应用运营时环境。调度器不是靠本人做有所的做事,而是借助框架内部分可怜重要的预制构件。Quartz不仅仅是线程和线程管理。为保险可伸缩性,Quartz选用了依据八线程的框架结构。运维时,框架初阶化一套worker线程,这套线程被调度器用来施行预订的学业。那正是Quartz怎么样能并发运维三个作业的原理。Quartz注重一套松耦合的线程池管理部件来管理线程环境。

认知Gradle Wrapper

Android Studio中暗中同意会利用 Gradle Wrapper
而不是直接选择Gradle。命令也是使用gradlew而不是gradle。那是因为gradle针对特定的费用条件的构建脚本,新的gradle恐怕或无法匹配旧版的创设环境。为了消除那几个标题,使用Gradle
Wrapper 来直接使用
gradle。相当于在异乡包裹了两个中间层。对开发者来说,直接运用Gradlew
即可,不必要关心 gradle的版本变化。Gradle Wrapper
会负责下载合适的的gradle版本来营造项目。

Usage

辟如二个职工太老了炒不了菜,给她换一份评释可能直接操办理离休退休手续休不处监护人务就,在CEO喊开饭的时候就平素忽略就好。就在敏感的小蕉的提出下,老总进行了组织整理,终于闲下来了,又重回了终日唠嗑收钱的吉日了,笔者吧,也再也并未被派去扫厕所。

2.搭建Quartz工程

Android 四个文本根本的 gradle 文件

Gradle项目有三个重点的文本需求深入掌握:项目根目录的 build.gradle ,
settings.gradle 和模块目录的 build.gradle 。

  • 1.settings.gradle 文本会在营造的 initialization
    阶段被实践,它用于告诉创设系统怎样模块必要包括到创设进度中。对于单模块项目,
    settings.gradle
    文件不是少不了的。对于多模块项目,就算没有该公文,创设系统就不能够分晓该用到怎么着模块。

  • 2.档次根目录的 build.gradle
    文件用来安插针对具有模块的有个别特性。它暗许包蕴1个代码块:buildscript{…}和allprojects{…}。前者用于配置创设脚本所用到的代码库和依靠关系,后者用于定义全部模块须要接纳的片段公家属性。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

buildscript:定义了 Android 编写翻译工具的类路径。repositories中,
jCenter是三个知名的 Maven 仓库。

allprojects:中定义的习性会被使用到拥有 moudle
中,可是为了保险各个品种的独立性,大家一般不会在那其间操作太多共有的东西。

  • 3.模块级配置文件 build.gradle 针对各样moudle
    的安顿,借使那里的定义的挑三拣四和顶层
    build.gradle定义的一样。它有三个关键的代码块:plugin,android 和
    dependencies。

extend Scheduling abstract class

use iBrand\Scheduling\Scheduling;

class YourSchedule extends Scheduling {

    public function schedule(){

        //Your schedule logic.
        $this->schedule->call(function () {

        })->daily();
    }
} 

那!!!天哪!!!那跟前些天要大家大快朵颐的,在事实上中国人民解放军海军事工业程大学业作中相见的多个题材不谋而合啊!!!正是if
else!!!看过3个多分支职分重跑类,就是由3个CODE来决定怎么着重跑。我的荧屏有24寸那么长,一显示器看下去,满显示器都是if
else!!!What the。

①创办2个新工程

定制项目性质(project properties)

在类型根目录的build.gradle配置文件中,大家得以定制适用于具有模块的属性,通过ext
代码块来促成。如下所示:

ext {
    compileSdkVersion = 22
    buildToolsVersion = "22.0.1"
}

下一场大家可以在模块目录的build.gradle配置文件中援引那一个属性,引用语法为rootProject.ext.{属性名}。如下:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
}

register

Add follow line to you ServiceProvider
register method.

$this->app->make(iBrand\Scheduling\ScheduleList::class)->add(YourSchedule::class);

 

 

if(some){}

  新建文件lib作为外部jar包,Quartz最新版本通过官网能够下载。导入你下载的quartz包,新建package为com.example.singleQuartz,里面就多少个简易的类SingleQuartzJob和SingleQuartzServer。SingleQuartzJob定义Job的兑现类,SingleQuartzServer任务调度服务。

Android studio gradle Task

澳门葡京备用网址 2

//构建
gradlew app:clean    //移除所有的编译输出文件,比如apk

gradlew app:build   //构建 app module ,构建任务,相当于同时执行了check任务和assemble任务

//检测
gradlew app:check   //执行lint检测编译。

//打包
gradlew app:assemble //可以编译出release包和debug包,可以使用gradlew assembleRelease或者gradlew assembleDebug来单独编译一种包

gradlew app:assembleRelease  //app module 打 release 包

gradlew app:assembleDebug  //app module 打 debug 包

//安装,卸载

gradlew app:installDebug  //安装 app 的 debug 包到手机上

gradlew app:uninstallDebug  //卸载手机上 app 的 debug 包

gradlew app:uninstallRelease  //卸载手机上 app 的 release 包

gradlew app:uninstallAll  //卸载手机上所有 app 的包

这一个都以基本的授命,在事实上项目中会依据分歧的安顿,会对那一个task
设置不相同的注重。比如 暗中同意的 assmeble 会依赖 assembleDebug
和assembleRelease,假设直白执行assmeble,最终会编写翻译debug,和release
的具有版本出来。假设大家只要求编写翻译debug
版本,大家可以运维assembleDebug。

除却还有一些常用的增加产量的别样命令,比如 install命令,会将编写翻译后的apk
安装到连年的装置。

else if(some else){

澳门葡京备用网址 3

lint 检测

  • 不经意编写翻译器的 lint 检查

android {

  lintOptions {
      abortOnError false
  }

}

if(some){}

②SingleQuartzJob.java定义Job的实现类

buildTypes 定义了编写翻译类型

android{

  buildTypes {
        release {
            minifyEnabled true  //打开混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false //关闭混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 }

else{}

package com.example.singleQuartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

/**
 * Created by XuHui on 2016/12/22.
 */
public class SingleQuartzJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.print("Hello, Quartz! - executing its JOB at "+
                new Date() + " by " + context.getTrigger().getJobKey() + "\n");
    }
}

productFlavors 多渠道打包

AndroidManifest.xml 里安装动态渠道变量

<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${UMENG_CHANNEL_VALUE}" />

在 build.gradle 设置 productFlavors ,
那里假定大家须求打包的水道为酷安市面、360、Samsung、百度、豌豆荚。

android {  

    productFlavors {
        kuan {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "kuan"]
        }
        xiaomi {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
        }
        qh360 {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"]
        }
        baidu {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
        }
        wandoujia {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
        }
    } 

}

要么批量改动

android {  

    productFlavors {
        kuan {}
        xiaomi {}
        qh360 {}
        baidu {}
        wandoujia {}
    }  

    productFlavors.all { 
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
    }
}

诸如此类在卷入的时候就足以选用渠道了

澳门葡京备用网址 4

要么用命令打包 ,比如:

gradlew assembleWandoujiaRelease  //豌豆荚 release 包

gradlew assembleWandoujiaDebug //豌豆荚 debug 包

}

③SingleQuartzServer.java达成职务调度

Signing 签名

在 android 标签下添加 signingConfigs 标签,如下:

android {
    signingConfigs {
        config {
            keyAlias 'yiba'
            keyPassword '123456'
            storeFile file('C:/work/Key.jks')
            storePassword '1234567'
        }
    }
 }   

能够在 release 和 debug 包中定义签名,如下:

android {
    signingConfigs {
        config {
            keyAlias 'yiba'
            keyPassword '123456'
            storeFile file('C:/work/Key.jks')
            storePassword '1234567'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
    }
}

else if(some others){}

澳门葡京备用网址 5澳门葡京备用网址 6

凭借管理

else if(some etc.){}…

package com.example.singleQuartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by XuHui on 2016/12/22.
 */
public class SingleQuartzServer {
    public static void main(String[] args) throws SchedulerException {
        /* 获取Scheduler实例 */
        SchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();

        /* 启动调度器 */
        scheduler.start();

        /* 任务:withIdentity(String name, String group),其中groupName可以自己定义,也可以用Scheduler提供的DEFAULT_GROUP。
                这里要求同组里面的jobName不能相同*/
        JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                .withIdentity("job", Scheduler.DEFAULT_GROUP)
                .build();

        /*触发器:SimpleTrigger和CronTrigger。实现的功能是:接下来30s后执行job,以后每个10s执行一次,重复10次,一共执行11次。
                  nextGivenSecondDate(null, 30)距离现在时间30s之后执行job,此处null可写作new Date(),可自行在api查看源码实现 */
        Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(10))
                .forJob(jobDetail)
                .build();

        /* 交由调度器调度Job */
        scheduler.scheduleJob(jobDetail, simpleTrigger);

        /* 3分钟任务调度之后关闭调度器 */
        try{
            TimeUnit.MINUTES.sleep(3);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scheduler.shutdown();
        }
    }
}

1、依赖 jcenter 包

各种库名称包罗七个因素:组名:库名称:版本号

 compile 'com.android.support:appcompat-v7:25.0.0'

望着那几个代码这太痛心了,后边也倒霉维护啊,这背后加3个职能岂不是要废?这么一大坨万一自小编还不敢轻易改,怕怕,怕怕。本着“作者不管笔者就要改的”的美意,作者决定重构一下以此代码。说走笔者就走呀。天上的蝇头参北斗啊…

View Code

贰 、正视地点 module

 compile project(':YibaAnalytics')

上面是纯技术干货,非技术宅的看官老爷能够一向左上角退出。

④实践职分调度

3、依赖 jar 包

  • 1、把 jar 包放在 libs 目录下
  • 贰 、在 build.gradle 中添加正视

dependencies {
   compile files('libs/YibaAnalytics5.jar')
}

澳门葡京备用网址 7

小编们项目选用的是SpringMVC嘛,所以就勇敢地运用Spring容器了。首先要有二个定时JOB吧,否则怎么来统一筹划重跑,经验上的话,每五分钟跑一遍,跑2次不成功,要么正是目的服务有毛病,要么正是地方抛非常挂掉了。那就脑袋一拍,咱就选择观看者方式,让种种执使用Quartz来驱动那么些定时职务好了。
定义一个bean然后类是底下那几个,还有targetMethod属性要定义好就行了。

  执行run->main可是发现报错了。此时是少了多个个日志包,slf4j-api-1.7.22.jar和slf4j-simple-1.7.22.jar,我们只要添加slf4j包就足以了。

4、依赖 aar 包

  • 1、把 aar 包放到 libs 目录下
  • 二 、在 build.gradle 中添加依赖

repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compile(name:'YibaAnalytics-release', ext:'aar')
}

如图所示:

澳门葡京备用网址 8

class=”org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean”>

透过官网能够下载http://www.slf4j.org/download.html,找到那多少个包放到lib下就足以了。

五 、自定义注重包目录

当我们的 aar 包必要被七个 module 依赖时,我们就不能够把 aar 包放在单一的
module 中,大家能够在档次的根目录成立一个索引,比如叫 aar
目录,然后把大家的 aar 包放进去,如图所示:

澳门葡京备用网址 9

在类型的根目录的 build.gradle 的 allprojects 标签下的 repositories 添加

 flatDir {
     dirs '../aar'
}

../aar 表示根目录下的 aar 文件夹。

如图所示:

澳门葡京备用网址 10

接下来就足以添加信赖了,如下所示:

 compile(name:'YibaAnalytics-release', ext:'aar')

概念1个调度中央,也正是上边那东西,用来保管整批的重跑职务。

Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
    at org.quartz.impl.StdSchedulerFactory.<init>(StdSchedulerFactory.java:303)
    at com.example.singleQuartz.SingleQuartzServer.main(SingleQuartzServer.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 7 more

⑥ 、依赖配置

稍许时候,你可能须要和sdk协调工作。为了能如愿编写翻译你的代码,你需求添加SDK到您的编写翻译环境。你不须求将sdk包蕴在您的APK中,因为它早已经存在于设备中,所以布置来啦,大家会有多少个例外的布署:

  • compile
  • apk
  • provided
  • testCompile
  • androidTestCompile

compile是暗中认可的不行,其意思是包蕴全体的重视包,即在APK里,compile的依赖会设有。

apk的情趣是apk中留存,可是不会进入编写翻译中,那么些貌似用的可比少。

provided的情致是提供编写翻译帮助,不过不会写入apk。

@Service(ObserverCenter.SERVICE_ID)

消除完上边日志包的标题,咱们运营方面程序能够见见平常的调度结果如下。

native包(so包)

用c恐怕c++写的library会被喻为so包,Android插件暗许景况下帮衬native包,你必要把.so文件放在对应的公文夹中:

澳门葡京备用网址 11

注意

jniLibs 目录应当和 Java 目录在同顶级

class ObserverCenter{

[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:30 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:40 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:50 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:00 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:10 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:20 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:30 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:40 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:50 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:00 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:10 CST 2016 by DEFAULT.job
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

defaultConfig 详解

defaultConfig 对应的是 ProductFlavor 类。

public static final String SERVICE_ID=”ObserverCenter”;

 ⑤职务调度的二十八线程

resConfigs : 过滤语言

假设您的app中仅协助1,2种语言,不过或许引用的lib库蕴含多样别的语言的strings能源,这么些时候大家得以经过resConfig内定大家须要的strings财富。

android {

    defaultConfig {
        applicationId "com.yiba.sharewe.lite.activity"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 46
        versionName "1.74"
        resConfigs 'en', 'zh-rCN' ,'es'  //本次打包,只把 en(英文)、zh-rCN(中文简体)、es(西班牙语)打进保内,其他语言忽略
    }
}

//hold住有所的调度职分总括机,在底下会定义的

  Scheduler是存在多对多的涉嫌,由于线程池的存在,调度器完成十二线程并发执行任务调度,直接看下边demo就能了解。

resConfigs : 过滤 drawable文件夹的能源

诚如景观下,我们打完包,res 下边包车型客车能源如图所示:

澳门葡京备用网址 12

前几日添加能源过滤规则:

android {

    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        resConfigs "hdpi"  //打包的时候只保留 drawable-xhdpi 文件夹里面的资源
    }

}

此次我们打包效果如下:

澳门葡京备用网址 13

List registedObservers = new ArrayList();

澳门葡京备用网址 14澳门葡京备用网址 15

buildTypes 详解

合法文书档案

buildTypes{}对应的是 BuildType 类

接轨关系

BuildType 继承 DefaultBuildType ; DefaultBuildType 继承 BaseConfigImpl

BaseConfigImpl
    --- DefaultBuildType 
          --- BuildType

buildTypes的属性:

name:build type的名字

applicationIdSuffix:应用id后缀

versionNameSuffix:版本名称后缀

debuggable:是否生成一个debug的apk

minifyEnabled:是否混淆

proguardFiles:混淆文件

signingConfig:签名配置

manifestPlaceholders:清单占位符

shrinkResources:是否去除未利用的资源,默认false,表示不去除。

zipAlignEnable:是否使用zipalign工具压缩。

multiDexEnabled:是否拆成多个Dex

multiDexKeepFile:指定文本文件编译进主Dex文件中

multiDexKeepProguard:指定混淆文件编译进主Dex文件中

buildType的方法:

1.buildConfigField(type,name,value):添加一个变量生成BuildConfig类。

2.consumeProguardFile(proguardFile):添加一个混淆文件进arr包。

3.consumeProguardFile(proguardFiles):添加混淆文件进arr包。

4.externalNativeBuild(action):配置本地的build选项。

5.initWith:复制这个build类型的所有属性。

6.proguardFile(proguardFile):添加一个新的混淆配置文件。

7.proguradFiles(files):添加新的混淆文件

8.resValue(type,name,value):添加一个新的生成资源

9.setProguardFiles(proguardFileIterable):设置一个混淆配置文件。

//提供三个给电脑注册的接口

package com.example.singleQuartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by XuHui on 2016/12/22.
 */
public class SingleQuartzThreadPoolServer {
    public static void main(String[] args) throws SchedulerException {
        /* 获取Scheduler实例 */
        SchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();

        /* 启动调度器 */
        scheduler.start();

        /******************************************************* Job-1 ****************************************************************/
        /* 任务:withIdentity(String name, String group),其中groupName可以自己定义,也可以用Scheduler提供的DEFAULT_GROUP。
                这里要求同组里面的jobName不能相同*/
        JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                .withIdentity("job1", Scheduler.DEFAULT_GROUP)
                .build();

        /*触发器:SimpleTrigger和CronTrigger。实现的功能是:接下来30s后执行job,以后每个10s执行一次,重复10次,一共执行11次。
                  nextGivenSecondDate(null, 30)距离现在时间30s之后执行job,此处null可写作new Date(),可自行在api查看源码实现 */
        Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(2))
                .forJob(jobDetail)
                .build();

        /* 交由调度器调度Job */
        scheduler.scheduleJob(jobDetail, simpleTrigger);

        /******************************************************* Job-2 ****************************************************************/
        jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                .withIdentity("job2", Scheduler.DEFAULT_GROUP)
                .build();

        simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(2))
                .forJob(jobDetail)
                .build();
        scheduler.scheduleJob(jobDetail, simpleTrigger);

        /******************************************************* Job-3 ****************************************************************/
        jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                .withIdentity("job3", Scheduler.DEFAULT_GROUP)
                .build();

        simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger3", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(2))
                .forJob(jobDetail)
                .build();
        scheduler.scheduleJob(jobDetail, simpleTrigger);

        /* 2分钟任务调度之后关闭调度器 */
        try{
            TimeUnit.MINUTES.sleep(2);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scheduler.shutdown();
        }
    }

}

initWith :复制属性

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        myType {
            initWith debug  //完全复制 debug 的所有属性‘
            minifyEnabled true //自定义打开混淆
        }
    }
}

public void register(Observer observer){

View Code

applicationIdSuffix 、versionNameSuffix :添加后缀

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.wifi.analytics"
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            applicationIdSuffix "zhao"  //applicationId 追加后缀名 zhao
            versionNameSuffix "debug"  //versionName 追加后缀名 debug1.0
        }
    }

效果图,如下:

澳门葡京备用网址 16

this.registedObservers.add(observer);

举办结果如下:

buildConfigField: 自定义属性

在 build.gradle 文件中定义 buildConfigField 属性

android {

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://yiba.com\""  //自定义String属性
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://yiba.com\""  //自定义String属性
        }
    }
}

然后点击同步按钮,然后就能够在 build 目录看到 debug 和 release 新闻。

debug 环境下的 BuildConfig 如下:
澳门葡京备用网址 17

release 环境下的 BuildConfig 如下:

澳门葡京备用网址 18

当然大家也足以在代码中获取自定义的值:

//获取变量值
String API = BuildConfig.API_ENV ;

地点演示了自定义 String 变量,也能够 自定义 int 、boolean

android {

    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "API_ENV", "\"http://www.baidu.com\"" //自定义 String 值
            buildConfigField "Boolean", "openLog", "true" //自定义 boolean 值
            buildConfigField "int", "age", "10"   //自定义 int 值
        }
    }
}

}

[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job1
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job2
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job3
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job3
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job1
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job2
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job1
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job3
Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job2
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

Gradle 实现差距化营造

//提供一个给电脑注销的接口

 (二)任务调度持久到数据库

情景1

LeakCanary 是 square 公司出品的3个检查和测试内部存款和储蓄器泄漏的开源库。

GitHub :

我们一般这样集成

dependencies {
    compile 'com.squareup.leakcanary:leakcanary-android:1.5.2'
}

下一场大家在 Application 类中开端化:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        LeakCanary.install(this);
    }
}

唯独这么集成有三个弊病,正是 debug 和 release 包都会把 LeakCanary
的源码打进去,假诺大家在 release 包中不把 LeakCanary
源码打进去,咋办? 幸好 LeakCanary 给我们提供了二个方法,方法如下:

dependencies {

 //打 debug 包
 debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'

 //打 release 包
 releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

}

leakcanary-android-no-op 是1个空壳,里面有3个空类,所以就足以制止把
LeakCanary 源码打进 release
包。可是那种艺术有个缺陷,假若部分开源库没有提供 releaseCompile
库,那大家改怎么办了?上面包车型客车情景2 就会讲到消除方案。

public void unRegister(Observer observer){

1.白手起家数据库和建表

情景2

Stetho 是 Faceboo
k开源的Andorid调节和测试工具。当您的采纳集成Stetho时,开发者能够访问Chrome,在Chrome
Developer Tools中查阅应用布局,互联网请求,sqlite,preference 等等。

官网:

从官网能够观望 stetho 没有提供 releaseCompile 包 , 情景1
的方案就不能够用了。新的思路集成方案如下:

dependencies {
    debugCompile 'com.facebook.stetho:stetho:1.5.0'
}

在 src 目录下创办 debug 目录、release 目录 ,然后分别在 debug 目录 和
release 目录 创设 java 目录 , 在 java 目录中开创包名,比如: com.app
, 如下图所示:

澳门葡京备用网址 19

debug 目录下开创 SDKManage 类 ,如下 :

public class SDKManager {

    public static void init(Context context) {
        //初始化 Stetho
        Stetho.initializeWithDefaults(context);
    }
}

release 目录下成立 SDKManage 类 ,如下 :

public class SDKManager {

    public static void init(Context context) { 
        //这是一个空方法,目的是不引入 Stetho 源码
    }

}

在住项目中的 MyApplication 类,并且成功 Stetho 的开头化,如下:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        SDKManager.init(this);
    }
}

那样我们便实现了差不离的差距化营造, 打出去的 release 包就从不 Stetho
源码。

this.registedObservers.remove(observer);

  上边包车型客车事例的职分是存在内部存款和储蓄器中的,要是程序关闭任务就会烟消云散,为了保持职务的可持久性,要求将职分调度存到数据库中。在你下载的quartz包下找到quartz-2.2.1\docs\dbTables\,找到你对号入座的数据库建表语言,那里自身用的是mysql数据库tables_mysql.sql。在您本地或许服务器数据库新建二个数据库quartz(你能够不建,首要为了和其余数据库区分)。将以下的建表语言导入该数据库。

SourceSet

}

澳门葡京备用网址 20澳门葡京备用网址 21

SourceSet 简介

SourceSet
能够定义项目结构,也能够修改项目协会。Java插件暗许完毕了多少个SourceSet,main
和 test。每一种 SourceSet 都提供了一与日俱增的习性,通过这几个属性,能够定义该
SourceSet 所包罗的源文件。比如,java.srcDirs,resources.srcDirs 。Java
插件中定义的别的任务,就根据 main 和 test 的那三个 SourceSet
的定义来寻觅产品代码和测试代码等。

//Quartz执行的法门

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;

SourceSet 定义源码目录

在 Android 项目中,大家得以在 src/main/java 目录新建 Java
文件,如下图所示:

澳门葡京备用网址 22

今天我们在 src 目录下,新建 test1 目录 ,发现无法在 test1 目录中新建
Java 文件,如下图所示:

澳门葡京备用网址 23

怎么在 test1 目录不能新建 Java 文件,因为 Gradle 中 SourceSet
暗中同意定义的源码文件路径是src/main/java ,
其余的文书下上边包车型大巴源码大家本来不可能访问。化解这些题材也很简短,我们须要在
SourceSet 中加进1个源码路径即可。如下所示:

android {

    sourceSets {
        main {
            java {
                srcDir 'src/test1' //指定源码目录
            }
        }
    }
}

然后共同一下,就能够在 test1 目录中新建 Java 文件了。如下图所示:

澳门葡京备用网址 24

当然大家也足以同时钦赐多少个源码目录,比就像是时钦定 test1 , test2 , test3
为源码目录。

android {

    sourceSets {
        main {
            java {
                srcDir 'src/test1' //指定 test1 为源码目录
                srcDir 'src/test2' //指定 test2 为源码目录
                srcDir 'src/test3' //指定 test3 为源码目录
            }
        }
    }
}

或者 这样写 :

android {
    sourceSets {
        main {
            java.srcDirs( 'src/test1' , 'src/test2' ,'src/test3' )
        }
    }
}

成效如下图所示:

澳门葡京备用网址 25

public void execute(){

View Code

SourceSet 定义财富目录

概念 test1 目录 Java 源代码路径、res 能源目录。目录结构如下图所示:

澳门葡京备用网址 26

android {

    sourceSets {
        main {
            java.srcDirs('src/test1/java')  //定义java 源代码
            res.srcDirs('src/test1/res')    //定义资源目录(layout , drawable,values)
        }
    }
}

//获得供给重跑的天职列表

2.导入mysql数据库的jdbc驱动包,mysql-connector-java-5.1.40-bin.jar

SourceSet 实现 layout 分包

对于一个大品类以来,页面太多,布局文件就广大,有时在诸多布局文件中找某些模块的布局文件,非常难过,为了化解这一个题目,大家得以在开创多个layout 目录,不相同模块的布局文件放在区别的 layout
目录中,那样查找起来,就简单很多。

例子:

譬如大家的类别中,有七个模块分别为:登录、注册。

  • 第3步:把品种中 layout 文件夹改名字为 layouts

  • 其次步:在 layouts 目录下,分别创制 login 、register 目录 。

  • 其三步:分别在 login 、register 目录下创立 layout
    目录。注意这一步是必须的,不然会报错。

  • 第⑤步:把 登录布局文件、注册布局文件 分别位于 第②步创造的对应的
    layout 目录下。

功能图如下:

澳门葡京备用网址 27

SourceSet 完成如下:

android {

    sourceSets {
        main {
            res.srcDirs 'src/main/res/layouts/login'  //定义登录布局目录
            res.srcDirs 'src/main/res/layouts/register'  //定义注册布局目录
        }
    }
}

List somethingNeedToRun = new ArrayList();

3.安插Quartz配置文件quartz.properities

SourceSet 定义 AndroidManifest 文件

点名 test1 目录下的 AndroidManifest 文件。项目结构如下图所示:

澳门葡京备用网址 28

代码如下:

android {

    sourceSets {
        main {
            manifest.srcFile 'src/test1/AndroidManifest.xml'
        }
    }
}

在组件化开发中, 大家需求针对 debug 与 release 格局下, 钦定分化的
Manifest 文件, 代码如下:

android {
    def appDebug = false;

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            appDebug = false;
        }

        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            appDebug = false;
        }
    }

    sourceSets {
        main {
            if (appDebug) {
                manifest.srcFile 'src/test1/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
}

//将职分广播到具备的职务观望者中,由观看者决定是或不是处理以及怎么着处理

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS

org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = 123456
org.quartz.dataSource.myDS.maxConnections = 5

SourceSet 定义 assets 目录

Android Studio 项目目录中,assets 私下认可目录如下:

澳门葡京备用网址 29

何以重新定义 assets 目录 。在品种的根目录下创办 assets
目录,如下图所示:

澳门葡京备用网址 30

sourceSets 定义代码如下:

android {

    sourceSets {
        main {
            assets.srcDirs = ['assets']
        }
    }
}

for(SomeRecords somethingNeedToRun in somethingNeedToRun){

4.职务调度的持久性

SourceSet 定义别的能源

android {

    sourceSets {
        main {
            jniLibs.srcDirs  //定义 jni 目录
            aidl.srcDirs  //定义 aidl 目录
        }
    }
}

for(Observer observer : registedObservers){

  其实介绍那部分重点是为着后边的Quartz集群做铺垫。为了印证职分调度的可持久性,大家从那两地点证实,当大家先后截至运维的时候,查看数据库能查看到任务调度的数目;当再次起动同一调度器的时候,原来的职务能继续执行。SingleQuartzKeep阿里ve.java如下,其实和上边的SingleQuartzServer基本等同,只是加载了Quartz配置文件。

applicationVariants

observer.handle(somethingNeedToRun);

澳门葡京备用网址 31澳门葡京备用网址 32

定义 versionName 、VersionCode

在包装的时候分 debug 、release 版本 , 需求控制 versionName

android {

     applicationVariants.all { variant ->
        def flavor = variant.mergedFlavor
        def versionName = flavor.versionName
        if (variant.buildType.isDebuggable()) {
            versionName += "_debug"  //debug 名字
        } else {
            versionName += "_release" //release 名字
        }
        flavor.versionName = versionName
    }

}

}

package com.example.singleQuartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by XuHui on 2016/12/22.
 */
public class SingleQuartzKeepAlive {
    public static void main(String[] args) throws SchedulerException {
         /* 获取Scheduler实例 */
        StdSchedulerFactory factory = new StdSchedulerFactory();
        factory.initialize("quartz.properities");
        Scheduler scheduler = factory.getScheduler();

        /* 启动调度器 */
        scheduler.start();

        JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
                .withIdentity("job", Scheduler.DEFAULT_GROUP)
                .build();

        Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
        SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(10))
                .forJob(jobDetail)
                .build();

        /* 交由调度器调度Job */
        scheduler.scheduleJob(jobDetail, simpleTrigger);

        /* 3分钟任务调度之后关闭调度器 */
        try{
            TimeUnit.MINUTES.sleep(3);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scheduler.shutdown();
        }
    }
}

概念 APK 包的名字

apply plugin: 'com.android.application'

android {

    defaultConfig {
        applicationId "android.plugin"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    //定义渠道
    productFlavors {
        xiaomi {
            //小米
        }
        wandoujia {
            // 豌豆荚
        }
    }

    //打包命名
    applicationVariants.all {
        variant ->
            variant.outputs.each {
                output ->

                    //定义一个新的apk 名字。
                    // 新名字 = app 名字+ 渠道号 + 构建类型 + 版本号 + 当前构建时间
                    def apkName = "appName_${variant.flavorName}_${buildType.name}_v${variant.versionName}_${getTime()}.apk";
                    output.outputFile = new File(output.outputFile.parent, apkName);
            }
    }

}

//获取当前时间
def getTime() {
    String today = new Date().format('YY年MM月dd日HH时mm分')
    return today
}

职能图如下:

澳门葡京备用网址 33

}

View Code

Task

}

小编们run->main发现报错了,报错如下。那么些因为大家不够了c3p0-0.9.1.2.jar,下载地址为。将那么些包参加lib中即可。

定义 task

//定义任务1
task task1<<{
    println 'task1'
}

//定义任务2
task task2<<{
    println 'task2'
}

}

澳门葡京备用网址 34澳门葡京备用网址 35

mustRunAfter 定义 task 执行种种

//task2 的执行顺讯在 task1 之后
task2.mustRunAfter task1
  • 测试1 : gradlew task1

效果如下:

:app:task1
task1
  • 测试2 : gradlew task2

效益如下:

:app:task2
task2
  • 测试3 : gradlew task1 task2

作用如下:

:app:task1
task1
:app:task2
task2
  • 测试4 : gradlew task2 task1

成效如下:

:app:task1
task1
:app:task2
task2

结论

要是单独实施 task1 就只会履行 task1 的任务;单独实施 task2 就只会实施
task2 的职分;
假定还要推行 task壹 、task2 , 一定会先举行 task1 , 等 task1
执行完后,就会执行 task2 内容。

扩展

地点 mustRunAfter 我们还有一种写法,如下图所示:

task2 {}.mustRunAfter task1

其一写法的法力和 mustRunAfter 是同等的,当然大家还足以在
花括号内部写一些职分,比如 :

task2 {
    println '我最先执行'
}.mustRunAfter task1

上面做个测试,测试命令如下:

gradlew task2 task1

意义如下:

我最先执行

:app:task1
task1
:app:task2
task2

好了,驱动类写完啦~谈谈观看类的子类把。首先会定义1个抽象类,作为有着调度任务的父类。每种观察者类都会登记到调度主题,所以预先达成好调度措施。@PostConstruct注明用于在实例构造完执行,那里用于把温馨注册到调度中央。@PreDestory申明用于在实例销毁在此以前,那里用于把自身从调度核心撤除。

Exception in thread "main" java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/ComboPooledDataSource
    at org.quartz.utils.PoolingConnectionProvider.initialize(PoolingConnectionProvider.java:210)
    at org.quartz.utils.PoolingConnectionProvider.<init>(PoolingConnectionProvider.java:155)
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1014)
    at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
    at com.example.singleQuartz.SingleQuartzKeepAlive.main(SingleQuartzKeepAlive.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: com.mchange.v2.c3p0.ComboPooledDataSource
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 10 more

Process finished with exit code 1

dependsOn 定义 task 依赖

task2 职责信赖于 task1 ,执行 task2 就会先实施 task1

task2.dependsOn task1

测试:

gradlew task2

职能如下:

:app:task1
task1
:app:task2
task2

public abstract class Observer(){

View Code

常用 Gradlew 命令

  • 一 、gradlew -v : 查看版本号

------------------------------------------------------------
Gradle 3.3
------------------------------------------------------------

Build time:   2017-01-03 15:31:04 UTC
Revision:     075893a3d0798c0c1f322899b41ceca82e4e134b

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_112 (Oracle Corporation 25.112-b15)
OS:           Windows 10 10.0 amd64
  • ② 、gradlew task : 查看全数的 task

@Resource(name=ObserverCenter.SERVICE_ID)

 运转结果寻常,咱们在数据库能够看来job和trigger表都有了任务调度记录数据。此时大家不妨只运转原来的调度器,看看是还是不是职务调度还是能平常进行。通过结果同学们就很精通了,此次职务调度大家只实行了五回,因为在程序中断以前运维了八回。

参考资料

Android
利用Gradle实现差别化创设

杨海 Android目录结构


个体微信号:zhaoyanjun125 , 欢迎关怀
澳门葡京备用网址 36

private ObserverCenter observerCenter;

澳门葡京备用网址 37澳门葡京备用网址 38

@PostConstruct

package com.example.singleQuartz;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Created by XuHui on 2016/12/23.
 */
public class SingleQuartzOldScheduler {
    public static void main(String[] args) throws SchedulerException {
        StdSchedulerFactory factory = new StdSchedulerFactory();
        factory.initialize("quartz.properities");
        Scheduler scheduler = factory.getScheduler();
        scheduler.start();
        try{
            TimeUnit.MINUTES.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scheduler.shutdown();
        }
    }
}
=================结果====================
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:16 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:26 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:36 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:46 CST 2016 by DEFAULT.job
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED paused.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete.

private registerToCenter(){

View Code

observerCenter.register(this);

5.结尾的工程目录结构

}

澳门葡京备用网址 39

@PreDestory

(三)Quartz集群

private unRegisterFromCenter(){

1.搭建web工程

observerCenter.register(this);

  看完第3某个从此,一大半同室早已知晓了,Quartz集群是怎么回事了。其实正是把调度职责存到数据库中,集群的时候每台服务器调度职务都指向同一数据库,从同一数据库取调度职分,那便是Quartz集群了。为在地面验证quartz集群,我重新搭建三个工程,里面撘四个web模块,目录结构如下。clusterweb_two和clusterweb_one文件和配备一模一样。

}

澳门葡京备用网址 40

void abstract handle(SomeRecords);

2.布置Quartz集群众文化艺术件quartz.properities

}

  多台服务器上的这些布局文件除了instanceId差别,那里能够设置成AUTO根据机器自动生成,其余宗旨都平等,必须界定的是数据库新闻必须一律。

概念四个抽记录类。

org.quartz.scheduler.instanceName = ClusterScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
# Cluster
org.quartz.jobStore.isClustered = true
org.quartz.scheduler.instanceId = AUTO

org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = 123456
org.quartz.dataSource.myDS.maxConnections = 5

public class SomeRecords{

3.配置web监听器

}

  我们把调度器放到web监听器中运作,当web运营时会运作那几个监听器,同时会运行调度器,监听器QuartzApplicationListener.java如下所示。

每一种继承了Observer的子类,在运用Spring的单实例方式,被容器构造达成后,都会自动注册到调度大旨,在接受到来自调度宗旨的职责后,各种子类能够控制自身需不必要处理,以及怎么处理。接下来是一个驱动类的DEMO,

澳门葡京备用网址 41澳门葡京备用网址 42

public class extends Observer(){

package com.example.web;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by XuHui on 2016/12/23.
 */
public class QuartzApplicationListener implements ServletContextListener {
    private Scheduler scheduler = null;
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        StdSchedulerFactory factory = new StdSchedulerFactory();
        try {
            factory.initialize("quartz.properities");
            scheduler = factory.getScheduler();
             /* 启动调度器 */
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job", Scheduler.DEFAULT_GROUP)
                .build();

        Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger", Scheduler.DEFAULT_GROUP)
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(10))
                .forJob(jobDetail)
                .build();
        try{
            /* 交由调度器调度Job */
            scheduler.scheduleJob(jobDetail, simpleTrigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        /* 3分钟任务调度之后关闭调度器 */
        try{
            TimeUnit.MINUTES.sleep(3);
        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        try{
            scheduler.shutdown();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

@Override

View Code

public void handle(SomeRecords){

4.web布置文件web.xml

Logger.log(“I don’t give a shit to the record!”)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <listener>
        <listener-class>
            com.example.web.QuartzApplicationListener
        </listener-class>
    </listener>
</web-app>

}

5.运维七个tomcat模拟集群

}

澳门葡京备用网址 ,  在idea中配置八个tomcat,并且让五个tomcat同时运行有点小麻烦,主要注意一点各类tomcat配置的Http
Port 和 JMX Port要不等。

在这些设计情势下,不会有这几个长的if
else,可维护性大大升高。后续在增多三个调度子类,以及去除七个调度子类,都至极方便,不会影响别的其它的处理器。

tomcat1 : Http Port : 8081 JMX Port : 1099

tomcat2 : Http Port : 8082 JMX Port : 1100

好啊今日的分享就到此处,我们有哪些建议足以在后台吐槽,反正作者也不会回的,毕竟我懒。

  tomcat1和tomcat2运维成功现在,此时八个劳务都已运营,此时唯有tomcat1发轫职责调度,tomcat2未开始展览职务调度。首先把tomcat1服务停掉,此时会出现tomcat2义务调度初始,那里跟做劳务高可信赖性很像。运转结果:

==============tomcat1=====================
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:00 CST 2016 by DEFAULT.job on webone
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:10 CST 2016 by DEFAULT.job on webone
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:20 CST 2016 by DEFAULT.job on webone
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:30 CST 2016 by DEFAULT.job on webone
D:\work\tomcat\apache-tomcat-8.0.23-windows-x64\apache-tomcat-8.0.23\bin\catalina.bat stop
Disconnected from server
==============tomcat2=====================
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:00 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:10 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:20 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:30 CST 2016 by DEFAULT.job on webtwo
Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:40 CST 2016 by DEFAULT.job on webtwo
[2016-12-23 11:29:48,369] Artifact clusterweb_two:war exploded: Artifact is deployed successfully

写在最后:Quartz基础部分总计起来也许不多,实际利用首若是Job中execute方法的兑现。看完上边的介绍之后大家发现,Quartz使用起来非凡便利,Quartz提供了过多日子和日历类。写过定时备份、定时查询总计方法的同校,知道那里写起来很辛劳。Quqatz大大减小了程序的代码量。此前和一个人同事谈论了什么日期用Quartz,很多时候我们都会融洽写职务调度。一般都是如此达成的:大家会启线程也就是定时器,定时去查询符合条件的Job,要是查询到多Job同时调度,大家会启2个线程池四线程并发运营。那样看起来Quartz都存有了,使用Quartz之后代码非常不难、可复用性高而且职责调度的可持久性,只须求配备Quartz配置文件就能够将调度职分存到数据库中,便是因为那点所以Quartz能够集群,对于职责数广大的集群环境下,那便是无与伦比的独到之处了。如若在搭建环境仍旧发现作品中有畸形或不足的地方,能够在凡间留言,大家一块儿学习。

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website