配置 Groovy 写Android JUnit

为什么使用Groovy写JUnit

Groovy 是动态语言,动态语言干测试其实非常好用。比如,我要创建一个多种类型的数组,只要这样写def array = [12,12,'1212'] 这样我就很简单的创建了多种类型的数组了。如果是用java代码的话,你可以尝试一下需要多少行。groovy的优点不只是这些,详情请看Groovy的特性

Android中的配置方法

在Android中需要使用Groovy需要使用这个开源项目,里面有详细的配置说明。但是我按那个配置,老是编译不过,改了一下就可以了,就写下自己的办法吧。

配置buildScript


buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-beta7'
        //groovy插件依赖
        classpath 'org.codehaus.groovy:groovy-android-gradle-plugin:1.2.0'

    }
}

在需要使用的项目中,加入插件

apply plugin: 'groovyx.android'


添加groovy依赖

我这里只是使用Groovy 作为测试使用,所以我是用的是testImplementation,如果要在开发中使用Groovy,请改为implementation 就可以了

dependencies {
//使用官方推荐的反而不行
//    testImplementation 'org.codehaus.groovy:groovy:2.4.11:grooid'
//我该为了java使用的Groovy就可以了
    testImplementation 'org.codehaus.groovy:groovy-all:2.4.12'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
}

开始开发

test/groovy文件夹中建立你的Groovy 脚本,开始开发吧

示例项目

Android Studio 3.0 中支持的Java8 特性

集成方法

在Android Studio 3.0 中其实已经支持了Java8 的部分特性,其中最多人用的就是Lambda 表达式也在列。

在AS3.0中使用lambda 其实也很简单

  1. 升级Android Plugin到3.0.0-alpha1 或者以上
  2. 在build.gradle 中加入一下代码

    android {
      ...
    // Configure only for each module that uses Java 8
    // language features (either in its source code or
    // through dependencies).
    compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
    }
    }

就这样两部可以了。

如果你以前使用了jack或者me.tatarka.retrolambda,只需要移除相关的代码就可以了.

AS3.0 中java8 支持的特性和Api等级

Java 8 Language Feature 最低兼容的API等级
Lambda expressions 任意API等级。但是,只有当lambda所捕获的所有值都是可序列化的时候才支持lambda序列化。
Method References 任意API等级.
Type Annotations 任意API等级。但是,类型注释信息在编译时可用,但在运行时不可用。此外,TYPE 在API级别24及受以下,但不支持ElementType.TYPE_USEElementType.TYPE_PARAMETER。 
Default and static interface methods 任意API等级。
Repeating annotations 任意API等级。
Java 8 Language API 最低兼容的API等级
java.lang.annotation.Repeatable API level 24 or higher.
AnnotatedElement.getAnnotationsByType(Class) API level 24 or higher.
java.util.stream API level 24 or higher.
java.lang.FunctionalInterface API level 24 or higher.
java.lang.reflect.Method.isDefault() API level 24 or higher.
java.util.function API level 24 or higher.

清理 mac 上 docker 脚本

#!/bin/bash


# remove exited containers:
docker ps --filter status=dead --filter status=exited -aq | xargs  docker rm -v
    
# remove unused images:
docker images --no-trunc  | awk '{ print $3 }' | xargs  docker rmi

# remove unused volumes:
find '/var/lib/docker/volumes/' -mindepth 1 -maxdepth 1 -type d | grep -vFf <(
  docker ps -aq | xargs docker inspect | jq -r '.[] | .Mounts | .[] | .Name | select(.)'
) | xargs  rm -fr

MetaWeblogApi for Hexo 开发纪录

为什么要开发MetaWeblogApi for hexo

  1. 讨厌每次都要敲命令行
  2. 想在任何地方编写文章并可以提交
  3. 喜欢编辑器沉浸式的编写体验

实现原理

participant 编辑器 as B
participant MetaWeblogApi as M
participant 博客Git地址 as A
participant 博客 as E

B->M: 发送Mardown格式的文本
note left of M: git pull 
M->B: 返回BlogId确认提交成功 
note right of M: 生成Hexo 的Markdown
M->A: 提交到
note right of M: 调用Hexo 生成
M->E: deploy 到博客地址

具体实现

外部访问Api实现部分

使用SpringBoot

  1. SpringMVC是目前Java最流行的框架
  2. Java是我本来的开发语言,趁手的才是最好的
  3. SpringBoot相对于原始的SpringMVC 更简单的配置

Metaweblog 的格式

Metaweblog的实现主要是参考这个开源项目,这个源码写得很直白,非常好懂。简单一点解释Metaweblog Api(后面简称M),和M对接的url其实只有一个,然后把访问不同的方法名封装在一个XML里面,通过解析这个xml,我们就能够知道这个请求到底需要访问什么方法。这个和我们一般的Api都不太一样,我在这个地方迷茫了好久。知道这个之后剩下的就是xml解析的问题了

使用Groovy XMLParser 解析xml

需要解析的xml

<?xml version="1.0"?>
<methodCall>
    <methodName>blogger.getUsersBlogs</methodName>
    <params>
        <param>
            <value>
                <string></string>
            </value>
        </param>
        <param>
            <value>
                <string>12</string>
            </value>
        </param>
        <param>
            <value>
                <string>12</string>
            </value>
        </param>
    </params>
</methodCall>

解析使用的groovy代码

methodCall = new XmlParser().parseText(text)
methodName = methodCall.methodName.text()
params = methodCall.params.param
mUserAccount = params[1].text()
mUserPassword = params[2].text()

使用groovy 解析的话就好像使用js一样,直接使用xml字段名就能获取到值,非常方便

Git的提交部分

JGit 是个Java实现的Git操作库,可以很方便的操作各种Git操作,我主要使用到以下几个方法

clone
mGit = Git.cloneRepository()
            .setURI(url)
            .setDirectory(new File(mGitConfig.repositoryLocation))
            .setTransportConfigCallback(mTransportConfigCallback)
            .setBranch('master')
            .setCloneSubmodules(true)
            .setTimeout(30000)
            .call()
update
def git = git()
git.pull().setTransportConfigCallback(mTransportConfigCallback).call()
git.submoduleUpdate().call()
 //如果不是干净的就合并
if (!git.status().call().clean) {
git.add().addFilepattern('.').call()          
git.commit().setMessage(buildCommitMessage(git.status().call())).call()
push
def pushResult = git().push()
                .setPushAll()
                .setTransportConfigCallback(mTransportConfigCallback)
                .call()

因为使用的giturl 提交,需要sshkey ,需要在~/.ssh/生成密钥

调用Hexo 部分

我重新拓展了groovy String 中的execute,使得每次执行命令都在博客的目录下

 String.metaClass.execute {
            if (new File(mConfigBean.repositoryLocation).exists()){
                delegate.execute(null,new File(mConfigBean.repositoryLocation))
            }else{
                delegate.execute(null,new File('/'))
            }

        }

但是我不建议这样做,这样写是方便了,但是会造成在别的地方执行execute会失败。建议是重新拓展一个方法。

因为使用groovy,我就可以这样执行hexo的命令了

初始化hexo

'npm install '.execute().text

生成和推送

'hexo generate -d'.execute()

docker封装

Docker 这个过程最蛋疼!!

  1. nodejs 要安装6版本
  2. npm不需要安装了,安装node的时候附带了

贴出构建Docker的Dockerfile

FROM java:8u111-jdk

MAINTAINER <Hangox,liang.hanguang93@gmail.com>

RUN apt-get update -y --no-install-recommends
RUN apt-get install -y --no-install-recommends git-core curl
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y  --no-install-recommends nodejs
RUN npm install hexo-cli -g

COPY . /app
WORKDIR /app
RUN ./gradlew assemble
RUN mv build/libs/MetaweblogApi-1.0.jar app.jar

ENV GIT_NAME HexoMetaApi
ENV GIT_EMAIL your@email.com
ENV ACCOUNT account
ENV PASSWORD password
ENV REPOSITORY ''
#
#VOLUME ["root/.ssh", "/hexo"]
#EXPOSE 9000

CMD java -jar app.jar \
        --blog.account=$ACCOUNT \
        --blog.password=$PASSWORD \
        --git.repository=$REPOSITORY \
        --git.name=$GIT_NAME \
        --git.email=$GIT_EMAIL

总结

SpringBoot 这个框架简化了很多SpringMVC的配置,搭配Groovy开发非常舒服。
Metaweblog api 的实现还在于很粗糙的程度,目前也只是实现了,newPosteditPost两个简单的操作,争取实现更多的操作。Docker构建的话,还是希望自己以后不要在智商掉线了。Docker的相关信息也是时候要总结一下了。

算法,两数之和

问题来源:two sum

问题描述

给定一个整数数组,返回两个数字的索引,使它们相加到一个特定的目标。 您可以假设每个输入都只有一个解决方案, 而您可能不会使用相同的元素两次。

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

正向思维解法

直接遍历所有的组合,求出问题的解

public int[] twoSum(int[] nums, int target) {
     for (int i = 0; i < nums.length; i++) {
           for (int j = i + 1; j < nums.length; j++) {
                    if (target == nums[j] + nums[i]) {
                        return new int[] { i, j };
                    }
         }
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
这是最简单的解法。两个嵌套循环,时间复杂度为O(n2)。空间复杂度为O(1)

逆向思维解法

用目标减去加数1,得到加数2,再在数组中寻找。

实现1

public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (target - nums[i] == nums[i]) {
                    return new int[] { i, j };
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }

分析
没错,就是和第一解法时间空间复杂度都是一样的😂。没事,我们可以优化一下的。

实现2

在实现一中,我们的瓶颈主要是在如何根据值去找到索引的问题上,所以这里我们使用Map去优化根据值去寻找位置的瓶颈

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        map.put(nums[i], i);
    }
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement) && map.get(complement) != i) {
            return new int[] { i, map.get(complement) };
        }
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
我们使用一个循环把数据与位置建立索引,然后获取的时候直接从map中直接获取,这样就可以避免了两个嵌套的循环了,是的时间复杂度为O(n) ,空间复杂度为O(1).

note: 在算法中,我们不考虑map这些数据结构具体实现使用的时间

实现3

实现2中使用了两个循环,能不能把第一个循环也去掉呢?答案是可以的。一边循环,一边插入map,然后获取这个差值是否已经加入到map中。

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] { map.get(complement), i };
        }
        map.put(nums[i], i);
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
这个复杂度和实现2是一样的,时间为O(n),空间为O(n),但细看还是有差别的,这个算法的实际上比实现2的快了1倍

Android Studio 3.0 Beta1 更新日志

翻译自谷歌博客

2017年8月9日,星期三

Android Studio 3.0 Beta 1现在可以在金丝雀和开发渠道中使用。

已知问题

如果您现有的Android Studio项目使用的是Android插件3.0.0的Alpha版本(如3.0.0-alpha9),则迁移到Android 3.0.0-beta1后sync your project将会遇到: Gradle project refresh failed

解决此问题的办法就是从菜单栏中选择 Build> Clean Project - 您只需对每个项目执行一次此操作。然后,您可以通过从工具栏中单击“ Sync Project ” ,将其与Gradle对应。

此版本包含各种错误修复,包括以下内容:

修复了使用Kotlin插件崩溃的类加载问题。
修复了菜单栏不再显示的问题。(问题#63743086

如何合法的修改Git子模块远端

首先直接修改根目录下的.gitmoudule 文件

[submodule "libraries/GaiaLibrary4BLE"]
    path = libraries/GaiaLibrary4BLE
    url = http://yousumodule_remote

然后同步设置

使用一下命令把设置同步到.git/config 文件中

git submodule synca

最后更新远端信息

使用以下命令更新远端信息

git submodule update --init --recursive --remote

其实就是从远端递归式的初始化的意思

JobScheduler 使用指南

什么是JobScheduler?

JobScheduler 是Android 5.0之后提供的后台执行操作的API。简单点描述就是可以根据一系列条件,目前包括,时间,是否在充电,ContentProvider 是否改变等条件触发工作。这是和AlarmManager最大的区别,AlarmManager 只能在一个时间启动,不能根据机子时间启动。

会在什么情况下使用呢?

  • 用户拍了一张照片的时候需要触发相应的操作,这个时候就可以监听照片的ContentProvider ,发生改变就能触发我的操作。当然,这个同样适用于所有ContentProvider
  • 固定时间唤醒进行响应的操作
  • 需要设备空闲的时候进行图片的OCR识别保存信息
  • 需要设备空闲而且在充电的时候并且连接着WIFI的时候才能图片上传到服务器

继续阅读“JobScheduler 使用指南”

Android SpringAnimator初探

介绍

Android 官方发布了SpringAnimator ,这个动画效果其实和Facebook的rebound是一样的,但是API的设计不一样,总体来说功能更多,设计也友好。推荐使用

实现的效果

实现一个很简单的拖拽放开回弹的效果

2017-07-13-14999572451387

使用

添加依赖

新的动画库是在26.0.0 这个版本的API包上的,所以需要target 和 compileVersion 都为26 的才能使用,下面是我的项目依赖配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.hangox.springanimatortest"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    dataBinding{
        enabled true
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.0.0-beta2'
    compile "com.android.support:support-dynamic-animation:26.0.0-beta2"
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.hangox:xLog:1.0'
    testCompile 'junit:junit:4.12'
}

主要代码实现

 mainBinding.icon.setOnTouchListener(new View.OnTouchListener() {

            PointF mPoint = new PointF();
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                XLog.v(motionEvent);
                switch (motionEvent.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        mPoint.set(motionEvent.getRawX(),motionEvent.getRawY());
                        return true;

                    case MotionEvent.ACTION_MOVE:
                        view.setTranslationX(motionEvent.getRawX() - mPoint.x+ view.getTranslationX());
                        view.setTranslationY(motionEvent.getRawY() - mPoint.y + view.getTranslationY());
                        mPoint.set(motionEvent.getRawX(),motionEvent.getRawY());
                        return true;

                    case MotionEvent.ACTION_UP:
                        SpringAnimation springAnimationY  = new SpringAnimation(view, DynamicAnimation.TRANSLATION_Y,0);
                        SpringAnimation springAnimationX = new SpringAnimation(view,DynamicAnimation.TRANSLATION_X,0);
                        springAnimationX.start();
                        springAnimationY.start();
                        return true;

                }
                return false;
            }
        });

总结

SpringAnimator的API设计要rebound的要简洁的多,很容易写成反弹的动画。但是也和rebound 有一样的问题,代码中我是先后start两个动画。并不能像普通的Animator 那样提供一个AnimatorSet 来管理开始和结束。

材料设计之表面

可供性(affordances)

有些东西我们只需要看到就能够感受到这些东西是干嘛的。举个例子,纸张,我们无需触摸就能够知道纸张可以轻易的拿起来,可以折叠,甚至可以撕掉。苹果,我们可以扔出去,也可以咬一口。这种感觉我们称之为 可供性。这些示例帮助我们更快的了解和各种对象交互的办法。但是,在UI中模拟现实生活中的对象是非常复杂的(参考苹果拟物设计)。但是,我们仍然可以使用现实中一部分的特性来帮助我们实现可供性。

表面

在Material Design 中我们想象表面都是由一张一张的纸张组成,我们称之为平面(surface)。我们通过纸张的特性来提供可供性,表面具有一部分的纸张特性,足够我们表达组件间的关系。

/

继续阅读“材料设计之表面”