Активные объекты - пример реализации
Метаданные
Активный объект позволяет клиенту взаимодействовать с асинхронным сервис-провайдером. Для создания своего активного объекта необходимо объявить наследника класса CActive, в нем реализовать методы, с помощью которых будут создаваться асинхронные запросы, а также метод, который будет вызываться планировщиком по окончанию выполнения запроса (RunL()). Ниже представлен пример простого активного объекта:
Contents |
MyActiveObject.h
#ifndef __MYACTIVEOBJECT_H__
#define __MYACTIVEOBJECT_H__
Класс CActive объявлен в файле e32base.h - необходимо включить этот заголовочный файл.
#include <e32base.h>Класс-наблюдатель (Observer). Используется для оповещения о завершении выполнения асинхронного запроса.
class MMyActiveObjectObserver
{
public:
Чистая виртуальная функция - должна быть реализована в потомках. Вызывается при завершении выполнения асинхронного запроса.
virtual void HandleRequestCompletedL(TInt aError) = 0;
};
Активный объект, который позволяет использовать RMyAsyncServiceProvider.
class CMyActiveObject : public CActive
{
public:
static CMyActiveObject* NewL(TInt aPriority);
~CMyActiveObject();
void DoAsyncAction(MMyActiveObjectObserver* aObserver);
protected:
// CActive
void RunL();
void DoCancel();
TInt RunError(TInt aError);
private:
CMyActiveObject(TInt aPriority);
void ConstructL();
private:
RMyAsyncServiceProvider iServiceProvider; // Service provider
MMyActiveObjectObserver* iObserver; // Observer
};
#endif // __MYACTIVEOBJECT_H__
MyActiveObject.cpp
#include "myactiveobject.h"Функция - фабрика для создания активного объекта. См. Двухфазное конструирование. Приоритет активного объекта передается в качестве параметра, возможные значения можно найти здесь.
CMyActiveObject* CMyActiveObject::NewL(TInt aPriority)
{
CMyActiveObject* self = new (ELeave) CMyActiveObject(aPriority);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
Конструктор. Используется в функции NewL(). Здесь устанавливается приоритет активного объекта. Изменить приоритет в дальнейшем нельзя.
CMyActiveObject::CMyActiveObject(TInt aPriority)
: CActive(aPriority)
{
}
Инициализируем активный объект.
void CMyActiveObject::ConstructL()
{
Добавляем созданный активный объект в планировщик (Active Scheduler). Если этого не сделать, то при попытке использования возникнет паника E32User-CBase. Метод CActiveScheduler::Add() не является сбрасываемым, следовательно, его можно вызывать и из конструктора.
CActiveScheduler::Add(this);
Выполняем соединение с асинхронным сервис-провайдером. Генерируем сброс в случае неудачи.
User::LeaveIfError(iServiceProvider.Connect());
}
Деструктор.
CMyActiveObject::~CMyActiveObject()
{
Метод Cancel() всегда должен вызываться в деструкторе активного объекта, для того чтобы отменить выполнение текущего запроса, если он есть. Если текущего запроса нет - метод Cancel() ничего не делает. Если же запрос есть и Cancel() не был вызван при уничтожении объекта - генерируется паника E32User-CBase 40.
Cancel();
Закрытие соединения с сервис-провайдером.
iServiceProvider.Close();
}
Следующий метод должен быть вызван пользователем, чтобы начать асинхронную операцию. Аргумент - указатель на объект класса-наблюдателя, чей метод HandleRequestCompletedL() будет вызван по окончанию выполнения асинхронной операции.
void CMyActiveObject::DoAsyncAction(MMyActiveObjectObserver* aObserver)
{
С помощью утверждения выполняется проверка, что на данный момент нет выполняющегося запроса. Если выполняющийся запрос есть - генерируется паника с кодом EAlreadyActive. Эта проверка позволяет предотвратить панику E32User-CBase 42 которая может быть сгенерированна при вызове SetActive(). Подробнее о паниках и утверждениях можно прочитать здесь.
__ASSERT_ALWAYS(!IsActive(), User::Panic(KMyActivePanic, EAlreadyActive));
Проверка указателя aObserver.
__ASSERT_ALWAYS(aObserver, User::Panic(KMyActivePanic, ENoObserver));
Выполняется передача запроса сервис-провайдеру. В качестве параметра передается ссылка на iStatus. Сервис-провайдер устанавливает iStatus равным KRequestPending. Как только сервис-провайдер закончит выполнение запроса, планировщик (Active Scheduler) установит значение iStatus равным результату выполненной операции (KErrNone если выполнение прошло успешно, или коду ошибки).
iServiceProvider.DoService(iStatus);
Информируем планировщик о том, что активный объект ожидает завершения выполнения операции.
SetActive();
}
Метод RunL() будет вызван планировщиком, когда выполнение запроса будет завершено. Все активные объекты должны реализовать этот метод. Планировщик выполняет его в рамках ловушки (TRAP) и, если происходит сброс, вызывает метод RunError().
void CMyActiveObject::RunL()
{
iStatus содержит результат выполнения операции. Если была ошибка - генерируется сброс.
User::LeaveIfError(iStatus.Int());
Этот код выполнится, только если запрос был обработан успешно. Вызывается метод наблюдателя, чтобы проинформировать о завершении выполнения запроса.
iObserver->HandleRequestCompletedL(KErrNone);
}
Все активные объекты должны реализовывать метод DoCancel(). Он вызывается в методе Cancel() и прерывает выполнение текущего запроса.
void CMyActiveObject::DoCancel()
{
iServiceProvider.Cancel();
}
Реализовывать метод RunError() не обязательно, но обычно это очень полезно.
Этот метод вызывается планировщиком, если во время выполнения RunL() произошел сброс. RunError() должен обработать полученный в качестве параметра код ошибки (если это возможно) и возвратить KErrNone. Реализация этого метода по умолчанию просто возвращает полученный код ошибки aError.
Если возвращаемое методом RunError() значение не является KErrNone, тогда планировщик вызывает собственную функцию Error(). Функция CActiveScheduler::Error() генерирует панику E32USER-CBase 47 (такое поведение
можно изменить, если реализовать свой планировщик - для этого нужно создать наследника CActiveScheduler и перекрыть метод Error(). Обычно, реализация собственного планировщика является излишней).
TInt CMyActiveObject::RunError(TInt aError)
{
Информируем наблюдателя об ошибке. Наблюдатель может корректно обработать возникшую ошибку и, возможно, выполнить другой асинхронный запрос. Выполнение RunError() не должно привести к сбросу, поэтому вызов HandleRequestCompletedL() происходит в рамках ловушки. Используется TRAP_IGNORE так как в этом методе неизвестно каким образом обрабатывать возможный сброс.
TRAP_IGNORE(iObserver->HandleRequestCompletedL(aError));
return KErrNone;
}
Внутренние ссылки
Активные объекты (Active Objects) в Symbian ОС
Активные объекты - часто встречающиеся ошибки реализации
Внешние ссылки
CActive в библиотеке разработчика Symbian OS
CActiveScheduler в библиотеке разработчика Symbian OS


(no comments yet)