Android Camera系统结构

说到架构/构架就觉得心虚,感觉这两个字太高端了,所以我只敢用结构来表达。

本身我也不是做HAL或者Driver的,我只是写APP的。

出于码农习惯,我会尝试去了解一些比自己用到的深入一点的东西,也比较想向系统这一层开发迈进(哪位高手收了我吧)

以下表述如果存在错误,请指教!

之前有在一篇博客中提到Camera相关的东西
http://guoh.org/lifelog/2012/09/asop-camera-source-code-reading-video-snapshot/

比如

$ANDROID_SRC_HOME/frameworks/base/core/java/android/hardware/Camera.java
$ANDROID_SRC_HOME/frameworks/base/core/jni/android_hardware_Camera.cpp

// 注:目前以下这两个文件在JB当中应该被移动位置了,由此可见Android也是个活跃的项目

$ANDROID_SRC_HOME/frameworks/base/include/camera/Camera.h
$ANDROID_SRC_HOME/frameworks/base/libs/camera/Camera.cpp

这篇博客是阅读http://source.android.com/devices/camera.html,结合在JB代码的基础上写的笔记,可能不是那么详细和有逻辑,只关注重点和要注意的地方,所以如果你刚好读到,但是你又没有什么Android Camera的基础的话,建议还是先看看http://developer.android.com/guide/topics/media/camera.htmlReference

正式开始
大体来说Camera分为这么多部分
APP — JNI — NATIVE — BINDER — CAMERA SERVICE — HAL — DRIVER

下面列出这每一部分对应于AOSP当中的代码

APP($ANDROID_SRC_HOME/frameworks/base/core/java/android/hardware/Camera.java)

JNI($ANDROID_SRC_HOME/frameworks/base/core/jni/android_hardware_Camera.cpp)

NATIVE($ANDROID_SRC_HOME/frameworks/av/camera/Camera.cpp)

BINDER($ANDROID_SRC_HOME/frameworks/av/camera/ICameraService.cpp
       $ANDROID_SRC_HOME/frameworks/av/camera/ICameraClient.cpp)

CAMERA SERVICE($ANDROID_SRC_HOME/frameworks/av/services/camera/libcameraservice/CameraService.cpp)

HAL($ANDROID_SRC_HOME/hardware/libhardware/include/hardware/camera.h
    $ANDROID_SRC_HOME/hardware/libhardware/include/hardware/camera_common.h
    $ANDROID_SRC_HOME/frameworks/av/services/camera/libcameraservice/CameraHardwareInterface.h)

对于Galaxy Nexus来讲,HAL的具体实现位于

$ANDROID_SRC_HOME/hardware/ti/omap4xxx/camera/CameraHal.cpp
$ANDROID_SRC_HOME/hardware/ti/omap4xxx/camera/CameraHalCommon.cpp

头文件位于

$ANDROID_SRC_HOME/hardware/ti/omap4xxx/camera/inc/

对于Galaxy Nexus来讲

DRIVER(https://developers.google.com/android/nexus/drivers)

不知道看了对Camera有没有个大体的认识?
大致了解了的话,就翻代码出来看看自己感兴趣的部分吧

你可以通过repo拉取所有代码到本地看,也可以在线查看。
android/hardware/Camera.java

public static Camera open(int cameraId) {
    return new Camera(cameraId);
}
Camera(int cameraId) {
    mShutterCallback = null;
    mRawImageCallback = null;
    mJpegCallback = null;
    mPreviewCallback = null;
    mPostviewCallback = null;
    mZoomListener = null;

    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }

    native_setup(new WeakReference<Camera>(this), cameraId);
}

以上代码描述了我们所要研究的东西的基础,开启camera,跟所有的硬件一样,使用之前需要先开启。
这里有个需要注意的地方就是mEventHandler,这个变量是做什么用的,文档有解释的比较清楚,如下:

Callbacks from other methods are delivered to the event loop of the
thread which called open(). If this thread has no event loop, then
callbacks are delivered to the main application event loop. If there
is no main application event loop, callbacks are not delivered.

如果你不是写一个玩具程序的话,你就应当注意这里的描述。

随着native_setup(new WeakReference(this), cameraId);,代码走到了
android_hardware_Camera.cpp

// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId)
{
    sp<Camera> camera = Camera::connect(cameraId);

    if (camera == NULL) {
        jniThrowRuntimeException(env, "Fail to connect to camera service");
        return;
    }

    // make sure camera hardware is alive
    if (camera->getStatus() != NO_ERROR) {
        jniThrowRuntimeException(env, "Camera initialization failed");
        return;
    }

    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
        return;
    }

    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong(thiz);
    camera->setListener(context);

    // save context in opaque field
    env->SetIntField(thiz, fields.context, (int)context.get());
}

随着sp camera = Camera::connect(cameraId);,代码走到了
av/include/camera/Camera.h,当然实现都在av/camera/Camera.cpp

sp<Camera> Camera::connect(int cameraId)
{
    ALOGV("connect");
    sp<Camera> c = new Camera();
    const sp<ICameraService>& cs = getCameraService();
    if (cs != 0) {
        c->mCamera = cs->connect(c, cameraId);
    }
    if (c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        c.clear();
    }
    return c;
}

这里getCameraService()是一个比较关键的东西,需要仔细看看。

// establish binder interface to camera service
const sp<ICameraService>& Camera::getCameraService()
{
    Mutex::Autolock _l(mLock);
    if (mCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("media.camera"));
            if (binder != 0)
                break;
            ALOGW("CameraService not published, waiting...");
            usleep(500000); // 0.5 s
        } while(true);
        if (mDeathNotifier == NULL) {
            mDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(mDeathNotifier);
        mCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(mCameraService==0, "no CameraService!?");
    return mCameraService;
}

然后就是av/camera/ICameraService.cpp当中代理类BpCameraService

// connect to camera service
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
{
    Parcel data, reply;
    data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
    data.writeStrongBinder(cameraClient->asBinder());
    data.writeInt32(cameraId);
    remote()->transact(BnCameraService::CONNECT, data, &reply);
    return interface_cast<ICamera>(reply.readStrongBinder());
}

最终转移到BnCameraServiceconnect方法,但是这是个纯虚函数,而且CameraService继承了BnCameraService,所以请看
av/services/camera/libcameraservice/CameraService.h,当然具体实现还是在av/services/camera/libcameraservice/CameraService.cpp当中。

sp<ICamera> CameraService::connect(
        const sp<ICameraClient>& cameraClient, int cameraId) {
    int callingPid = getCallingPid();

    LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);

    if (!mModule) {
        ALOGE("Camera HAL module not loaded");
        return NULL;
    }

    sp<Client> client;
    if (cameraId < 0 || cameraId >= mNumberOfCameras) {
        ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
            callingPid, cameraId);
        return NULL;
    }

    char value[PROPERTY_VALUE_MAX];
    property_get("sys.secpolicy.camera.disabled", value, "0");
    if (strcmp(value, "1") == 0) {
        // Camera is disabled by DevicePolicyManager.
        ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
        return NULL;
    }

    Mutex::Autolock lock(mServiceLock);
    if (mClient[cameraId] != 0) {
        client = mClient[cameraId].promote();
        if (client != 0) {
            if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
                LOG1("CameraService::connect X (pid %d) (the same client)",
                     callingPid);
                return client;
            } else {
                ALOGW("CameraService::connect X (pid %d) rejected (existing client).",
                      callingPid);
                return NULL;
            }
        }
        mClient[cameraId].clear();
    }

    if (mBusy[cameraId]) {
        ALOGW("CameraService::connect X (pid %d) rejected"
                " (camera %d is still busy).", callingPid, cameraId);
        return NULL;
    }

    struct camera_info info;
    if (mModule->get_camera_info(cameraId, &info) != OK) {
        ALOGE("Invalid camera id %d", cameraId);
        return NULL;
    }

    int deviceVersion;
    if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) {
        deviceVersion = info.device_version;
    } else {
        deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
    }

    switch(deviceVersion) {
      case CAMERA_DEVICE_API_VERSION_1_0:
        client = new CameraClient(this, cameraClient, cameraId,
                info.facing, callingPid, getpid());
        break;
      case CAMERA_DEVICE_API_VERSION_2_0:
        client = new Camera2Client(this, cameraClient, cameraId,
                info.facing, callingPid, getpid());
        break;
      default:
        ALOGE("Unknown camera device HAL version: %d", deviceVersion);
        return NULL;
    }

    if (client->initialize(mModule) != OK) {
        return NULL;
    }

    cameraClient->asBinder()->linkToDeath(this);

    mClient[cameraId] = client;
    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
    return client;
}

比如我们这里以HAL 1.0来看,参见
av/services/camera/libcameraservice/CameraClient.h

av/services/camera/libcameraservice/CameraClient.cpp

CameraClient::CameraClient(const sp<CameraService>& cameraService,
        const sp<ICameraClient>& cameraClient,
        int cameraId, int cameraFacing, int clientPid, int servicePid):
        Client(cameraService, cameraClient,
                cameraId, cameraFacing, clientPid, servicePid)
{
    int callingPid = getCallingPid();
    LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId);

    mHardware = NULL;
    mMsgEnabled = 0;
    mSurface = 0;
    mPreviewWindow = 0;
    mDestructionStarted = false;

    // Callback is disabled by default
    mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
    mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
    mPlayShutterSound = true;
    LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
status_t CameraClient::initialize(camera_module_t *module) {
    int callingPid = getCallingPid();
    LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId);

    char camera_device_name[10];
    status_t res;
    snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);

    mHardware = new CameraHardwareInterface(camera_device_name);
    res = mHardware->initialize(&module->common);
    if (res != OK) {
        ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
                __FUNCTION__, mCameraId, strerror(-res), res);
        mHardware.clear();
        return NO_INIT;
    }

    mHardware->setCallbacks(notifyCallback,
            dataCallback,
            dataCallbackTimestamp,
            (void *)mCameraId);

    // Enable zoom, error, focus, and metadata messages by default
    enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
                  CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);

    LOG1("CameraClient::initialize X (pid %d, id %d)", callingPid, mCameraId);
    return OK;
}

当然还会用到
libhardware//include/hardware/camera.h

libhardware//include/hardware/camera_common.h

av/services/camera/libcameraservice/CameraHardwareInterface.h

status_t initialize(hw_module_t *module)
{
    ALOGI("Opening camera %s", mName.string());
    int rc = module->methods->open(module, mName.string(),
                                   (hw_device_t **)&mDevice);
    if (rc != OK) {
        ALOGE("Could not open camera %s: %d", mName.string(), rc);
        return rc;
    }
    initHalPreviewWindow();
    return rc;
}

这只是一个接口,具体的实现是厂商来做的啦

以上是从上层调用到底层,接着我们还会看一个从底层调用到上层的情况。

Posted in Android | Tagged | Leave a comment

在Linux上开启Core dump来调试

这是一篇原来使用Core dump的记录,整理资料的时候看到的,没有深入的分析,只是用法

基本知识不清楚的话,请在网络上搜寻查阅

http://en.wikipedia.org/wiki/Core_dump

http://en.wikipedia.org/wiki/GNU_Debugger

guohai@KNIGHT:~$ ulimit -c
0
guohai@KNIGHT:~$ ulimit -c unlimited
guohai@KNIGHT:~$ ulimit -c
unlimited
guohai@KNIGHT:~$ ./a.out
Floating point exception (core dumped)

a.out是需要分析的程序,以上命令就是Linux上使用方法,很简单

更多情况请参考

http://www.cppblog.com/kongque/archive/2011/03/07/141262.aspx

那么在Android上怎么开启呢(首先得有root权限)?

$ adb remount
$ adb shell
root@android:/ # ulimit -c
unlimited

更改Core dump档案存储的路径(这个存储的路径可以根据需要定制)

root@android:/ # echo "/data/coredump/%p_%e" > /proc/sys/kernel/core_pattern

这样当有native crash存在的时候就会出现对应的Core dump档案了

然后就把档案拷贝到宿主机上,用GDB去载入档案,分析出错的原因

———–EOF———–

Posted in Android, C++, Linux | Leave a comment

Dalvik标记贴

https://android.googlesource.com/platform/dalvik.git

Dalvik

https://android.googlesource.com/platform/dalvik.git/+/master/docs/

Dalvik Docs

http://elinux.org/Android_Dalvik_VM

http://en.wikipedia.org/wiki/Dalvik_(software)

Dalvik, Android’s virtual machine, generates significant debate

DEX文件格式分析

http://hi.baidu.com/seucrcr/item/2b988c570cb63c9208be1778

【翻译】Dalvik——如何控制vm

http://hllvm.group.iteye.com/group/topic/17798

[资料] Dalvik VM的JIT编译器的资料堆积(dumping…work in progress)

Posted in Dalvik | Leave a comment

Run Dalvik on X86

很早以前就想过自己编译一个X86上的Dalvik出来玩玩,尝试之后没有成功,就放下了,最近几个月来又想起来搞搞,按照/PATH/TO/AOSP/dalvik/docs/hello-world.html来编译dalvikvm,创建dex档案,设置各种环境变量之后,运行出现错误

E/dalvikvm(10105): execv '/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/bin/dexopt' failed: No such file or directory
W/dalvikvm(10102): DexOpt: --- END 'Foo.jar' --- status=0x0100, process failed
E/dalvikvm(10102): Unable to extract+optimize DEX from 'Foo.jar'
Dalvik VM unable to locate class 'Foo'
W/dalvikvm(10102): threadid=1: thread exiting with uncaught exception (group=0xf5a1d9c8)
java.lang.NoClassDefFoundError: Foo
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "Foo" on path: DexPathList[[zip file "Foo.jar"],nativeLibraryDirectories=[/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:53)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    ... 1 more

在网络上搜了很久没有出现过这种错误现象的,实在无赖提了个问题
在StackOverflow上,一个星期过去了,还是无人问津

最终请教了一个做Dalvik相关工作的同事,在他的帮助之下解决了问题

原来是我的ANDROID_ROOT写的有问题,可能是我没有理解好/PATH/TO/AOSP/dalvik/docs/hello-world.html的意思,也或者是它的内容比较陈旧。
后来改了就可以正常运行了,我改过的rund请参见https://gist.github.com/guohai/5048153

现在描述下主要过程
1. Download AOSP source code

2. Compile dalvik vm on Ubuntu 11.10 X64

source build/envsetup.sh
lunch 2 # choose full_x86-eng, because we want to run it on X86 directly
make dalvikvm core dexopt ext framework android.policy services

3. Compile Java code and package it to dex.

4. Run & enjoy it.

guohai@KNIGHT:~/dev/src/android/git/aosp$ ./rund -cp Foo.jar Foo
I/dalvikvm( 3473): DexOpt: mismatch dep name: '/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/core.odex' vs. '/system/framework/core.odex'
E/dalvikvm( 3473): /home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/ext.jar odex has stale dependencies
I/dalvikvm( 3473): Zip is good, but no classes.dex inside, and no valid .odex file in the same directory
I/dalvikvm( 3473): DexOpt: mismatch dep name: '/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/core.odex' vs. '/system/framework/core.odex'
E/dalvikvm( 3473): /home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/framework.jar odex has stale dependencies
I/dalvikvm( 3473): Zip is good, but no classes.dex inside, and no valid .odex file in the same directory
I/dalvikvm( 3473): DexOpt: mismatch dep name: '/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/core.odex' vs. '/system/framework/core.odex'
E/dalvikvm( 3473): /home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/android.policy.jar odex has stale dependencies
I/dalvikvm( 3473): Zip is good, but no classes.dex inside, and no valid .odex file in the same directory
I/dalvikvm( 3473): DexOpt: mismatch dep name: '/home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/core.odex' vs. '/system/framework/core.odex'
E/dalvikvm( 3473): /home/guohai/dev/src/android/git/aosp/out/target/product/generic_x86/system/framework/services.jar odex has stale dependencies
I/dalvikvm( 3473): Zip is good, but no classes.dex inside, and no valid .odex file in the same directory
I/dalvikvm( 3473): DexOpt: source file mod time mismatch (4244043d vs 425baf6a)
Hello, Dalvik!

一些对我有些帮助的资料

http://stackoverflow.com/questions/6146983/helloworld-cannot-run-under-dalvikvm

http://stackoverflow.com/questions/11773506/how-to-launch-jar-with-exec-app-process-on-android-ics

http://blog.csdn.net/johnnynuaa/article/details/6543425

附上自己无法运行的启动脚本

#!/bin/sh

# base directory, at top of source tree; replace with absolute path
base=`pwd`

# configure root dir of interesting stuff
root=$base/out/target/product/generic_x86/system
export ANDROID_ROOT=$root #为什么这里不能指到target的路径?

export LD_LIBRARY_PATH=$root/lib:$LD_LIBRARY_PATH

# configure bootclasspath
bootpath=$root/framework
export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/services.jar

# this is where we create the dalvik-cache directory; make sure it exists
export ANDROID_DATA=/tmp/dalvik_$USER
mkdir -p $ANDROID_DATA/dalvik-cache

exec $base/out/host/linux-x86/bin/dalvikvm -Xdexopt:none $@
Posted in Android, Dalvik, Linux | 15 Comments

[菜鸟学C++]拷贝构造函数和赋值运算符

什么是拷贝构造函数,什么时候要用,什么时候需要禁止
http://blog.csdn.net/arssqz/article/details/6361986

为什么要禁止,怎样禁止拷贝构造函数
http://jrdodds.blogs.com/blog/2004/04/disallowing_cop.html
http://c2.com/cgi/wiki?YouArentGonnaNeedIt

编译器存在哪些陷阱
http://www.cnblogs.com/yfanqiu/archive/2012/05/08/2489905.html

Google和Qt的的宏的区别
http://stackoverflow.com/questions/1454407/macros-to-disallow-class-copy-and-assignment-google-vs-qt

典型的Google宏的写法
http://src.chromium.org/svn/trunk/src/third_party/cld/base/macros.h

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
//
// For disallowing only assign or copy, write the code directly, but declare
// the intend in a comment, for example:
// void operator=(const TypeName&);  // DISALLOW_ASSIGN
// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
// semantically, one should either use disallow both or neither. Try to
// avoid these in new code.
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)

主要记录自己碰到的一个问题,编译器显示:

AbstractClass.h:20:12: cannot declare parameter ‘<anonymous>’ to be of abstract type ‘AbstractClass’

其实对于一个比较有经验的人或者理论知识扎实的人,看到这行信息就应该想象到是出现了类似这样的错误

void im_an_error_method(AbstractClass )
{
     ac.im();
 }

参数没有变量名,参数类型是一个抽象类(无法实例化)

可惜我无法确定,因为这个头文件不是我写的,没有足够的证据去指出别人错误的地方。
所以我只能尝试检查自己的代码,结果发现错误的行数是他头文件的

DISALLOW_COPY_AND_ASSIGN(AbstractClass);

这样一行,很奇怪,按道理应该这么写是不应该出错的,因为大家都是这么写的。
于是我尝试将这行注释掉,或者将这行以代码的形式手动展开编译,即这样

AbstractClass(const AbstractClass&);
void operator=(const AbstractClass&);

发现也可以编译通过
想想到这里应该可以看明白为什么编译器错误提示当中有个’anonymous’错误了吧。

最终原因定位在macro有写错,所以在极端情况下就出编译错误了。

最后借用http://www.artima.com/cppsource/bigtwo.html的一个例子来回顾下

class Example {
  SomeResource* p_;
public:
  Example() : p_(new SomeResource()) {}
  ~Example() {
      delete p_;
   }
};

这例子有错误吗?有几处错误?

Posted in C++ | 2 Comments

[菜鸟学C++]抽象类

菜鸟学C++
http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list
理论不懂就实践,实践不行就理论

AbstractClass.h

#ifndef ABSTRACTCLASS_H_
#define ABSTRACTCLASS_H_

class AbstractClass
{
  public:
    AbstractClass();
    virtual ~AbstractClass();

  public:
    virtual void im() = 0;
};
#endif /* ABSTRACTCLASS_H_ */

AbstractClass.cc

#include "AbstractClass.h"

AbstractClass::AbstractClass()
{
}

AbstractClass::~AbstractClass()
{
}

void AbstractClass::im()
{}

Main.cc

#include <iostream>

#include "AbstractClass.h"

using namespace std;

class ConcreteClass : public AbstractClass
{
  public:
    ConcreteClass() {};
    virtual ~ConcreteClass() {};

  public:
    virtual void im()
    {
        cout << "ConcreteClass::im" << endl;
    };
};

void hello(AbstractClass *ac)
{
    ac->im();
}

int main(int argc, char* argv[])
{
    AbstractClass *pAC = new ConcreteClass();
    hello(pAC);
    delete pAC;
    pAC = NULL;
    return 0;
}
~/dev/workspace/labs/ccpp/cc_abstract_class$ g++ Main.cc
/tmp/ccQrfLyb.o: In function `ConcreteClass::ConcreteClass()':
Main.cc:(.text._ZN13ConcreteClassC2Ev[_ZN13ConcreteClassC5Ev]+0x14): undefined reference to `AbstractClass::AbstractClass()'
/tmp/ccQrfLyb.o: In function `ConcreteClass::~ConcreteClass()':
Main.cc:(.text._ZN13ConcreteClassD2Ev[_ZN13ConcreteClassD5Ev]+0x1f): undefined reference to `AbstractClass::~AbstractClass()'
/tmp/ccQrfLyb.o:(.rodata._ZTI13ConcreteClass[typeinfo for ConcreteClass]+0x10): undefined reference to `typeinfo for AbstractClass'
collect2: ld returned 1 exit status

然后这样编译就可以了

~/dev/workspace/labs/ccpp/cc_abstract_class$ g++ Main.cc AbstractClass.cc
Posted in C++ | Comments Off

小结二零一二

这一年最深的体会就是过的很快,一年都在忙碌中读过,忙工作[占了绝大部分的时间],忙自己的事情,周末很少有两天能无所事事的。技术上没有什么太大的长进,想学的东西也没有怎么学,所以说起来今年不算满意。
时常在遇到路人,看他/她们在路边摊,在商场,在餐厅等等地方总能很开心的样子,为什么自己却没有这种心情,不是说自己觉得生活不开心,只是没有觉得高兴的东西,觉得这都似乎很平常。这一年感觉自己有些变化的就是平时说话的样子,非正式时间场合的讲话总能那么随意,不靠谱。以前我一直觉得自己没有啥脾气,不管自己还是和他人合作事情,都似乎总是那么温和,但是今年我发现自己有时候还是会“怒”。我公司老大跟我讲,不要总当老好人,该说NO的时候就要说,这个是我应该学习的地方。
在现在这家公司做了也超过一年了,觉得马马虎虎,可能公司的氛围不大适合我,公司重在员工之间的相互challenge,重在用bug来推动工作的进行[bug不一定要解的,都想先推给别人或者不解,实在是不行了才解],估计很少有人真心的想把这某个东西做好[这不是我自大或者说公司的坏话],不过这也可以理解,公司大了,相互合作的人多了,都有自己的想法。公司有位比较资深的工程师的签名大意就是“这间公司现在很少有人会去考虑一个软件的架构”,我也拜读过一位已离职的工程师2009年写的关于公司问题点的文章,到目前为止个人感觉都还基本适合,不过想想这应该是高级管理层应该去思考的问题吧。(毛病又犯了,又开始挑问题了 T_T,我想我应当才是完美主义者吧,哈哈哈)
今年书到是读了几本,但是不多,技术书籍没有很认真去读。有一本书印象比较深[当然不是技术书籍啦],打工旅行,不知道什么时候能放下所有的东西,出去走走?
明年争取能成为一位Linux下稍微熟练的C/C++码农[折腾了五六年的Java,现在来看C++也不是很难,只是不习惯,觉得不好用,常常怀疑自己会不会成为C++没学好,Java也忘记的那种人],学习Dalvik的基本知识[都想学了N久了,还是没入门]。
多关心下老爸,老妈,他/她们真的开始老了。。。

Posted in Life | Leave a comment

开发高响应的Android应用

网络上从来没有间断过对Android为什么这么慢的质疑,硬件配置高,但用起来就是不顺,顿卡,等等各种现象。

https://plus.google.com/100838276097451809262/posts/VDkV9XaJRGS

http://mobile.163.com/12/1123/05/8GVKOL7D0011309K_all.html

http://www.evolife.cn/html/2011/62866.html

可以参阅以上一些文章

除了Android的framework本身的设计因素外,那还有哪些地方值得我们注意的呢?
1、控制好APP的thread,UI响应的事情放到UI thread(也就是默认的main thread),其他比较耗时或者
跟UI不紧密的操作放到worker thread(这个需要开发者自己设计实现的一个thread,多半是执行和UI没有
什么关系的操作,并最终把需要显示的数据post回给UI thread),一般它需要快速响应来自UI thread的事
件请求,比如添加一个action(action就是UI thread接收到用户的事件之后需要做的动作)进来,删除某个
action,调整action的优先级。

2、不要让APP的thread爆掉,Java语言当中thread的使用很是简单和方便,正是因为好用所以不要滥用。

3、用户离开APP的时候,不需要再执行的操作就不必要再执行了。因为Android kernal支持完全的多进程,
所以用户离开了可能一些操作还是会占用CPU在执行,这是应当避免的。虽然会增加程序开发的复杂度,但是谁让
你做Android开发呢?

4、适当降低worker thread的优先级,这点很容易被人忽视,默认创建出来的thread的优先级和
main thread是一样,如果不降低它也会跟main thread抢CPU

5、友好的loading提示,这一招是心理战,但是也管用。

当然还有你的程序要写的高效率啦,现在很多人都能抱着能运行就好的心态,bug能不解就不解的心态,
这样怎么可能出高质量的APP的呢?高品质的东西是细细打磨出来的。

PS. 以下一些命令或者工具可能是有帮助的
$ adb shell top -m MAX_PROCESS_COUNT
$ adb shell top -m MAX_THREAD_COUNT -t

例如
$ adb shell top -m 20

Systrace参见

http://developer.android.com/tools/help/systrace.html

Traceview参见

http://developer.android.com/tools/help/traceview.html

Posted in Android | Leave a comment

Android ExpandableListView放置不同类型的视图

通常的ExpandableListView的group item是一种视图,child item是另外一种视图,同一种item不会出现多种类型的视图,这是默认的设计。但是我们有时候为了实现更为复杂的界面,就需要ExpandableListView支援同一类型的item放置不同类型的视图。其实做法也很简单,就是Adapter需要实现HeterogeneousExpandableList接口的getChildType,getChildTypeCount,getGroupType和getGroupTypeCount这四个方法。因为ExpandableListView为了效率的因素,它的item的视图是reuse的,如果不指定各个item所用视图的类型,ExpandableListView就会默认采用第一个视图,所以如果不实现这四个方法,呈现出来的界面就是错误的,或者程序直接会crash。详细例子可以参见https://github.com/guohai/and-expandable-listview 。效果如下图:
and-expandable-listview
注意点:getChildTypeCount是用到所有不同类型的child item的总数,不管是同一group item下的不同类型,还是不同group item下的不同类型,只要重复就只算一次。getGroupTypeCount也是如此。
getChildType或者getGroupType是返回位置,所以都是从0开始的,不超过最后一个不重复的child或者group item的位置。

Posted in Android | Leave a comment

Building and Flashing ROM for PandaBoard ES(详细记录版)

之前有写过一篇http://guoh.org/lifelog/2012/06/building-and-flashing-rom-for-pandaboard-es/
那篇只是记录主要的问题,没有把详细过程记录下来,因为我当初也是参考别人的出来了,所以就觉得没有必要把详细的东西记录下来。
很多同学看了那边文章还是弄不出来,于是我答应周末有空的时候再完整做一个这个过程供大家参考。
因为这个板子是裸板,没有什么窗口可以显示当前板子的状态,所以可能这个难住了一些同学。买一个板子配套的扩展板又太贵了,不划算。
所以我建议如果大家实在搞不出来,也不知道原因的话,可以买根Linux下可用的串口线(如果你用Mac OS的话自己酌情处理),结合PuTTY用着包你瞬间明白烧录ROM其实也不难。
下面开始正文:
一,Build ROM
编译需要X64的操作系统,官方也是这么说的
X32位机器出现如下错误
/lib64/ld-linux-x86-64.so.2: No such file or directory

关于所用源码
官方也说了,pandaboard只支持master的code,所以源码需要是master的

我这里是目前最新的代码,2012-09-08这天的代码

repo init -u https://android.googlesource.com/platform/manifest
repo sync
repo forall -c 'git checkout -b master aosp/master'

安装一个patch(源码不同这个patch就不同哦)

https://developers.google.com/android/nexus/drivers#panda

PandaBoard binaries for Android 4.0.4 (IMM76I to IMM76L)

在X64上开始编译

guohai@KNIGHT:~/dev/src/android/git/panda$ source build/envsetup.sh

including device/asus/grouper/vendorsetup.sh
including device/generic/armv7-a-neon/vendorsetup.sh
including device/generic/armv7-a/vendorsetup.sh
including device/moto/wingray/vendorsetup.sh
including device/samsung/crespo4g/vendorsetup.sh
including device/samsung/crespo/vendorsetup.sh
including device/samsung/maguro/vendorsetup.sh
including device/samsung/toro/vendorsetup.sh
including device/sony/lt26/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
including sdk/bash_completion/adb.bash

guohai@KNIGHT:~/dev/src/android/git/panda$ lunch 

You’re building on Linux

Lunch menu… pick a combo:
1. full-eng
2. full_x86-eng
3. vbox_x86-eng
4. full_mips-eng
5. full_grouper-userdebug
6. mini_armv7a_neon-userdebug
7. mini_armv7a-userdebug
8. full_wingray-userdebug
9. full_crespo4g-userdebug
10. full_crespo-userdebug
11. full_maguro-userdebug
12. full_toro-userdebug
13. full_lt26-userdebug
14. full_panda-userdebug

在此我选择
full_panda-userdebug

Which would you like? [full-eng] full_panda-userdebug

============================================
PLATFORM_VERSION_CODENAME=AOSP
PLATFORM_VERSION=4.0.9.99.999.9999.99999
TARGET_PRODUCT=full_panda
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.0.0-25-generic-x86_64-with-Ubuntu-11.10-oneiric
HOST_BUILD_TYPE=release
BUILD_ID=OPENMASTER
OUT_DIR=out
============================================

我用

guohai@KNIGHT:~/dev/src/android/git/panda$ make -j16

编译花了2个多钟头,偶中途去了趟超市,做了顿饭,整个ROM才编译完成,千万不要傻傻的等哦,找点别的事情干干(当然啦,偶尔过来看看有没有什么意外情况出现build failure是可以的,我已经build很多遍了,所以机器上的编译环境一般没有什么问题,比较放心)

直到build完成,出现
Creating filesystem with parameters:
Size: 268435456
Block size: 4096
Blocks per group: 32768
Inodes per group: 8192
Inode size: 256
Journal blocks: 1024
Label:
Blocks: 65536
Block groups: 2
Reserved block group size: 15
Created filesystem with 970/16384 inodes and 41312/65536 blocks
+ ‘[' 0 -ne 0 ']‘
Install system fs image: out/target/product/panda/system.img
out/target/product/panda/system.img+out/target/product/panda/obj/PACKAGING/recovery_patch_intermediates/recovery_from_boot.p maxsize=274053120 blocksize=4224 total=167387526 reserve=2770944
DroidDoc took 1181 sec. to write docs to out/target/common/docs/doc-comment-check

这样我们需要的ROM就准备好了

二,Flash ROM
1、未插入电源的状态下,拔掉SD卡

插入USB线,串口线(可选)

连接电源

2、执行

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ ./usbboot2 ./bootloader.bin 

using built-in 2ndstage.bin
waiting for OMAP44xx device…
reading ASIC ID
CHIP: 4440
IDEN: 0000000000000000000000000000000000000000
MPKH: 0000000000000000000000000000000000000000000000000000000000000000
CRC0: 229e85ba
CRC1: 00000000
sending 2ndstage to target… f0030002
waiting for 2ndstage response…
sending image to target…
looks everything is ok…

注:最后这一句”look everything is ok…”是我自己加在omap4boot源码里的,这里使用的usbboot2就是omap4boot编译出来的

Putty会给出如下反馈
见图01-usbboot.png
01-usbboot

最后一句是Fastboot entered…
就表明板子已经进入了fastboot mode

3、插入SD Card

执行

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot oem format


OKAY [ 0.656s]
finished. total time: 0.656s

Putty会给出如下反馈
见图02-fastboot-oem-format.png
02-fastboot-oem-format

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot flash xloader ./xloader.bin

sending ‘xloader’ (23 KB)…
OKAY [ 0.007s]
writing ‘xloader’…
OKAY [ 0.321s]
finished. total time: 0.328s

见图03-fastboot-flash-xloader.png
03-fastboot-flash-xloader

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot flash bootloader ./bootloader.bin

sending ‘bootloader’ (161 KB)…
OKAY [ 0.015s]
writing ‘bootloader’…
OKAY [ 0.384s]
finished. total time: 0.399s

见图04-fastboot-flash-bootloader.png
04-fastboot-flash-bootloader

验证板子目前是否处于fastboot mode

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot devices

7024000200000001 fastboot

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot erase cache

erasing ‘cache’…
OKAY [103.063s]
finished. total time: 103.063s

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot flash userdata

sending ‘userdata’ (10428 KB)…
OKAY [ 0.585s]
writing ‘userdata’…
OKAY [ 4.786s]
finished. total time: 5.372s

见图05fastboot-erase-cache_flash-userdata.png
05-fastboot-erase-cache_flash-userdata

guohai@KNIGHT:~/dev/src/android/git/panda/device/ti/panda$ fastboot flashall

——————————————–
Bootloader Version…: U-Boot 1.1.4-gedeced79
Baseband Version…..:
Serial Number……..: 7024000200000001
——————————————–
checking product…
OKAY [ 0.001s]
sending ‘boot’ (3750 KB)…
OKAY [ 0.224s]
writing ‘boot’…
OKAY [ 1.993s]
sending ‘recovery’ (4096 KB)…
OKAY [ 0.237s]
writing ‘recovery’…
OKAY [ 2.113s]
sending ‘system’ (163150 KB)…
OKAY [ 9.107s]
writing ‘system’…
OKAY [ 58.941s]
rebooting…

finished. total time: 72.623s

见图06-flash-all.png 07-flash-all-done.png
06-flash-all
07-flash-all-done

到此完成,板子自动重启
稍等一会儿就出现ANDROID的Logo啦

另外据说PLATFORM_VERSION=4.0.9.99.999.9999.99999
就是Jelly Bean,进去之后发现真的就是Jelly Bean

以前编译ICS也是这一样的步骤,只是代码不同,打的patch不同
要参观多图(非高清),请移步http://www.flickr.com/photos/46848122@N08/sets/72157631476114706/

Posted in Android | 3 Comments