2015年6月17日星期三

Android:注册一个方向传感器的回调,能够让app常驻内存不被杀死

一个意外的发现。这能使app的oom_adj即使在后台时也是1(值越小越不容易被杀,一般后台app是7, 后台service是8)


随便什么地方加上一段代码就行了:

?
1
2
3
4
5
6
OrientationEventListener mOrientationListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
    public void onOrientationChanged(int orientation) {
    }
};
if (mOrientationListener.canDetectOrientation())
    mOrientationListener.enable();

进一步设想,换成别的传感器类型也行,没有做跟多的实验,估计会有更少费系统能量的方法。

这个方法比起   startForeground(随便什么整数, new Notification(/*不要参数*/)) 的效果还要好,该方法只能让oom_adj降到2。

2015年6月10日星期三

拒绝ndk-build (Android Native Development Kit)

什么破东西,为了做一个和java毫无关系的lib,exe之类的东西,还非得把代码放在jni目录下。不想忍受这点的人,有轻微强迫症的,或者非要使用configure自动工具的人,可以直接用NDK里的gcc或者toolchain......

Eclipse或者Android Studio里都不能调试C, 实际还得在gdb命令行里做,那这些代码放在Eclipse里毫无用处。虽然据说新的Android Studio里集成了C/C++开发,但是到什么程度还不知道。
废话少说, 
直接用NDK里的gcc是这样的:  (假设NDK_ROOT已经被设定好了)
就是几个环境变量的事儿。
MAX_API_LVL=`cd $NDK_ROOT/platforms && ls -d android-?? | tail -n 1 | sed s/android-//`
SYS_ROOT=$NDK_ROOT/platforms/android-$MAX_API_LVL/arch-arm
TOOL_CHAIN_DIR=`ls -d $NDK_ROOT/toolchains/arm-linux-androideabi-*/prebuilt/* | tail -n 1`
CC="$TOOL_CHAIN_DIR/bin/arm-linux-androideabi-gcc --sysroot=$SYS_ROOT"

CXX="$TOOL_CHAIN_DIR/bin/arm-linux-androideabi-g++ --sysroot=$SYS_ROOT"
CXX="$CXX -fno-rtti -fno-exceptions"

##STL_ROOT=`ls -d $NDK_ROOT/sources/cxx-stl/gnu-libstdc++/?.? | tail -n 1` || exit 1
##CXX="$CXX -I$STL_ROOT/include -I $STL_ROOT/libs/armeabi/include"
第1行是自动取最大Android API Level。可以强制改成14(表示Android4.0)之类的特定值。
第3行里的androideabi-*的*是gcc版本号,自动取最大号。可以强制换成4.8之类的。
STL_ROOT那两行是为了C++的stl库的,用得着时就去掉注释。

然后就可以用$CC和$CXX来编译C和C++ ($CC也能编译C++但是和$CXX有微妙区别)。
$CC a.c
$CXX a.cc
2016/07/28: 最新的[NDK里关于NDK的官方利用说明:Standalone Toolchain](https://developer.android.com/ndk/guides/standalone_toolchain.html) 里也提到了上述简化利用方法,说得更全面。

小菜都可以这样干,只是一些庞大的auto config之类交叉编译的时候,就有include和lib以及附属编译工具误用本机之类的问题,手动硬搞很辛苦。
所以还是得先生成一个正规的交叉编译用的一套东西(toolchain),里面就会包含gcc,include,lib等东西,当使用其中的gcc时,它会自动使用自己身边的include,lib作为系统目录,所以其中的bin/arm-linux-androideabi-gcc等工具可以直接使用而无须末尾指定--sysroot=...参数,配套工具都会自动从自己身旁找,不容易和本机的工具混淆。
正规的交叉编译用的toolchain的生成方法(只一次):例如生成到./std_toolchain目录下。
MAX_API_LVL=`cd $NDK_ROOT/platforms && ls -d android-?? | tail -n 1 | sed s/android-//`
$NDK_ROOT/build/tools/make-standalone-toolchain.sh --install-dir=./std_toolchain --platform=android-$MAX_API_LVL
同理,MAX_API_LVL是自动取得最大的Android API level,不满意的就自己换个固定数值。
2016/07/28: 上面的make...sh报警将要过时,所以最后一句改成make.....py了:
$NDK_ROOT/build/tools/make_standalone_toolchain.py --install-dir ./std_toolchain --arc arm --api $MAX_API_LVL
然后,最好把toolchain里的bin目录加到PATH里。
export PATH="$PWD/std_toolchain/bin:$PATH"
可以测试一下其中的gcc用起来不需要--sysroot=...参数的。
arm-linux-androideabi-gcc  a.c
arm-linux-androideabi-g++  a.cc
然后,该干嘛干嘛。例如,交叉编译ffmpeg:
OTHER_FFMPEG_OPTIONS="--disable-everything --disable-doc --enable-protocol=pipe --enable-filter=scale --enable-filter=crop --enable-filter=transpose --enable-demuxer=rawvideo --enable-decoder=rawvideo --enable-muxer=image2 --enable-muxer=image2pipe --enable-muxer=mjpeg --enable-encoder=mjpeg --enable-encoder=png"

./configure --enable-cross-compile --cross-prefix=arm-linux-androideabi- \
    --target-os=linux --arch=arm \
    $OTHER_FFMPEG_OPTIONS
 注意cross-prefix设定成了"arm-linux-androideabi-",这个才是接口之处,所有的gcc等命令名都会被插入这个前缀。如果不把toolchain的bin目录加到PATH里,那么cross-prefix得加上全路径,例如$PWD/std_toolchain/bin/arm-linux-androideabi-。
显然toolchain方法更加靠谱,不用指定那什么sysroot了。

2016/07/28:发现ndk-build可以通过选项指定不用那破jni目录了。例如:
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
具体的例子在这儿

从Android访问PC端的port (reverse port forwarding)

从Android里想访问PC上或者PC连接的其他的PC的port, 这就是端口反向转接。 正向的呢,adb的forward命令可以让人从PC的port转接到Android。但是反向的只有Android5.0开始才支持,那这时可以在android上安装SshServer,然后在通过ssh tunnel来反向port 转接


大致就是
Device side:

    install SSH Server, add ssh server on 22222 port and user q
    start ssh server
PC side:
    adb forward tcp:22222 tcp:22222    //这是为了能够从PC访问android里的ssh server的22222端口。
    ssh -f -N -R 3000:localhost:3000  q@localhost -p 22222
Then you can use port 3000 in device,
 it will be forwarded to PC's 3000 port


实验了几个其他的sshserver, 都不靠谱,不是死就是反向port forward做的错误。

2015年6月9日星期二

google似乎把ADB输出的回车换行问题改掉了

以前Android的ADB命令输出里总是自动把LF换称CR+LF,注意,不光是Windows OS, Linux也是。导致当你想取得原本的二进制输出时得再反转换一下。到底是什么缘故导致这个转换的?原因有点复杂。完全避免的方法也有。以后(6.0?)该没问题了。


google改动内容在这儿,没有测试过,看起来没问题。
到目前位置,市面上所有的Android设备,都存在这个这个小问题:
在Linux/Mac上执行adb命令输出什么东西时,就算是二进制输出,那么LF(0x0a)字符会被转换成CR(0x0d) + LF。Windows上更可笑,会变成CR+CR+LF。
这个很好验证:
从Android上抓一个文件下来,例如/default.prop, 看看里面应该只有LF(0x0a),而没有CR(0x0d)的。
?
1
adb pull /default.prop     local_output_file1
然后在通过adb shell cat命令输出到本地,看看文件里是不是多了好多CR(0x0d)了。
?
1
adb shell cat /default.prop > local_output_file2
这个不是简单的因为Windows OS的缘故,Linux也出毛病。
解决方法一大堆,例如上述的pull,或者通过socket输出到PC。
其实还有方法可以完全不需要转换,也不需要pull,socket什么的。首先总结原因是两个:
  1. Android那边首先做了LF->CR+LF的转换。具体的在ASOP代码里有,adbd用了pty这东西来做输入输出。很古老的东西,没什么兴趣说,google最近改掉了
  2. Windows OS这边的adb工具更额外做了一层LF->CR+LF的转换。这个到是Windows的默认风格。
解决方法有两个步骤(Linux/Mac只要第一个步骤就行了)
1. 在android那边设定stty -onlcr来禁止发自android那边的转换。
这个也有两个方法,一个是借助于外部工具stty(从busybox里来的),那么
预先把busybox工具放到android里:
adb push busybox /data/local/tmp/
adb shell chmod 755 /data/local/tmp/busybox
然后,那就是在执行自己的命令之前执行busybox stt -onlcr。例如
adb shell '/data/local/tmp/busybox stty -onlcr; cat /default.prop' > local_output_file2
还有一种方法就是自己的C代码里,执行这一段:
?
1
2
3
4
5
6
7
#include <termios.h>
if (isatty(STDOUT_FILENO)) {
    struct termios term;
    tcgetattr(STDOUT_FILENO, &term);
    cfmakeraw(&term);
    tcsetattr(STDOUT_FILENO, TCSANOW, &term);
}
2. Windows这边才需要这个步骤。那就是不直接使用adb工具了,而是直接和adb工具所服务的5037端口打交道,发送命令,取得结果,这个具体的说起来有点啰嗦,如果是nodejs,那么用adbkit好了,其他的我没多看,大致就是
?
1
2
3
4
5
1. 向localhost:5037 发送host:transport:设备序列号
    得到回答,如果是OKAY四个字那就到step2,否则就错误。 
2. 继续向上述port发送shell:命令内容
    得到回答,如果是OKAY四个字那就到step2,否则就错误。 
然后从这个连接里能够读到原始的输出。