生存是唯一的长路

模块

  • 模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。

  • 涉及到概念 包(Package) 和 模块(Module)

  • 创建自己的模块时,要注意:

    • 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
    • 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。

使用模块

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

    ' a test module '

    __author__ = 'Michael Liao'

    import sys

    def test():
    args = sys.argv
    if len(args)==1:
    print('Hello, world!')
    elif len(args)==2:
    print('Hello, %s!' % args[1])
    else:
    print('Too many arguments!')

    if __name__=='__main__':
    test()
  • 注释:

    • 第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行
    • 第2行注释表示.py文件本身使用标准UTF-8编码;
    • 第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
    • 第6行使用__author__变量把作者写进去
  • 重点if __name__=='__main__':

  • Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试.

作用域

  • Python中,是通过_前缀 标记 private
  • note: python中语法没有限制 _开头的变量/函数!

安装第三方模块

  • pip命令

    1
    pip install xxx
  • 或者在文件中自定义路径.

函数式编程

  • 纯函数式编程没有变量,一个函数只要输出确定,输出就是确定的,称为没有副作用.使用变量的函数内部由于变量状态不确定性,有副作用.
  • 函数式编程另一个特点是允许函数本身作为参数传入,也可以直接返回另外一个函数.
  • python对函数式编程提供有限支持.(还使用变量 so不是纯函数式编程语言)

高阶函数

  • python中函数本身也可以赋值给变量,变量可以指向函数
  • 函数的参数可以传入另一个函数,这种函数称为高阶函数.

map/reduce

  • map()接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回

  • 示例

    1
    list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # list所有数字转为字符串
  • reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> from functools import reduce
    >>> def fn(x, y):
    ... return x * 10 + y
    ...
    >>> def char2num(s):
    ... digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    ... return digits[s]
    ...
    >>> reduce(fn, map(char2num, '13579'))
    13579

filter

  • filter()接收一个函数和一个序列。filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

  • filter()函数返回的是一个 惰性序列 Iterator,也就是一个惰性序列,需要用list()函数获得所有结果并返回list

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def is_odd(n):
    return n % 2 == 1

    list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 只保留奇数

    def not_empty(s):
    return s and s.strip()

    list(filter(not_empty, ['A', '', 'B', None, 'C', ' '])) # 删除空字符

sorted

  • sorted()函数接收一个key函数来实现自定义的排序.第三个参数reverse=True决定正序倒序.

  • 示例:

    1
    2
    3
    4
    5
    >>> sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower)
    ['about', 'bob', 'Credit', 'Zoo']

    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
    ['Zoo', 'Credit', 'bob', 'about']

返回函数

  • 将函数作为结果返回.(很随便😂)

  • 闭包(Closure):相关参数和变量都保存在返回函数.

  • **note:**返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用

  • 示例(坑):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def count():
    fs = []
    for i in range(1, 4):
    def f():
    return i*i
    fs.append(f)
    return fs

    f1, f2, f3 = count()

    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9
  • 返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9

  • 一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def count():
    def f(j):
    def g():
    return j*j
    return g
    fs = []
    for i in range(1, 4):
    fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

匿名函数

  • 不显式定义的函数. python中是 lambda 但与java不同.

  • python中lambda限制,只能有一个表达式,不用写return,返回值就是该表达式的结果

  • 匿名函数不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

  • 示例

    1
    f = lambda x: x * x

装饰器

  • 代码运行期间,动态增加功能方式称为装饰器(Decorator)

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def log(func):
    def wrapper(*args, **kw):
    print('call %s():' % func.__name__)
    return func(*args, **kw)
    return wrapper

    @log
    def now():
    print('2015-3-25')

    >>> now()
    call now():
    2015-3-25
  • 如示例,装饰器 接受一个函数作为参数,并返回原函数.在原函数定义时,以 @ xxx作为标记.示例1 为2层,如果打印文本可以自定义.

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def log(text):
    def decorator(func):
    def wrapper(*args, **kw):
    print('%s %s():' % (text, func.__name__))
    return func(*args, **kw)
    return wrapper
    return decorator

    @log('execute')
    def now():
    print('2015-3-25')

    >>> now()
    execute now():
    2015-3-25

    打印可自定义 又加上了一层.

  • 如上两种定义后,函数对象的名称等发生了变化,so,终极版

  • Python内置的functools.wraps 会将原始函数的__name__等属性复制到wrapper()函数中.

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import functools
    # 不带参数
    def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
    print('call %s():' % func.__name__)
    return func(*args, **kw)
    return wrapper

    # 带参数

    def log(text):
    def decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
    print('%s %s():' % (text, func.__name__))
    return func(*args, **kw)
    return wrapper
    return decorator

偏函数

  • functools.partial的作用是把一个函数的某些参数设置默认值,返回一个新的函数.

  • 示例:

    1
    2
    3
    4
    import functools
    int2 = functools.partial(int, base=2)

    int2('1000000')
  • 注意: 仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值

  • 由于python 中函数也可以最为参数传入,so,参数固定成某一个函数也可.

  • 同理: *args**kw 也可以.

  • 简化函数调用.

高级特性

  • 代码精炼
  • python代码量越少,开发效率越高.

切片

  • 取list或tuple的部分元素

  • 示例

    1
    2
    L[0:3] #0到3,但结果不包括索引3
    L[:3] #0可省略
    1
    2
    L[-2:]  #-2到0 但不包括索引0
    L[-2:-1] #-2
    1
    2
    3
    L[:10:2] #前10 每隔2个取一个
    L[::5] #所有,每隔5个取一个
    L[:] #原样复制
  • 对tuple取切片,结果依然是tuple.

  • 字符串可以看作一个list,每个字符占一位.

迭代

  • 迭代很随便…太随便了…

  • list tuple不多说了

  • dict字典也可以迭代,因为无序,输出的顺序不一定相同.
    默认迭代的是key 但value 也可以迭代,key value也可以同时迭代

    1
    2
    for value in d.values()
    for k, v in d.items()
  • 上文书中说的,字符串可以当作list,所以也可以迭代.

  • 问题来了,如何判断可迭代对象?
    collections模块的Iterable类型判断

    1
    2
    3
    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
  • 类似c/java的带下标循环实现?
    Python内置的enumerate函数,把一个list变成索引-元素对

    1
    2
    for i, value in enumerate(['A', 'B', 'C']):
    print(i, value)

列表生成式

  • 用来生成list

  • 示例: [1x1, 2x2, 3x3, …, 10x10]

    1
    2
    [x * x for x in range(1, 11)]
    [m + n for m in 'ABC' for n in 'XYZ'] #两层
  • for可以同时循环两个甚至多个变量,dict的items()可以同时迭代key和value

    1
    2
    3
    d = {'x': 'A', 'y': 'B', 'z': 'C' } # 多个变量
    [k + '=' + v for k, v in d.items()] # 生成list
    [s.lower() for s in L] # 全部小写

生成器

  • generator 依照某种算法不断循环生成数据,而不是一次性生成完.节省大量空间.
  • 创建generator
    • 把列表生成式的[]改成()

      1
      g = (x * x for x in range(10))

      调用next(g)可获取下一个值.
      最常用for n in g:代入for循环.也没有抛出错误.

    • 定义一个包含yield关键字的函数.

      1
      2
      3
      4
      5
      6
      7
      def fib(max):
      n, a, b = 0, 0, 1
      while n < max:
      yield b
      a, b = b, a + b
      n = n + 1
      return 'done'
      • 变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
      • 用for循环调用generator时,拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中.

迭代器

  • 可以被for循环的对象统称为可迭代对象:Iterable
    使用isinstance()判断一个对象是否是Iterable对象

    1
    isinstance({}, Iterable)
  • 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator 表示一个惰性计算的序列 Iterator甚至可以表示一个无限大的数据流
    isinstance()判断一个对象是否是Iterator对象

    1
    isinstance((x for x in range(10)), Iterator)
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

  • Python的for循环本质上就是通过不断调用next()

函数

常用

  • 参数数量不对 TypeError错误

  • 参数类型错误,TypeError的错误,并且给出错误信息

  • int()转换为int类型

  • float()

  • bool()

  • str()

  • 函数名为一指向函数对象的引用,可以将变量指向函数,再调用

    1
    2
    3
    >>> a = abs # 变量a指向abs函数
    >>> a(-1) # 所以也可以通过a调用abs函数
    1

定义函数

  • 示例

    1
    2
    3
    4
    5
    def my_abs(x):
    if x >= 0:
    return x
    else:
    return -x

    def 函数名(参数) :
    函数体
    return

  • 没有return 则返回 None

  • import 包含模块,java一样

  • 空函数 pass 语句.

    1
    2
    def nop():
    pass
  • 示例 返回多个参数

    1
    2
    3
    4
    5
    6
    import math

    def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

    返回的是一个tuple … 按位置赋值给变量

函数参数

  • 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

默认参数

  • 示例:

    1
    2
    3
    4
    def power(x, n=2):
    ...

    power(5) power(5, 2) power(n=3,5)
  • 默认参数必须指向不变对象

  • 多个参数时,变化大的参数在前,变化小的参数在后。变化小的参数就可以作为默认参数。

  • 多个默认参数,可以按顺序提供默认参数

  • 也可以不按顺序提供。当不按顺序提供部分默认参数时,需要把参数名写上 enroll('Adam', 'M', city='Tianjin')

    • 默认参数 不可变性 每次调用均会改变其值.

可变参数

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def calc(*numbers):
    sum = 0
    for n in numbers:
    sum = sum + n * n
    return sum

    nums = [1, 2, 3]
    calc(*nums) #相同
    calc(1, 2, 3)
  • 传入的参数个数可变,传入组装成了tuple

  • 当作c语言指针吧😂 传入数组的地址

命名关键字参数

  • 示例:

    1
    2
    3
    4
    def person(name, age, *, city, job):
    print(name, age, city, job)

    person('Jack', 24, city='Beijing', job='Engineer')
  • 命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数

  • 函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*

    1
    2
    def person(name, age, *args, city, job):
    print(name, age, args, city, job)
  • 如果没有可变参数,就必须加一个作为特殊分隔符。如果缺少,Python解释器将无法识别位置参数和命名关键字参数

关键字参数

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

    extra = {'city': 'Beijing', 'job': 'Engineer'}
    person('Jack', 24, city=extra['city'], job=extra['job'])
    #相同
    extra = {'city': 'Beijing', 'job': 'Engineer'}
    person('Jack', 24, **extra)
  • 关键字参数在函数内部组装为一个dict

  • 函数内部获得是 dict的拷贝,修改对原值无影响.

  • 可以传入任意不受限制的关键字参数

递归函数

  • python 不含尾递归优化,注意层级,否则非常容易堆栈溢出.

数据类型

  • 整数 浮点数

字符串

  • 字符串以单引号 ’ 或 " 包括.
  • 转义字符 \ 依然有效. r’'表示此字符串默认不转移.
  • /n有效, 可用 开头 … 替换

布尔

  • 只有True 和 Flase
  • and or not 3种常用操作

常量

  • 通常都为大写表示.
  • 除法有两种 / 或 //
    / 结果可能为浮点, // 结果为整数(地板除)

其他

  • None 空值,不为0.谨记
  • 动态语言,so,变量的类型无所谓.
  • 浮点数精度无限,但超过限制直接表示为 inf

字符编码

  • 简而言之 文件使用utf-8就对了.
  • utf-8 属于万国码的简化,低位与 ASCII 兼容.

python字符串编码

  • py3中默认是 Unicode 万国码.

  • ord()获取字符的整数表示,chr()把编码转换为字符.

  • Python对bytes类型的数据用带b前缀的单引号或双引号表示 x = b'ABC'

  • 以Unicode表示的str通过encode()方法可以编码为指定的bytes

    1
    2
    3
    4
    'ABC'.encode('ascii')
    b'ABC'
    '中文'.encode('utf-8')
    b'\xe4\xb8\xad\xe6\x96\x87'
  • 对应的decode()方法

    1
    2
    3
    4
    >>> b'ABC'.decode('ascii')
    'ABC'
    >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    '中文'

    可以传入errors='ignore’忽略错误的字节

    1
    b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
  • len(),str计算字符.bytes计算字节

  • 开头,第一行,可执行文件(windows会忽视注释)
    第二行,以utf-8处理文件.

    1
    2
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

字符串格式化

  • 占位符与c基本相同 %d %s %x %f

    1
    2
    print('%2d-%02d' % (3, 1))
    print('%.2f' % 3.1415926)

    需要 % 时, %%

  • format()

    1
    2
    >>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
    'Hello, 小明, 成绩提升了 17.1%'

list tuple

list 列表

  • 示例, = [ , , ] 可变 有序

    1
    classmates = ['Michael', 'Bob', 'Tracy']
  • 索引以 0 开始,可以倒序 -n 当然越界都是 IndexError 错误

  • .len(),list元素个数.

  • .append(x) 末尾添加

  • .insert(i,x) 对应索引处插入

  • .pop(i) 删除i对应元素,为空则删除末尾元素.

  • 替换i位置,直接赋值.

  • list的元素可以为一个list.多重数组.

tuple 元组

  • 示例= ( , , ) 不可变 有序 更安全,其他与list相同

    1
    t = (1, 2)
  • t = (1,)声明一个元素元组,比较特殊.

条件判断

  • 示例

    1
    2
    3
    4
    5
    6
    if true :
    print("1")
    elif true :
    print("2")
    else :
    print("3")
  • if 条件可简写,if x x非零数值、非空字符串、非空list 则为true

  • 提及用户输入 input()

  • input默认返回字符串,如需要其他类型 int()转换.

循环

  • 示例 与c相同

    1
    2
    for x in names:
    print(x)
  • 示例 与c相同

    1
    2
    while n > 0:
    print(n)
  • break 跳出此层循环

  • continue 跳出本轮循环

dict 和 set

dict

  • 字典,使用键值对储存数据,索引极快,空间换时间.

  • 对应其他语言的 map

  • 示例: {‘’: ,‘’: ,…}

    1
    2
    3
    d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    d['Michael']
    95
  • key

    • 必须为不可变对象,整数、字符串等

    • 一个key对应一个value

    • key不存在

      • in判断

        1
        2
        >>> 'Thomas' in d
        False
      • get()获取

        1
        2
        d.get('Thomas') # 返回空
        d.get('Thomas', -1) # 不存在? 返回-1: ;
    • pop()删除

      1
      d.pop('Bob')

set

  • key的集合,但不存在value

  • 无序、不重复、元素为不可变对象.

  • set可以看作是一个无序和无重复元素的集合.

  • 创建set需要一个list

  • 示例 set([ , , …])

    1
    2
    3
    >>> s = set([1, 2, 3])
    >>> s
    {1, 2, 3}
  • .add(key)/.remove(key)

  • 不同的set之间可以交集(&)取并集(|).


编程环境

  • Android Studio 3.0.1

问题

  • MyPrivacy 在android M上闪退,在模拟器中复现.提示
    1
    android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

过程

  • 进入第二个Activity应用设置时,才出现,而且 7.1无问题.模拟器复现,抓log.

解决

  • log的意思是启动activity的context不是 activity.
  • 对应代码
    1
    2
    3
    Intent intent = new Intent(MyApplicantion.getContext(),   AppSettingActivity.class);
    intent.putExtra("PackageName", AppId);
    MyApplicantion.getContext().startActivity(intent);
  • 需要对intent声明 FLAG_ACTIVITY_NEW_TASK
    1
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

备注

  • 网上的原因分析:
  • 因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的context(ApplicationContext)并没有所谓的任务栈,所以就出现问题了。需要指定Activity为FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候,就会为它创建一个新的任务栈了。–android开发艺术探究


编程环境

  • Android Studio 3.0

问题

  • 横屏模式下,显示内容被遮挡

过程

  • Google😂

解决

  • ScrollView 只允许嵌套一个子布局.超出范围部分会自动增加滚动条

备注

  • ScrollView 只能添加竖直方向滚动条.


资料来源如下
给初学者的RxJava2.0教程(demo代码来源)

http://www.jianshu.com/u/c50b715ccaeb

编程环境

  • Android Studio 2.2.3

  • 在Gradle配置:
    1
    2
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

观察者模式

  • 在Eventbus中亦涉及了相关概念,比较简单.包括Observable(被观察者)、Observer(观察者)、subscribe().事件由Observable(被观察者)开始发出,通过subscribe()最终被传递到Observer(观察者).而整个过程中你是站在Observer(观察者)的位置,也就是事件的末尾,观察Observable(被观察者).

  • 上图

  • demo

    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
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onComplete();
    }
    }).subscribe(new Observer<Integer>() {
    @Override
    public void onSubscribe(Disposable d) {
    Log.d(TAG, "subscribe");
    }

    @Override
    public void onNext(Integer value) {
    Log.d(TAG, "" + value);
    }

    @Override
    public void onError(Throwable e) {
    Log.d(TAG, "error");
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "complete");
    }
    });
  • 事件由emitter.onNext(1);开始,最终被public void onNext(Integer value)相应.被观察者事件发送结束调用emitter.onComplete();,同时观察者最终以public void onComplete()相应.


  • note: 上下游以.subscribe建立连接后,事件才会开始发送.

Observable(被观察者) Observer(观察者)

  • ObservableEmitter
    ObservableEmitter: Emitter意为发射器,事件发送.onNext(T value)、onComplete()和onError(Throwable error)分别对应next事件、complete事件和error事件。
  • Observer中onNext(Integer value)、onError(Throwable e)、onComplete()对应接受next事件、complete事件和error事件
  • 被观察者发送complete事件和error事件后,观察者接受后不再继续响应事件,即使被观察者还在发送事件.complete事件和error事件互斥.
  • 在Observer中,调用Disposable.dispose(),切断管道.被观察者继续发送,但观察者不再响应.

subscribe

  • 建立Observable(被观察者) Observer(观察者)之间的管道.有多个重载.
  • 重载
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public final Disposable subscribe() {}

    public final Disposable subscribe(Consumer<? super T> onNext) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}

    public final void subscribe(Observer<? super T> observer) {}
  • 重载说明
    • 不带参数 : 观察者不关心任何事件(有卵用😵)
    • 只带onNext : 观察者只响应next事件.
    • 其他类似…演绎推理…
    • 最后一个是传入完整的Observer对象.(demo就是🙃)

操作符

  • 基于Rxjava的观察者模式可以拆分大多数的业务逻辑,即使再增加很多功能整体也不会过于混乱.
  • 但Rxjava的强大并不局限在拆分逻辑.由被观察者到观察者的整个事件传递过程,基于Rxjava我们可以任意拆分 合并 转换 事件、切换线程等.

  • note: 操作符搭配 Lambda 表达式食用更佳 🤣

创建

  • 产生并发送 Obserable 事件.
  • 仅常用,详细在 RxJava 2.x 使用详解(二) 创建操作符

.creat

  • 前面demo中已经实际使用过了
  • 用于产生一个 Obserable 被观察者对象,demo如上所示.

.just

  • 对于简单的几个数据,直接使用just发送即可,无需创建 Obserable 对象.just最多可以接收 10 个参数.
  • demo
    1
    2
    Observable.just("test","test2")
    .subscribe(str -> Log.i("tag", str));
    相当于顺序调用onNext(“test”)和onNext(“test2”),最后调用onComplete方法。

.fromArray

  • 功能与just类似但fromArray来接收任意长度的数据数组,也可以直接传入数组fromArray(new int[]{1, 2, 3}) 
  • demo
    1
    2
    Observable.fromArray(1, 2, 3, 4, 5)
    .subscribe(integer -> Log.i("tag", String.valueOf(integer)));
    fromArray不支持直接传入list进,list会被当作一个整体发送.

.fromIterable

  • 功能与fromArray类似,但是可以接收 list 类型,遍历可迭代数据集合.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");

    Flowable.fromIterable(list).subscribe(
    s -> Log.i("tag", s)
    );

.timer

  • 指定一段时间间隔后发送数据(一次性),不太常用.

线程切换

  • 默认事件传递的双方在同一线程工作.

  • demo

    1
    2
    3
    4
    5
    observable.subscribeOn(Schedulers.newThread())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .observeOn(Schedulers.io())
    .subscribe(consumer);
  • 方法:

    • .subscribeOn() : 指定被观察者发送事件线程,仅第一次调用时有效!
    • .observeOn() : 指定观察者/流变换(对发送的事件处理🖖)线程,多次调用,多次切换有效
  • 参数:

    • Schedulers.io() : 适用于io密集型操作,网络通信、磁盘操作等.
    • Schedulers.computation() : CPU密集操作,需要大量CPU计算的操作.
    • Schedulers.newThread() : 创建新线程.
    • AndroidSchedulers.mainThread() : Android主线程,通常为更新UI等.

过滤

  • 对 Obserable 事件筛选.
  • 仅常用,详细在RxJava 2.x 使用详解(三) 过滤操作符

.filter

  • 基本过滤操作符,按照任意自定规则过滤.

组合

转换

.map

  • 处理前后事件数量之比1:1,事件变换前后顺序不变
  • map作用是对Observable发送的每一个事件,应用处理变换函数,再继续像下游发送.中间过程可以转换事件类型、改变事件内容等等.只需要变换后的事件类型与下游接收的类型匹配即可.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onNext(2);
    emitter.onNext(3);
    }
    }).map(new Function<Integer, String>() {
    @Override
    public String apply(Integer integer) throws Exception {
    return "This is result " + integer;
    }
    }).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
    Log.d(TAG, s);
    }
    });

    这里是把int类型转换为了string类型,与观察者接收的类型匹配即可.

.flatMap

  • 处理前后事件数量之比 1:n,事件变换前后顺序不保证
  • flatMap,通俗点就是把Observable发送的事件拆散变换再,继续像下游发送.1个Observable事件可拆成任意个.只需要变换后的事件类型与下游接收的类型匹配即可.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onNext(2);
    emitter.onNext(3);
    }
    }).flatMap(new Function<Integer, ObservableSource<String>>() {
    @Override
    public ObservableSource<String> apply(Integer integer) throws Exception {
    final List<String> list = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
    list.add("I am value " + integer);
    }
    return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
    }
    }).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
    Log.d(TAG, s);
    }
    });

    这里是flatMap把一个int类型事件拆成了3个String类型,运行结果看,最终事件到达顺序与onNext(1);onNext(2);的发送顺序无关

.concatMap

  • 处理前后事件数量之比 1:n,事件变换前后顺序按顺序
  • 与flatMap作用相同,只是保证了事件严格按顺序达到下游.
  • demo 就不上了,直接替换flatMap的位置就好.

.zip

  • 处理前后事件数量之比 n:1,事件变换前后顺序严格按顺序
  • zip.最常见的压缩文件格式,在这里也是类似的意思,zip可以严格按照顺序合并多个 不同类型 Observable发送的事件.总的发送事件数量与上游Observable发送最少的那个数量相同.
  • demo
    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
    69
    Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    Log.d(TAG, "emit 1");
    emitter.onNext(1);
    Thread.sleep(1000);

    Log.d(TAG, "emit 2");
    emitter.onNext(2);
    Thread.sleep(1000);

    Log.d(TAG, "emit 3");
    emitter.onNext(3);
    Thread.sleep(1000);

    Log.d(TAG, "emit 4");
    emitter.onNext(4);
    Thread.sleep(1000);

    Log.d(TAG, "emit complete1");
    emitter.onComplete();
    }
    }).subscribeOn(Schedulers.io());

    Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
    @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception {
    Log.d(TAG, "emit A");
    emitter.onNext("A");
    Thread.sleep(1000);

    Log.d(TAG, "emit B");
    emitter.onNext("B");
    Thread.sleep(1000);

    Log.d(TAG, "emit C");
    emitter.onNext("C");
    Thread.sleep(1000);

    Log.d(TAG, "emit complete2");
    emitter.onComplete();
    }
    }).subscribeOn(Schedulers.io());

    Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
    @Override
    public String apply(Integer integer, String s) throws Exception {
    return integer + s;
    }
    }).subscribe(new Observer<String>() {
    @Override
    public void onSubscribe(Disposable d) {
    Log.d(TAG, "onSubscribe");
    }

    @Override
    public void onNext(String value) {
    Log.d(TAG, "onNext: " + value);
    }

    @Override
    public void onError(Throwable e) {
    Log.d(TAG, "onError");
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "onComplete");
    }
    });
    这里两个事件发送在同一线程中.当两个事件发送不再同一线程时,情况类似,不过当异步时,数量较少的事件发送完成,发送Complete事件后,通道随即被切断.

.Concat

  • 处理前后事件数量之比 n:1,事件变换前后顺序严格按顺序
  • Concat,可以严格按照顺序合并 相同类型 Observable发送的事件.

Backpressure


  • 被翻译为背压…(如此文不达意的直译,能忍?往下都是因为原文…😈)

  • 其实概念有够简单:将整个事件产生/传递/处理的过程想象为一条河流由上而下, Backpressure 指的是上游产生的事件太快,远远超过了下游的处理速度,以至于缓冲区溢出.上游来了洪水,下游径流量不够,以至于中间河道跨过了堤岸,溢出.

Flowable基础

  • Rxjava 1.x中需要自行通过操作符处理,到了2.0中,则有了专门对付发洪水上游的被观察者- Flowable .我们常用的 observable 在2.x中一般用于不涉及 Backpressure 的情况.而对应与 observable 的 Observer ,改为了 Subscriber .

  • demo

    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
    Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
    @Override
    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
    Log.d(TAG, "emit 1");
    emitter.onNext(1);
    Log.d(TAG, "emit 2");
    emitter.onNext(2);
    Log.d(TAG, "emit 3");
    emitter.onNext(3);
    Log.d(TAG, "emit complete");
    emitter.onComplete();
    }
    }, BackpressureStrategy.ERROR); //增加了一个参数

    Subscriber<Integer> downstream = new Subscriber<Integer>() {

    @Override
    public void onSubscribe(Subscription s) {
    Log.d(TAG, "onSubscribe");
    s.request(Long.MAX_VALUE); //注意这句代码
    }

    @Override
    public void onNext(Integer integer) {
    Log.d(TAG, "onNext: " + integer);

    }

    @Override
    public void onError(Throwable t) {
    Log.w(TAG, "onError: ", t);
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "onComplete");
    }
    };

    upstream.subscribe(downstream);
  • 注意两个地方

    • Flowable创建时比 observable 多了一个参数.(参数作用下节说明)
    • Subscriber中调用了 s.request .
  • Flowable 与 Observable 最大的不同就是 Flowable再次发送事件需要等待 Subscriber 中调用 .request

  • .request() 实质上是下游告知上游自己的处理能力,使得上游根据下游处理能力发送事件.多次调用,上游表示处理能力的数字会叠加,上游每发送一个事件,该数字减一,到0抛出异常

    • 上下游在同一线程时,下游没有或没有及时调用 .request ,上游会抛出异常
    • 异步线程时,下游即使没有调用 .request 会有128个事件的缓存区.上游可继续发出事件,缓存区超出128个事件后,抛出异常.

Flowable拓展

  • 这里对 Flowable 多的参数进行说明.

  • 参数
    • BackpressureStrategy.BUFFER : 默认缓存区128,这个参数极大拓展了缓存区,使得 Flowable 表现与 Observable 差不多.
    • BackpressureStrategy.DROP : 128缓存区满了,就丢弃上游事件,直到下游处理了一个事件,缓存区 -1 ,再允许存入新的上游事件.
    • BackpressureStrategy.LATEST : 永远保存最后达到的128个上游事件,上游有新的事件到达满载的缓存区时,丢弃第一个存入缓存区的上游事件.

  • 对于不是由我们编写的 Flowable 也可以通过 interval 操作符来加工.
    1
    2
    Flowable.interval(1, TimeUnit.MICROSECONDS)
    .onBackpressureDrop() //加上 Backpressure 策略
  • 对应上文,指定参数有3,意思同上.
    • onBackpressureBuffer()
    • onBackpressureDrop()
    • onBackpressureLatest()


编程环境

  • Android Studio 3.0

问题

  • Glide是一个通用的图片缓存框架,但是在MyPrivacy显示appIcon时,传入 一个Drawable对象,提示类型不匹配.

  • (注意: 这里是直接传入Drawable对象,不是经过 R.xx 引用 !)

过程

  • 查阅资料后,确认Glide不支持直接加载传入的Drawable对象,转换为bitDrawable类型也不可.

  • 解决思路来自

    https://github.com/bumptech/glide/issues/588

  • 不支持直接加载,但Glide的.error(Icon)错误时显示 .placeholder(Icon)占位符,支持Drawable对象

解决

  • 不再直接加载 .load 传入空字符串, 通过 .placeholder 简洁加载.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Drawable Icon = xxx;

    RequestOptions options = new RequestOptions()
    .error(Icon)
    .placeholder(Icon);

    Glide.with(context)
    .load("")
    .apply(options)
    .into(imageView);

备注

  • placeholder 在Glide 4.x版本中,移入了 RequestOptions 对象中.


  • ubuntu16.04
  • 使用busybox编译最小文件系统,使用qemu运行起来。
  • 内容来自 奔跑吧linux内核第6章
  • 这里将输入代码过程集合到了几个.sh文件,不做重复的工作 !
  • 当然网好是前提,最好挂代理.

安装工具

  • 首先需要安装qemu gcc, ubuntu16.04中自带的gcc版本较低,这里我们安装书中推荐的gcc-arm-linux-gnueabi

    1
    sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential
  • 下载busybox源码

    • 书中推荐版本是1.24,但最新版本已经到了busybox-1.27.2.这里我们使用最新版

      1
      wget https://busybox.net/downloads/busybox-1.27.2.tar.bz2
    • 解压到 busybox 文件夹

      1
      tar -jxvf busybox-1.27.2.tar.bz2
  • 下载linux内核源码

    • 还是以配套的4.0源码为例,(提醒:内核解压后大约占800MB,请预留出足够空间)

      1
      wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.0.tar.gz
    • 解压到linux文件夹

      1
      tar -jxvf linux-4.0.tar.gz

编译最小文件系统

  • 别问我最小文件系统是什么,我也有点😵,但是先用起来.

  • 首先利用 busybox 手工编译一个最小文件系统。
    在busybox文件夹下

    1
    2
    3
    4
    export ARCH=ARM
    export CROSS_COMPILE=arm-linux-gnueabi-
    make menuconfig
    make install
  • 进入menuconfig后,配置静态编译

    1
    2
    3
    Busybox Settings --->
    Build Options --->
    [*] Build BusyBox as a static binary (no shared libs)
  • 然后 make install 编译完成。编译完成后,把 busybox 根目录下面的_install 目录拷贝到 linux-4.0 下。

  • 进入_install 目录,创建 etc、dev 等目录。

    1
    2
    3
    4
    mkdir etc
    mkdir dev
    mkdir mnt
    mkdir -p etc/init.d/
  • 在_install /etc/init.d/目录下创建 文件名rcS 的文件,写入以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mkdir –p /proc
    mkdir –p /tmp
    mkdir -p /sys
    mkdir –p /mnt
    /bin/mount -a
    mkdir -p /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev –s

    同时使用 chmod +x rcS修改rcS的可执行权限.

  • 在_install /etc 目录创建文件名 fstab 的文件,并写入以下内容。

    1
    2
    3
    4
    5
    proc /proc proc defaults 0 0
    tmpfs /tmp tmpfs defaults 0 0
    sysfs /sys sysfs defaults 0 0
    tmpfs /dev tmpfs defaults 0 0
    debugfs /sys/kernel/debug debugfs defaults 0 0
  • 在_install /etc 目录创建文件名 inittab 的文件,并写入如下内容。

    1
    2
    3
    4
    ::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::ctrlaltdel:/bin/umount -a –r
  • 在_install/dev 目录下创建如下设备节点,以root权限执行

    1
    2
    3
    cd _install/dev/
    sudo mknod console c 5 1
    sudo mknod null c 1 3

.sh(配合chmod +x使用)

  • build.sh: 编译busybox

    1
    2
    3
    4
    export ARCH=ARM
    export CROSS_COMPILE=arm-linux-gnueabi-
    make menuconfig
    make install
  • creat.sh: _install文件夹下处理

    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
    rm -rf etc
    rm -rf dev
    rm -rf mnt
    mkdir etc
    mkdir dev
    mkdir mnt
    mkdir -p etc/init.d/

    echo "mkdir -p /proc
    mkdir -p /tmp
    mkdir -p /sys
    mkdir -p /mnt
    /bin/mount -a
    mkdir -p /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s
    " > etc/init.d/rcS
    chmod +x etc/init.d/rcS

    echo "proc /proc proc defaults 0 0
    tmpfs /tmp tmpfs defaults 0 0
    sysfs /sys sysfs defaults 0 0
    tmpfs /dev tmpfs defaults 0 0
    debugfs /sys/kernel/debug debugfs defaults 0 0
    " > etc/fstab

    echo "::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::ctrlaltdel:/bin/umount -a -r
    " > etc/inittab

    cd dev/
    sudo mknod console c 5 1
    sudo mknod null c 1 3

编译内核

  • 编译内核

    1
    2
    3
    4
    5
    cd linux-4.0
    export ARCH=arm
    export CROSS_COMPILE=arm-linux-gnueabi-
    make vexpress_defconfig
    make menuconfig
  • 配置 initramfs,在 initramfs source file 中填入_install。另外需要把 Default kernel command string 清空。

    1
    2
    3
    4
    5
    General setup --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    (_install) Initramfs source file(s)
    Boot options -->
    ()Default kernel command string
  • 配置 memory split 为“3G/1G user/kernel split”以及打开高端内存。

    1
    2
    3
    Kernel Features --->
    Memory split (3G/1G user/kernel split) --->
    [ *] High Memory Support
  • 开始编译 kernel

    1
    2
    make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
    make dtbs
  • 运行 QEMU 来模拟 4 核 Cortex-A9 的 Versatile Express 开发平台。

    1
    qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic

.sh(配合chmod +x)

  • build.sh : 编译内核

    1
    2
    3
    4
    5
    6
    export ARCH=arm
    export CROSS_COMPILE=arm-linux-gnueabi-
    make vexpress_defconfig
    make menuconfig
    make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
    make dtbs
  • run.sh : 运行arm内核

    1
    qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographicyun