0%

多项目构建

初始化顶级项目
  1. 项目初始化

    1
    2
    3
    mkdir multi-project
    cd multi-project
    gradle init
  2. 顶级构建,也就是采用跟项目的配置,从而减少子项目的重复配置,可以把子项目的公共配置抽离到跟项目配置里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //跟项目build.gradle添加
    allprojects {
    repositories {
    jcenter() //将jcenter仓库配置到所有项目
    }
    }
    subprojects {
    version = '1.0' //设置版本号
    }
添加Groovy library子项目
  1. 执行mkdir multi-library创建一个子项目名为multi-library

  2. 进入multi-library子项目根目录,新建一个build.gradle

    1
    2
    3
    4
    5
    6
    7
    8
    //multi-library/build.gradle
    apply plugin : 'groovy'
    dependencies {
    compile 'org.codehaus.groovy:groovy:2.4.10'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4', {
    exclude module : 'groovy-all'
    }
    }
  3. 修改顶级项目根项目的settings.gradle,添加include 'multi-library'

  4. 完善子项目,创建两个目录mkdir -p multi-library/src/main/groovy/greetermkdir -p multi-library/src/test/groovy/greeter

  5. 在目录src/main/groovy/greeter新建GreetingFormatter.groovy文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //multi-library/src/main/groovy/greeter/GreetingFormatter.groovy
    package greeter
    import groovy.transform.CompileStatic
    @CompileStatic
    class GreetingFormatter {
    static String greeting(final String name) {
    "Hello, ${name.capitalize()}"
    }
    }
  6. 在目录src/test/groovy/greeter新建GreetingFormatterSpec.groovy文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //multi-library/src/test/groovy/greeter/GreetingFormatterSpec.groovy
    package greeter
    import spock.lang.Specification
    class GreetingFormatterSpec extends Specification {
    def 'Creating a greeting'() {
    expect: 'The greeting to be correctly capitalized'
    GreetingFormatter.greeting('gradlephant') == 'Hello, Gradlephant'

    }
    }
  7. 最后在顶级项目的根目录运行./gradlew build,到此一个项目依赖一个子项目就完成了

添加一个java 子项目
  1. 执行mkdir java-demo创建一个子项目名为java-demo

  2. 进入java-demo子项目根目录,新建一个build.gradle

    1
    2
    apply plugin : 'java' 
    apply plugin : 'application'
  3. 完善子项目,创建目录mkdir -p java-demo/src/main/java/greeter

  4. 修改顶级项目根项目的settings.gradle,添加include 'java-demo'

  5. 在目录java-demo/src/main/java/greeter新建Greeter.java文件

    1
    2
    3
    4
    5
    6
    7
    8
    package greeter;

    public class Greeter {
    public static void main(String[] args) {
    final String output = GreetingFormatter.greeting(args[0]);
    System.out.println(output);
    }
    }
  6. 进入java-demo子项目根目录,修改build.gradle

    1
    2
    3
    apply plugin : 'java' 
    apply plugin : 'application'
    mainClassName = 'greeter.Greeter'
  7. 最后在顶级项目的根目录运行./gradlew build,会出现依赖错误

    1
    2
    3
    * What went wrong:
    Execution failed for task ':java-demo:compileJava'.
    > Compilation failed; see the compiler error output for details.
  8. 解决,进入java-demo子项目根目录,修改build.gradle

    1
    2
    3
    4
    5
    6
    apply plugin : 'java' 
    apply plugin : 'application'
    mainClassName = 'greeter.Greeter'
    dependencies {
    compile project(':multi-library')
    }
  9. 重新执行./gradlew build,编译成功

  10. 添加test测试编译,进入java-demo子项目根目录,修改build.gradle

1
2
3
4
5
6
7
8
9
10
apply plugin : 'java' 
apply plugin : 'application'
apply plugin : 'groovy'
mainClassName = 'greeter.Greeter'
dependencies {
compile project(':multi-library')
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4', {
exclude module : 'groovy-all'
}
}
  1. 创建测试目录mkdir -p java-demo/src/test/groovy/greeter,添加GreeterSpec.groovy文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //java-demo/src/test/groovy/greeter/GreeterSpec.groovy
    package greeter

    import spock.lang.Specification

    class GreeterSpec extends Specification {

    def 'Calling the entry point'() {

    setup: 'Re-route standard out'
    def buf = new ByteArrayOutputStream(1024)
    System.out = new PrintStream(buf)

    when: 'The entrypoint is executed'
    Greeter.main('gradlephant')

    then: 'The correct greeting is output'
    buf.toString() == "Hello, Gradlephant\n".denormalize()
    }
    }
  2. 单项目编译,执行./gradlew :java-demo:test 其中java-demo为项目名,test为那种编译类型,也可以执行去子项目跟目录执行../gradlew test进行单模块编译

添加文档子项目
  1. 在顶级项目的build.gradle添加插件asciidoctor该插件的作用主要是将文档生成网页文件

    1
    2
    3
    4
    plugins {
    //apply false将插件添加到整个项目中,但不会将其添加到根项目中。
    id 'org.asciidoctor.convert' version '1.5.6' apply false //文档插件
    }
  2. 创建文档项目目录,在顶级项目跟目录,执行mkdir docs

  3. 然后在docs目录新建个build.gradle,在里头添加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    apply plugin : 'org.asciidoctor.convert'  //将插件用于该子项目
    //asciidoctor任务
    asciidoctor {
    sources {
    include 'greeter.adoc' //文档资源文件,需要自己新建
    }
    }
    //将asciidoctor任务添加到构建生命周期中,以便如果为顶级项目执行构建,则也将构建文档。
    build.dependsOn 'asciidoctor'
  4. 修改顶级项目根项目的settings.gradle,添加include 'docs'

  5. 然后新建个文档docs/src/docs/asciidoc/greeter.adoc,没有该目录创建就行,内容随便,后面会把此文件文档转为网页文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    = Greeter Command-line Application

    A simple application demonstrating the flexibility of a Gradle multi-project.

    == Installation

    Unpack the ZIP or TAR file in a suitable location

    == Usage

    [listing]
    ----
    $ cd greeter-1.0
    $ ./bin/greeter gradlephant

    Hello, Gradlephant
    ----
  6. 然后在顶级项目跟目录,运行task任务asciidoctor,执行./gradlew asciidoctor

  7. 会在目录docs/build/asciidoc/html5目录生成网页文件greeter.html

将文档包含到发布的项目
  1. 要将文档包含到发布的项目的目录可以新建task任务即可,修改java-demo项目的配置文件build.gradle增加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    distZip {
    from project(':docs').asciidoctor, {
    into "${project.name}-${version}"
    }
    }
    distTar {
    from project(':docs').asciidoctor, {
    into "${project.name}-${version}"
    }
    }
  2. 然后重新编译文件即可,在顶级项目跟目录执行./gradlew build

  3. 最后会在java-demo//build/distributions目录生成两个greeter-1.0.zipgreeter-1.0.tar,里面包含了编译好了的网页文件

整理顶级构建脚本

在java-demo和muilt-library项目中有相同的配置,这里把他们抽到顶级项目里配置

  1. 在顶级项目的build.gradle里添加公共配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    configure(subprojects.findAll {it.name == 'java-demo' || it.name == 'multi-library'} ) { //指定配置那些项目

    apply plugin : 'groovy'
    dependencies {
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4', {
    exclude module : 'groovy-all'
    }
    }
    }
  2. 删除子项目公有的配置,最终所有build.gradle配置如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    //顶级项目build.gradle
    plugins {
    //apply false将插件添加到整个项目中,但不会将其添加到根项目中。
    id 'org.asciidoctor.convert' version '1.5.6' apply false //文档插件
    }
    allprojects {
    repositories {
    jcenter() //将jcenter仓库配置到所有项目
    }
    }
    subprojects {
    version = '1.0' //设置版本号
    }
    configure(subprojects.findAll {it.name == 'java-demo' || it.name == 'multi-library'} ) {

    apply plugin : 'groovy'

    dependencies {
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4', {
    exclude module : 'groovy-all'
    }
    }
    }
    //----------------------------------------------------------
    //multi-library项目build.gradle
    dependencies {
    compile 'org.codehaus.groovy:groovy:2.4.10'
    }
    //-----------------------------------------------------------
    //java-demo项目build.gradle
    apply plugin : 'java'
    apply plugin : 'application'
    mainClassName = 'greeter.Greeter'

    dependencies {
    compile project(':multi-library')
    }

    distZip {
    from project(':docs').asciidoctor, {
    into "${project.name}-${version}"
    }
    }
    distTar {
    from project(':docs').asciidoctor, {
    into "${project.name}-${version}"
    }
    }
  3. 最后执行./gradlew clean build重新构建

总结

  1. 子项目只需build.gradle配置即可,且子项目不能gradle init

  2. 要父项目包含子项目,需要在setting.gradle设置include '项目名'

  3. 子项目依赖其他子项目只需要设置compile project(':子项目名')

  4. 插件需要在最顶部定义

  5. 运行单模块执行./gradlew :子项目名:构建命令构建命令目前包括test、build、clean

  6. 抽离子项目共有配置到父项目用configure

  7. 常用命令

    1
    gradle :eurekaserver:build //执行子项目eurekaserver构建build命令

安装Gradle

  1. java环境必须
  2. 下载Gradle,其中binary-only为单独的安装包,complete为文档加安装包,然后解压
  3. 在path中添加系统环境变量F:\xuan\gradle-4.7\bin
  4. 执行gradle -v检查是否安装成功

使用Gradle

  1. 进入一个Gradle项目,或者拉取一个 git clone git@github.com:gradle/gradle-build-scan-quickstart.git

Gradle build scans使用

Build Scans是用于开发和维护Gradle构建的重要工具。它为你提供了构建的详细信息,并为你识别构建环境、依赖或性能上存在的问题,同时可以帮你更全面地理解并提升构建过程,也便于与他人的合作。

大概理解就是该插件能把构建过程的数据分享出去,方便让别人查看分析构建信息。

  1. 进入项目跟目录执行构建扫描命令./gradlew build --scan,有可能会提示Do you accept these terms?是否接受许可协议,输入yes即可,成功后日志会输出一个链接类似https://scans.gradle.com/s/z5i6rmnpd4sbu,访问该链接就可以查看构建日志信息了(有的打开也许需要邮箱)。

  2. 上面虽然得到了链接,但是可以直接在build.gradle添加相关配置信息,构建扫描插件build-scan,为了将构建扫码发布到https://gradle.com/terms-of-service需要接受协议

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    plugins {
    id 'com.gradle.build-scan' version '1.13.2' //如果是低版本一定要放到其他插件前面
    }
    //配置扫描发送地址,以及同意协议
    buildScan {
    termsOfServiceUrl = 'https://gradle.com/terms-of-service'
    termsOfServiceAgree = 'yes' //同意协议
    tag 'xuan test' //打标签
    //项目的地址,这里需要是http地址,用git开头的地址编译报错
    link 'GitHub','https://github.com/gradle/gradle-build-scan-quickstart'
    }
  3. 执行gradle build -Dscan运行得到链接,然后访问即可

Creating New Gradle Builds

初始化gradle 项目
1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir gradle-demo #创建项目目录
cd gradle-demo #进入项目目录
gradle init #初始化为gradle项目
#生成如下目录文件
.
├── build.gradle #项目配置脚本
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar #wrapper可执行jar
│ └── gradle-wrapper.properties #wrapper 配置文件
├── gradlew # unix systems(linux)系统下的脚本
├── gradlew.bat # window 下的脚本
└── settings.gradle #配置那些项目参与构建脚本
创建task任务名叫copy
1
2
3
4
task copy(type: Copy, group: "Custom", description: "复制src目录到dest目录") {
from "src" //前提需要有src目录
into "dest" //不需要创建,会自动创建
}

命令行执行./gradlew copy代表执行task里的copy任务

Gradle 应用插件

插件仓库

这里以base插件,base插件功能主要是打包为zip文件

1
2
3
4
5
6
7
8
9
10
plugins {   //这个代码快必须放顶部
id "base"
}
task copy(type: Copy, group: "Custom", description: "复制src目录到dest目录") {
from "src"
into "dest"
}
task zip(type: Zip, group: "Util", description: "压缩src目录文件") {
from "src"
}

执行./gradlew zip,然后在目录 .\build\distributions下就可以看到gradle-demo.zip文件了

常用命令
1
2
3
./gradlew tasks #查看可用的task任务
./gradlew zip --scan #结合scan执行zip,分析执行信息
./gradlew properties #查看可用的配置属性,类似环境变量(配置属性)的

build.gradle可以更改properties变量的值,但是不能新增,例如修改项目描述和版本号

1
2
3
//放在build.gradle文件中,但是不能放在plugins前面,因为plugins这个必须在顶部
description = "该项目为测试学习用"
version = "1.0"

手册

Gradle 命令手册

注意事项

不能在gradle项目的子目录执行gradle init

RecyclerView 基本使用

  1. 添加依赖v7包依赖,版本号和compileSdkVersion一致,不然报错

    1
    2
    implementation 'com.android.support:appcompat-v7:25.0.0'
    implementation 'com.android.support:recyclerview-v7:25.0.0'
  2. 在layout里添加RecyclerView布局的引用

    1
    2
    3
    4
    5
    <android.support.v7.widget.RecyclerView
    android:id="@+id/recyView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />
  3. 添加一个item的布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
    android:id="@+id/recyNumberPhone"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <TextView
    android:id="@+id/recyNumberName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
    </android.support.constraint.ConstraintLayout>
  4. 创建适配器RecyNumberAdapter继承 RecyclerView.Adapter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class RecyNumberAdapter extends RecyclerView.Adapter<RecyNumberAdapter.NumberHolder>{
    private Context context; //上下文
    private List<Map<String,String>> mDatas; //数据源

    public RecyNumberAdapter(Context context,List<Map<String,String>> mDatas){
    //构造方法传入数据
    this.context=context;
    this.mDatas=mDatas;
    }

    @Override
    public NumberHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // 填充布局item
    View view = LayoutInflater.from(context).inflate(R.layout.recy_number, null);
    NumberHolder holder = new NumberHolder(view);
    return holder;
    }

    @Override
    public void onBindViewHolder(final NumberHolder holder, final int position) {
    //绑定view
    holder.recyNumberName.setText(String.valueOf(mDatas.get(position).get("name"))); //用户名
    holder.recyNumberPhone.setText(String.valueOf(mDatas.get(position).get("id"))); //电话号码
    }

    @Override
    public int getItemCount() {
    return mDatas.size(); //数据长度
    }
    //类部类
    class NumberHolder extends RecyclerView.ViewHolder{
    //获取item子布局的控件
    private TextView recyNumberPhone;
    private TextView recyNumberName;
    public NumberHolder(View view){
    super(view);
    recyNumberPhone=view.findViewById(R.id.recyNumberPhone);
    recyNumberName=view.findViewById(R.id.recyNumberName);
    }
    }
    }
  5. 调用测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    RecyclerView recyNumberView=findViewById(R.id.personalNumber);
    // 设置布局管理器有横向,表格等等
    GridLayoutManager gridLayoutManager=new GridLayoutManager(this,4);
    recyNumberView.setLayoutManager(gridLayoutManager);
    //测试数据
    List<Map<String,String>> datas=new ArrayList<>();
    Map<String,String> map=new HashMap<>();
    map.put("id","13432861290");
    map.put("name","张三");
    datas.add(map);
    //设置适配器
    recyNumberView.setAdapter(new RecyNumberAdapter(this,datas));
  6. 添加点击事件,在RecyNumberAdapter里添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private OnItemClickListener mItemClickListener;
    //item的回调接口
    public interface OnItemClickListener {
    void onItemClick(View view, int Position);
    }
    //定义一个设置点击监听器的方法
    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    this.mItemClickListener = itemClickListener;
    }
    //在覆写的onBindViewHolder方法中添加
    //如果设置了回调,则设置点击事件
    if (mItemClickListener != null) {
    holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    mItemClickListener.onItemClick(holder.itemView, position);
    }
    });
    }
  7. 调用的时候只需要recyNumberAdapter.setOnItemClickListener即可

dubbo官网/dubbo仓库地址

  1. 环境准备新建一个空的project,分别三个model

    model名称 model类型 说明
    common_interface java类型空项目 用于存放服务提供者和服务消费者的公共接口,避免写两次而已
    consumer springboot空项目 消费者服务依赖common_interface model
    provider springboot空项目 提供者服务依赖common_interface model
  2. 上面项目建立好,model依赖关系加好之后,下面开始引入dubbo框架

  3. 两个springboot项目都引入dubbo依赖这里用的gradle,仓库地址可以去dubbo仓库地址查看

    1
    compile group: 'com.alibaba', name: 'dubbo', version: '2.6.1'
  4. common_interface项目中添加一个接口类

    1
    2
    3
    4
    5
    package exxk.dubbo.commonimpl;

    public interface DemoService {
    String sayHello(String name);
    }
  5. provider项目中实现DemoService接口,在java目录下新建impl包,并添加一个DemoServiceImpl实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package exxk.dubbo.provider.impl;

    import exxk.dubbo.commonimpl.DemoService;

    public class DemoServiceImpl implements DemoService{
    @Override
    public String sayHello(String name) {
    return "hello"+ name;
    }
    }
  6. provider项目中resource目录下添加一个dubbo配置文件dubbo-provider.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--该提供者服务名称-->
    <dubbo:application name="dubbo-provider"/>
    <!--组播模式的注册中心,推荐用zookeeper-->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>
    <!--暴露的端口服务-->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!--声明暴露服务公共接口类-->
    <dubbo:service interface="exxk.dubbo.commonimpl.DemoService" ref="demoService"/>
    <!--提供者实现类-->
    <bean id="demoService" class="exxk.dubbo.provider.impl.DemoServiceImpl"/>
    </beans>
  7. provider项目中java目录下添加一个dubbo 启动类Provider.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package exxk.dubbo.provider;

    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class Provider {
    public static void main(String[] args) throws Exception {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
    new String[]{"dubbo-provider.xml"}); //读取dubbo配置文件
    context.start();
    //按任何键推出
    System.in.read();
    }
    }
  8. 上面的服务提供者基本完成,然后启动服务提供者,直接运行Provider.java静态方法即可

  9. consumer项目中resource目录下添加一个dubbo配置文件dubbo-consumer.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <dubbo:application name="dubbo-consumer"/>
    <!--注册中心地址(多播)-->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>
    <dubbo:reference id="demoService" interface="exxk.dubbo.commonimpl.DemoService"/>
    </beans>
  10. consumer项目中java目录下添加一个dubbo 启动类Consumer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exxk.dubbo.consumer;

import exxk.dubbo.commonimpl.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {
public static void main(String[] args) throws Exception{
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(
new String[]{"dubbo-consumer.xml"});
context.start();
DemoService demoService= (DemoService) context.getBean("demoService");
String hello= demoService.sayHello("world");
System.out.print(hello);
}
}
  1. 启动consumer服务消费者项目,这里用debug模式运行Consumer.java在里面打断点,主要是日志不好找,因此debug

基于springboot优化启动类

在springboot启动类添加dubbo启动,去掉默认的dubbo启动

  1. 最直接的是把dubbo启动类的配置xml内容直接放到application启动类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
    new String[]{"dubbo-provider.xml"}); //读取dubbo配置文件
    context.start();
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  2. 这种当然不优雅,可以直接把xml配置文件配置到注解@ImportResource

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  3. 代码是少了,但是据说,dubbo包含web框架,会让springboot当成web程序运行,这里自定义SpringApplicationBuilder禁用web

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    ApplicationContext context= new SpringApplicationBuilder()
    .sources(ProviderApplication.class)
    .web(WebApplicationType.NONE) //禁用web服务
    .run(args);
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  4. 同步锁线程也换种方式实现,利用同步工具类CountDownLatch,该工具类的大概作用就是有等待指定线程(数)执行完了,再执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @SpringBootApplication
    @ImportResource({"classpath:dubbo-provider.xml"})
    public class ProviderApplication {

    @Bean
    public CountDownLatch closeLatch() {
    return new CountDownLatch(1);
    }

    public static void main(String[] args) throws InterruptedException {
    //SpringApplication.run(ProviderApplication.class, args);
    ApplicationContext context= new SpringApplicationBuilder()
    .sources(ProviderApplication.class)
    .web(WebApplicationType.NONE) //禁用web服务
    .run(args);

    CountDownLatch closeLatch=context.getBean(CountDownLatch.class);
    closeLatch.await(); //等待所有子线程完成
    }
    }
  5. 最后删除旧的启动器,到此大功告成

Spring Boot干货系列总纲 阅读笔记

配置文件解析

默认src/main/resources目录下,两种格式application.propertiesapplication.yml

自定义属性,在配置文件application.properties定义diy.name="hello" ,在使用的地方加上注解

1
2
@Value("${diy.name}")
private String name;

自定义配置类,需要在springboot入口类添加@EnableConfigurationProperties({ConfigBean.class})

1
2
3
4
5
6
@ConfigurationProperties(prefix = "diy")
public class ConfigBean {
private String name;
private String want;
// 省略getter和setter
}

有bugbug。。。。。。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
批量修改文件编码
*/
console.log("开始修改文件编码");
var fs= require("fs");
//---------同步----------------
var files =fs.readdirSync("./");
files.forEach(function (filename){
var stats=fs.statSync(filename);
console.log(filename+"文件状态",stats);
//if(stats.isDirectory()) filename +='/';
//process.
});
//----------异步------------------
fs.readdir("./",function(err,files){
var len=files.length;
var file=null;
for(var i=0;i<len;i++){
file=files[i];
console.log("读取文件",file);
var stats=fs.stat(file);
console.log("文件状态",stats);
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env node
/*
批量修改文件编码
*/
console.log("开始修改文件编码");
var fs = require("fs");
var jschardet = require("jschardet");
var path = "node_modules/jschardet";
readDir(path);
/*
修改文件编码
*/
function readDir(dirPath) {
console.log("目录:",dirPath);
var files = fs.readdirSync(dirPath);
files.forEach(function(file) {
var filepath = dirPath +"/"+file;
var stats = fs.statSync(filepath);
//console.log(filename+"文件状态",stats);
if (stats.isFile()) {
var buff = fs.readFileSync(file);
var info = jschardet.detect(buff);
console.log(filename + "文件编码", info);
} else if (stats.isDirectory()) {
console.log("目录" + filepath);
readDir(filepath);
}
});
}

安装运行

  1. tomcat 下载apache-tomcat-8.5.12-windows-x64然后解压安装

  2. 编辑tomcat/conf/server.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 9301为自定义端口号,默认为8080 -->  
    <Connector port="9301" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <!--复制或者修改host-->
    <Host name="localhost" appBase="webapps"
    unpackWARs="true" autoDeploy="true">
    <!-- 新加context /app为url上下文,app为webapps下的app.war包-->
    <Context path="/app" docBase="app" reloadable="false"
    source="org.eclipse.jst.jee.server:tsj-spring"/>
    <Valve className="org.apache.catalina.valves.AccessLogValve"
    directory="logs" prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    </Host>
  3. 运行tomcat,window下双击打开bin目录下的startup.bat启动app.war

  4. 访问为ip:9301/app/

tomcat远程自动部署

  1. 安装tomcat8.5.x

  2. 修改tomcat配置文件conf/server.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8019" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
    <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
    type="org.apache.catalina.UserDatabase"
    description="User database that can be updated and saved"
    factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
    pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>
    <Service name="Catalina">
    <Connector port="8005" protocol="HTTP/1.1"
    connectionTimeout="20010"
    redirectPort="8454" />
    <Connector port="8021" protocol="AJP/1.3" redirectPort="8454" />

    <Engine name="Catalina" defaultHost="H8005">

    <Realm className="org.apache.catalina.realm.LockOutRealm">
    <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
    resourceName="UserDatabase"/>
    </Realm>

    <Host name="H8005" appBase="webapps"
    unpackWARs="true" autoDeploy="true">

    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />

    </Host>
    </Engine>
    </Service>
    </Server>
  3. 修改conf/tomcat-users.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <tomcat-users xmlns="http://tomcat.apache.org/xml"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
    version="1.0">

    <role rolename="admin-gui"/>
    <role rolename="admin-script"/>
    <role rolename="manager-gui"/>
    <role rolename="manager-script"/>
    <role rolename="manager-jmx"/>
    <role rolename="manager-status"/>
    <user username="admin" password="ideaadmin" roles="manager-gui,manager-script,manager-jmx,manager-status,admin-script,admin-gui"/>

    </tomcat-users>
  4. 启动tomcat执行./bin/catalina.sh start | tail -f ./logs/catalina.out,如果要修改启动内存,启动前提前修改catalina.sh

  5. conf/Catalina/h8005添加manager.xml

    1
    2
    3
    4
    <Context privileged="true" antiResourceLocking="false"
    docBase="${catalina.home}/webapps/manager">
    <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="^.*$" />
    </Context>
  6. 然后打开http://ip:8005/manager,输入用户名`admin`密码`idaeadmin`,进入之后保留**manager**删除其他所有applications

  7. 修改maven的.m2/setting.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    <servers>
    <server>
    <id>innerCs</id>
    <username>admin</username>
    <password>ideaadmin</password>
    </server>
    ...
    </servers>
  8. 在项目里面的pom.xml添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <build>
    <finalName>InnerCS</finalName>
    <plugins>
    ...
    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
    <url>http://ip:8005/manager/text</url>
    <path>/</path>
    <uriEncoding>UTF-8</uriEncoding>
    <server>innerCs</server>
    </configuration>
    </plugin>
    </plugins>
    </build>
  9. 编译Lifecyle->clean->install部署lifecyle->deploy发布到maven私库

  10. 远程第一次部署plugins->tomcat7->deploy,会上传ROOT.war/webapps/并解压运行生成ROOT因为我配置的path为/所以是ROOT

注意事项
  1. 第九步时,如果时maven多模块项目,需要在父项目,添加上传依赖jar的地址

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 配置maven地址 -->
    <distributionManagement>
    <snapshotRepository>
    <id>nexus-snapshots</id>
    <name>Nexus Snapshot Repository</name>
    <url>http://192.168.101.200:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
    </distributionManagement>
  2. 还需要在maven的setting.xml配置maven的用户名,才有权限上传

    1
    2
    3
    4
    5
    <server>
    <id>nexus-snapshots</id>
    <username>admin</username>
    <password>admin123</password>
    </server>
  3. 多模块项目需要在父级里面进行clean-install-deploy,注意勾选idea maven右侧菜单里面的Profiles,不然父级不知道编译那个子项目

常见问题

  1. tomcat启动时出现java.lang.IllegalArgumentException: Illegal character(s) in message header field: Pragma:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    java.lang.IllegalArgumentException: Illegal character(s) in message header field: Pragma:
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.checkMessageHeader(HttpURLConnection.java:511)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.isExternalMessageHeaderAllowed(HttpURLConnection.java:481)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(HttpURLConnection.java:2895)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestProperty(HttpsURLConnectionImpl.java:325)
    2019-02-20 17:40:47 331 - at mmo.common.utils.HttpUtils.sendPost(HttpUtils.java:28)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.service.QQGroupApiService$1.run(QQGroupApiService.java:169)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.entities.ext.QueueThreadHandle.run(QueueThreadHandle.java:52)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.service.QueueThreadPoolServer$1.run(QueueThreadPoolServer.java:26)
    2019-02-20 17:40:47 331 - at java.lang.Thread.run(Thread.java:748)

    解决:执行env检查环境变量中是否有JAVA_HOME,没有设置好这些环境变量

ZSH

zsh (Mac 系统自带,无需安装)。

安装Oh-My-Zsh管理zsh的配置工具 sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

常用功能

配置文件cat ~/.zshrc

命令历史记录功能ctrl+r

历史记录存放在cat ~/.zsh_history

ctrl+r搜索命令历史记录,!!执行上一条命了

补全TAB

命令别名alias

~/.zshrc中添加alias name=command即可

查看所有命令别名alias

插件

git

iterm2 修改配色

Solarized Dark Higher Contrast

在这里找到自己https://github.com/mbadolato/iTerm2-Color-Schemes/tree/master/schemes喜欢的

然后保存文件,双击安装,然后color->color下来框选择自己安装的

item2 安装powerlevel9k主题

1
2
3
git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k #下载主题
vim ~/.zshrc #编辑配置文件设置主题ZSH_THEME="powerlevel9k/powerlevel9k",去用户名添加 DEFAULT_USER="your user name"
git clone https://github.com/supermarin/powerline-fonts.git #下载字体

安装字体,双击/Monaco/Monaco for Powerline.otf文件安装字体

参考

https://gist.github.com/qhh0205/5570934d25a627dd9e9629a8ceeb415c

Compos file 版本3以上

Compos file v3官网

build

只支持单机运行

1
2
3
4
5
6
7
8
version: "3" #指明版本3,docker stack需要版本3以上
services:
webapp:
build: #集群部署会忽略构建镜像,stack只支持提前构建好镜像
context: ./dir #构建上下文路径
dockerfile: Dockerfile-alternate #构建文件
args: #构建参数
buildno: 1

configs (swarm模式)

只支持集群模式运行,只需要在单机上引用,会自动在其他节点创建配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3.3"
services:
redis:
image: redis:latest
deploy:
replicas: 1
configs:
- my_config
- my_other_config
configs:
my_config:
file: ./my_config.txt
my_other_config:
external: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: '3'
services:
nginx:
image: harbor.iexxk.dev/library/web-gate-id-v3:S3.2.1
restart: unless-stopped
ports:
- "88:80"
# volumes:
# - ./config/default.conf:/etc/nginx/conf.d/default.conf 不支持集群使用configs代替
configs:
- source: nginx_config
target: /etc/nginx/conf.d/default.conf
env_file:
- ./config/nginx.env #env也支持集群
configs:
nginx_config:
file: ./config/default.conf #支持集群

command

会覆盖dockerfile里面的命令例如:

1
2
command: bundle exec thin -p 3000 #docker-compose.yml覆盖dockerfile的命令
command: ["bundle", "exec", "thin", "-p", "3000"] #dockerfile等效于上面的命令

deploy(swarm模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
version: '3.2'
services:
redis:
image: redis:alpine
labels: #容器上的标签
com.example.description: " containers 上的标签"
deploy:
mode: global #可选global(全局模式,每台机一个)/replicated(多台)
replicas: 6 #部署的总数量
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
endpoint_mode: vip #可选vip(默认)/dnsrr,主要是IP啥的
labels: #服务器上的标签
com.example.description: "This label will appear on the web service"
placement: #指定部署的信息
constraints:
- node.role == manager #只在管理节点运行
- engine.labels.operatingsystem == ubuntu 14.04
preferences:
- spread: node.labels.zone
resources: #资源限制
limits:
#cpu个数0.5指一个cpu的50%,2.00指2个cpu(猜测99%)其中docker stats命令可以查看cpu使用率和内存使用,其中cpu使用率是指单核的使用率,也就是如果cpus设置为2,使用率200%就代表2个核都使用达到了100%
cpus: '0.50'
memory: 50M
reservations:
cpus: '0.25'
memory: 20M
restart_policy: #重启策略
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
update_config: #更新配置策略
parallelism: 2
delay: 10s
order: stop-first
depends_on: #依赖的服务模块,在db启动才启动服务,但是不能保证db启动完,如果要设置启动顺序见
#https://docs.docker.com/compose/startup-order/
- db
- redis
healthcheck: #健康检查
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
logging: #日志服务
driver: syslog
options:
syslog-address: "tcp://192.168.0.42:123"
volumes: #自定义挂在卷
- type: volume
source: mydata
target: /data
volume:
nocopy: true
- type: bind
source: ./static
target: /opt/app/static
ports: #自定义端口
- target: 80
published: 8080
protocol: tcp
mode: host

docker-compose 网络互相访问

让一个docker-compos里面的服务verse-web-admin-novel-1访问另一个gate-v3_defaul中的spring-gateway:8080

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#查看网络
[root@frts-p1 verse]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e8a9788c1674 bridge bridge local
305ea0a9d372 gate-v3_default bridge local
2770fd13abc4 host host local
0a9e80edc8d3 verse_default bridge local
#查看容器名
[root@frts-p1 verse]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db427829805e web:1 "/docker-entrypoint.…" 2 minute ago Up 2 second 92->80/tcp verse-web-admin-novel-1
69f449d6f2aa gat:1 "sh -c 'java $JAVA_O…" 2 months ago Up 2 months 8080/tcp gate-v3-spring-gateway-1
#连接网络 docker network connect <目标网络名称> <容器ID或名称>
[root@frts-p1 verse]# docker network connect gate-v3_default verse-web-admin-novel-1

docker-compose配置verse文件:

1
2
3
4
5
6
7
8
9
version: '3'
services:
web-admin-novel:
image: harbor.hcytech.dev/library/web-admin-novel:S1.3.1
restart: unless-stopped
ports:
- "92:80"
volumes:
- ./config/web-admin.conf:/etc/nginx/conf.d/default.conf

web-admin.conf配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 后端接口代理地址
upstream api_server {
server spring-gateway:8080;
}
server {
listen 80;
server_name localhost;
underscores_in_headers on;
client_max_body_size 256m;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
location /api/ {
rewrite ~/api/(.*)$ /$1 break;
proxy_pass http://api_server/api/;
proxy_set_header Host $host;
proxy_pass_request_headers on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 60000;
proxy_send_timeout 60000;
proxy_intercept_errors on; # 开启拦截错误
error_page 500 = /500handler;
error_page 404 = /404handler;
}
}

docker-compose配置gate-v3文件:

1
2
3
4
5
6
7
8
9
version: '3'
services:
spring-gateway:
image: harbor.hcytech.dev/library/hcytech-gateway:S1.0.3
volumes:
- ./config/application.yaml:/home/application.yaml
env_file:
- ./config/gateway.env
- ./config/redis.env

问题

错误services.nginx.ports.0 must be a string or number是因为自定义端口,只支持3.2以上

1
2
3
4
5
6
version: '3.2' 
ports: #自定义端口
- target: 80
published: 8080
protocol: tcp
mode: host

将一个html H5网页打包成一个apk

  1. MainActivity初始化一个webview,启用JavaScript脚步,然后设置自定义Webviewclient,接着就是设置些缓存等,这些非必须,最后终哟啊的一个方法就是加载webview.loadUrl这里添加你的h5链接,如果是本地,直接是路径就可以了,最后设置setContentView(webview);,里面还加了webview返回是网页的返回,到返回完了,在返回就是退出应用提示dialog

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    public class MainActivity extends AppCompatActivity {
    private WebView webview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //实例化WebView对象
    webview = new WebView(this);
    //设置WebView属性,能够执行Javascript脚本
    webview.getSettings().setJavaScriptEnabled(true);
    //重写WebViewClient
    webview.setWebViewClient(new WebViewClientDiy(this));
    //额外的设置,缓存等
    webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
    webview.getSettings().setJavaScriptEnabled(true);
    webview.getSettings().setDomStorageEnabled(true);
    webview.getSettings().setAppCacheMaxSize(1024 * 1024 * 8);
    String appCachePath = this.getApplicationContext().getCacheDir()
    .getAbsolutePath();
    webview.getSettings().setAppCachePath(appCachePath);
    webview.getSettings().setAllowFileAccess(true);
    webview.getSettings().setAppCacheEnabled(true);


    //加载需要显示的网页
    //webview.loadUrl("file:///android_asset/index.html");//显示本地网页
    webview.loadUrl("http://dev.clothes.yaokexing.com/mobile/welcome");//显示远程网页
    //设置Web视图
    setContentView(webview);
    }

    @Override//设置回退
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
    webview.goBack(); //goBack()表示返回WebView的上一页面
    return true;
    } else {
    AlertDialog exitDialog = new AlertDialog.Builder(this).create();
    exitDialog.setTitle("系统提示");
    exitDialog.setMessage("你确定要退出吗");
    exitDialog.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    finish();
    }
    });
    exitDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(MainActivity.this, "欢迎回来", Toast.LENGTH_SHORT).show();
    }
    });
    //onKeyListener用于设置监听手机back键的操作
    exitDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
    return true;// false时dialog会消失
    }
    return true;
    }
    });
    exitDialog.show();
    }
    return super.onKeyDown(keyCode, event); //退出程序
    }
  2. 自定义WebViewClient类,里面主要覆写shouldOverrideUrlLoading这个方法,因存在老版本和新版本,所以把判断是否用webview加载独立方法出来,一些第三方登录需要用系统的浏览器,所以这里就起到作用了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class WebViewClientDiy extends WebViewClient {
    Context context;
    public WebViewClientDiy(Context context) {
    this.context=context;
    }

    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
    final Uri uri = Uri.parse(url); //老版本
    return handleUri(uri,view);
    }

    @TargetApi(Build.VERSION_CODES.N)
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    final Uri uri = request.getUrl(); //新版本
    return handleUri(uri,view);
    }

    private boolean handleUri(final Uri uri,WebView view) {
    Log.i("测试", "Uri =" + uri);
    if (uri.toString().contains("weixin://wap/pay?")){
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse(uri.toString()));
    context.startActivity(intent); //系统默认加载方法
    return true;
    }
    view.loadUrl(uri.toString()); //调用webview的加载方法
    return true;
    }
    }
  3. AndroidMainfest.xml添加网络权限<uses-permission android:name="android.permission.INTERNET"/>,到此基本就可以用了

  4. 现在做一些优化关闭标题栏,修改AndroidMainfest.xml的them为自定义样式android:theme="@style/AppTheme.NoActionBar">

    改样式需要在styles.xml添加如下内容

    1
    2
    3
    4
    5
    <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowFullscreen">false</item>
    </style>