Flysands Blog


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

使用emcas+ledger记账

发表于 2017-12-27 | 分类于 emacs |

1 复式记账法

复式簿记是一种把每笔交易都记录到复数个账户中的簿记方法。举个例子,想像你面前有两个桶,分别是「资产」(Assets)和「费用」(Expenses),左边的桶里装满了豆子,右边的桶是空的。你用 1000 元听了一场演唱会,为了记录这笔费用,你把 1000 粒豆子从 Assets 桶里转移到了 Expenses 桶里,代表你的资产减少了 1000 元,而你花在演唱会上的费用增加了 1000 元,在这个过程中,豆子的总量没有变化(资产减少的豆子与费用增加的豆子数量一致),这便是最简单的复式簿记。

2 分配

  • 资产 Assets - 现金、银行存款、有价证券等;
  • 负债 Liabilities - 信用卡、房贷、车贷等;
  • 收入 Income - 工资、奖金等;
  • 费用 Expenses - 外出就餐、购物、旅行等;
  • 权益 Equity - 用于「存放」某个时间段开始前已有的豆子;

复式记账法 有两个入口,并且两个入口相加等于零。借和贷相加等于零。借和贷很好理解,打个比方你向XX借了5000块钱,你的资产增加了5000,你的负债就要加上-5000,相当于从你的负债桶里贷出5000到你的资产桶。另外举一个例子,你买衣服花了2000块钱,这笔交易可以理解成向资产桶里贷出2000块钱,借给你的费用桶,所以你的费用桶增加2000。只需要记住 借增贷减 即可。

  • 借贷记账

    想像成一个装着你一生(过去和未来)所有劳动成果的桶,每次你的收入都是从桶里贷出来。打个比方你发了工资10000块,你的资产桶增加了10000。这10000是收入桶贷给你的。所以资产桶+10000,相应的收入桶-10000。

恒等式:

  1. (Income + Liabilities) + (Assets + Expenses) + Equity = 0
  2. 每笔交易在不同账户的数字加起来和为 0 是复式簿记的重要特性和原则,也是用来检验账目正确性的重要依据。

3 使用emacs和ledger记账

3.1 开户

除开startup代码块之外,另外新建income,liabilities,expenses,assets四个代码块。分别用来记录收入,负债,消费,资产。

2017/12/27 * Opening Balance
    Assets:招商银行                                            95198 CNY
    Assets:微信钱包                                              239 CNY
    Assets:现金                                                    3 CNY
    Assets:支付宝                                                  2 CNY
    Assets:外借                                                80000 CNY
    Liabilities:花呗                                           -6526 CNY
    Equity:初始平衡                                          -168916 CNY

3.2 记账

在expenses下记录我们的第一笔消费,比如花了两块钱买菜。相当于从Assets中贷出2元放到Expenses桶中。其中第二行的+2 CNY被省略掉了。

2017/12/27 * 买菜
    Assets:现金                                                   -2 CNY
    Expenses:生活消费                                              
  • 小技巧

    在 org-mode 的 ledger 代码区, 按 C-c ' , 可以快速进入一个临时的 ledger-mode buffer。在这个 ledger-mode buffer 中, 可以使用 ledger-mode 的键绑定:

    • M-m m a (spacemacs)添加记录
    • SPC j = (Spacemacs) 版面调整
    • , c (Spacemacs) 提交修改并返回 org-mode buffer

3.3 统计

  • 月度统计

    月度统计每个月的收支状况。

    2017/12/27 * 买菜
        Assets:现金                                                   -2 CNY
        Expenses:生活消费                                              
    

    C-c C-c 执行代码块,生成月度报告。

  • 总体统计

    总体统计当前的财务状况。

    2017/12/27 * Opening Balance
        Assets:招商银行                                            95198 CNY
        Assets:微信钱包                                              239 CNY
        Assets:现金                                                    3 CNY
        Assets:支付宝                                                  2 CNY
        Assets:外借                                                80000 CNY
        Liabilities:花呗                                           -6526 CNY
        Equity:初始平衡                                          -168916 CNY
    
    2017/12/27 * 买菜
        Assets:现金                                                   -2 CNY
        Expenses:生活消费                                              
    
    
    

    C-c C-c 执行代码块,生成总览报告。满足恒等式。

android 5.1.1 源码编译

发表于 2017-12-25 | 分类于 android |

1 Android 5.1.1源码编译

1.1 编译环境

本文编译环境采用ubuntu 16.04 + Nexus 5 + Android 5.1.1.注意对号入座.

1.2 配置环境

  1. 安装openjdk 7

    andoird 5.1.1编译需要依赖openjdk7,不能使用oracle的jdk.

    安装openjdk 7.

    sudo add-apt-repository ppa:openjdk-r/ppa  
    sudo apt-get update   
    sudo apt-get install openjdk-7-jdk
    
  2. 安装依赖包

    sudo apt-get install git-core gnupg flex bison gperf build-essential \
         zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
         lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
         libgl1-mesa-dev libxml2-utils xsltproc unzip
    

1.3 源码准备

  1. 下载源码 从官方同步太慢,建议直接从清华开源镜像站同步.
    • 下载repo

      mkdir ~/bin
      PATH=~/bin:$PATH
      curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o repo
      chmod +x repo
      

      在.bashrc中添加export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'.然后source ~/.bashrc.

    • 同步特定版本的Android源码

      mkdir WORKING_DIRECTORY
      cd WORDING_DIRECTORY
      repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-5.1.1_r14
      repo sync
      
  2. 下载驱动文件 前往 android驱动下载页面 下载对应驱动文件.本次编译选用设备为Nexus 5.编译版本号为LMY48M.把下载好的驱动包放入android根目录解压,并执行解压出来的三个脚本.注意脚本会提示你查看版权直接q,然后输入I ACCEPT.
  3. 检查make版本 执行make -version命令检查本机的make版本.如果make版本为4.x,需要进行降级处理.重新安装一份3.81或者3.82即可.

    make -version
    GNU Make 3.82
    Built for x86_64-unknown-linux-gnu
    Copyright (C) 2010  Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    #### make completed successfully  ####
    
    wget -o make.tar.gz http://ftp.gnu.org/gnu/make/make-3.81.tar.gz
    tar -xvzf make-3.81.tar.gz
    cd make-3.81
    ./configure
    sudo make install
    
  4. fix build-android-from-sources-unsuppoerted-reloc android 5.1源码在ubuntu 16.04环境下编译存在一个由gcc版本引起的bug.需要手动修复.在源码根目录下执行如下命令即可.

    cp /usr/bin/ld.gold prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6/x86_64-linux/bin/ld
    
  5. 编译 所有环境准备完毕之后,直接开编即可.注意lunch的时候要选择对应的手机型号.本文为aosp_hammerhead-userdebug.

    source build/envsetup.sh
    lunch
    make -j4
    

编译xposed源码

发表于 2017-12-25 | 分类于 android |

1 Android 5.1.1 编译Xposed框架

本文假设你了解android源码编译,git,xposed以及android Native相关知识.有关android源码编译可以参考之前的文章Android 5.1.1源码编译.编译主机环境为ubuntu 16.04 + java 1.8.

1.1 安装jdk 1.8

Ubuntu 16.04默认的的java版本为1.6.编译Xposed框架最新版需要java 1.8.所以从官网下载jdk 1.8,文件名为jdk-7u80-linux-x64.tar.gz,然后执行命令进行手动安装.

cd /opt/
tar –zxvf jdk-8u151-linux-x64.tar.gz
sudo mkdir /usr/lib/jvm
sudo mv ./jdk1.8.0_151 /usr/lib/jvm

然后再export 4个java相关的环境变量.执行java -version确认版本切换是否成功.

export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_151
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

1.2 同步xposed源码

1.2.1 XposedTools

XposedTools是官方提供的一套工具集,方便用来编译和打包Xposed框架.

直接从官网clone源码备用.

cd ~
git clone https://github.com/rovo89/XposedTools.git

XposedTools目录下面有两个重要文件.其中build.pl是编译脚本,该脚本根据build.conf文件里的选项进行编译.build.conf.sample是官方提供的编译选项模板,需要自行修改.首先将build.conf.sample更名为build.conf.然后修改outdir,javadir和AospDir,路径要匹配自己的android源码位置.

[General]
outdir = /home/mobsec/Android/aosp_5.1/out
javadir = /home/mobsec/Android/aosp_5.1/XposedBridge

[Build]
# Please keep the base version number and add your custom suffix
version = 65 (custom build by xyz / %s)
# makeflags = -j4

[GPG]
sign = release
user = 852109AA!

# Root directories of the AOSP source tree per SDK version
[AospDir]
22 = /home/mobsec/Android/aosp_5.1
#21 = /android/aosp/500

# SDKs to be used for compiling BusyBox
# Needs https://github.com/rovo89/android_external_busybox
[BusyBox]
arm = 21
x86 = 21
armv5 = 17

1.2.2 xposed

xposed为整个Xposed框架的native部分,主要内容为xposed修改的app_process源码和相关库文件.这里需要提一下,这部分源码有两种同步方式.第一采用修改local_manifests,然后repo sync的方式.第二种则为手动下载合并.本文采用第二种方式.

cd aosp_dir
cd cd frameworks/base/cmds/
git clone https://github.com/rovo89/Xposed.git xposed

1.2.3 android_art

Xposed为了支持新的runtime,修改了art的源码.我们需要把原来的art源码替换成Xposed提供的代码.

cd aosp_dir
rm -rf art
git clone https://github.com/rovo89/android_art.git art
cd art
git checkout xposed-lollipop-mr1

1.2.4 XposedBridge

XposedBridge的代码直接clone即可,没有版本的限制.需要注意与javadir对应.

cd aosp_dir
git clone https://github.com/rovo89/XposedBridge.git

1.3 编译整个框架

编译Native代码的时候需要依赖java层代码的输出文件XposedBridge.jar.可以直接把XposedBridge.jar直接copy到build.conf文件中的outdir/java/路径下进行预编译.也可以手动编译整个XposedBridge.jar.

1.3.1 编译java层代码

编译Xposed java层代码需要配置好android sdk环境.先下载sdk工具,然后解压.

wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz

执行list sdk命令查看可以安装的sdk包.-a选项表示列举所有包.安装source的时候才需要使用-a选项.

./android list sdk --extended --proxy-host mirrors.neusoft.edu.cn --proxy-port 80 -s -a

依次安装Android SDK Build-Tools 23.0.3,Android SDK Platform 23.

./android list sdk --extended --proxy-host mirrors.neusoft.edu.cn --proxy-port 80 -s
./android update sdk --no-ui --filter 包id --proxy-host mirrors.neusoft.edu.cn --proxy-port 80 -s

使用带-a参数的list命令,安装source包.

./android list sdk --extended --proxy-host mirrors.neusoft.edu.cn --proxy-port 80 -s -a
./android update sdk --no-ui --filter 包id --proxy-host mirrors.neusoft.edu.cn --proxy-port 80 -s

然后在Xposed目录下,添加local.properties文件.并在其中填写sdk路径.

sdk.dir=/home/mobsec/sdk

XposedBridge采用gradle编译,如果遇到lint error会终止整个编译.我们可以修改lint error或者修改配置忽略lint error.

在XposedBridge/hiddenapistubs/build.gradle文件中的android段落中,添加lintOptions.这样修改之后,当编译遇到lint error时不会终止编译过程.

apply plugin: 'com.android.library'

android {
    // Only build the release variant
    // add fix
    lintOptions {
        abortOnError false
    }
    variantFilter { variant ->
        if (variant.buildType.name != BuilderConstants.RELEASE) {
            variant.ignore = true
        }
    }
}

准备工作完成,直接使用命令编译Xposed java代码.

cd XposedTools_dir
./build.pl -a java

编译成功输出以下日志.

BUILD SUCCESSFUL

Total time: 0.871 secs

Copying APK to XposedBridge.jar...
Skipping non-existent /home/mobsec/Android/aosp_5.1/XposedBridge/app/build/outputs/apk/app-release.apk
Skipping non-existent /home/mobsec/Android/aosp_5.1/XposedBridge/app/build/outputs/apk/app-release-unaligned.apk
/home/mobsec/Android/aosp_5.1/XposedBridge/app/build/outputs/apk/app-release-unsigned.apk => /home/mobsec/Android/aosp_5.1/out/java/XposedBridge.jar

Done!

1.3.2 编译native层代码

cd XposedTools_dir
./build.pl -t arm:22

依赖注入

发表于 2017-12-21 | 分类于 android |

1 前言

这里讨论的目的一直是 解耦 ,实现 高内聚,低耦合 。从最开始的例子一步步抽丝剥茧,逐渐解耦。直至符合 对新增开发,对修改封闭 这一软件设计原则。

这里有几个名词要先预热一下:

  1. 依赖倒置原则(DIP)

    依赖倒置是面向对象开发领域中的软件设计原则,它倡导上层模块不依赖于底层模块,抽象不依赖细节。良好的面向对象代码设计都要遵从这一原则。

  2. 控制反转(Ioc)

    依赖反转是遵守依赖倒置这个原则而提出来的一种设计模式,它引入了IoC容器的概念。早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了,因为大多数应用程序都是由两个或是更多的类通过彼此的合作来实现企业逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。

  3. 依赖注入(DI)

    依赖注入是为了实现控制反转的一种手段之一。另外一种是依赖查找。

2 何为依赖

什么是依赖?依赖是一种关系,简单来讲就是需要。比如程序员依赖电脑,电脑被程序员依赖。在面向对象编程中,代码可以这样编写。很简单,就是Coder需要电脑,依赖电脑,有了电脑才能工作。

dependency-1.png

class Coder {

    Computer mComputer;

    public Coder () {
        mComputer = new Computer();
    }

    public startWork() {
        mComputer.powerOn();
    }
}

3 依赖倒置原则

依赖倒置(Dependency inversion principle)是面向对象设计领域的一种软件设计原则。具体描述为 上层模块不应该依赖底层模块,他们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象 。总结起来就是要面向接口编程。

回到之前Coder和Computer的例子,假如Coder的公司赚钱了,要给所有Coder的电脑都升级为MacComputer。最直接的方案是新增一个MacComputer类。然后在Coder中new MacComputer。

class Coder {

    // Computer mComputer;
    MacComputer mMacComputer;

    public Coder () {
        // mComputer = new Computer();
        mMacComputer = new MacComputer();
    }

    public startWork() {
        // mComputer.powerOn();
        mMacComputer.powerOn();
    }
}

新增一个MacComputer,改动了三处。明显不符合 对新增开放,对修改关闭 。从划分模块的角度看,这种做法也不符合 依赖倒置原则 。因为Coder属于MacComputer的上层模块。MacComputer为底层模块。存在 上层模块依赖于底层模块 这种情况。

这次,我们对代码做一下改进。新增一个Computable借口,让MacComputer实现Computable接口。Coder中持有一个Computable的实例引用。

dependency-2.png

public interface Computable {
    public void powerOn();
        }

public class Coder {
    Computable mComputer;
    public Coder () {
        mComputer = new MacComputer();
    }

    public startWork() {
        mComputer.powerOn();
    }
}

public class MacComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("Mac power on");
    }
}

再看看改版后的代码,比上一版有所改善。假如当公司又上了一个台阶,要给每个Coder配最新版的MacPro。我们只需要新增一个MacProComputer。并且修改实例化代码mComputer = new MacProComputer即可。改动次数由之前的三次,改为一次。虽然很少,仅有一次。并且这一次只体现为 对象实例化 。

public interface Computable {
    void powerOn();
        }

public class Coder {
    Computable mComputer;
    public Coder () {
        // 唯一改动的一处
        mComputer = new MacProComputer();
    }

    public startWork() {
        mComputer.powerOn();
    }
}

public class MacComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("Mac power on");
    }
}

public class MacProComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("MacPro power on");
    }
}

这次,我们消灭掉对象获取的这一处依赖。通过构造函数传参的方式,获取对象实例。不主动new生成实例。当我们要新增一种电脑类型的时候,并不需要改动Coder类中的任何代码。高层模块和底层模块都依赖于抽象接口Computable。所以,这一版设计符合 开闭原则,依赖倒置原则 。

public interface Computable {
    void powerOn();
        }

public class Coder {
    Computable mComputer;
    public Coder (Computable computable) {
        mComputer = computable;
    }

    public startWork() {
        mComputer.powerOn();
    }
}

public class MacComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("Mac power on");
    }
}

public class MacProComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("MacPro power on");
    }
}

public class Company {
    public void init() {
        Coder coder = new Coder(new MacProComputer());
    }
}

上面的设计称之为 控制反转 。很难理解的一个名词。为什么是反转,对比上一版代码。Coder对于mComputer成员的初始化享有绝对的控制权。因为在Coder中直接new了一个MacProComputer对象,是主动获取。再看看上面的代码,mComputer的获取途径是由构造函数传递进来的参数。Coder是被动获取。所以称之为 控制反转 。丧失了主动控制权。外部负责实例化MacProComputer的类称之为控制反转容器。 事实上,上面的代码展示了基于构造函数的依赖注入达到控制反转的目的。

所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

4 依赖注入

Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

4.1 基于构造函数的依赖注入

上面的例子已经展示过基于构造函数注入依赖。

4.2 基于set方法的依赖注入

public interface Computable {
    void powerOn();
        }

public class Coder {
    Computable mComputer;
    public Coder () {
    }

    public void setComputer(Computable computable) {
        mComputer = computable;
    }

    public startWork() {
        mComputer.powerOn();
    }
}

public class MacComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("Mac power on");
    }
}

public class MacProComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("MacPro power on");
    }
}

public class Company {
    public void init() {
        Coder coder = new Coder();
        coder.setComputer(new MacProComputer());
    }
}

4.3 基于接口的依赖注入

public interface ComputerSetter {
    void set(Computable computable);
}
public interface Computable {
    void powerOn();
        }

public class Coder implements ComputerSetter{
    Computable mComputer;
    public Coder () {
    }

    @Override
    public void set(Computable computable){
        mComputer = computable;
    }
    public void setComputer(Computable computable) {
        mComputer = computable;
    }

    public startWork() {
        mComputer.powerOn();
    }
}

public class MacComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("Mac power on");
    }
}

public class MacProComputer implements Computable {
    @Override
    public void powerOn() {
        System.out.println("MacPro power on");
    }
}

public class Company {
    public void init() {
        Coder coder = new Coder();
        coder.set(new MacProComputer());
    }
}

4.4 基于注解的依赖注入

参考Dagger2。

5 Dagger2

为什么会有Dagger2?当我们的代码遵循 开闭原则,依赖倒置等原则之后 。并把对象实例化的权力移交到外部之后,总归有一处地方(称之为注射器,参考以下代码)是要来实例化对象。更为复杂的是注射器中的实例初始化还要依照一定的先后顺序生成对象。Dagger2的出现进一步解放了我们的双手,让我们不必自己去编写注射器代码,只需要依照Dagger2的约定配置。Dagger2会自动生成相应的注射器。会在适当的时候注入。 所以Dagger2本质上是一个依赖注入框架,依赖注入的目的就是为了给需求方在合适的时候注入依赖。

B b = new B(5);
C c = new C(110,"110");
D d = new D(110, c);
A a = new A();
a.setB(b);
a.setC(c);
a.setD(d);

前门提到过Dagger2是 基于注解 来实现依赖注入。所以,在使用Dagger2之前,我们需要了解这些注解的含义,如果对注解是什么还不清楚的可以自行Google一下。Dagger2中主要有6种注解。前四种通俗易懂,后两种理解起来就有一定难度了。

5.1 @Inject

用来修饰构造函数的时候,表示提供依赖。用来修饰成员变量,表示需要依赖。该变量由注入框架负责实例化。

5.2 @Module

带有此注解的类,用来提供依赖,里面定义一些用@Provides注解的以provide开头的方法,这些方法就是所提供的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖。

5.3 @Component

它是一个桥梁,一端是目标类@Inject,另一端是目标类所依赖类的实例@Module,它也是注入器负责把目标类所依赖类的实例注入到目标类中,同时它也管理Module。

5.4 @Provides

修饰方法,表示该方法提供依赖。

5.5 @Qulifier

当某个对象需要注入依赖时,Dagger2就会根据Module中标记了@Provide的方法的返回值来确定由谁为这个变量提供实例。那问题来了,如果有两个一样的返回类型,该用谁呢。我们把这种场景叫做依赖迷失,见名知意,Dagger这时候就不知道用谁来提供依赖,自然就迷失了。所以我们引入了@Qulifier这个东西,通过自定义Qulifier,可以告诉Dagger2去需找具体的依赖提供者。

5.6 @Scope

Dagger2可以通过自定义Scope注解,来限定通过Module和Inject方式创建的类的实例的生命周期能够与目标类的生命周期相同。或者可以这样理解:通过自定义Scope注解可以更好的管理创建的类实例的生命周期。(需要自己手动控制,并不是加了@Scope就可以自动与目标类的生命周期一致。比如在Activity的OnCreate和OnDestroy中加入Component的创建和销毁代码,就能实现和Activity目标类生命周期一致。另外@Scope还可以用来实现Component中的局部单例。)

5.7 小结

@Inject和@Module(其中的@Provides)都是属于依赖提供方,含有@Inject标注的成员变量的类属于依赖需求方。@Component属于一个桥梁,把依赖需求方和依赖提供方联系起来。@Qulifier用来解决依赖提供方的依赖迷失,@Scope用来解决实例的创建和声明生命周期。

5.8 注入步骤

  1. 查找Module中是否存在创建该类的方法
  2. 若存在创建类方法,查看该方法是否存在参数。
    1. 若存在参数,则按从 步骤1 开始依次初始化每个参数。
    2. 若不存在参数,则直接初始化该类实例,一次依赖注入到此结束。
  3. 若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数。
    1. 若存在参数,则从 步骤1 开始依次初始化每个参数。
    2. 若不存在参数,则直接初始化该类实例,一次依赖注入到此结束。

5.9 Component组织方式

5.9.1 为何要划分

假如一个app(app指的是Android app)中只有一个Component,那这个Component是很难维护、并且变化率是很高,很庞大的,就是因为Component的职责太多了导致的。所以就有必要把这个庞大的Component进行划分,划分为粒度小的Component。

5.9.2 以何种粒度划分

  • 要有一个全局的Component(可以叫ApplicationComponent),负责管理整个app的全局类实例(全局类实例整个app都要用到的类的实例,这些类基本都是单例的,后面会用此词代替)。
  • 每个页面对应一个Component,比如一个Activity页面定义一个Component,一个Fragment定义一个Component。当然这不是必须的,有些页面之间的依赖的类是一样的,可以公用一个Component。

5.9.3 为什么需要页面粒度划分

  • 一个app是由很多个页面组成的,从组成app的角度来看一个页面就是一个完整的最小粒度了。
  • 一个页面的实现其实是要依赖各种类的,可以理解成一个页面把各种依赖的类组织起来共同实现一个大的功能,每个页面都组织着自己的需要依赖的类,一个页面就是一堆类的组织者。
  • 划分粒度不能太小了。假如使用mvp架构搭建app,划分粒度是基于每个页面的m、v、p各自定义Component的,那Component的粒度就太小了,定义这么多的Component,管理、维护就很非常困难。

5.9.4 组织Component

  1. 依赖方式

    一个Component是依赖于一个或多个Component,Component中的dependencies属性就是依赖方式的具体实现。视角在子Component,表明子Component依赖父Component。Component依赖方式只能访问通过Component接口暴露的对象。不能访问父Component的依赖图表。

  2. 包含方式

    一个Component是包含一个或多个Component的,被包含的Component还可以继续包含其他的Component。这种方式特别像Activity与Fragment的关系。SubComponent就是包含方式的具体实现。视角在父Component,表明父Component包含子Component。SubComponent可以访问父Component的所有依赖图表。

  3. 继承方式

    官网没有提到该方式,具体没有提到的原因我觉得应该是,该方式不是解决类实例共享的问题,而是从更好的管理、维护Component的角度,把一些Component共有的方法抽象到一个父类中,然后子Component继承。

基本排序算法

发表于 2017-01-29 | 分类于 algorithm |

1 TODO 排序算法   @home ALGORITHM

1.1 性能指标

  • 效率从高到低
    1. 常数级
    2. 对数级
    3. 次线性级
    4. 线性级
    5. n log(n)
    6. 平方级
    7. 指数级

1.2 排序算法

  • 使用环境
    1. 小规模数据(因为复杂度为O(n平法))
    2. 原本几乎有序(原本几乎有序,在理想状况下,算法复杂度为O(n))

1.3 代码实现

def insert_sort(ary):
    """ 插入法排序 """
    """ 算法复杂度O(n平方)"""
    """ 始终保证A[0,i]是有序的 """
    """ 依次插入值,如果插入的值比左边的小,依次往左移动直至合适位置 """
    sort_ary = [0 for i in range(len(ary))]
    for i in range(len(ary)):
        sort_ary[i] = ary[i]
        for j in range(i, 0, -1):
            if sort_ary[j] < sort_ary[j - 1]:
                tmp = sort_ary[j - 1]
                sort_ary[j - 1] = sort_ary[j]
                sort_ary[j] = tmp
    for i in range(len(sort_ary)):
        ary[i] = sort_ary[i]


def median_sort(ary):
    """ 中值法排序 """
    """ 算法复杂度O(nlog(n)) """
    """ 递归,分治思想 """
    """ 找到中值元素,比中值小的放左边,大于或者等于中值的放右边 """
    """ 递归中值左右子数组 """
    median_sort_internal(ary, 0, len(ary) - 1)


def median_sort_internal(ary, left, right):
    """ 递归函数 """
    if right <= left:
        return
    else:
        mid = (right - left + 1) / 2
        select_kth(ary, left, right, mid)
        median_sort_internal(ary, left, left + mid - 1)
        median_sort_internal(ary, left + mid + 1, right)


def select_kth(ary, left, right, k):
    """ 选择第k大的元素,并分区 """
    idx = left
    index = partition(ary, left, right, idx)
    if (left + k) - 1 == index:
        return index
    else:
        if (left + k - 1) < index:
            return select_kth(ary, left, index - 1, k)
        else:
            return select_kth(ary, index + 1, right, k - (index - left + 1))


def partition(ary, left, right, index):
    """ 分块函数,并返回index对应值在分块完成的下标 """
    store = left
    value = ary[index]
    tmp = ary[right]
    ary[right] = value
    ary[index] = tmp
    for i in range(left, right, +1):
        if ary[i] < value:
            tmp = ary[i]
            ary[i] = ary[store]
            ary[store] = tmp
            store = store + 1
            ary[right] = ary[store]
            ary[store] = value
    return store


def quick_sort(ary, left, right):
    """ 快速排序 """
    """ 快速排序和中值排序的方法几乎一样,区别在于快速排序可以随机选择枢纽值,而不是选择中值 """
    if (left >= right):
        return
    """ 选择最右边的为枢纽值 """
    key = ary[right]
    i = left
    j = left
    for i in xrange(left, right):
        if (ary[i] < key):
            tmp = ary[i]
            ary[i] = ary[j]
            ary[j] = tmp
            j = j + 1
            tmp = ary[j]
            ary[j] = key
            ary[right] = tmp
            quick_sort(ary, left, j - 1)
            quick_sort(ary, j + 1, right)


def bubble_sort(ary):
    """ 冒泡排序 """
    """ 时间复杂度O(n平方) """
    """ 如其名,像气泡一样网上冒 """
    """ 如果a[i]大于a[i+1],则交换.需要进行n-1次扫描. """
    """ 第一次扫描把最大值放到最后,第二次把第二大的值放到最大值前门,依次类推 """
    length = len(ary)
    for i in range(length - 1, -1, -1):
        for j in range(i):
            if ary[j] > ary[j + 1]:
                tmp = ary[j]
                ary[j] = ary[j + 1]
                ary[j + 1] = tmp


def select_sort(ary):
    """ 选择排序 """
    """ 时间复杂度O(n平方) """
    """ 不停选择最大值往最后放,先选完再交换 """
    for i in range(len(ary)):
        index = select_max(ary, len(ary) - i)
        tmp = ary[len(ary) - i - 1]
        ary[len(ary) - i - 1] = ary[index]
        ary[index] = tmp


def select_max(ary, length):
    """ 选择最大值,返回下标 """
    max = ary[length - 1]
    index = length - 1
    for i in range(length):
        if max < ary[i]:
            max = ary[i]
            index = i
    return index


def heap_sort(ary):
    """ 堆排序 """
    """ 时间复杂度O(nlogn),最坏情况也是n(nlogn) """
    """ 可以把堆当做完全二叉树 """
    """ 利用堆的性质,不停添加有序区域,直至全部有序. """
    """ 1. 构造大顶堆 """
    """ 2. 取出根元素和最后一个元素互换. """
    """ 3. 重复执行以上过程 """
    build_heap(ary)
    length = len(ary)
    for i in range(length - 1, -1, -1):
        tmp = ary[0]
        ary[0] = ary[i]
        ary[i] = tmp
        """ 大根堆根顶元素互换,然后重新构造大根堆 """
        heapify(ary, 0, i)


def build_heap(ary):
    """ 初始化堆,从无序堆到大根堆 """
    """ 假设完全二叉树的层级为n,从n-1层开始构造子大根堆,逐渐递减n.直到n为0. """
    length = len(ary) / 2 - 1
    for i in range(length, -1, -1):
        heapify(ary, i, len(ary))


def heapify(ary, idx, max_len):
    """ 调整堆 """
    left = 2 * idx + 1
    right = 2 * idx + 2
    largest = idx
    if left < max_len and ary[left] > ary[idx]:
        largest = left
    if right < max_len and ary[right] > ary[largest]:
        largest = right
    if not largest == idx:
        tmp = ary[idx]
        ary[idx] = ary[largest]
        ary[largest] = tmp
        heapify(ary, largest, max_len)


def counting_sort(ary):
    """ 计数排序 """
    """ 非比较排序,时间复杂度为线性. """
    """ 适合于排序n个元素,元素的取值为[0, k],n远大于k """
    k = ary[0]
    for i in range(len(ary)):
        if ary[i] > k:
            k = ary[i]
            k_ary = [0 for i in range(k + 1)]
            tmp = 0
    for i in range(len(ary)):
        k_ary[ary[i]] += 1
    for i in range(len(k_ary)):
        if k_ary[i] > 0:
            for j in range(k_ary[i]):
                ary[tmp] = i
                tmp += 1


def bucket_sort(ary):
    """ 桶排序 """
    """ 跟计数排序类似 """
    """ 使用hash函数均匀的将n个元素分散到k个桶中,桶必须是有序的,i-1桶中的元素一定都要比i桶中的元素小 """
    """ 每个桶使用插入排序 """
    k = ary[0]
    for i in range(len(ary)):
        if ary[i] > k:
            k = ary[i]
            """ hash(x) = x / 3 """
            bucket_ary = [[] for i in range(k / 3 + 1)]
    for i in range(len(ary)):
        index = ary[i] / 3
        bucket_ary[index].append(ary[i])
        tmp = 0
    for i in range(len(bucket_ary)):
        insert_sort(bucket_ary[i])
        for j in range(len(bucket_ary[i])):
            ary[tmp] = bucket_ary[i][j]
            tmp += 1


ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
insert_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
quick_sort(ARRAY, 0, len(ARRAY) - 1)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
median_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
select_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
bubble_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
heap_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
counting_sort(ARRAY)
print ARRAY
ARRAY = [2, 5, 6, 8, 1, 3, 4, 2, 9, 7]
bucket_sort(ARRAY)
print ARRAY

九宫格问题

发表于 2017-01-29 | 分类于 algorithm |

1 DONE 九宫格问题(幻方)   @office ALGORITHM

参考百度百科,幻方法则

  1. 奇阶幻方(Loubere法)(link) 奇数阶幻方最经典的填法是罗伯特法(也有人称之为楼梯法).填写方法是这样: 把1(或最小的数)放在第一行正中; 按以下规律排列剩下的n*n-1个数:
    1. 每一个数放在前一个数的右上一格;
    2. 如果这个数所要放的格已经超出了顶行那么就把它放在底行,仍然要放在右一列;
    3. 如果这个数所要放的格已经超出了最右列那么就把它放在最左列,仍然要放在上一行;
    4. 如果这个数所要放的格已经超出了顶行且超出了最右列,那么就把它放在前一个数的下一行同一列的格内;
    5. 如果这个数所要放的格已经有数填入,处理方法同(4).
  2. 单偶阶幻方(Strachey法)(link)
    1. 计算 m = (n - 2) / 4
    2. 分割成4个奇幻方,左上为A,右下为B,右上为C,左下为D
    3. 使用罗伯法分别填充A,B,C,D
    4. 在A阵中取左侧m列与D阵对应小格对调,在C阵中取右侧m-1列与B阵对应小格对调(m-1为零不需要对调)
    5. 最后在A阵中间行取中心格与左侧一格与D阵对应小格对调
  3. 双偶阶幻方(Spring法)(link)
    1. 先把数字按顺序填.然后,按n*n把它分割成m*m个小方阵.
    2. 每个小方阵对角线上的数字,换成和它互补的数.
  4. 代码

      def loubere(n, index=1):
          """ 奇幻方楼梯法 """
          """ 1居上行正中央 依次斜填切莫忘 上出框界往下写 右出框时左边放 重复便在下格填 出角重复一个样 """
          half = n / 2
          matrix = [[0 for i in range(n)] for i in range(n)]
        if n % 2 == 1:
            formatprint(matrix)
            matrix[0][half] = index
            i = 0
            j = half
            formatprint(matrix)
            for a in range(index + 1, n * n + index):
                j = j + 1
                i = i - 1
                if i in range(0, n) and j >= n:
                    j = 0
                if i < 0 and j in range(0, n):
                    i = n - 1
                if i < 0 and j >= n:
                    i = i + 2
                    j = j - 1
                if matrix[i][j] != 0:
                    i = i + 2
                    j = j - 1
                    matrix[i][j] = a
                    formatprint(matrix)
        else:
            print "input one odd number"
        return matrix
    
    
    def spring(n):
        """ spring法生成双偶阶幻方 """
        """ 1. 先把数字按顺序填.然后,按n*n把它分割成m*m个小方阵. """
        """ 2. 每个小方阵对角线上的数字,换成和它互补的数. """
        matrix = [[x + y * n + 1 for x in range(n)] for y in range(n)]
        if n % 4 == 0:
            formatprint(matrix)
            m = n / 2
            sum = n * n + 1
            # 对角线条件
            # 行下标 - 列下标 % m = 0 或者 行下标 + 列下标 % m = m - 1
            for i in range(n):
                for j in range(n):
                    if (i - j) % m == 0 or (i + j) % m == m - 1:
                        matrix[i][j] = sum - matrix[i][j]
                        formatprint(matrix)
                        formatprint(matrix)
        else:
            print "input x = n * 4"
        return matrix
    
    
    def strachey(n):
        """ strachey法生成单偶幻方 """
        """ 1. 计算 m = (n - 2) / 4 """
        """ 2. 分割成4个奇幻方,左上为A,右下为B,右上为C,左下为D """
        """ 3. 使用罗伯法分别填充A,B,C,D """
        """ 4. 在A阵中取左侧m列与D阵对应小格对调,中间行取m-1列.在C阵中取右侧m-1列与B阵对应小格对调(m-1为零不需要对调) """
        """ 5. 最后在A阵中间行取中心格与左侧一格与D阵对应小格对调 """
        matrix = [[0 for i in range(n)] for i in range(n)]
        if n % 2 == 0 and not n % 4 == 0:
            m = n / 4
            half = n / 2
            A = loubere(half)
            B = loubere(half, half * half + 1)
            C = loubere(half, half * half * 2 + 1)
            D = loubere(half, half * half * 3 + 1)
            for i in range(half):
                for j in range(half):
                    if i == j and i == half / 2:
                        tmp = A[i][j]
                        A[i][j] = D[i][j]
                        D[i][j] = tmp
                    if j < m:
                        tmp = A[i][j]
                        A[i][j] = D[i][j]
                        D[i][j] = tmp
                        """ 中间行只换m-1列,所以需要换回去 """
                        if i == half / 2 and j == m - 1:
                            tmp = A[i][j]
                            A[i][j] = D[i][j]
                            D[i][j] = tmp
                        if j < m - 1:
                            tmp = C[i][half - 1 - j]
                            C[i][half - 1 - j] = B[i][half - 1 - j]
                            B[i][half - 1 - j] = tmp
            for i in range(n):
                for j in range(n):
                    if i < half and j < half:
                        matrix[i][j] = A[i][j]
                    elif i < half and j >= half:
                        matrix[i][j] = C[i - half][j - half]
                    elif i >= half and j < half:
                        matrix[i][j] = D[i - half][j]
                    else:
                        matrix[i][j] = B[i - half][j - half]
                        formatprint(matrix)
        else:
            print "input x = 4m + 2"
        return matrix
    
    
    def formatprint(array):
        """ 格式化输出 """
        print "--------------------------"
        for i in array:
            print i
            print "--------------------------"
    
    
    def check(matrix):
        """ 幻方检查 """
        sum = 0
        ret = True
        matrix_lenth = len(matrix)
        for i in range(matrix_lenth):
            for j in range(matrix_lenth):
                sum += matrix[i][j]
                sum = sum / matrix_lenth
        for i in range(matrix_lenth):
            row = 0
            col = 0
            for j in range(matrix_lenth):
                row += matrix[i][j]
                col += matrix[j][i]
            if not row == sum or not col == sum:
                ret = False
                break
            line1 = 0
            line2 = 0
        for i in range(matrix_lenth):
            line1 += matrix[i][i]
            line2 += matrix[i][matrix_lenth - i - 1]
        if not line1 == sum or not line2 == sum:
            ret = False
        return ret
    
    
    print check(loubere(3))
    print check(spring(8))
    print check(strachey(10))
    
123
Flysands

Flysands

Keep life stayreal

16 日志
9 分类
14 标签
GitHub E-Mail 微博
© 2016 — 2017 Flysands
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.3