当前位置:文档之家› Android深入浅出之AudioTrack

Android深入浅出之AudioTrack

Android深入浅出之AudioTrack
Android深入浅出之AudioTrack

Android深入浅出之AudioTrack

第一部分 AudioTrack分析

一目的

本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。

分析的流程是:

●先从API层对应的某个类开始,用户层先要有一个简单的使用流程。

●根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或

者第一次见到的类或者方法,都会解释。也就是深度优先的方法。

1.1 分析工具

分析工具很简单,就是sourceinsight和android的API doc文档。当然还得有android的源代码。我这里是基于froyo的源码。

注意,froyo源码太多了,不要一股脑的加入到sourceinsight中,只要把framwork目录下的源码加进去就可以了,后续如要用的话,再加别的目录。

二Audio系统

先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:

●AudioManager:这个主要是用来管理Audio系统的

●AudioTrack:这个主要是用来播放声音的

●AudioRecord:这个主要是用来录音的

其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。一般看来,最简单的就是播放声音了。所以我们打算从AudioTrack开始分析。

三AudioTrack(JAVA层)

JAVA的AudioTrack类的代码在:

framework/base/media/java/android/media/AudioTrack.java中。

3.1 AudioTrack API的使用例子

先看看使用例子,然后跟进去分析。至于AudioTrack的其他使用方法和说明,需要大家自己去看API文档了。

//根据采样率,采样精度,单双声道来得到frame的大小。

int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点

AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道

AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节

//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。

//创建AudioTrack

AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,

AudioFormat.CHANNEL_CONFIGURATION_ STEREO,

AudioFormat.ENCODING_PCM_16BIT,

bufsize,

AudioTrack.MODE_STREAM);//

trackplayer.play() ;//开始

trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据

….

trackplayer.stop();//停止播放

trackplayer.release();//释放底层资源。

这里需要解释下两个东西:

1 AudioTrack.MODE_STREAM的意思:

AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。

这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。

而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。

这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。

2 StreamType

这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。

Android将系统的声音分为以下几类常见的(未写全):

●STREAM_ALARM:警告声

●STREAM_MUSCI:音乐声,例如music等

●STREAM_RING:铃声

●STREAM_SYSTEM:系统声音

●STREAM_VOCIE_CALL:电话声音

为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。

其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。

3.2 分析之getMinBufferSize

AudioTrack的例子就几个函数。先看看第一个函数:

AudioTrack.getMinBufferSize(8000,//每秒8K个点

AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道

AudioFormat.ENCODING_PCM_16BIT);

----->AudioTrack.JAVA

//注意,这是个static函数

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {

int channelCount = 0;

switch(channelConfig) {

case AudioFormat.CHANNEL_OUT_MONO:

case AudioFormat.CHANNEL_CONFIGURATION_MONO:

channelCount = 1;

break;

case AudioFormat.CHANNEL_OUT_STEREO:

case AudioFormat.CHANNEL_CONFIGURATION_STEREO:

channelCount = 2;--->看到了吧,外面名字搞得这么酷,其实就是指声道数

break;

default:

loge("getMinBufferSize(): Invalid channel configuration.");

return AudioTrack.ERROR_BAD_VALUE;

}

//目前只支持PCM8和PCM16精度的音频

if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)

&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {

loge("getMinBufferSize(): Invalid audio format.");

return AudioTrack.ERROR_BAD_VALUE;

}

//ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间

if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {

loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");

return AudioTrack.ERROR_BAD_VALUE;

}

//调用native函数,够烦的,什么事情都搞到JNI层去。

int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if ((size == -1) || (size == 0)) {

loge("getMinBufferSize(): error querying hardware");

return AudioTrack.ERROR;

}

else {

return size;

}

native_get_min_buff_size--->在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数

static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, jint sampleRateInHertz, jint nbChannels, jint audioFormat)

{//注意我们传入的参数是:

//sampleRateInHertz = 8000

//nbChannels = 2;

//audioFormat = AudioFormat.ENCODING_PCM_16BIT

int afSamplingRate;

int afFrameCount;

uint32_t afLatency;

//下面涉及到AudioSystem,这里先不解释了,

//反正知道从AudioSystem那查询了一些信息

if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {

return -1;

}

if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {

return -1;

}

if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {

return -1;

}

//音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中

//找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来

//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。

// Ensure that buffer depth covers at least audio hardware latency

uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);

if (minBufCount < 2) minBufCount = 2;

uint32_t minFrameCount =

(afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;

//下面根据最小的framecount计算最小的buffersize

int minBuffSize = minFrameCount

* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)

* nbChannels;

return minBuffSize;

}

getMinBufSize函数完了后,我们得到一个满足最小要求的缓冲区大小。这样用户分配缓冲区就有了依据。下面就需要创建AudioTrack对象了

3.3 分析之new AudioTrack

先看看调用函数:

AudioTrack trackplayer = new AudioTrack(

AudioManager.STREAM_MUSIC,

8000,

AudioFormat.CHANNEL_CONFIGURATION_ STEREO,

AudioFormat.ENCODING_PCM_16BIT,

bufsize,

AudioTrack.MODE_STREAM);//

其实现代码在AudioTrack.java中。

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,

int bufferSizeInBytes, int mode)

throws IllegalArgumentException {

mState = STATE_UNINITIALIZED;

// 获得主线程的Looper,这个在MediaScanner分析中已经讲过了

if ((mInitializationLooper = Looper.myLooper()) == null) {

mInitializationLooper = Looper.getMainLooper();

}

//检查参数是否合法之类的,可以不管它

audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);

//我是用getMinBufsize得到的大小,总不会出错吧?

audioBuffSizeCheck(bufferSizeInBytes);

// 调用native层的native_setup,把自己的WeakReference传进去了

//不了解JAVA WeakReference的可以上网自己查一下,很简单的

int initResult = native_setup(new WeakReference(this),

mStreamType, 这个值是AudioManager.STREAM_MUSIC

mSampleRate, 这个值是8000

mChannels, 这个值是2

mAudioFormat,这个值是AudioFormat.ENCODING_PCM_16BIT

mNativeBufferSizeInBytes, //这个是刚才getMinBufSize得到的

mDataLoadMode);DataLoadMode是MODE_STREAM

....

}

上面函数调用最终进入了JNI层android_media_AudioTrack.cpp下面的函数static int

android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,

jint streamType, jint sampleRateInHertz, jint channels,

jint audioFormat, jint buffSizeInBytes, jint memoryMode)

{

int afSampleRate;

int afFrameCount;

下面又要调用一堆东西,烦不烦呐?具体干什么用的,以后分析到AudioSystem再说。

AudioSystem::getOutputFrameCount(&afFrameCount, streamType);

AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);

AudioSystem::isOutputChannel(channels);

popCount是统计一个整数中有多少位为1的算法

int nbChannels = AudioSystem::popCount(channels);

if (streamType == javaAudioTrackFields.STREAM_MUSIC) {

atStreamType = AudioSystem::MUSIC;

}

int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;

int format = audioFormat == javaAudioTrackFields.PCM16 ?

AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;

int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);

//上面是根据Buffer大小和一个Frame大小来计算帧数的。

// AudioTrackJniStorage,就是一个保存一些数据的地方,这

//里边有一些有用的知识,下面再详细解释

AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();

jclass clazz = env->GetObjectClass(thiz);

lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);

lpJniStorage->mStreamType = atStreamType;

//创建真正的AudioTrack对象

AudioTrack* lpTrack = new AudioTrack();

if (memoryMode == javaAudioTrackFields.MODE_STREAM) {

//如果是STREAM流方式的话,把刚才那些参数设进去

lpTrack->set(

atStreamType,// stream type

sampleRateInHertz,

format,// word length, PCM

channels,

frameCount,

0,// flags

audioCallback,

&(lpJniStorage->mCallbackData),//callback, callback data (user)

0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack

0,// 共享内存,STREAM模式需要用户一次次写,所以就不用共享内存了

true);// thread can call Java

} else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {

//如果是static模式,需要用户一次性把数据写进去,然后

//再由audioTrack自己去把数据读出来,所以需要一个共享内存

//这里的共享内存是指C++AudioTrack和AudioFlinger之间共享的内容

//因为真正播放的工作是由AudioFlinger来完成的。

lpJniStorage->allocSharedMem(buffSizeInBytes);

lpTrack->set(

atStreamType,// stream type

sampleRateInHertz,

format,// word length, PCM

channels,

frameCount,

0,// flags

audioCallback,

&(lpJniStorage->mCallbackData),//callback, callback data (user));

0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack

lpJniStorage->mMemBase,// shared mem

true);// thread can call Java

}

if (lpTrack->initCheck() != NO_ERROR) {

LOGE("Error initializing AudioTrack");

goto native_init_failure;

}

//又来这一招,把C++AudioTrack对象指针保存到JAVA对象的一个变量中

//这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了。

env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);

env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);

}

1 AudioTrackJniStorage详解

这个类其实就是一个辅助类,但是里边有一些知识很重要,尤其是Android封装的一套共享内存的机制。这里一并讲解,把这块搞清楚了,我们就能轻松得在两个进程间进行内存的拷贝。AudioTrackJniStorage的代码很简单。

struct audiotrack_callback_cookie {

jclass audioTrack_class;

jobject audioTrack_ref;

}; cookie其实就是把JAVA中的一些东西保存了下,没什么特别的意义

class AudioTrackJniStorage {

public:

sp mMemHeap;//这两个Memory很重要

sp mMemBase;

audiotrack_callback_cookie mCallbackData;

int mStreamType;

bool allocSharedMem(int sizeInBytes) {

mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");

mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);

//注意用法,先弄一个HeapBase,再把HeapBase传入到MemoryBase中去。

return true;

}

};

2 MemoryHeapBase

MemroyHeapBase也是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端(Bnxxx),一个代理端Bpxxx。看看MemoryHeapBase定义:

class MemoryHeapBase : public virtual BnMemoryHeap

{

果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了

//Bp端调用的函数最终都会调到Bn这来

对Binder机制不了解的,可以参考:

https://www.doczj.com/doc/128358923.html,/Innost/archive/2011/01/08/6124685.aspx

有好几个构造函数,我们看看我们使用的:

MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)

: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),

mDevice(0), mNeedUnmap(false)

{

const size_t pagesize = getpagesize();

size = ((size + pagesize-1) & ~(pagesize-1));

//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它

//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件

int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);

mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存

//不明白得去man mmap看看

mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是

ashmem_create_region返回的文件描述符

}

MemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。

getBaseID()--->返回mFd,如果为负数,表明刚才创建共享内存失败了

getBase()->返回mBase,内存位置

getSize()->返回mSize,内存大小

有了MemoryHeapBase,又搞了一个MemoryBase,这又是一个和Binder机制挂钩的类。

唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset

那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类

这样就不用用户到处去计算读写位置了。

class MemoryBase : public BnMemory

{

public:

MemoryBase(const sp& heap, ssize_t offset, size_t size);

virtual sp getMemory(ssize_t* offset, size_t* size) const;

protected:

size_t getSize() const { return mSize; }

ssize_t getOffset() const { return mOffset; }

const sp& getHeap() const { return mHeap; }

};

好了,明白上面两个MemoryXXX,我们可以猜测下大概的使用方法了。

●BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,

●然后把BnMemoryBase传递到BpXXX

●BpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且Android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。

另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。

3.4 分析之play和write

JAVA层到这一步后就是调用play和write了。JAVA层这两个函数没什么内容,都是直接转到native层干活了。

先看看play函数对应的JNI函数

static void

android_media_AudioTrack_start(JNIEnv *env, jobject thiz)

{

//看见没,从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针

//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改!

AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(

thiz, javaAudioTrackFields.nativeTrackInJavaObj);

lpTrack->start(); //这个以后再说

}

下面是write。我们写的是short数组,

static jint

android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz,

jshortArray javaAudioData,

jint offsetInShorts,

jint sizeInShorts,

jint javaAudioFormat) {

return (android_media_AudioTrack_native_write(env, thiz,

(jbyteArray) javaAudioData,

offsetInShorts*2, sizeInShorts*2, javaAudioFormat)

/ 2);

}

烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去

jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,

jint offsetInBytes, jint sizeInBytes) {

ssize_t written = 0;

// regular write() or copy the data to the AudioTrack's shared memory?

if (pTrack->sharedBuffer() == 0) {

//创建的是流的方式,所以没有共享内存在track中

//还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建

//共享内存

written = pTrack->write(data + offsetInBytes, sizeInBytes);

} else {

if (audioFormat == javaAudioTrackFields.PCM16) {

// writing to shared memory, check for capacity

if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {

sizeInBytes = pTrack->sharedBuffer()->size();

}

//看见没?STATIC模式的,就直接把数据拷贝到共享内存里

//当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的//共享设进去的

memcpy(pTrack->sharedBuffer()->pointer(),

data + offsetInBytes, sizeInBytes);

written = sizeInBytes;

} else if (audioFormat == javaAudioTrackFields.PCM8) {

PCM8格式的要先转换成PCM16

}

return written;

}

到这里,似乎很简单啊,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。反正JNI这层是再看不出什么有意思的东西了。

四AudioTrack(C++层)

接上面的内容,我们知道在JNI层,有以下几个步骤:

●new了一个AudioTrack

●调用set函数,把AudioTrackJniStorage等信息传进去

●调用了AudioTrack的start函数

●调用AudioTrack的write函数

那么,我们就看看真正干活的的C++AudioTrack吧。

AudioTrack.cpp位于framework/base/libmedia/AudioTrack.cpp

4.1 new AudioTrack()和set调用

JNI层调用的是最简单的构造函数:

AudioTrack::AudioTrack()

: mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。

{

}

接下来调用set。我们看看JNI那set了什么

lpTrack->set(

atStreamType, //应该是Music吧

sampleRateInHertz,//8000

format,// 应该是PCM_16吧

channels,//立体声=2

frameCount,//

0,// flags

audioCallback, //JNI中的一个回调函数

&(lpJniStorage->mCallbackData),//回调函数的参数

0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上

0,//共享buffer地址,stream模式没有

true);//回调线程可以调JAVA的东西

那我们看看set函数把。

status_t AudioTrack::set(

uint32_t sampleRate,

int format,

int channels,

int frameCount,

uint32_t flags,

callback_t cbf,

void* user,

int notificationFrames,

const sp& sharedBuffer,

bool threadCanCallJava)

{

...前面一堆的判断,等以后讲AudioSystem再说

audio_io_handle_t output =

AudioSystem::getOutput((AudioSystem::stream_type)streamType,

sampleRate, format, channels, (AudioSystem::output_flags)flags);

//createTrack?看来这是真正干活的

status_t status = createTrack(streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, output);

//cbf是JNI传入的回调函数audioCallback

if (cbf != 0) { //看来,怎么着也要创建这个线程了!

mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

}

return NO_ERROR;

}

看看真正干活的createTrack

status_t AudioTrack::createTrack(

int streamType,

uint32_t sampleRate,

int format,

int channelCount,

int frameCount,

const sp& sharedBuffer,

audio_io_handle_t output)

{

status_t status;

//啊,看来和audioFlinger挂上关系了呀。

const sp& audioFlinger = AudioSystem::get_audio_flinger();

//下面这个调用最终会在AudioFlinger中出现。暂时不管它。

sp track = audioFlinger->createTrack(getpid(),

streamType,

sampleRate,

format,

channelCount,

frameCount,

((uint16_t)flags) << 16, sharedBuffer,

output,

&status);

//看见没,从track也就是AudioFlinger那边得到一个IMemory接口

//这个看来就是最终write写入的地方

sp cblk = track->getCblk();

mAudioTrack.clear();

mAudioTrack = track;

mCblkMemory.clear();//sp的clear,就看着做是delete XXX吧

mCblkMemory = cblk;

mCblk = static_cast(cblk->pointer());

mCblk->out = 1;

mFrameCount = mCblk->frameCount;

if (sharedBuffer == 0) {

//终于看到buffer相关的了。注意我们这里的情况

//STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。

//反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到

//的buffer了。

mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);

}

return NO_ERROR;

}

还记得我们说MemoryXXX没有同步机制,所以这里应该有一个东西能体现同步的,

那么我告诉大家,就在audio_track_cblk_t结构中。它的头文件在

framework/base/include/private/media/AudioTrackShared.h

实现文件就在AudioTrack.cpp中

audio_track_cblk_t::audio_track_cblk_t()

//看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了

//等以后介绍同步方面的知识时,再细说

: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),

userBase(0), serverBase(0), buffers(0), frameCount(0),

loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),

flowControlFlag(1), forceReady(0)

{

}

到这里,大家应该都有个大概的全景了。

●AudioTrack得到AudioFlinger中的一个IAudioTrack对象,这里边有一个

很重要的数据结构audio_track_cblk_t,它包括一块缓冲区地址,包括一些进程

间同步的内容,可能还有数据位置等内容

●AudioTrack启动了一个线程,叫AudioTrackThread,这个线程干嘛的呢?

还不知道

●AudioTrack调用write函数,肯定是把数据写到那块共享缓冲了,然后

IAudioTrack在另外一个进程AudioFlinger中(其实AudioFlinger是一个服务,

在mediaservice中运行)接收数据,并最终写到音频设备中。

那我们先看看AudioTrackThread干什么了。

调用的语句是:

mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

AudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。

反正最终会调用AudioTrackAThread的threadLoop函数。

先看看构造函数

AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) : Thread(bCanCallJava), mReceiver(receiver)

{ //mReceiver就是AudioTrack对象

// bCanCallJava为TRUE

}

这个线程的启动由AudioTrack的start函数触发。

void AudioTrack::start()

{

//start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的threadLoop

sp t = mAudioTrackThread;

t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);

//让AudioFlinger中的track也start

status_t status = mAudioTrack->start();

}

bool AudioTrack::AudioTrackThread::threadLoop()

{

//太恶心了,又调用AudioTrack的processAudioBuffer函数

return mReceiver.processAudioBuffer(this);

}

bool AudioTrack::processAudioBuffer(const sp& thread)

{

Buffer audioBuffer;

uint32_t frames;

size_t writtenSize;

...回调1

mCbf(EVENT_UNDERRUN, mUserData, 0);

...回调2 都是传递一些信息到JNI里边

mCbf(EVENT_BUFFER_END, mUserData, 0);

// Manage loop end callback

while (mLoopCount > mCblk->loopCount) {

mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);

}

//下面好像有写数据的东西

do {

audioBuffer.frameCount = frames;

//获得buffer,

status_t err = obtainBuffer(&audioBuffer, 1);

size_t reqSize = audioBuffer.size;

//把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停

//地write呢,怎么会这样?

mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);

audioBuffer.size = writtenSize;

frames -= audioBuffer.frameCount;

releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK

操作了

}

while (frames);

return true;

}

难道真的有两处在write数据?看来必须得到mCbf去看看了,传的是EVENT_MORE_DATA标志。

mCbf由set的时候传入C++的AudioTrack,实际函数是:

static void audioCallback(int event, void* user, void *info) {

if (event == AudioTrack::EVENT_MORE_DATA) {

//哈哈,太好了,这个函数没往里边写数据

AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;

pBuff->size = 0;

}

从代码上看,本来google考虑是异步的回调方式来写数据,可惜发现这种方式会比较复杂,尤其是对用户开放的JAVA AudioTrack会很不好处理,所以嘛,偷偷摸摸得给绕过去了。

太好了,看来就只有用户的write会真正的写数据了,这个AudioTrackThread除了通知一下,也没什么实际有意义的操作了。

让我们看看write吧。

4.2 write

ssize_t AudioTrack::write(const void* buffer, size_t userSize)

{

够简单,就是obtainBuffer,memcpy数据,然后releasBuffer

眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了 do {

audioBuffer.frameCount = userSize/frameSize();

status_t err = obtainBuffer(&audioBuffer, -1);

size_t toWrite;

toWrite = audioBuffer.size;

memcpy(audioBuffer.i8, src, toWrite);

src += toWrite;

}

userSize -= toWrite;

written += toWrite;

releaseBuffer(&audioBuffer);

} while (userSize);

return written;

}

obtainBuffer太复杂了,不过大家知道其大概工作方式就可以了

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)

{

//恕我中间省略太多,大部分都是和当前数据位置相关,

uint32_t framesAvail = cblk->framesAvailable();

cblk->lock.lock();//看见没,lock了

result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));

//我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道

//没有一个好的方法来集中处理这种事情吗?

if (result == DEAD_OBJECT) {

result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount, mFrameCount, mFlags, mSharedBuffer,getOutput());

}

//得到buffer

audioBuffer->raw = (int8_t *)cblk->buffer(u);

return active ? status_t(NO_ERROR) : status_t(STOPPED);

}

在看看releaseBuffer

void AudioTrack::releaseBuffer(Buffer* audioBuffer)

{

audio_track_cblk_t* cblk = mCblk;

cblk->stepUser(audioBuffer->frameCount);

}

uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) {

uint32_t u = this->user;

u += frameCount;

if (out) {

if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;

}

} else if (u > this->server) {

u = this->server;

}

if (u >= userBase + this->frameCount) {

userBase += this->frameCount;

}

this->user = u;

flowControlFlag = 0;

return u;

}

奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?

再去看看obtainBuffer?为何写得这么晦涩难懂?

windowsXP下Android安卓开发环境搭建详细教程及图解

注:本教程是讲解在Windows XP下搭建安卓开发环境的。 安装目录: 步骤1 安装JDK 步骤2 安装Android SDK 步骤3 安装Tomcat 步骤4 安装Ant 步骤5 安装Eclipse 步骤6 安装Eclipse的ADT插件 步骤7 在图形界面下管理AVD 步骤8 设置Android系统语言 本教程的软件可以从我博客“绿杨芳草”下载。 方法/步骤 1、安装JDK 运行安装程序【jdk-6u22-windows-i586-p.exe】,分别点击下一步进行安装。 在安装过程中先后会出现两次选择安装目录的界面,全部改为以下路径: jdk安装目录:C:\Java\jdk1.6.0_22 jre安装目录:C:\Java\jre6\

安装好之后,配置环境变量: 打开环境变量窗口方法:右键【我的电脑】--单击【属性】--单击【高级】--单击【环境变量】。 在上方的用户变量中依次新建如下变量,并分别填入如下路径: 变量名:JAVA_HOME 变量值:C:\Java\jdk1.6.0_22 变量名:PATH 变量值:%JAVA_HOME%/bin 变量名:CLASSPATH 变量值:.;%JAVA_HOME%/lib/tools.jar;%JAVA_HOME%/lib/dt.jar 图1 配置完成之后,分别点击【开始】--【运行】--输入【cmd】--输入【javac】--按【回车键】,若看到以下信息,则代表配置成功。

图2 2、安装Android SDK 将【android-sdk_r17-windows.zip】解压到E:\Android目录下(Android目录自己新建,以后所有关于Android开发相关软件都会统一放到该目录中),得到一个android-sdk-windows 文件夹,该文件夹包含如下文件结构: add-ons:该目录下存放额外的附件软件。刚解压后,该目录为空。 platforms:该目录下存放不同版本的Android版本。刚解压后,该目录为空。 tools:该目录下存放了大量Android开发、调试的工具。 SDK Manager.exe:该程序就是Android SDK和AVD(Android虚拟设备)管理器。 通过该工具可以管理Android SDK和AVD。 运行E:\Android\android-sdk-windows目录下的【SDK Manager.exe】 然后等待更新...(该步骤必须联网,因为SDK安装包需要在线获取)。 在更新的过程中若遇到如下的提示窗口:

android系统刷机关于BIN的解释及方式

特别介绍一下关于刷机的方式。这或许对很多脑子里一塌糊涂的朋友有用。必看 1、官方一件升级。(主要作为手机死了的挽救措施。)需要之后自己root,刷rec和刷rom 2、先root,然后刷rec,然后找到rom放进sd卡,然后进入rec开始卡刷rom。Rom一般自带root。(((((特别注明,我上面编辑的帖子就是这种方法。root-->刷rec-->刷rom)))))))) 3、刷bin 或者迷你bin,然后刷卡里准备好的rom。迷你bin一般带有刷好的rec。 4、直接刷一步到位的bin包,里面包含有rom。无需root和刷rec。 教学完毕 以上4个方法为目前论坛上五花八门的教程帖子的完全总结。。不外乎就这些方法了。。。换言之,我们想给自己的手机换个系统总共可以采取以下方式。 (一)下载官方升级工具直接升级系统。此方法root 刷bin 刷rec 刷rom 一步操作完成。缺点:安装升级工具的驱动比较麻烦,系统只能采用官方的。 (二)保持你手机bin版本不变,采用root-->刷rec-->刷rom的形式操作。此时你下载的rom包需要与你手机bin版本一致。缺点:论坛上属于你手机bin版本的rom很少,很难找,有些不能用。 (三)改变手机bin版本,采用root(通常此步可以省略,因为下一步的刷bin本身是具备root功能的,当然已经root的也没有什么影响)-->刷bin(或者刷mini bin)-->刷rec(通常此步也不需要,因为bin包一般自带rec)-->刷rom的形式操作。缺点:刷b in有变砖风险优点:可以任意选择bin版本,即是说,假如你要安装一个与自己手机bin版本不一致的rom包,只需要先刷一个对应的bin即可,系统选择很广。 (四)刷一步到位的bin包。不需要root,不需要刷rec 和刷rom。(形式类似官方升级,操作也基本一致,即是下载升级工具,装升级工具驱动,卸载电池手机卡后点击升级工具按提示操作即可。) 以上1,3,4方法都属于刷bin范畴。刷bin方法可参考下面步骤。(刷bin有变砖风险,以下步骤不一定完全绝对,我在此发出来,仅仅是为了大致说明一下思路,所以概不承担任何责任。另外由于下面安装驱动的方法来源于百度,原版在百度上图片不能显示,故在此不提供图片,希望理解) 下载各位大大或者官方升级工具,然后解压到电脑上一个方便找到的地方,其中会有一个驱动文件夹。 下载bin包(mini bin包和一步到位bin包类似)解压存放到电脑上相对容易找到的地方,(刷bin和mini bin 请注意提前下载rom到手机内存卡,一步到位bin不需要此步操作)

Android应用程序开发个人总结

Android应用程序开发个人总结 Android应用程序开发个人总结范文 篇一:Android应用程序开发-个人总结 一、项目简介: 送祝福App是一款逢年过节为方便发送祝福信息的软件,并且介绍各种节日的相关情况。包含节日祝福短信,支持直接发送短信给好友,也可定时发送给好友,编写模版。为生活提供了便利。 二、个人在项目中承担的任务: 我是任务是整合成员负责的模块,把各个模块整合到一起,然后把主界面的效果做出来,主界面参考的是微信的界面 三、学习本门课程收获、体会及建议: 在本门课程中,我初步认识了安卓应用开发所需基本知识,比如安装体系结构,常用的控件以及API的使用方法,知道了安卓体系存储数据的方式。这一学期,安卓相关的知识自己掌握的不多,不过也透过安卓课程看到移动端应用的火热。这一个学期里,老师讲得比较细,自己单独下来练一个知识点了,也可以,不过当几个知识点的内容整合到一起,自己就凌乱了,自己的思路就不清晰。在期末项目中,我是任务是整合成员负责的模块,把各个模块整合到一起,然后把主界面的效果做出来,主界面参考的是微信的界面,由于时间和能力的限制,只完成了部分功能,功能没有完全实现。 篇二:Android程序员-201*年度个人工作总结和计划

逝者如斯夫,不舍昼夜!转眼间,自己在XX网里参加工作已经有一年有余了。回首即将逝去的201*年,可以说是平淡而不普通的一年,发现自己在各方面都比上一年有所进步、提高。一年的结束孕育着新一年的到来,新的一年意味着新的起点、新的挑战。昂首期待未来,总结以往经验。在新一年来临之际,我现把这一年的工作总结一下以及将新一年计划列一下。 大概3月份左右,XXXXApp第一期基本开发完成比较稳定的版本。在此之前,我主要负责参考XXXXApp的Android客户端的开发工作。XXXX第一期开发的难度系数相对比较低,开发起来还是相对的容易,所学到的基础知识基本上可以开发出来。我个人认为当时的开发效率还是比较低的,这里说的是整个项目的开发效率,不是个人负责工作的完成效率,换句话来说是团队的整体开发效率不高,没有达到高效。这个也许是团队协调与沟通交流还存在不足的地方。XXXXApp实际上基本可以满足用户的使用(表面),但是项目的代码管理(本质)比较杂乱、扩展性差、健壮性也不错、兼容性也不算好。这无疑导致开发的工作量大大的增加。产生这些问题的原因有团队的项目开发经验缺乏,有自己个人的开发水平有限、技能不足。简言之,我的专业技能还不够强。 在3月份之前,公司聘请了外包团队与我们的团队一起开发问答App。公司的目的培训公司的团队,让我们学习外包团队的技能。在外包团队的参与和指导下问答App开发工作相当顺利,团队的分工清晰、交流充分、协调都比较好。问答App的工作接近尾声,意味着XXX

分析Android 开机启动慢的原因

开机启动花了40多秒,正常开机只需要28秒就能开机起来。 内核的启动我没有去分析,另一个同事分析的。我主要是分析从SystemServer启来到开机动画结束显示解锁界面的这段时间,也就是开机动画的第三个动画开始到结束这段时间,这是个比较耗时阶段,一般都在17秒左右(见过牛B的手机,只需5秒)。 SystemServer分两步执行:init1和init2。init1主要是初始化native的服务,代码在sy stem_init.cpp的system_init,初始化了SurfaceFlinger和SensorService这两个native的服务。init2启动的是java的服务,比如ActivityManagerService、WindowManagerService、PackageManagerService等,在这个过程中PackageManagerService用的时间最长,因为PackageManagerService会去扫描特定目录下的jar包和apk文件。 在开机时间需要40多秒的时,从Log上可以看到,从SurfaceFlinger初始化到动画结束,要27秒左右的时间,即从SurfaceFlinger::init的LOGI("SurfaceFlinger is starting")这句Log到void SurfaceFlinger::bootFinished()的LOGI("Boot is finished (%ld ms)", long(ns 2ms(duration)) ),需要27秒左右的时间,这显然是太长了,但到底是慢在哪了呢?应该在个中间的点,二分一下,于是想到了以启动服务前后作为分隔:是服务启动慢了,还是在服务启动后的这段时间慢?以ActivityManagerService的Slog.i(TAG, "System now ready")的这句Log为分割点,对比了一下,在从SurfaceFlinger is starting到System now read y多了7秒左右的时间,这说明SystemServer在init1和init2过程中启动慢了,通过排查,发现在init1启动的时候,花了7秒多的时间,也就是system_init的LOGI("Entered system _init()")到LOGI("System server: starting Android runtime.\n")这段时间用了7秒多,而正常情况是400毫秒便可以初始化完,通过添加Log看到,在SensorService启动时,用了比较长的时间。 不断的添加Log发现,在启动SensorService时候,关闭设备文件变慢了,每次关闭一个/dev/input/下的设备文件需要100ms左右,而SensorService有60~70次的关闭文件,大概有7s左右的时间。 调用流程是: frameworks/base/cmds/system_server/library/system_init.cpp: system_init->SensorServi ce::instantiate frameworks/native/services/sensorservice/SensorService.cpp: void SensorService::onFi rstRef()->SensorDevice& dev(SensorDevice::getInstance()) hardware/libsensors/SensorDevice.cpp: SensorDevice::SensorDevice()->sensors_open hardware/libsensors/sensors.cpp: open_sensors->sensors_poll_context_t sensors_poll_context_t执行打开每个传感器设备时,遍历/dev/input/目录下的设备文件,以匹配当前需要打开的设备,遍历文件是在 hardware/libsensors/SensorBase.cpp的openInput下实现,如果打开的设备文件不是正在打开的设备文件,会执行下面语句的else部分: if (!strcmp(name, inputName)) { strcpy(input_name, filename); break;

安卓android课程设计报告

安卓a n d r o i d课程设计 报告 Company Document number:WTUT-WT88Y-W8BBGB-BWYTT-19998

湘潭大学课程设计报告课程名称______手机安卓开发_______ 课题名称______天气预报APP_______指导老师______周唯___________ 姓名____黄柳________ 班级____12计2班________ 小组______棒棒糖_______ 时间____2015年1月26 日_____ 一、需求分析 (一)引言 1.编写目的 1.1 为用户提供一个显示天气预报的软件,可以根据百度地图显示用户需求的城 市位置,以及用图标表示各城市对应的气象信息,可以简洁的表示出“晴、 雨、雪”和“气温”气象信息。 2. 项目风险 2.1 (项目风险识别是指找出影响项目目标顺利实现的主要风险因素,并识别出 这些风险究竟有哪些基本特征、可能会影响到项目的那些方面。) 2.2 联网收费视用户电话运营商的收费标准决定 2.3 手机或者无线信号不好时,可能无法连接或更新天气情况 3. 预期读者和阅读建议 3.1 预期读者:会使用已经安装安卓系统的手机或者平板电脑及其他移动设备的 用户 3.2 阅读建议:读者应通过该文档可以了解相关项目的需求分析、使用注意事项 以及可能存在的风险 4. 产品范围 2.2手机,平板电脑,MP4,导航仪,上网本,电纸书等使用安卓系统的数码领 域 (二)综合描述 1.产品的状况

1.1 Android是一种以Linux为基础的开放源代码操作系统,主要使用于便携设 备。目前未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。 Android操作系统最初由Andy Rubin开发,最初主要支持手机。2005年由 Google收购注资,并组建开放手机联盟开发改良,逐渐扩展到平板电脑及 其他数码领域上。我们开发的相关项目正是一个在安卓平台上开发的软件, 可以用这个软件来实现用户在一些简单的手持设备如手机或者平板电脑等上 面及时的了解到用户所在城市的天气预报。 2. 产品的功能 2.1 天气信息获取 2.2 天气信息显示 3. 系统配置功能 3.1 用户类和特性 安卓平台用户:安卓新用户可以使用软件来了解用户所在城市的天气预报。熟悉安卓平台用户还可以选择对相关软件的系统按自己的喜好进行配置并保留自己的个性化系统配置。 安卓软件开发者:安卓软件开发者(喜好者)可以通过相关的开源代码来了解软件运行的原理。 3.2 运行环境 软件需要在支持安卓系统的可移动设备中正确安装才能正常运行。安卓版本需在及以上,同时建议用户如果是在手机移动设备使用的话,需确保手机支持联网服务。 3.3 设计和实现上的限制 Eclipse是开发相关应用软件的首选集成开发环境,安卓开发环境首先需要安装支持java应用软件运行的java开发软件包(即JDK),然后安装集成开发环境Eclipse,最后安装Android SDK的Eclipse的ADT插件。 Android SDK和Eclipse的ADT插件是必须使用的。 软件支持高效、快速的数据存储方式,包括快速数据存储方式SharedPreferences、文件存储和轻量级关系数据块SQLite,可软件可以使用适合的方法对数据进行进程保存和访问。 3.4 假设和约束(依赖) 软件必须在安卓系统下才可以正常运行,若需要获取天气更新或者是短信服务,需确保用户的数码设备支持联网及短信服务。 (三) 外部接口需求 1 用户的手机设备可以支持联网。 2 用户界面 3 软件接口 软件提供轻量级的进程间通信机制Intent,实 现跨进程组件通信。 4 访问硬件的API库函数通讯接口 4.1 HTTP 协议:有多种接口可以调用 你可以用Java类库封装的 HttpConnection 或者用Apach的开 源项目的 HttpGet 或者HttpPost ,

android2.2刷机教程

Htc desire 制作金卡、root、升级2.2 系统的一条龙教程 声明: 1、此贴非本人原创,而是综合许多作者成果并结合自己升级过程中解决所遇到问题经验而成的(本人已成功升级),希望对想升级2.2的机友有帮助。 2、让我们对那些辛勤付出的原创高手们表示崇高的敬意和衷心的感谢。 3、刷机有风险,此贴仅供学习交流之用,不提倡按此刷机,刷机过程有很多意外因素,如按此教程刷机出现不良后果自负。 入门: 1、请先安装ANDROID驱动,可以用通用驱动或HTC Sync(内含驱动的)。保证你的手机能被电脑识别。下载地址:https://www.doczj.com/doc/128358923.html,/hk-tc/SupportDownload.aspx?p_id= 312&cat=3&dl_id=933 2、请使用安装了WINDOWS XP的电脑(其他系统我没试过,不知道怎么用。) 3、有个金士顿、SANDISK的原装TF卡。

4、网络连接,有些要上网找资源、下载软件的。 5、耐心加细心。 正式开始 第一步:制作金卡。 关于什么是金卡,坛子里有很多介绍,希望朋友们善用搜索引擎,在搜索引擎里搜索一下“制作金卡教程” 等即可查到。我这里简单地解释一下流程,其中某些步骤比网上的教程要简单,容易多了。 1、将TF卡插入你的G7中,注意,TF卡最好为金士顿、SANDISK等名牌非假货TF卡。 2、在Market里下载Estrongs或类似的资源管理器,以文本形式打开/sys/class/mmc_host/mmc*/mmc*:*/cid 文件。这个* 可能是数字或字母(网上教程说这里都是1或0,比如/sys/class/mmc_host/mmc0/mmc0:1/cid,这个是不准确的),这个文件打开来是一个32位的字符,中英混合的,记下这一串字符。(这个文件可能不好找,大家多找找)找到以后就把卡拔下,用读卡器插入电脑中,或者直接用手机以磁盘管理器模式连接电脑。并拔下电脑上除了你的TF卡以外的其他U盘、移动硬盘,方便后面的辨认。

Android 开机启动流程

Android的开机流程 1. 系统引导bootloader 1) 源码:bootable/bootloader/* 2) 说明:加电后,CPU将先执行bootloader程序,此处有三种选择 a) 开机按Camera+Power启动到fastboot,即命令或SD卡烧写模式,不加载内核及文件系统,此处可以进行工厂模式的烧写 b) 开机按Home+Power启动到recovery模式,加载recovery.img,recovery.i mg包含内核,基本的文件系统,用于工程模式的烧写 c) 开机按Power,正常启动系统,加载boot.img,boot.img包含内核,基本文件系统,用于正常启动手机(以下只分析正常启动的情况) 2. 内核kernel 1) 源码:kernel/* 2) 说明:kernel由bootloader加载 3. 文件系统及应用init 1) 源码:system/core/init/* 2) 配置文件:system/rootdir/init.rc, 3) 说明:init是一个由内核启动的用户级进程,它按照init.rc中的设置执行:启动服务(这里的服务指linux底层服务,如adbd提供adb支持,vold提供SD卡挂载等),执行命令和按其中的配置语句执行相应功能 4. 重要的后台程序zygote 1)源码:frameworks/base/cmds/app_main.cpp等 2) 说明:zygote是一个在init.rc中被指定启动的服务,该服务对应的命令是/system/bin/app_process a)建立Java Runtime,建立虚拟机 b) 建立Socket接收ActivityManangerService的请求,用于Fork应用程序 c) 启动System Server 5. 系统服务system server 1)源码:frameworks/base/services/java/com/android/server/SystemServer.jav a 2) 说明:被zygote启动,通过SystemManager管理android的服务(这里的服务指frameworks/base/services下的服务,如卫星定位服务,剪切板服务等) 6. 桌面launcher 1)源码:ActivityManagerService.java为入口,packages/apps/launcher*实现 2) 说明:系统启动成功后SystemServer使用xxx.systemReady()通知各个服务,系统已经就绪,桌面程序Home就是在ActivityManagerService.systemReady()通知的过程中建立的,最终调用()启launcher 7. 解锁 1) 源码: frameworks/policies/base/phone/com/android/internal/policy/impl/*lock* 2) 说明:系统启动成功后SystemServer调用wm.systemReady()通知WindowManagerService,进而调用PhoneWindowManager,最终通过LockPatternKeyguardView显示解锁界面,跟踪代码可以看到解锁界面并不是一个Activity,这是只是向特定层上绘图,其代码了存放在特殊的位置

RFID手持机市场分析(Android系统)

Android系统RFID手持机市场分析 一、产品市场应用分析 金盾信安手持机设计基于开放性和服务免费Android系统平台,目标市场根据功能的选配可以辐射到军=用JD-8180A和民用JD-8180B,由于产品本身定位的新颖性、应用正对性和具备Android系统平台的二次开发等特性,我们市场定位在中高端市场,但基于目前市场来看,产品基本上能够满足各行各业RFID解决方案的应用。 该产品最大亮点是采用全新无线网络数据通信技术,提高产品工作效率。 Android强大的联网功能,对手持机3G视频功能和通讯信息即时上传具有强大的支持;工业化外型设计,外壳坚固,防水抗震,能够适应高低温环境,适于在厂矿野外等恶劣的作业环境;具有大容量存储、超长使用时间、多种通讯及扩展接口、硬件模块可以根据不同的需要进行配置、软件可以全面定制等特点。另外,整个设备内部的预留空间大,内部预留接口多,适于用户进一步扩展,非常适合具有自行开发能力的行业解决方案供应商进行二次开发应用,能够大为降低开发和使用成本。 二、产品市场竞争分析: (一)总量规模 1.军用JD-8180A市场: 军用JD-8180A是新一代具有完全独立自主知识产权的移动手持终端系列产品,集语音、视频、数据传输于一体,该产品采用终端的应急通讯系统,可以应用到军队、公安、建筑、采矿、监狱、消防、地震救灾、武警防爆、港口作业、国土勘察、海关缉私、电力、铁路、高速公路、精准农业、畜牧业管理、食品安全、易燃易爆品溯源等领域。据资料统计,2011年国内市场需求将达到上千万台。 2.民用JD-8180B市场: 据市场调研厂商In-Stat公司预测表示,2011年企业在RFID技术上的投资将由去年的68亿美元提高到百亿美元。2011 年以后,国内企业和机构从自身需求出发而推动的项目将显著增长,成为RFID 又一块活跃的市场点。市场对RFID 手持机的需求主要集中在以下领域: (1)港口码头、集装箱。RFID 技术已经开始应用于港口车辆、集装箱和货物的识别。每个集装箱场地的海关和企业需配置大量的RFID 手持机,估计需求量可达10 万台以上。 (2)零售、服装、制造业产品的库存追踪、仓储、后台出货及收货、移动盘点、查价和货架调整。在国外,RFID 技术已经普遍应用于零售业和服装流通管理。RFID 手持机将成为主要的应用设备。估计国内需求量可达100 万台以上,出口保守估计将超过国内总量。

Android开发实战中常用安卓开发框架

Android是基于Linux平台的开源移动操作系统,主要适用于移动设备,如智能手机和平板电脑(当然现在已经适用车控应用了),由Google公司和开放手机联盟领导及开发。不完全统计,Android平台手机的全球市场份额已经达到80%,本文介绍了android开发者实战中最喜欢使用的几种Android开发框架。 1) Corona SDK Corona SDK是一个跨平台技术的工具,它利用Lua开发脚本,从一个特定的移动操作系统开发应用。应用程序可以在模拟器中测试,然后编译为Android的本机代码。同时Corona SDK 还支持与Map、Facebook、OpenFient、GameCenter的集成。然而在编译项目时,程序员需要把代码上传到Corona的服务器上去编译,不能在本地直接编译。 2) Phonegap Phonegap是一款开源的开发框架,旨在让开发者使用HTML、Java、CSS等Web APIs开发跨平台的移动应用程序。它需要特定平台提供的附加软件,例如iPhone的iPhone SDK,Android的Android SDK等,也可以和DW5.5及以上版本配套开发。使用PhoneGap比为每个平台分别建立应用程序稍好一些,因为虽然基本代码是一样的,但是程序员仍然需要为每个平台分别编译应用程序。 3) Xamarin Xamarin 始创于2011年,旨在使移动开发变得难以置信地迅捷和简单。Xamarin的产品简化了针对多种平台的应用开发,包括iOS、Android、Windows Phone和Mac App。Xamarin 由许多著名的开源社区开发者创立和参与,而且也是Mono项目的主导者——C#与?NET框架的开源、跨平台实现。 而这其中Xamarin Studio就是一个用C#语言开发跨平台应用的集成开发环境,它和iOS 以及Android SDK紧密整合。Xamarin Studio提供了很多现代开发所需功能,包括代码完成、调试器、打包和发布应用(支持通过TestFlight发布应用程序)。对于Android,Xamarin还包含了自己的UI生成器(对于iOS,则使用苹果Xcode的UI生成器),此外还集成了Git和Subversion。Android开发者可以使用Xamarin Studio在模拟器和真机上对移动应用进行测试和bug调试。 4) Sencha Touch 2 Sencha Touch是由Sencha公司开发的移动Web应用开发框架,用以提升主流移动设备在浏览器上的触碰操作,增强用户体验。该框架以久负盛名的Ext JS富客户端框架为基础,并支持最新的HTML5及CSS3标准,与流行的Apple iOS和Andriod设备兼容。一方面,它以Webkit浏览器引擎为基础,提供了出色的性能和用户体验;另一方面,它提供了基于GPL V3许可的开源版本和详尽的API文档,体现了良好的开放性和易用性。因此,该框架可帮助移动应用开发人员提升开发效率,从而创造出更多富有创意的移动应用。 5) Appcelerator Appcelerator是一个构建基于SOA的RIA应用的全集成平台,用来构建富应用、交互式的网站和应用程序。Appcelerator应用的语言——Web Expression Language,是一种对HTML 的扩展,它采用直观、声明的方法来构建动态的web应用。

Android系统(安卓)手机刷机前需要知道的一些基础知识

一、关于Root 1、什么是“root”? “Root”权限相当于电脑的管理员权限,“Root”后用户可以完全权限访问Linux内核:“Root用户”可以编辑修改系统内的任何东西。出于安全考虑,“普通用户”没有该完全访问权限。 root你的手机之后,你就可以读写未root时你无法访问的手机内部核心区块。 当然,是否root完全取决你自己。普通手机用户不root也可以很好的使用自己的手机。 2、为何要“root”我的手机? 最主要的原因是刷第三方ROM(相当于重装电脑的操作系统)。另外,有ROOT权限后就可以运行一些需要root的软件。 Root后,你可以做的事: a)运行更多的Linux命令(android ADB)和超级用户权限。 b)可以使用许多需要root权限的软件(例如,钛备份,Root Explorer等)。 c)更改某些重要的核心系统设置。比如,关闭官方 Rom的安全校验以使我们可以刷写第三方固件,或者运行某些有趣的调节程序比如说超频,再或者还可以让我们安装核心recovery程序。 3、root安全吗? 是的。如果你胆大心细,按照教程来做,不会发生什么蛋疼的事。 4、root后会删除我的个人资料吗? 不会。root仅仅是改变系统权限而已。 5、root会使我的保修失效吗? 退一万步说,你可以unroot你的手机并恢复出厂设置,来假装什么事也没发生过。 6、root过程是可逆的吗? 是的。只要你愿意完全可以unroot你的手机。 二、关于S-OFF和刷机 1、“S-OFF”是什么?“S-ON”和“S-OFF”有什么区别?我为何需要“S-OFF”? 每一部手机缺省状态下都是“S-ON”。为了获取完全的写入权限(例如刷自制固件,装自制recovery工具),需要转换为“S-OFF”。 通常手机都是处于“S-ON”(Security ON)状态,此时手机“/system”和“/recovery”分区无法被读写。最直接的表现就是如果用RE管理器等高权限文件管理器删除“/system”文件夹下的程序,重启后程序又会出现(这也好多网友说获得root后还是删除不了系统自带软件的原因);另外无法修改开机第一屏画面。 S-OFF不同于root:S-OFF可以让你通过Bootloader里的Fastboot或者自制recovery(多数人用的都是clockwork)刷写自制固件。 2、如何“S-OFF”? 目前有两种S-OFF的方法: a)通过安装工程模式的hBoot来S-OFF。即所谓的软解。 b)通过某些设备,例如白卡来S-OFF。即所谓白卡解锁。 软解的手机进入Bootloader后第一行的末尾显示的是:ENG S-OFF,白卡解的手机则是:SHIP S-OFF。ENG意为engineering即工程模式,SHIP即意为原厂发布的。3、如何改回“S-ON”? 因某些原因(送去保修等)想要恢复S-ON的话,需要下载对应你手机版本的HTC 官方刷写程序RUU刷回原厂固件。这样S-ON就回来了。

基于MT6752的 android 系统启动流程分析报告

基于MT6752的Android系统启动流程分析报告 1、Bootloader引导 (2) 2、Linux内核启动 (23) 3、Android系统启动 (23) 报告人: 日期:2016.09.03

对于Android整个启动过程来说,基本可以划分成三个阶段:Bootloader引导、Linux kernel启动、Android启动。但根据芯片架构和平台的不同,在启动的Bootloader阶段会有所差异。 本文以MTK的MT6752平台为例,分析一下基于该平台的Android系统启动流程。 1、Bootloader引导 1.1、Bootloader基本介绍 BootLoader是在操作系统运行之前运行的一段程序,它可以将系统的软硬件环境带到一个合适状态,为运行操作系统做好准备,目的就是引导linux操作系统及Android框架(framework)。 它的主要功能包括设置处理器和内存的频率、调试信息端口、可引导的存储设备等等。在可执行环境创建好之后,接下来把software装载到内存并执行。除了装载software,一个外部工具也能和bootloader握手(handshake),可指示设备进入不同的操作模式,比如USB下载模式和META模式。就算没有外部工具的握手,通过外部任何组合或是客户自定义按键,bootloader也能够进入这些模式。 由于不同处理器芯片厂商对arm core的封装差异比较大,所以不同的arm处理器,对于上电引导都是由特定处理器芯片厂商自己开发的程序,这个上电引导程序通常比较简单,会初始化硬件,提供下载模式等,然后才会加载通常的bootloader。 下面是几个arm平台的bootloader方案: marvell(pxa935) : bootROM + OBM + BLOB informax(im9815) : bootROM + barbox + U-boot mediatek(mt6517) : bootROM + pre-loader + U-boot broadcom(bcm2157) : bootROM + boot1/boot2 + U-boot 而对MT6752平台,MTK对bootloader引导方案又进行了调整,它将bootloader分为以下两个部分: (1) 第1部分bootloader,是MTK内部(in-house)的pre-loader,这部分依赖平台。 (2) 第2部分bootloader,是LK(little kernel的缩写,作用同常见的u-boot差不多),这部分依赖操作系统,负责引导linux操作系统和Android框架。 1.2、bootloader的工作流程 1.2.1 bootloader正常的启动流程 先来看启动流程图:

搭建安卓开发环境-详细教程

搭建安卓开发环境-详细教程 注:本教程是讲解在Windows XP下搭建安卓开发环境的,不是XP系统的朋友请绕行! 在开始搭建之前,请大家先到本人的网盘中下载所需的6个文件。 下载网址为:https://www.doczj.com/doc/128358923.html,/c0balfh535(超链接,按住Ctrl键,单击蓝色文字,即可直接跳转) 安装目录: 步骤1→安装JDK---------------------------------对应的安装文件:jdk-6u22-windows-i586.exe 步骤2→安装Android SDK--------------------对应的安装文件:android-sdk_r17-windows.zip 步骤3→安装T omcat----------------------------对应的安装文件:apache-tomcat-7.0.26-windows-x86.zip 步骤4→安装Ant---------------------------------对应的安装文件:apache-ant-1.8.3-bin.zip 步骤5→安装Eclipse-----------------------------对应的安装文件:eclipse.part1.rar和eclipse.part2.rar 步骤6→安装Eclipse的ADT插件----------对应的安装文件:ADT-17.0.0.zip 步骤7→在图形界面下管理AVD 步骤8→设置Android系统语言 ===========================================1、安装JDK 运行安装程序【jdk-6u22-windows-i586-p.exe】,分别点击下一步进行安装。 在安装过程中先后会出现两次选择安装目录的界面,全部改为以下路径: jdk安装目录:C:\Java\jdk1.6.0_22 jre安装目录:C:\Java\jre6\ 安装好之后,配置环境变量: 打开环境变量窗口方法:右键【我的电脑】--单击【属性】--单击【高级】--单击【环境变量】。 在上方的用户变量中依次新建如下变量,并分别填入如下路径: →变量名:JAVA_HOME 变量值:C:\Java\jdk1.6.0_22 →变量名:PATH 变量值:%JAVA_HOME%/bin →变量名:CLASSPATH 变量值:.;%JAVA_HOME%/lib/tools.jar;%JAVA_HOME%/lib/dt.jar

Android手机刷机变砖后无法开机的修复方法

对于广大Android刷友来说,最怕遇到的就是传说中的手机变板砖,无法启动的情况。在Android手机刷机的时候,难免会遇到刷机失败的时候,下面给大家说下如何修复能够让你的手机重获新生。1. 在PC上安装配置Android SDK Android手机刷机失败后手机变砖无法在手机上完成操作,智能借助Android SDK工具,所以你必须在操作的PC上下载并安装Android SDK程序,程序下载地址和安装配置方法见Android SDK官网。 2. 安装Android手机USB驱动程序 安装完Android SDK后就可以把手机通过USB线缆连接至PC,开启手机电源后PC会提示安装设备驱动程序,不要让系统自动查找驱动程序安装,选择自定义驱动程序位置,因为已经安装了Android SDK,驱动程序的位置默认为Android SDK的安装目录下的 usb_driver子目录,选择从这个目录安装驱动程序。 3. 下载一个可用Recovery程序 Android手机刷机失败后手机变砖的Android手机能否复活的关键就在于Recovery程序,

因为recovery程序意味着你可以再次刷新的ROM,相当于给PC重新安装操作系统。Android平台有很多可用的recovery程序,推荐使用最常见的Clock Recovery程序,注意recovery程序针对不同手机型号有不同版本的程序,下载时注意和手机型号匹配。把下载的recovery程序放在某一目录备用,比如 c: ecovery-RA-Magic-v2.3.1.img。 4. 关键步骤:重新给变砖的手机安装recovery程序 在PC上打开命令提示符(Windows系统,开始,运行,输入cmd回车),输入 #adb devices 回车,如果返回一串序列号说明手机已经正常连接。在命令提示符使用cd命令切换到recovery程序所在的目录。然后输入下面的命令: #adb reboot bootloader 回车后手机应该会进入fastboot模式,然后再执行下面的命令把recovery程序安装到手机上 #fastboot flash recovery-RA-Magic-v2.3.1.img 回车后如果看到下面的提示就说明recovery程序已经成功安装。 fastboot flash recovery recovery-RA-Magic-v2.3.1.img sending ‘recovery’ (4594 KB)… OKAY writing ‘recovery’… OKAY 拔掉手机USB连接线,关机,重新按指定键看看是否可以重新进入recovery模式了,到这一部,你的Android手机刷机后无法开机的问题就修复成功了,现在可以重新刷机给你心爱的手机再安装一个操作系统了。

linux内核启动 Android系统启动过程详解

linux内核启动+Android系统启动过程详解 第一部分:汇编部分 Linux启动之 linux-rk3288-tchip/kernel/arch/arm/boot/compressed/ head.S分析这段代码是linux boot后执行的第一个程序,完成的主要工作是解压内核,然后跳转到相关执行地址。这部分代码在做驱动开发时不需要改动,但分析其执行流程对是理解android的第一步 开头有一段宏定义这是gnu arm汇编的宏定义。关于GUN 的汇编和其他编译器,在指令语法上有很大差别,具体可查询相关GUN汇编语法了解 另外此段代码必须不能包括重定位部分。因为这时一开始必须要立即运行的。所谓重定位,比如当编译时某个文件用到外部符号是用动态链接库的方式,那么该文件生成的目标文件将包含重定位信息,在加载时需要重定位该符号,否则执行时将因找不到地址而出错 #ifdef DEBUG//开始是调试用,主要是一些打印输出函数,不用关心 #if defined(CONFIG_DEBUG_ICEDCC)

……具体代码略 #endif 宏定义结束之后定义了一个段, .section ".start", #alloc, #execinstr 这个段的段名是 .start,#alloc表示Section contains allocated data, #execinstr表示Section contains executable instructions. 生成最终映像时,这段代码会放在最开头 .align start: .type start,#function /*.type指定start这个符号是函数类型*/ .rept 8 mov r0, r0 //将此命令重复8次,相当于nop,这里是为中断向量保存空间 .endr b 1f .word 0x016f2818 @ Magic numbers to help the loader

基于Android的移动终端GPS测量系统的设计与开发(发表)

基于Android的移动终端GPS测量系统的设计与开发 秦超,姜良君,蔡永香 (长江大学地球科学学院,湖北荆州,434023) 摘要:本文针对手机GPS功能进行移动应用开发,设计并实现了基于Android平台的移动终端GPS测量系统。在分析学生野外测量实习需求的基础上,对系统进行了功能模块划分;在系统的程序模块详细设计中,着重探讨了数据库的设计与地块长度/面积计算方法。测试结果表明,该系统已能替代GPS手持终端,满足野外测量实习的需要。 关键词:Android;移动终端;GPS测量 1 引言 第三代移动通讯(3G)技术为移动终端用户带来了更快的数据传输率,也为电信网络与计算机网络的融合提供了支持。随着3G网络的使用,移动终端不仅是通讯网络的终端,也将成为互联网的终端[1]。因此,移动终端的应用和需要的服务将会有很大的发展空间。Google于2007年11月5日推出了基于Linux平台的移动终端系统软件平台——Android,该平台由操作系统、中间件、用户界面和应用软件组成,是首个为移动终端打造的真正开放和完整的移动软件[2],并融合了GPS技术,支持GPS的应用开发。 GPS的出现为测量技术带来了一场深刻的技术革命。利用GPS的定位功能,对于任何点状、线状、面状地物,都可以通过GPS的定位功能获取其地理坐标数据,通过地图投影方法,可计算出线状地物的长度,面状地物的面积等。以前学生野外测量实习时,常用的是手持GPS,这是一种专用的测量设备,需要专门购置和携带。而随着智能手机的发展和普及,学生基本上是人手一部,因此在基于Android平台的移动终端上开发GPS测量系统,可使得手机用户随时利用手机进行测量定位,操作方便、便于携带,具有很强的实用性。 因此,本文利用智能手机自带的GPS定位功能,通过获取测量点的地理坐标数据,进行点、线、面地物的空间位置测量,通过地图投影方法,计算出线状地物的长度或面状地物的面积及周长,并能够将结果可视化的显示在屏幕上。 2 系统功能模块划分 野外测量实际上包括独立点位测量、线状地物或面状地物的连续测量、测量数据存储、查看、显示与更新、地物面积、长度的计算等。因此,基于Android的GPS测量系统主要功能模块可以划分为:独立点位测量、地物点的动态定位测量、测量结果显示与更新、地物面积(或长度)测量。 1)独立点位测量:测量时对独立点逐一定位,获取其地理坐标。其属性信息,如地物名称、所属图层类型、所属图层名称、测量点位置序号等,可事先在室内输入系统。 2)地物点的动态定位测量:在线状边界测量时采用,通过设置定位间距(或者定位时间间隔),随着设备的移动每隔设定的间距(或时间)自动采集一次定位数据;而对于开始点、结束点以及转角点等这些特殊的点位,可通过点击特殊点定位按钮定 * 本项目获长江大学楚天学者项目资助

相关主题
文本预览
相关文档 最新文档