正则表达式 - 字符匹配不以某字段开头或者结尾

正则表达式 - 字符匹配不以某字段开头或者结尾

最近有一个需求,要求是判断某个字符串不以什么开头。然后就开始探索这个不以什么开头和不易什么结尾的正则怎么写,why?

不以某字符串开头

这里需要提一个概念叫 `否定式前项匹配` 这个东东。

向前匹配

根据匹配的字符序列后面存在`一个特定的字符序列`或者`不存在一个特定的序列`来决定是否匹配。对于向前匹配,出现在指定项后面的字符序列不会被正则表达式返回。

这里说后面存在一个特定字符序列, 也称之为肯定式向前查找
不存在一个特定的序列,也称之为否定式向前查找
这两个概念后续再深入

开始解决这个问题不以某字符串开头:

^(?!str)

这里使用了下面几个元符号:

  1. `^` 判断是否是开头
  2. `?!` 这里是否定向前查询

示例:

  1. 不以test 开头字符串
/^(?!test).*/

继续阅读正则表达式 - 字符匹配不以某字段开头或者结尾

Spring Boot实现异步任务

Spring Boot中的另外一个任务:异步任务。所谓异步任务,其实就是异步执行程序,有些时候遇到一些耗时的的任务,如果一直卡等待,肯定会影响其他程序的执行,所以就让这些程序需要以异步的方式去执行。那么下面就来介绍Spring Boot 如何实现异步任务。

一、使用注解@EnableAsync 开启异步调用方法

在application启动类中,加上@EnableAsync注解,Spring Boot 会自动扫描异步任务。

package com.weiz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
//扫描 mybatis mapper 包路径
@MapperScan(basePackages = "com.weiz.mapper")
//扫描 所有需要的包, 包含一些自用的工具类包 所在的路径
@ComponentScan(basePackages = {"com.weiz","org.n3r.idworker"})
//开启定时任务
@EnableScheduling
//开启异步调用方法
@EnableAsync
public class SpringBootStarterApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootStarterApplication.class, args);
    }

}

二、创建异步执行类,定义@Component及@Async组件

创建com.weiz.tasks包,在tasks包里增加AsyncTask 异步任务类,加上@Component 注解,然后在需要异步执行的方法前面加上@Async注解,这样Spring Boot容器扫描到相关异步方法之后,调用时就会将这些方法异步执行。

package com.weiz.tasks;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

@Component
public class AsyncTask {
    
    @Async
    public Future<Boolean> doTask11() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("任务1耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true);
    }
    
    @Async
    public Future<Boolean> doTask22() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(700);
        long end = System.currentTimeMillis();
        System.out.println("任务2耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true);
    }
    
    @Async
    public Future<Boolean> doTask33() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(600);
        long end = System.currentTimeMillis();
        System.out.println("任务3耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true); 
    }
}

说明:@Async 加上这个注解,就表示该方法是异步执行方法。

三、调用

创建一个DoTask调用类,我们看看这几个方法,是怎么执行的:

package com.weiz.tasks;

import java.util.concurrent.Future;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("tasks")
public class DoTask {

    @Autowired
    private AsyncTask asyncTask;

    @RequestMapping("test1")
    public String test1() throws Exception {

        long start = System.currentTimeMillis();

        Future<Boolean> a = asyncTask.doTask11();
        Future<Boolean> b = asyncTask.doTask22();
        Future<Boolean> c = asyncTask.doTask33();

        while (!a.isDone() || !b.isDone() || !c.isDone()) {
            if (a.isDone() && b.isDone() && c.isDone()) {
                break;
            }
        }

        long end = System.currentTimeMillis();

        String times = "任务全部完成,总耗时:" + (end - start) + "毫秒";
        System.out.println(times);

        return times;
    }
}

继续阅读Spring Boot实现异步任务

SpringBoot如何实现一个实时更新的进度条的示例代码

前言

导入excel表格批量修改状态,期间如果发生错误则所有数据不成功,为了防止重复提交,做一个类似进度条的东东。

那么下面我会结合实际业务对这个功能进行分析和记录。

正文

思路

前端使用bootstrap,后端使用SpringBoot分布式到注册中心,原先的想法是导入表格后异步调用修改数据状态的方法,然后每次计算修改的进度然后存放在session中,前台jquery写定时任务访问获取session中的进度,更新进度条进度和百分比。但是这存在session在服务间不共享,跨域问题。那么换一个方式存放,存放在redis中,前台定时任务直接操作获取redis的数据。

实施
进度条

先来看一下bootstrap的进度条

<div class="progress progress-striped active">
  <div class="progress-bar progress-bar-success" role="progressbar"
     aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
     style="width: 40%;">
    40%
  </div>
</div>

 进度条更新主要更新style="width: 40%;"的值即可,div里面的40%可以省略,无非时看着明确。

可以考虑将进度条放入弹出层。

定时任务
//点击确认导入执行此方法
function bulkImportChanges() {
  //获取批量操作状态文件
  var files = $("#importChanges").prop("files");
  var changesFile = files[0];
  var formData = new FormData();
  formData.append("importFile",changesFile);
  $.ajax({
    type : 'post',
    url : "/risk/bulk***es",
    data : formData,
    processData : false,   //文件ajax上传要加这两个的,要不然上传不了
    contentType : false,   //
    success : function(obj) {
      //导入成功
      if (obj.rspCode == "00") {
        //定时任务获取redis导入修改进度
        var progress = "";
        var timingTask = setInterval(function(){
          $.ajax({
            type: 'post',
            url: "/risk/t***k",
            dataType : 'json',
            success: function(result) {
              progress = result.value;
              if (progress != "error"){
                var date = progress.substring(0,6);
                //这里更新进度条的进度和数据
                $(".progress-bar").width(parseFloat(date)+"%");
                $(".progress-bar").text(parseFloat(date)+"%");
              }
            }
          });
          //导入修改完成或异常(停止定时任务)
          if (parseInt(progress)==100 || progress == "error") {
            //清除定时执行
            clearInterval(timingTask);
            $.ajax({
              type: 'post',
              url: "/risk/de***ess",
              dataType : 'json',
              success: function(result) {
                $("#bulkImportChangesProcessor").hide();
                if (parseInt(progress) == 100) {
                  alert("批量导入修改状态成功");
                }
                if (progress == "error") {
                  alert("批量导入修改状态失败");
                }
                //获取最新数据
                window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ;
              }
            });
          }
        }, 1000)
      }else {
        $("#bulkImportChangesProcessor").hide();
        alert(obj.rspMsg);
        window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ;
      }
    }
  });
}

解释:点击确认导入文件后成功后开启定时任务每一秒(一千毫秒)访问一次后台获取redis存放的进度,返回更新进度条,如果更新完成或者更新失败(根据后台返回的数据决定)则停止定时任务显示相应的信息并刷新页面。获取最新数据。

后台控制层
/**
 * 退单管理批量修改状态导入文件
 * @param importFile
 * @return
 */
 @ResponseBody
 @RequestMapping("/bulk***es")
 public Map<String,Object> bulk***es(MultipartFile importFile){
   log.info("退单管理批量修改状态导入文件,传入参数:"+importFile);
 Map<String,Object> map = new HashMap<>();
 List<Bulk***esEntity> fromExcel = null;
 try{
      //使用工具类导入转成list
  String[] header = {"sy***um","t***mt","ha***ult","re***nd","sy***nd","r**k"};
  fromExcel = importExcelUtil.importDataFromExcel(importFile, header, BulkImportChangesEntity.class);
  if (fromExcel.size()==0){
  map.put("rspCode","99");
  map.put("rspMsg","导入数据不能为空");
  return map;
  }
 }catch (Exception e){
  map.put("rspCode","99");
  map.put("rspMsg","导入操作表失败,请注意数据列格式");
  return map;
 }
 try {
  //这里会对list集合中的数据进行处理

  log.info("调用服务开始,参数:"+JSON.toJSONString(fromExcel));
  //String url = p4_zuul_url+"/***/ri***eat/bu***nges";
  String url = p4_zuul_url+"/***-surpass/ri***eat/bu***nges";
  String result = HttpClientUtil.doPost(url,JSON.toJSONString(fromExcel));
  log.info("调用服务结束,返回数据:"+result);
  if (result != null){
  map = JSONObject.parseObject(result, Map.class);
  log.info("批量修改状态导入:"+JSON.toJSONString(map));
  }
 }catch (Exception e){
  map.put("rspCode","99");
  map.put("rspMsg","导入操作表失败");
  log.info("bu***es exception",e);
  return map;
 }
 return map;
 }

 /**
 * 获取退单管理批量修改状态导入文件进度条进度
 * @return
 */
 @ResponseBody
 @RequestMapping("/t***sk")
 public Map<String,Object> t***sk(){
 Map<String,Object> map = new HashMap<>();
    //获取redis值
 String progress = HttpClientUtil.doGet(
  p4_zuul_url + "/" + p4_redis + "/redis***ler/get?key=progressSchedule");
 if (progress != null){
  map = JSONObject.parseObject(progress, Map.class);
  log.info("进度条进度:"+JSON.toJSONString(map));
  map.put("progressSchedule",progress);
 }else {
  HttpClientUtil.doGet(
   p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule");
 }
 return map;
 }

 /**
 * 清除redis进度条进度
 * @return
 */
 @ResponseBody
 @RequestMapping("/de***ess")
 public Map<String,Object> de***ess(){
 Map<String,Object> map = new HashMap<>();
 String progress = HttpClientUtil.doGet(
  p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule");
 if (progress != null){
  map = JSONObject.parseObject(progress, Map.class);
  log.info("返回数据:"+JSON.toJSONString(map));
 }
 return map;
 }

导入时调用第一个bulk***es方法,定时任务调用t***sk方法,导入完成或发生错误调用de***ess方法删除redis数据,避免占用资源。

服务层
@Async//开启异步
 @Transactional(rollbackFor = Exception.class)//事务回滚级别
 @Override
 public void bulkImportChanges(List<BulkImportChangesParam> list) {
 //初始化进度
 Double progressBarSchedule = 0.0;
 redisClient.set("progressSchedule", progressBarSchedule + "");//存redis
 try {
  for (int i = 1; i <= list.size(); i++) {
  RiskRetreatEntity entity = riskRetreatMapper.selectRetreatListBySysRefNum(list.get(i-1).getSysRefNum());
  if (entity == null){
   //查询结果为空直接进行下次循环不抛出
   continue;
  }
  //实体封装
        ···
        //更新
  riskRetreatMapper.updateRetreatByImport(entity);
  //计算修改进度并存放redis保存(1.0 / list.size())为一条数据进度
  progressBarSchedule = (1.0 / list.size()) * i*100;
  redisClient.set("progressSchedule", progressBarSchedule+"");
  if (i==list.size()){
   redisClient.set("progressSchedule", "100");
  }
  }
 }catch (Exception e){
  //当发生错误则清除缓存直接抛出回滚
  redisClient.set("progressSchedule","error");
  log.info("导入更新错误,回滚");
  log.info("bulkImportChanges exception:",e);
  throw e;
 }
 }

每更新一条数据存放进度,当发生错误则进行回滚。如果开启异步则需要在启动类添加注解@EnableAsync。

@EnableAsync
···//其他注解
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

继续阅读SpringBoot如何实现一个实时更新的进度条的示例代码

认识ARM64汇编

之前说过学习汇编就是学习寄存器和指令,查看代码请连接真机。

寄存器

arm64汇编中寄存器是64bit的,使用X[n]表示,低32位以w[n]表示

64位架构中有3164位的通用寄存器。

可以通过register read查看

(lldb) register read
General Purpose Registers:
        x0 = 0x0000000000000001
        x1 = 0x00000002805d8a20
        x2 = 0x00000002837e7980
        x3 = 0x00000002805d8a20
        x4 = 0x0000000000000001
        x5 = 0x0000000000000001
        x6 = 0x0000000100d54000  
        x7 = 0x0000000000000000
        x8 = 0x0000200000000000
        x9 = 0x000161a1d63f7945 (0x00000001d63f7945) (void *)0x01d63f7cb0000001
       x10 = 0x0000000000000000
       x11 = 0x000000000000006d
       x12 = 0x000000000000006d
       x13 = 0x0000000000000000
       x14 = 0x0000000000000000
       x15 = 0x00000001ca634e6d  "touchesBegan:withEvent:"
       x16 = 0x000000019c4cd47c  libobjc.A.dylib`objc_storeStrong
       x17 = 0x0000000000000000
       x18 = 0x0000000000000000
       x19 = 0x0000000100f17810
       x20 = 0x00000002837e7920
       x21 = 0x00000002805d8a20
       x22 = 0x00000001ca634e6d  "touchesBegan:withEvent:"
       x23 = 0x0000000100e11a30
       x24 = 0x00000002837e7980
       x25 = 0x0000000100e11a30
       x26 = 0x00000002805d8a20
       x27 = 0x00000001ca62d900  "countByEnumeratingWithState:objects:count:"
       x28 = 0x00000002805d8a20
        fp = 0x000000016f47d730
        lr = 0x00000001009866dc  ArmAssembly`-[ViewController touchesBegan:withEvent:] + 84 at ViewController.m:38
        sp = 0x000000016f47d720
        pc = 0x0000000100986720  ArmAssembly`foo1 + 16 at ViewController.m:46
      cpsr = 0x80000000
(lldb) 

继续阅读认识ARM64汇编

WP Statistics插件在开启缓存功能后无法统计访问量

WP Statistics插件在开启缓存功能后无法统计访问量。

跟踪发现统计访问量使用的url返回401错误,如下:

https://www.mobibrw.com/wp-json/wpstatistics/v1/hit?_=1603432478&_wpnonce=1b5c35aa31&wp_statistics_hit_rest=yes&ua=Mozilla/5.0%20(Macintosh;%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit/605.1.15%20(KHTML,%20like%20Gecko)%20Version/14.0%20Safari/605.1.15&url=https://www.mobibrw.com/2020/27156&referred=

Failed to load resource: the server responded with a status of 401 (Unauthorized)

比较奇怪的是,如果此时用户登陆,那么反倒不返回任何错误了。

继续阅读WP Statistics插件在开启缓存功能后无法统计访问量

Android App上架说明

Android App应用商店上架说明

目录

一、App上架所需证件 2

1.1、软件著作权申请 2

1.2、特殊资质证明 2

1.3公司营业执照、税务登记证 2

二、Android主流应用市场上架说明 2

2.1、华为应用市场 2

2.2、小米应用商店 3

2.3Oppo应用商店 3

2.4Vivo应用商店 3

2.5、应用宝 3

2.6Android其他应用市场 4

一、App上架所需证件

1.1、软件著作权申请

参考资料

http://www.ccopyright.com.cn/

http://www.ccopyright.com.cn/index.php?optionid=1079

1.2、特殊资质证明

新闻、直播、广播电视节目、股票、彩票、银行等,部分应用市场需要提交特殊运营资质证明材料,提交审核时候应用市场会有相应提示,根据各大应用市场要求填写即可。

1.3公司营业执照、税务登记证

当注册的开发者账号为企业账号时候,部分市场需要提供者两个证件。注册各大应用市场会有具体要求。

二、Android主流应用市场上架说明

2.1、华为应用市场

华为开发者账号登录注册地址

https://developer.huawei.com/consumer/cn/

华为市场App上架说明

https://developer.huawei.com/consumer/cn/doc

华为应用市场REST API自动化发布应用

https://developer.huawei.com/consumer/cn/doc/development/AppGallery-connect-Guides/agcapi-overview

2.2、小米应用商店

https://dev.mi.com/console/

小米开发者账号注册

https://dev.mi.com/docs/appsmarket/distribution/account_register/

小米应用商店App上架说明

https://dev.mi.com/docs/appsmarket/distribution/app_submit/

小米应用市场REST API自动化发布应用

https://dev.mi.com/console/doc/detail?pId=33

https://dev.mi.com/console/doc/detail?pId=2352

2.3Oppo应用商店

Oppo开发者账号注册登录地址 OPPO不支持个人发布应用

https://open.oppomobile.com/

2.4Vivo应用商店 VIVO 个人开发者无法注册账号

Vivo开发者账号注册登录地址

https://dev.vivo.com.cn/home

Vivo应用商店App上架说明

https://dev.vivo.com.cn/documentCenter/doc/52

VIVO应用市场REST API自动化发布应用

https://dev.vivo.com.cn/documentCenter/doc/327

VIVO提供了一个APK传包平台,貌似可以上传到其他应用商店,但是只有企业用户才能申请,具体怎么操作,暂时还不清楚。

2.5、应用宝

注册开发者账号

https://wiki.open.qq.com/index.php

创建应用

https://wiki.open.qq.com/wiki/%E5%88%9B%E5%BB%BA%E6%96%B0%E5%BA%94%E7%94%A8

应用宝暂时没办法通过 REST API自动化发布应用

注意每个应用第一次在腾讯应用商店上架,通过审核后,都需要人工提交工单到应用宝,准备跟上架一样的材料,并且提供其他应用商店的上架链接,上架截图,然后等工单审批通过后,才能正常搜索到。

这个工单类型是 “应用宝外显级别调整申请”,如下图:

也就是说应用宝必须是其他应用商店已经上线的情况下,才能提供搜索下载安装功能。

2.6、魅族应用商店

应用商店上架说明:

https://open.flyme.cn/openNew/application.html

2.7Android其他应用市场

360应用市场,百度应用市场、阿里应用分发平台、魅族应用商店。

参考链接


App上架到各大安卓应用市场流程

如何申请软件著作权?

一、关于文件准备

1、请自行浏览中国版权登记门户网 软件著作权登记唯一官方网站 中国版权保护中心网站 中国版权登记门户网 软件著作权登记唯一官方网站 中国版权保护中心网站 基本格式要求按照官网上的要求准备就好了,这个不赘述。

注意需要登陆账号之后,提供实名认证,包括身份证的正反面照片,手持身份证的照片,等待两三天实名审核通过之后才能提交申请。

关于费用问题,目前已经不收费了,这个注意。

继续阅读如何申请软件著作权?

gradle cache目录(.gradle)剖析

gradle下载后会对文件路径进行修饰,本文给出反向解析,把文件路径修改为原始路径的办法。

之所以研究这个,本来的目的是为了让Gradle支持离线编译,但是由于Gradle目录组织的缺陷,如.gradle/caches/modules-2/metadata-2.23(metadata-xx跟使用的gradle版本有关)目录下module-artifacts.bin等bin文件中存的是本机的绝对路径,导致就算将.gradle拷贝给另一台机器,还是需要联网验证。

将gradle的jcenter重定向的方法见我的另一篇文章:[android]gradle下载加速 - 知乎专栏,例如阿里云的jcenter是Index of /repositories/jcenter

我在内网中搭建了jcenter仓库。然而我不想搭建一个大而全的jcenter的仓库,搭建大而全的仓库可以使用nexus来搭建,阿里云也是那么搭的,由于公司的网络需要上网认证,时不时会断一下,所以我的做法是通过利用.gradle目录来创建jcenter仓库。

下面先介绍.gradle目录的组织。

1 .gradle顶级目录

目录| 功能
caches | gradle缓存目录
daemon | daemon日志目录
native | gradle平台相关目录
wrapper | gradle-wrapper下载目录

2 caches目录

目录 | 功能
2.14.1 | gradle程序的脚本(gradle程序版本)
3.2.1 | gradle程序的脚本(gradle程序版本)
jars-1 | ?
jars-2 | ?
modules-2 | 下载缓存目录

2.1 caches/modules-2目录

目录 | 功能
files-2.1 | gradle下载的jar/aar目录
metadata-2.16 | gradle-2.14.1的描述文件?
metadata-2.23 | gradle-3.2.1的描述文件?

2.1.1 files-2.1的目录组织(jar/aar都下载到这里)

${org}/${package}/${version}/${shanum1}/${package-version}.pom 
${org}/${package}/${version}/${shanum2}/${package-version}.jar

例如:

#jcenter上jar的路径
https://jcenter.bintray.com/com/android/tools/lint/lint-api/25.1.3/lint-api-25.1.3.jar
#对应的缓存目录为
.gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/${shasum1}/lint-api-25.1.3.jar

#看下这个目录下有什么?
~ find .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/ -type f
#除了jar包还有pom,用于描述jar。
.gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar
.gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/c4844e26d84dd1f450f90d89d7e2d2d09f52760/lint-api-25.1.3.pom

#看下jar的shasum
~ shasum .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar

14c6a94811fb8114a61b8f3ab29214f9466b5c59  .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar
#即目录名。

注意:创建jcenter时,对于jar包,可以没有pom,但是如果使用aar,则必须有pom,所以最好是每个版本都有个pom。因为pom中也描述了依赖关系。

3 daemon目录(无需离线)

用于存放gradle daemon的运行日志。按gradle程序版本存放。

目录 | 功能
2.14.1 | gradle-2.14.1运行的日志
3.2.1 | gradle-3.2.1运行的日志

4 native目录(无需离线)

用于存放平台相关(Windows/Linux/Mac)的库。

目录 | 功能
19 | gradle-2.14.1对应的lib目录,按平台存放,如osx-amd64
21 | gradle-3.2.1对应的lib目录,按平台存放,如osx-amd64
jansi | ?

5 wrapper目录

用于存放gradle-wrapper下载gradle的zip包和解压后的文件夹。 
wrapper的目录规则是 
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip 
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip.lck 
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip.ok

其中base36的规则为:

从gradle-wrapper的jar包中提取的Java代码如下:

import java.math.BigInteger;
import java.security.MessageDigest;

public class Hash {

    public static void main(String[] args) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] bytes = args[0].getBytes();
            messageDigest.update(bytes);
            String str = new BigInteger(1, messageDigest.digest()).toString(36);
            System.out.println(str);
        } catch (Exception e) {
            throw new RuntimeException("Could not hash input string.", e);
        }
    }
}

c++跟java代码见

6. 使用.gradle/caches/modules-2创建jcenter的方法

以下是mirror.sh,我把它放在.gradle目录下。运行脚本会在.gradle目录下生成jcenter目录。把它移走或直接把jcenter目录加入http服务器即可。

目前我的做法是在本地使用gradle编译一次,然后把gradle下载下来的jar/aar/pom全部提交到版本管理,然后由持续集成去拉版本库上的.gradle目录,然后生成jcenter提供给内网编译服务器。

#!/bin/bash
#use .gradle/caches/modules-2/files-2.1/ to create jcenter
SHPATH=$(cd "$(dirname "$0")"; pwd)
DIR="${SHPATH}/caches/modules-2/files-2.1/"
DIR_LENGTH=${#DIR}
if [ `uname` = "Darwin" ]; then
#mac will add "/"
	DIR_LENGTH=$((DIR_LENGTH+1))
fi
rm -rf ${SHPATH}/jcenter/*
find ${DIR} -type f | grep -Ev "DS_Store" | while read line
do
	SRC=${line}
	URL=${line:${DIR_LENGTH}}

	ORG=${URL%%/*}
	URL=${URL#*/}

	MODULE=${URL%%/*}
	URL=${URL#*/}

	REVISION=${URL%%/*}
	URL=${URL#*/}

	SHA1=${URL%%/*}
	URL=${URL#*/}

	FILE=${URL}
#echo "ORG=$ORG, MODULE=$MODULE, REVISION=$REVISION, SHA1=$SHA1, FILE=$FILE"

	DST=${SHPATH}/jcenter/${ORG//.//}/${MODULE}/${REVISION}/${FILE}
echo "$DST"
	mkdir -p `dirname ${DST}`
	if [ ! -f ${DST} ]; then
		cp -a ${SRC} ${DST}
fi
done

另外,上面提到aar有可能会因为缺失pom导致无法使用,我还写了一个脚本,也是放在.gradle目录下,用于修复aar问题。本地运行时,可以只运行这个脚本,因为它会顺路执行上面的脚本。

以下是fix_aar_cache.sh的内容:

#!/bin/bash
SHPATH=$(cd "$(dirname "$0")"; pwd)

SRC_DIR=${SHPATH}/jcenter
CACHE_DIR="${SHPATH}/caches/modules-2/files-2.1/"
${SHPATH}/mirror.sh > /dev/null

DIR_LENGTH=${#SRC_DIR}
if [ `uname` = "Darwin" ]; then
#mac will add "/"
	DIR_LENGTH=$((DIR_LENGTH+1))
fi
find ${SRC_DIR} -name "*.aar" | while read line
do
	SRC=${line}
	LOCAL_POM=${SRC/.aar/.pom}
if [ -f ${LOCAL_POM} ]; then
continue
fi
echo "$LOCAL_POM not found"

	URL=${line:${DIR_LENGTH}}
	URL=${URL/.aar/.pom}
	REMOTE_POM=http://maven.aliyun.com/nexus/content/repositories/jcenter/${URL}
	REMOTE_POM_SHA1=${REMOTE_POM}.sha1

	SHA1=`wget -q -O- ${REMOTE_POM_SHA1}`
	SHA1=${SHA1:2} #remove additional \r\n
echo $SHA1
#reverse to get it
	FILE=${URL##*/}
	URL=${URL%/*}

	REVISION=${URL##*/}
	URL=${URL%/*}

	MODULE=${URL##*/}
	URL=${URL%/*}

	ORG=${URL}
echo "ORG=$ORG, MODULE=$MODULE, REVISION=$REVISION, FILE=$FILE"
#org /->.
	DST=${CACHE_DIR}/${ORG//\//\.}/${MODULE}/${REVISION}/${SHA1}/${FILE}
echo "$DST"
	mkdir -p `dirname ${DST}`
	if [ ! -f ${DST} ]; then
		wget -O ${DST} ${REMOTE_POM}
fi
done

参考链接


集成华为HMS debug/release 修改包名,取不同包名下的agconnect-services.json 文件

debug/release 修改包名,取不同包名下的agconnect-services.json 文件 V2 解决每次更换包名,都是要手动删除agconnect-services.json文件操作

问题描述

集成华为HMS Core/Push在打多渠道包的时候,我需要区分debug版本,release版本,其中涉及到包名的不同,我使用release编译的时候,发现如下错误信息。这个原因是因为你的agconnect-services文件里面含有一个 package_name 参数,这个参数是需要指定包名的,如果 package_name 填写的报名,和目前你所使用的包名没有对应上就会出现这样的错误

修改过程中遇到的错误信息
* What went wrong:
Execution failed for task ':app:processDebugAGCPlugin'.
> ERROR: Failed to verify AGConnect-Config '/client/package_name', expected: 'com.gxx.fast', but was: 'com.gxx.fast.debug'
不是很完美的解决问题

既然是需要区分包名的,我不如直接copy 2份出来,放到src下面,并新建一个类 pushservices 里面存放 debug/release 的类并存放 agconnect-services.json文件,这样我们想使用哪个版本的,就使用哪个版本的

继续阅读集成华为HMS debug/release 修改包名,取不同包名下的agconnect-services.json 文件

Android Studio使用gradle实现资源自动拷贝

把下面的代码拷贝到appgradle.build文件内

android.applicationVariants.all{ variant ->
    delete "${buildDir}/intermediates/merged_assets/${variant.dirName}"  // buildDir是app下的build目录
    variant.mergeAssets.doLast{
        def sourceDir = "${buildDir}/../"   // 资源存放目录,这里是app下
        print "${buildDir} \n"  // 打印路径
        copy { // 将from目录下的资源拷贝到into 下目录去
            from "${sourceDir}/fromDir"
            into "${outputDir}/res"
        }
    }
}

继续阅读Android Studio使用gradle实现资源自动拷贝