简世博客

一个简单的世界——博客空间,写了一些Android相关的技术文章,和一些点滴的想法

0%

CPU时间

进程时间也称CPU时间,用以度量进程使用的中央处理器资源。进程时间以时钟嘀嗒计算,实际时间(Real),用户CPU时间(User),系统CPU时间(Sys)

实际时间指实际流逝的时间;用户时间和系统时间指特定进程使用的CPU时间:

real time是从进行开始执行到完成所经历的墙上时钟时间(wall clock)时间,包括其他进程使用的时间片(time slice)和本进程耗费在阻塞(如等待I/O操作完成)上的时间。
user time是进程执行用户态代码(内核外)耗费的CPU时间,仅统计该进程执行时实际使用的CPU时间,而不计入其他进程使用的时间片和本进程阻塞的时间
sys time 是该进程在内核态运行所耗费的CPU时间,即内核执行系统调用所使用的CPU时间

CPU总时间(user + sys)是CPU执行用户进程操作和内核(代表用户进程执行)系统调用所耗时间的总和,即该进程(包括线程和子进程)所使用的实际CPU时间。若程序循环遍历数组,则增加用户CPU时间;若程序执行exec或fork等系统调用,则增加系统CPU时间。
在多核处理器机器上,若进程含有多个线程或通过fork调用创建子进程,则实际时间可能小于CPU总时间,因为不同线程或进程可并行执行,但其时间会计入主进程的CPU总时间。若程序在某段时间处于等待状态而并未执行,则实际时间可能大于CPU总时间:
real < CPU 表明进程为计算密集型(CPU bound),利用多核处理器的并行执行优势
real ≈ CPU 表明进程为计算密集型,未并行执行
real > CPU 表明进程为I/O密集型 (I/O bound),多核并行执行优势并不明显

作为一个android开发,快速查看android源码是非常重要的辅助手段,在这篇博客里列举一一些常用的看android源码的方式,有时候还需要几种方式混合使用。

我在工作中一般是本地下载完整的源码,在androidStudio中导入源码中常用的项目,遇到需要查找的时候用本地搜索+在线OpenGrok一起查找。


在线看android源码:


1,http://androidxref.com (基于OpenGrok,速度快,方便)

2,http://www.grepcode.com/(可以看除android外其他源码,但是android源码版本很低,没有最新android源码)

3,Android SDK Search插件(地址

4. Android 官方代码库 goolesource  (官方平台,然而经常卡,翻完了墙还是卡)

https://android-review.googlesource.com/ (android Code Review 平台, 但是并没有什么卵用,google 只能看到一些外部开发者向google提patch的记录,内部提交记录这上面看不到)

https://android.googlesource.com/ (android官方的代码查看平台,也可以在这里git clone 某个项目,然后本地阅读)





下载 android 完整源码:


官方代码下载平台:https://source.android.com/source/downloading

国内镜像平台: 

清华镜像:https://mirror.tuna.tsinghua.edu.cn/help/AOSP/

科大镜像:https://lug.ustc.edu.cn/wiki/mirrors/help/aosp


android7.0上已经支持分屏,但是国内有的手机虽然升到了7.0,但是还不能使用分屏功能。

在这种情况下,可以使用命令启动分屏, 并且是无需root的。

首先要先连接adb,不会连接的请自行百度。


之后打开命令行,输入命令 adb shell am start -n 应用的包名/Activity名称  --stack 3


比如打开哔哩哔哩 就是adb shell am start -n tv.danmaku.bili/.ui.splash.SplashActivity --stack 3


如果不知道包名/Activity名称 可以这样做:1打开想要启动的应用,2 输入命令 adb shell dumpsys activity activities

然后做安卓开发的朋友应该就能看到包名/Activity名称了。

如果不是业内人士,可以在输入该命令后,在输出里找 mFocusedActivity: 这一行。

比如  mFocusedActivity: ActivityRecord{1978f4d u0 com.android.settings/.MainSettings t815}

这里的com.android.settings/.MainSettings 就代表 设置的包名/Activity名称。


另外注意,android里有一个bug,用这个命令打开分屏时,启动的这个应用必须是完全关闭的,也就是从最近任务里关闭掉,并且没有后台运行的,否则手机会出现一系列bug,严重的时候只能重启才能重新进分屏,到现在为止,谷歌团队貌似也没有修复这个问题,或许用命令打开分屏人家本来就没打算让用户使用呢。

转自:点击打开链接

“好的文章不是写出来的,而是改出来的。” 代码提交也是如此。

程序员写完代码,往往迫不及待地敲下:git commit,然后发现提交中少了一个文件,或者提交了多余的文件,或者发现提交中包含错误无法编译,或者提交说明中出现了错别字。

Git 提供了一个修改当前提交的快捷命令:git commit --amend,相信很多人都用过,不再赘述。

如果你发现错误出现在上一个提交或其他历史提交中怎么办呢?我有一个小窍门,在《Git权威指南》里我没有写到哦。

比如发现历史提交 54321 中包含错误,直接在当前工作区中针对这个错误进行修改,然后执行下面命令。

git commit --fixup 54321

你会发现使用了 --fixup 参数的提交命令,不再询问你提交说明怎么写,而是直接把错误提交 54321 的提交说明的第一行拿来,在前面增加一个前缀“fixup!”,如下:

fixup! 原提交说明

如果一次没有改对,还可以再接着改,甚至你还可以针对这个修正提交进行 fixup,产生如下格式的提交说明:

fixup! fixup! 原提交说明

当开发工作完成后,待推送/评审的提交中出现大量的包含“fixup!”前缀的提交该如何处理呢?

如果你执行过一次下面的命令,即针对错误提交 54321 及其后面所有提交执行交互式变基(注意其中的 --autosquash 参数),你就会惊叹 Git 设计的是这么巧妙:

$ git rebase -i --autosquash 54321^

交互式变基弹出的编辑器内自动对提交进行排序,将提交 54321 连同它的所有修正提交压缩为一个提交。

二分查找

想必大家都玩过猜数字游戏吧:一个人在1到100的数字中随意选择一个,另外一个人来猜,小孩子总是一个挨着一个地猜, 懂得折半查找的大人总是获胜者。Git 提供的 git bisect 这一命令,就是采用这样的二分查找快速地在提交中定位 Bug, 少则几次,多则十几次就会定位到引入Bug的提交。

  1. 首先执行下面命令启用二分查找。

     $ git bisect start
    
  2. 标记一个好版本。下面的命令使用 tag(v2.7.0)来标记 Git 2.7.0 版本是好版本,换做40位的提交号也行。

     $ git bisect good v2.7.0
    
  3. 标记 Git 2.8.0-rc0 是一个坏版本。注意:马上就是见证奇迹的时刻。

     $ git bisect bad v2.8.0-rc0
     Bisecting: 297 revisions left to test after this (roughly 8 steps)
     [563e38491eaee6e02643a22c9503d4f774d6c5be] Fifth batch for 2.8 cycle
    

    看到了么?当完成对一个好版本和一个坏版本的标记后,Git 切换到一个中间版本(563e384),并告诉我们大概需要8步可以找到元凶。

  4. 在这个版本下执行前面的测试操作:

     $ make -j8 && make install //编译项目
     $ check //测试是否有问题
     found error! //发现确实有问题
    
  5. 对这个版本进行标记。

    这是一个坏版本:

     $ git bisect bad
     Bisecting: 126 revisions left to test after this (roughly 7 steps)
     [e572fef9d459497de2bd719747d5625a27c9b41d] Merge branch 'ep/shell-command-substitution-style'
    

我们可以机械地重复上面4、5的步骤,直到最终定位。但是人工操作很容易出错。如果对版本标记错了,把 good 写成了 bad 或者相反, 就要执行 git bisect reset 重来。(小窍门:git bisect log 可以显示 git bisect 标记操作日志)


转自:点击打开链接

我是做安卓ROM开发的,每个安卓Rom的代码,不包括编译后生成的文件,就接近100G,每次下载都需要很久,需要在几个机型上工作时,多下载几份代码,电脑也很快就硬盘空间不足。

在此整理积累一些repo git 同步超大的工程太慢的解决方法。


一  


--depth 1 这个参数可以在git clone 和 repo init 的时候使用, 这样同步的时候不会同步历史记录。


二 


repo sync 用-c, --current-branch来指定只抓取特定分支的代码 !

  -l, --local-only      only update working tree, don't fetch
  -n, --network-only    fetch only, don't update working tree
  -c, --current-branch  fetch only current branch from server



可以用一套代码切换分支来对不同机型操作,然而实际过程中,总是会遇到各种各样的问题。




可以参考这篇文章:点击打开链接


首先贴一张网上疯传的错误的图

这里写图片描述

然后才是正确的图

这里写图片描述


官方demo能出现这个效果原因是apiDemo里面的代码动了手脚。


具体的分析已经有大神分析过了,抄袭人家的也不好,链接贴出来


Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解

摘录android的官方注释


/**
 * Make this drawable mutable. This operation cannot be reversed. A mutable
 * drawable is guaranteed to not share its state with any other drawable.
 * This is especially useful when you need to modify properties of drawables
 * loaded from resources. By default, all drawables instances loaded from
 * the same resource share a common state; if you modify the state of one
 * instance, all the other instances will receive the same modification.
 *
 * Calling this method on a mutable Drawable will have no effect.
 *
 * @return This drawable.
 * @see ConstantState
 * @see #getConstantState()
 */
public Drawable mutate() {
    return this;
}

上面的注释已经说的很明白了:让这个draw变成一个可变的drawable。什么是可变的drawable呢?


android中从同一个资源文件中加载出来的drawable会共享状态,如果你加载出来多个drawable,当改变了其中一个的状态时,其他drawable的状态也会相应改变。

如果把这个drawable变为mutate drawable后,这个drawable就不会与其他drawable共享状态。

特别注意,这个mutate操作是不可逆转的。


记录一下一个非常好的shell学习的网站

http://c.biancheng.net/cpp/shell/


在网上搜索很久,终于找到一个很好用的反射工具类,略微修改后,终于可以正常使用了。

注意:混淆后的代码不能用反射调用。


package com.android.systemui.recents.misc;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectUtil {

/**
 * 使用反射调用方法
 *
 * @param o 被调用对象
 * @param methodName 被调用对象的方法名称
 * @param args 被调用方法所需传入的参数列表
 */
public static Object callMethod(Object o,String methodName,Object... args) {
    Object result = null;
    try {
        int argslen = args.length;
        Class c = o.getClass();
        Method m = null;

        Class&lt;?&gt; argsTypes[] = new Class&lt;?&gt;[argslen];
        //基本类型在参数传递过程中会自动封住成对象类型,这里还原成基本类型的Class
        for (int i=0;i&lt;argslen;i++) {
            argsTypes[i] = args[i].getClass();
            if (argsTypes[i] == Integer.class) {
                argsTypes[i] = int.class;
            }
            if (argsTypes[i] == Float.class) {
                argsTypes[i] = float.class;
            }
            if (argsTypes[i] == Long.class) {
                argsTypes[i] = long.class;
            }
            if (argsTypes[i] == Byte.class) {
                argsTypes[i] = byte.class;
            }
            if (argsTypes[i] == Double.class) {
                argsTypes[i] = double.class;
            }
            if (argsTypes[i] == Boolean.class) {
                argsTypes[i] = boolean.class;
            }
        }

        if (argslen == 0) {
            m = c.getDeclaredMethod(methodName);
        } else {
            Method[] methodes = c.getDeclaredMethods();
            for (Method method: methodes) {
                Class&lt;?&gt;[] parameterTypes = method.getParameterTypes();
                if (method.getName().equals(methodName) &amp;&amp; parameterTypes.length == argslen) {
                    int i;
                    for (i=0;i&lt;argslen;i++) {
                        if (argsTypes[i] != parameterTypes[i]) {
                            break;
                        }
                    }
                    if (i == argslen) {
                        m = method;
                        break;
                    }
                }
            }
        }
        if (m == null) {
            String argsName = Arrays.toString(argsTypes).replace(&quot;[&quot;, &quot;(&quot;).replace(&quot;]&quot;, &quot;)&quot;);
            throw new NoSuchMethodException(methodName + argsName);
        }
        m.setAccessible(true);
        result = m.invoke(o,args);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return result;
}

/**
 * 使用反射设置变量值
 *
 * @param target 被调用对象
 * @param fieldName 被调用对象的字段,一般是成员变量或静态变量,不可是常量!
 * @param value 值
 * @param &lt;T&gt; value类型,泛型
 */
public static &lt;T&gt; void setValue(Object target,String fieldName,T value) {
    try {
        Class c = target.getClass();
        Field f = c.getDeclaredField(fieldName);
        f.setAccessible(true);
        f.set(target, value);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

/**
 * 使用反射获取变量值
 *
 * @param target 被调用对象
 * @param fieldName 被调用对象的字段,一般是成员变量或静态变量,不可以是常量
 * @param &lt;T&gt; 返回类型,泛型
 * @return 值
 */
public static &lt;T&gt; T getValue(Object target,String fieldName) {
    T value = null;
    try {
        Class c = target.getClass();
        Field f = c.getDeclaredField(fieldName);
        f.setAccessible(true);
        value = (T) f.get(target);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return value;
}

}