Coding and Learning

oubindo的技术与生活博客


  • Home

  • Archives

gradle学习

Posted on 2018-08-09

gradle学习之路

1.gradle init生成gradle需要的文件。

2.gradle task –scan 可以生成报表,方便我们查看和分析什么耗时

3.build.gradle 里面写的是项目多模块可以同时使用的配置。当我们在这里加上

1
2
3
4
5
allprojects {
repositories {
jcenter()
}
}

的时候,配置就对全局有效。也可以改成subprojects。而且这两个配置可以在根项目里使用很多次。

4.生成library: gradle init –type groovy-library/java-library/java-application

5.多个模块的通用设置放在根目录的build.gradle下面。可以用configure语句

1
2
3
4
5
6
7
8
9
10
configure(subprojects.findAll { it.name == 'greeter' || it.name == 'greeting-library' }) { 

apply plugin: 'groovy'

dependencies {
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4', {
exclude module: 'groovy-all'
}
}
}

6.只在根目录,进行其他subproject的编译
./gradlew :{subproject}/{task}

7.在Java library中进行build,可以在build/libs目录下找到jar。或者也可以使用./gradlew jar命令打jar包

美国大学Online课程及个人评测

Posted on 2018-05-10

美国大学Online课程及个人评测

已刷

1.Coursera MachineLearning:非常不错的机器学习入门课程,讲解细致,测验和program exercise都是很有效的补充学习资料。

2.Stanford MachineLearning:也是Ng的课程,但是这课程相对上门更难一点,有大量的数学证明,对于想要知根知底的学生来说是一个不错的补充学习资料。我只看了前10个讲座,后面的没看了。

待刷

1.Coursera Algorithm II:

2.Coursera DeepLearning:

3.MIT math for computer sciense 这门课大多是离散数学的东西,可以等有空的时候再看,优先级低一些。

外国公开课程地址

1.MIT open courses

2.华盛顿大学 open courses
部分课程有视频在youtube上,很多的课程,几乎涵盖了所有课题。可以作为我以后刷课的主力。

3.直接搜课程英文名,然后看别人的推荐

Kotlin深入学习系列(二):OOP篇

Posted on 2018-04-10 | In Kotlin

类与对象

一.类

构造函数

在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。其中由于主构造函数内不能写代码,主要的初始化代码需要在 init{} 中书写。在实例初始化期间,初始化块按照它们出现在类体中的顺序执行,与属性初始化器交织在一起。也就是说这里的初始化块init其实就是被当成了属性初始化。主构造函数中的参数默认为类参数使用。

次构造函数可以通过constructor(param)来定义,所有的构造函数都必须委托给主构造函数,可以直接委托或者通过别的次构造函数简洁委托。 委托方法是在constructor后面加个this()。

但是这里又有一个坑,次构造函数不能使用val或者var!例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Pair<K, V>(val first: K, val second: V) {

init{
println("init")
println(first)
println(second)
}

constructor(val first: K): this(first, 0){ // 这里的val报错。
println("constructor")
println(first)
println(second)

}
}

继承

Kotlin所有类都有一个超类Any。声明类时如果使用了open来修饰,那就可以被继承,否则默认不允许继承。

继承的时候如果子类有主构造函数,那么必须用父类的主构造函数参数初始化。否则必须有次构造函数调用super来调用父类的构造函数。

1
2
3
4
5
6
7
8
9
10
11
class HeritLearning: ClassLearning{
constructor(name: String) : super(name){

}

}

// 或者
class HeritLearning(name: String) : ClassLearning(name){

}

如果一个方法同时和多个超类的相同,那就在调用父类方法的时候使用super<父类>.func()来区分父类。

覆盖属性或方法

开放覆盖的属性和方法必须用open修饰,覆盖的必须用override修饰。覆盖属性的时候var可以覆盖val,反之则不行。

接口

接口默认为open,不需要主构造函数。

二.字段

字段的完整语法:

1
2
3
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

也就是是说,当我们没有声明getter和setter时默认就已经有这些东西了。当我们声明以后可以得到自定义的实现。如果想屏蔽getter或者setter,可以使用private set。

1
2
var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默认实现

在访问器里面,我们可以使用field来代替此字段

1
2
3
4
var counter = 0 // 注意:这个初始器直接为幕后字段赋值
set(value) {
if (value >= 0) field = value
}

lateinit

属性初始化要点如下:

  • 当我们在类外面声明属性为可空或非空时,都必须进行初始化
  • 声明为lateinit的对象必须是可空对象,非空对象不行。
  • 使用.isInitialized来判断属性是否被初始化了。

三.扩展

所有扩展的定义域都在包里,使用之外的扩展就需要导入。

1.扩展函数
kotlin可以通过扩展来扩展类的新功能而无需继承或装饰。原理就是:

1
2
3
4
5
6
7
fun ClassLearning.f(str: String){
this.arr[3] = 0
}

open class ClassLearning(val name: String, val age: Int){
var arr = Array<Int>(5, {1;2;4;2;1})
}

反编译之后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public final class ClassLearningKt {
public static final void f(@NotNull ClassLearning $receiver, @NotNull String str) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
Intrinsics.checkParameterIsNotNull(str, "str");
$receiver.getArr()[3] = 0;
}

public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
ClassLearning l = new ClassLearning("oubin");
f(l, "f方法");
}
}

可以看到被扩展的对象被打包成了receiver,然后就是这个对象直接操作他自身的元素了。也就是说,扩展对象并没有改变他们所扩展的类,而是通过生成另外一个方法来操作。所以我们可以发现,这个方法绝对是编译器生成的,所以对于运行期重载的情况怕是用不了了。

1
2
3
4
5
6
7
8
9
10
11
12
13
open class C

class D: C()

fun C.foo() = "c"

fun D.foo() = "d"

fun printFoo(c: C) {
println(c.foo())
}

printFoo(D()) // 会print c

2.扩展属性
扩展属性不能有初始化器,他们的行为只能由显式的getter和setter定义。扩展属性也是在新类中生成了一个get方法,然后将扩展属性的get()返回的值进行返回。

3.伴生对象扩展:同样伴生对象也可以定义扩展函数和属性。

四.数据类

1
2
3
4
5
data class Person(val name: String){
var age: Int = 0
}

fun Person.copy(name:String = this.name) = Person(name)

数据类可以将部分属性放入括号内,从而排除这些属性。进行copy的时候可以如上定义,然后Person对象可以直接调用copy函数

数据类的解构声明:

1
2
3
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")

他是使用了利用了componentN()函数。

五.嵌套类

内部类通过标记为inner可以访问外部类的成员,因为会带有一个对外部类的对象的引用。

匿名内部类可以使用对象表达式来实现。

六.对象

1.对象表达式: 样式为 object: X(){…}
当我们不需要任何超类型时,就写做 val a = object{ val x = 3}, 这样我们就可以访问a.x
它的实现原理是通过new Object()去生成了这样一个对象并定义相关属性。

对象表达式可以访问外部的属性,并且比java匿名内部类更牛的是它不限制是final对象。

2.伴生对象
当我们在类中定义伴生对象时发生了什么:

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
class HeritLearning(name: String) : ClassLearning(name){
companion object {
val x: String = "ds"

fun get(){
println()
}
}
}

// 反编译后为
public static final class Companion {
@NotNull
public final String getX() {
return HeritLearning.x;
}

public final void get() {
System.out.println();
}

private Companion() {
}

// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}

可以看到我们的companion被编译成了一个静态内部类,属性被定义为外部类的静态属性,而方法确是这个静态内部类的方法。由此,我们知道伴生方法是访问不了非伴生属性的。

七.委托

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Base {
fun print()
}

class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 输出 10
}

Kotlin深入学习系列(一):基础篇

Posted on 2018-04-08 | In Kotlin

基础篇

基本类型

1.数字
数字与java中基本相同,主要是下面几点不同:

  • kotlin对于数字没有隐式扩展转换(如从int转为long)
  • 在kotlin中字符不是数字

2.表示方式
kotlin会把数字存储为jvm的原生类型,但是只有在需要可空引用或泛型时数字会被装箱。
但是这里存在着一个坑:当取值在[-128, 127]之间时不会被装箱,而在这之外的int数字才会被装箱,如下

1
2
3
4
5
6
7
8
9
val b:Int = 5    // 被编译为 short b = 5;
val b1:Int? = b // 被编译为 Integer i = Integer.valueOf(5)
val b11:Int? = b
println(b1 === b11) // 注意这里打印的是 'true'

val a: Int = 10000
val a1: Int? = a
val a11: Int? = a
println (a1 === a11 ) //注意这里打印的是 'false'

这是因为JVM把[-128,127]的所有int数字的装箱全部缓存了,任何指向这个范围的对象,都不可能被另外”创建”,应该是为了缓存最常用的数字以节省性能吧。

3.在kotlin中万物皆对象,=== 是比较对象之间的地址是否相等, == 是比较对象的大小是否相等。

4.数据类型转换:需要使用toByte(),toInt之类的转换。

5.浮点数比较有些很棒的特性:

  • 区间实例以及区间检测:a..b、 x in a..b、 x !in a..b。 这里可以使用两个点来表示,甚至比python更方便。
  • NaN与其自身相等,而且比任何其他元素都大

6.字符Char不能直接当做数字,用单引号,与java一样。并且当需要可空引用时也会被装箱。

7.数组:数组直接用Array表示

1
2
3
4
5
6
7
8
9
// 一.已知所有元素
val a = arrayOf(1,2,3) // array[1,2,3]
val b = Array(5, { i -> (i * i).toString() }) // 数组大小和一个Array构造函数
// 二.空数组
val c = emptyArray()/ arrayOfNulls<String>(0) // 创建一个长度为0的null数组
// 三.基本类型
val d = intArrayOf(1,2,3) // 对于基本类型数组有特殊优化,但是得到的IntArray并不是Array的子类。
// 四.创建一个已知数量的数组
val a = arrayOfNulls<String>(5)

kotlin中数组是invariant的,不能把Array赋值给Array,但是可以是Array

8.字符串模板
字符串可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。模板表达式以美元符($)开头,由一个简单的名字构成。如果要直接输出$符,只要不加变量即可:

1
2
3
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”
print("$8.8")

控制流

1.if语句表达三元运算符:val max = if (a > b) a else b。与python中的表达十分相似:max = a if a > b else b

2.When表达式
when是一个加强的switch语句,可以作为语句也可以作为表达式。可以替代if else if链

1
2
3
4
5
fun hasPrefix(x: Any) = when(x) {
1,2 -> true
is String -> x.startsWith("prefix")
else -> false
}

3.for可以循环任何提供了迭代器的对象。

4.Kotlin中任何表达式都可以用标签(label)来标记。标签的格式为标识符后跟@符号

1
2
3
4
5
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}

例子中的break可以直接跳出两重循环。同样的return,continue都可以使用这种操作。

生成模型与判别模型

Posted on 2018-03-11 | In machine learning

生成模型与判别模型

1.生成模型表示了给定输入X产生输出Y的生成关系。它由数据学习联合概率分布P(X,Y),然后求出条件概率分布P(Y|X)作为预测模型,即生成模型:

P(Y|X)=P(X,Y)/P(X)

生成模型关心的是生成关系。典型的生成模型有朴素贝叶斯法和隐式马尔科夫模型。
生成模型:无穷样本==》概率密度模型 = 产生模型==》预测

2.判别模型由数据直接学习决策函数f(X)和条件概率分布P(Y|X)作为预测的模型。它关心的是给定一个输入X,应该预测什么样的输出Y。
典型的判别模型有:knn,SVM,感知机等。

判别模型:有限样本==》判别函数 = 预测模型==》预测

r

TextPathView源码学习

Posted on 2018-03-09 | In 源码周读

TextPathView源码学习

PathMeasure

PathMeasure是Path的坐标计算器,可以获取Path上每一个构成点的坐标。
初始化:

1
2
3
4
PathMeasure pathMeasure = new PathMeasure();
pathMeasure.setPath(path, true);
// 或者使用有参构造法
PathMeasure(Path path, boolean forceClosed)

forceClosed,简单的说,就是Path最终是否需要闭合,如果为True的话,则不管关联的Path是否是闭合的,都会被闭合。对绑定的Path没有影响,但是对PathMeasure的测量结果有影响,会包含最后一段闭合的路劲。

重要api:

  • getSegment(start, stop, dst, move):将start到stop之间的path添加到dst中,可以重复添加,不会被覆盖
  • nextContour():如果有多个contour的时候,跳入到下一个contour

核心代码

1.通过Paint.getTextPath获取文字path:

1
2
3
4
5
6
7
8
9
10
11
12
protected void initTextPath() {
mDst.reset();
mFontPath.reset();
mTextPaint.getTextPath(mText, 0, mText.length(), 0,mTextPaint.getTextSize(), mFontPath);
// pathMeasure绑定path,就可以进行测量
mPathMeasure.setPath(mFontPath, false);
mLengthSum = mPathMeasure.getLength();
//获取所有路径的总长度
while (mPathMeasure.nextContour()) {
mLengthSum += mPathMeasure.getLength();
}
}

2.通过动画进度来控制绘制

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
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (float) valueAnimator.getAnimatedValue();
drawPath(mAnimatorValue);
}
});

void startAnimation(){
initTextPath() // 获取到Text的path,放在mDst中
mAnimation.start()
}

// 绘制出已计算出的
void View.onDraw(){
canvas.drawPath(mDst, mDrawPaint); // 这里的mDst由drawPath随时更新
}

// 根据动画进行比例计算出需要绘制的path
public void drawPath(float progress) {
mAnimatorValue = progress;
mStop = mLengthSum * progress;

//重置路径,目的是从开头的字开始重新测量。
mPathMeasure.setPath(mFontPath, false);
mDst.reset();
mPaintPath.reset();

//根据进度获取路径
while (mStop > mPathMeasure.getLength()) {
mStop = mStop - mPathMeasure.getLength();
mPathMeasure.getSegment(0, mPathMeasure.getLength(), mDst, true); // 这里计算出来的Path会和后面的0-stop的path相加
if (!mPathMeasure.nextContour()) {
break;
}
}
mPathMeasure.getSegment(0, mStop, mDst, true);

//绘画画笔效果
if (showPainterActually) {
mPathMeasure.getPosTan(mStop, mCurPos, null);
drawPaintPath(mCurPos[0], mCurPos[1], mPaintPath);
}

//绘画路径
postInvalidate();
}

从上面可以看到,只要我们修改一下,把while循环的起点改为每一个contour,分别绘制对应的比例就行。

1
2
3
4
5
6
7
8
9
10
11
while (mPathMeasure.nextContour()) {
mLength = mPathMeasure.getLength();
mStop = mLength * mAnimatorValue;
mPathMeasure.getSegment(0, mStop, mDst, true);

//绘画画笔效果
if (showPainterActually) {
mPathMeasure.getPosTan(mStop, mCurPos, null);
drawPaintPath(mCurPos[0],mCurPos[1],mPaintPath);
}
}

启示

onDraw里面是不能进行耗时操作的,所以在动画进行的回调函数中去进行progress的判断,然后更新onDraw里面要用到的数值。

。

开发模式进阶

Posted on 2018-03-07 | In Android进阶

常见的开发模式

MVP

MVP也就是model-view-presenter模式。由presenter来连接model和view之间的联系,并且进行view里面的逻辑操作。好处是使得view更加简洁,逻辑清楚易于维护。

缺点是

  • 大量引入类,开发者可能不能辨别三层的具体分工导致无法解耦。
  • MVP以UI和事件为驱动,数据被动的通过UI来展示,但是数据的时变性,我们希望数据能转被动为主动,由数据来推动UI。
  • 一旦V层某个UI元素更改,那么对应的接口和事件监听都得改。

下面分析google的mvp项目

todo-mvp

google Mvp架构

技术亮点:
1.xml属性:parentActivityName:指定activity的父activity,系统会读取该属性,以确定当用户按下操作栏中的“向上”按钮时应该启动哪一个 Activity。 系统还可以利用这些信息通过 TaskStackBuilder 合成 Activity 的返回栈。

2.xml属性:noHistory:当用户离开 Activity 并且其在屏幕上不再可见时,是否应从 Activity 堆栈中将其移除并完成。当为true时onActivityResult无法取得数据。

MVVM

参考如何构建Android MVVM应用框架
具体角色与MVP相似,Presenter由ViewModel替换,Model的角色扩展了,添加了进行数据的获取,存储,数据状态变化的任务。MVVM的目标和思想与MVP类似,利用数据绑定(Data Binding)、依赖属性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一个更加灵活高效的架构。

数据驱动
在常规的开发模式中,数据变化需要更新UI的时候,需要先获取UI控件的引用,然后再更新UI。获取用户的输入和操作也需要通过UI控件的引用。在MVVM中,这些都是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变也能自动反馈到数据层,数据成为主导因素。这样MVVM层在业务逻辑处理中只要关心数据,不需要直接和UI打交道

低耦合度
MVVM模式中,数据是独立于UI的。数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。即便是控件改变了(比如:TextView换成EditText),ViewModel也几乎不需要更改任何代码。它非常完美的解耦了View层和ViewModel,解决了上面我们所说的MVP的痛点。

更新UI
在MVVM中,数据发生变化后,我们在工作线程直接修改(在数据是线程安全的情况下)ViewModel的数据即可,不用再考虑要切到主线程更新UI了,这些事情相关框架都帮我们做了。

可复用性
一个ViewModel可以复用到多个View中。同样的一份数据,可以提供给不同的UI去做展示。对于版本迭代中频繁的UI改动,更新或新增一套View即可。如果想在UI上做A/B Testing,那MVVM是你不二选择。

架构:

ViewModel与View协作
ViewModel和View是通过绑定的方式连接在一起的,绑定分成两种:一种是数据绑定,一种是命令绑定。数据的绑定DataBinding已经提供好了,简单地定义一些ObservableField就能把数据和控件绑定在一起了。我们看ViewModel的结构:

  • Context:用于网络判断生命周期,防止请求回来时Activity已经销毁及工具类等
  • Model:其实就是Java Bean,可以用来生成对应的ObservableField。这是肯定需要持有的。
  • Data Field(数据绑定):需要绑定到控件上的ObservableField字段。
  • Command(命令绑定):对事件的处理,比如下拉,加载,点击等。存在的问题是如果不使用三方库可能需要持有View的引用,等会需要特别注意一下这里。使用DataBinding就可以在xml中绑定View的处理命令
  • Child ViewModel:子vm,比如一个Activity由两个Fragment,这样就可以有两个子vm

ViewModel与Model协作
ViewModel通过传参数到Model层获取网络数据(数据库同理),然后把Model的部分数据映射到ViewModel的一些字段(ObservableField),并在ViewModel保留这个Model的引用

ViewModel与ViewModel的协作
用Messenger来在ViewModel中进行通讯。

Clean-Architecture

基本思想:

项目架构:

Presenter层可以使用MVP或MVVM模式,Data Layer负责获取数据,不管是数据库还是磁盘,网络都是用这层来处理。Domain layer负责处理使用场景,也就是负责处理逻辑。相当于Presenter层的Presenter需要依赖Domain layer才能真正处理逻辑。

12

oubindo

17 posts
8 categories
12 tags
© 2019 oubindo
Powered by Hexo
|
Theme — NexT.Muse v5.1.4