Android RIL 本地代码(c/c++) 和 Java代码部分分析
第一部分, c/c++代码
Android系统源代码目录里面: hardware/ril 目录包含了所有有关于telephony的底层代码.
1.目录架构(20101215的git版本):
ril
|-- CleanSpec.mk
|-- include
| |-- telephony
| |-- ril_cdma_sms.h //CDMA SMS
| |-- ril.h //Android RIL 框架的一些接口和数据结构
|-- libril
| |-- Android.mk
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- ril_commands.h //RIL命令列表
| |-- ril.cpp
| |-- ril_event.cpp
| |-- ril_event.h
| |-- ril_unsol_commands.h //RIL 主动上报信息列表
|-- reference-cdma-sms
| |-- Android.mk
| |-- reference-cdma-sms.c
| |-- reference-cdma-sms.h
|-- reference-ril
| |-- Android.mk
| |-- atchannel.h //负责向modem读写数据
| |-- atchannel.c
| |-- at_tok.h
| |-- at_tok.c
| |-- misc.h
| |-- misc.c
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- reference-ril.c //主要负责与modem进行交互
|-- rild
|-- Android.mk
|-- MODULE_LICENSE_APACHE2
|-- NOTICE
|-- radiooptions.c //调试时配置Modem参数
|-- rild.c //RIL守护进程
其中include/telephony目录下面的ril.h文件,定义了104个如下的宏:
RIL_REQUEST_XXXX
这些宏代表客户进程向Android telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等等。
2.目录hardware/ril/libril
本目录下代码负责与客户进程进行交互。在接收客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。
v 文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。
v 文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;以及WAKEType???
v 文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending-> ev->func())
v 文件ril.cpp:
? RIL_register函数:打开监听端口,接收来自客户进程的命令请求(s_fdListen =android_get_control_socket(SOCKET_NAME_RIL);),当与某客户进程连接建立时,调用listenCallback函数;创建一单独线程监视并处理所有事件源(通过ril_event_loop)
? listenCallback函数:当与客户进程连接建立时,此函数被调用。此函数接着调用 processCommandsCallback处理来自客户进程的命令请求
? processCommandsCallback函数:具体处理来自客户进程的命令请求。对每一个命令,ril_commands.h中都规定了对应的命令处理函数(dispatchXXX),processCommandsCallback会调用这个命令处理函数进行处理。
? dispatch系列函数:此函数接收来自客户进程的命令己相应参数,并调用onRequest进行处理。
? RIL_onUnsolicitedResponse函数:将来自网络端的事件封装(通过调用responseXXX)后传给客户进程。
? RIL_onRequestComplete函数:将命令的最终响应结构封装(通过调用responseXXX)后传给客户进程。
? response系列函数:对每一个命令,都规定了一个对应的response函数来处理命令的最终响应;对每一个网络端的事件,也规定了一个对应的response函数来处理此事件。response函数可被onUnsolicitedResponse或者onRequestComplete调用。
3. 目录hardware/ril/reference-ril分析:
本目录下代码主要负责与modem进行交互。
v 文件reference-ril.c:此文件核心是两个函数:onRequest和onUnsolicited
? onRequest 函数:在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的ATcommand,发送给modem,然后睡眠等待。当收到此ATcommand的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete-> sendResponse)。
? onUnsolicited函数:这个函数处理modem从网络端收到的各种事件,如网络信号变化,拨入的电话,收到短信等。然后将时间传给客户进程(RIL_onUnsolicitedResponse -> sendResponse)
v 文件atchannel.c:负责向modem读写数据。其中,写数据(主要是ATcommand)功能运行在主线程中,读数据功能运行在一个单独的读线程中。
? at_send_command_full_nolock函数:运行在主线程里面。将一个ATcommand命令写入modem后进入睡眠状态(使用pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了ATcommand的最终响应并返回。
? readerLoop函数: 运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前ATcommand的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(ATcommand的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。
第二部分, Java代码
1.package简介:
Android中,telephony相关的java代码主要在下列目录中:
v frameworks/base/telephony/java/android/telephony
提供Android telephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager、SMSManager。
v frameworks/base/telephony/java/com/android/internal/telephony
v frameworks/base/services/java/com/android/server
提供一系列内部接口,目前第三方应用还不能使用。当前似乎只packages/apps/Phone能够使用.
v packages/apps/Phone
目录packages/apps/Phone是一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。
2.详细介绍
v TelephonyManager(telephony/java/android/telephony/TelephonyManager.java)主要使用两个IBinder接口(AIDL接口)来访问telephony功能:
? ITelephony, 提供与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
? ITelephonyRegistry,其提供一个通知机制,将一些底层状态或变更通知给电话服务的用户,如网络状态、信号强度等。它的服务实现在框架代码中,即:frameworks/base/services/java/com/android/server/TelephonyRegistry.java。
底层通知的来源是GSMPhone/CDMAPhone通过PhoneNotifier接口的实现者DefaultPhoneNotifier将具体的事件转化为函数调用,通知到TelephonyRegistry。TelephonyRegistry再通过两种方式通知用户:
§ Broadcast机制广播事件;
§ 通过服务用户在TelephonyRegistry.java中注册的IPhoneStateListener接口,实现回调机制。注册函数是用户可见的:
TelephonyManager.listen();
->TelephonyRegistry.listen(,IPhoneStateListener callback,,);
v 接口PhoneNotifier定义电话事件的通知方法
v 类DefaultPhoneNotifier从PhoneNotifier派生而来。在其方法实现中,通过调用service ITelephonyRegistry来发布电话事件。
v 接口Phone描述了对电话的所有操作接口。 PhoneBase直接从Phone派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA和GSM的操作。
v PhoneProxy也从Phone直接派生而来。当当前不需要区分具体是CDMA Phone还是GSMPhone时,可使用PhoneProxy。
备注: |
在PhoneApp创建时, sPhoneNotifier = new DefaultPhoneNotifier(); ... sCommandsInterface = newRIL(context, networkMode, cdmaSubscription); 然后根据当前phone是cdma还是gsm,创建对应的phone,如 sProxyPhone = newPhoneProxy(new GSMPhone(context,sCommandsInterface, sPhoneNotifier));
|
v PhoneFactory.java: 获取电话实例,如GSMPhone/CDMAPhone.
PhoneFactory.makeDefaultPhones();
->PhoneFactory.makeDefaultPhone();//注册相应的Phone实例
->Phone phone = (Phone)PhoneFactory.getDefaultPhone();
v 接口IPhoneStateListener: 手机状态监听接口(AIDL),其服务实现在/frameworks/base/telephony/java/android/telephony/PhoneStateListener.java
v 接口CommandsInterface 描述了对电话的所有操作接口,如命令, 查询状态,以及电话事件监听等。
v 类BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。
v 类RIL又派生自BaseCommands。RIL负责实际实现CommandsInterface中的接口方法。RIL通过Socket和rild守护进程进行通讯。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,发送给rild。RIL中的几个类如下:
? RILRequest:代表一个电话服务命令请求;
? RIL.RILSender:负责处理命令的发送;
? RIL.RELReceiver:负责处理命令相应以及主动上报信息的接受;线程RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种。
§ 电话事件,RIL_UNSOL_xxx, RIL读取相应数据后,发送message给对应的handler(详见函数processUnsolicited);
§ 命令的异步响应。(详见函数processSolicited)
v RILConstants.java : 定义了电话服务的具体命令。
v 抽象类Call代表一个call,有两个派生类CdmaCall和GsmCall。
第三部分, 流程分析
1.Outgoing Call流程。
v TwelveKeyDialer.java
|_onKeyUp()
|_placeCall()
v OutgoingCallBroadcaster.java,
|_onCreate()
|_sendOrderedBroadcast(broadcastIntent, PERMISSION,
new OutgoingCallReceiver(), null, Activity.RESULT_OK, number,null);
|_ OutgoingCallReceiver
|_onReceive()
|_ doReceive()
|_context.startActivity(newIntent);
v InCallScreen.java
|_ onCreate()
|_onNewIntent()
|_placeCall()
v PhoneUtils.java
|_placeCall()
v GSMPhone.java,
|_dial()
v GsmCallTracker.java,
|_dial()
v RIL.java
|_dial()
|_ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
|...
|send(rr);
2. Incoming Call的流程:
v 创建GsmPhone时,mCT = new GsmCallTracker(this);
v 创建GsmCallTracker时:
? cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE,null);
? mCallStateRegistrants.add(r);
v RIL中的RILReceiver线程首先读取从rild中传来的数据:
? processResponse()
? processUnsolicited()
v 对应于incoming call,RIL收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,触发mCallStateRegistrants中的所有记录。
v GsmCallTracker处理EVENT_CALL_STATE_CHANGE,调用pollCallsWhenSafe
v 函数pllCallsWhenSafe 处理:
§ lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
§ cm.getCurrentCalls(lastRelevantPoll);
v RIL::getCurrentCalls
§ RILRequestrr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS,result);
§ ...
§ send(rr);
v 接着RIL调用processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS的返回结果
v GsmCallTracker的handleMessage被触发,处理事件EVENT_POLL_CALLS_RESULT,调用函数 handlePollCalls
v handlPollCalls 调用phone.notifyNewRingingConnection(newRinging);
v PhoneApp中创建CallNotifier
v CallNotifier注册:
§ registerForNewRingingConnection ->mNewRingingConnectionRegistrants.addUnique(h, what, obj);
第四部分, 参考文档
1.《Android系统原理及开发详解》
2. http:/www.meegozu.com/thread-391-1-1.html
3. http://www.netmite.com/android/mydroid/development/pdk/docs/telephony.html
4. http://wenku.baidu.com/view/8d57336aaf1ffc4ffe47ac75.html
5. http:/wenku.baidu.com/view/ca78fabef121dd36a32d8258.html
[置顶] Android Message System
2010-10-19 8:43:00 阅读210 评论0 192010/10 Oct19
Android的消息系统框架如下图所示:
Android消息系统构成元素如下:
1. 消息队列
2. 发送消息
3. 读取消息
4. 消息分发
5. 消息循环线程
Android消息系统设计的类:
1. Looper:负责管理线程的消息队列和消息循环。
1) 构造一个MessageQueue实例;
2) 维护一个线程实例;
3) Looper一旦调用loop(),则会一直执行,直到遇到一个target=null的Message;
2. MessageQueue:以执行时间为序列的一个Message队列。以IdleHandler接口的函数queueIdle()表征MessageQueue状态是否为Idle.
3. Message:消息
1) 执行时间;
2) Handler(既:target);
3) Runnable;
4. Handler :
1) 设置Message.target (默认为 this);
2) 将Message实例加入MessageQueue中;
3) 处理消息
4) 发送消息
调用层次结构:
1. Handler通过snedMessage()等发送Message消息到MessageQueue;
2. MessageQueue通过enqueueMessage()将接收到的Message放入队列,相反则通过removeMessage将接收到的Message从队列中移除;
3. Looper通过loop()在MessageQueue队头取出Message,直到遇到target为null的Message
4. Looper 将读取到的Message发送到Handler进行处理;
[置顶] Android 网络开发详解
2010-9-13 16:33:28 阅读51 评论0 132010/09 Sept13
1. 概要
2. Android平台上可以使用的几种网络接口
Android平台的网络应用绝大部分都是基于Java的编程接口的,也就是说我们开发类似的运用的时候可以有多种选择,比J2me确实好多了。
2.1. 标准Java接口
java.net.*下面提供了访问 HTTP 服务的基本功能。使用这部分接口的基本操作主要包括:
· 创建 URL 以及 URLConnection / HttpURLConnection 对象
· 设置连接参数
· 连接到服务器
· 向服务器写数据
· 从服务器读取数据
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
try {
// 创建一个 URL 对象
URL url = new URL(your_url);
// 创建一个 URL 连接,如果有代理的话可以指定一个代理。
URLConnection connection = url.openConnection(Proxy_yours);
// 对于 HTTP 连接可以直接转换成 HttpURLConnection 这样就可以使用一些 HTTP 连接特定的方法,
//HttpURLConnection connection = (HttpURLConnection)url.openConnection(Proxy_yours);
// 在开始和服务器连接之前,可能需要设置一些网络参数
connection.setConnectTimeout(10000);
connection.addRequestProperty("User-Agent", "J2me/MIDP2.0");
// 连接到服务器
connection.connect();
// 往服务器写数据,数据会暂时被放到内存缓存区中
// 如果仅是一个简单的 HTTP GET,这一部分则可以省略
OutputStream outStream = connection.getOutputStream();
ObjectOutputStream objOutput = new ObjectOutputStream(outStream);
objOutput.writeObject(new String("this is a string..."));
objOutput.flush();
// 向服务器发送数据并获取应答
InputStream in = connection.getInputStream();
// 处理数据
...
} catch (Exception e) {
// 网络读写操作往往会产生一些异常,所以在具体编写网络应用时
// 最好捕捉每一个具体以采取相应措施
}
2.2. Apache接口
Apache HttpClient 是一个开源项目,弥补了 java.net.* 灵活性不足的缺点,为客户端的HTTP编程提供高效、最新、功能丰富的工具包支持。Android 平台引入了 Apache HttpClient 的同时还提供了对它的一些封装和扩展,例如设置缺省的HTTP超时和缓存大小等。早期的 Android 曾同时包括 Commons HttpClient (org.apache.commons.httpclient.*) 和 HttpComponents (org.apache.http.client.* ),不过当前版本 (1.5) 中开发者只能使用后者,也就是说类似以下的一些类:
使用这部分接口的基本操作与 java.net.* 基本类似,主要包括:
· 创建 HttpClient 以及 GetMethod / PostMethod, HttpRequest 等对象
· 设置连接参数
· 执行 HTTP 操作
· 处理服务器返回结果
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.params. HttpConnectionParams;
import org.apache.http.client.params. HttpClientParams;
try {
// 创建 HttpParams 以用来设置 HTTP 参数(这一部分不是必需的)
HttpParams params = new BasicHttpParams();
// 设置连接超时和 Socket 超时,以及 Socket 缓存大小
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// 设置重定向,缺省为 true
HttpClientParams.setRedirecting(params, true);
// 设置 user agent
HttpProtocolParams.setUserAgent(params, userAgent);
// 创建一个 HttpClient 实例 ,注意 HttpClient httpClient = new HttpClient(); 是Commons HttpClient
// 中的用法,在 Android 1.5 中我们需要使用 Apache 的缺省实现 DefaultHttpClient
HttpClient httpClient = new DefaultHttpClient(params);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
// 错误处理,例如可以在该请求正常结束前将其中断
httpGet.abort();
}
// 读取更多信息
Header[] headers = response.getHeaders();
HttpEntity entity = response.getEntity();
Header header = response.getFirstHeader("Content-Type");
} catch (Exception ee) {
//
} finally {
// 释放连接
client.getConnectionManager().shutdown();
}
android.net.* 实际上是通过对 Apache 的 HttpClient 的封装来实现的一个 HTTP 编程接口,同时还提供了 HTTP 请求队列管理, 以及 HTTP 连接池管理,以提高并发请求情况下(如转载网页时)的处理效率,除此之外还有网络状态监视等接口。
以下是一个通过 AndroidHttpClient 访问服务器的最简例子:
import import android.net.http.AndroidHttpClient;
try {
AndroidHttpClient client = AndroidHttpClient.newInstance(“your_user_agent”);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
// 错误处理
}
// 关闭连接
client.close();
} catch (Exception ee) {
//
}
另外当我们的应用需要同时从不同的主机获取数目不等的数据,并且仅关心数据的完整性而不关心其先后顺序时,也可以使用这部分的接口。典型用例就是 android.webkit 在转载网页和下载网页资源时,具体可参考 android.webkit.* 中的相关实现。
Android电源管理分析
2011-1-18 11:29:26 阅读139 评论0 182011/01 Jan18
Android的电源管理部分,在纵向上分为四层:
第一部分:Application层
主要是利用Android Frameworks提供的API进行应用级的开发,需要注意的是相关权限的申明:
< uses-permission android:name="android.permission.WAKE_LOCK" />
< uses-permission android:name="android.permission.DEVICE_POWER" />
第二部分:Frameworks层
1. / frameworks/base/core/java/android/os/PowerManager.java
v PowerManager:提供对设备的电源进行管理
? 常见锁类型
Flag Value |
CPU |
Screen |
Keyboard |
PARTIAL_WAKE_LOCK |
On |
Off |
Off |
SCREEN_DIM_WAKE_LOCK |
On |
Dim |
Off |
SCREEN_BRIGHT_WAKE_LOCK |
On |
Bright |
Off |
FULL_WAKE_LOCK |
On |
Bright |
Bright |
? 附加锁类型:仅仅是对Screen有影响
Flag Value |
Description |
ACQUIRE_CAUSES_WAKEUP |
This flag will force the screen and/or keyboard to turn on immediately. |
ON_AFTER_RELEASE |
If this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer |
v WakeLock:电源管理对象锁
v Usage:
? 获取PowerManager实例PowerManager pm=(PowerManager) getSystemService(Context.POWER_SERVICE);
? 获取PowerManager.WakeLock实例mWakeLock=pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "TEST");
? Device On
mWakeLock.acquire();
? Device Off
mWakeLock.release();
2. / frameworks/base/core/java/android/os/Power.java
提供一些电源管理的函数,如reboot()。其是JNI的上层接口,其往下调用android_os_power.cpp
3. / frameworks/base/services/java/com/android/server/PowerManagerService.java
电源管理服务,AIDL接口IPowerManager的实现者。
第三部分:JNI层
/frameworks/base/core/jni/android_os_power.cpp
第四部分:Native层
/hardware/libhardware/power/power.c
与Linux kernel交互
第五部分:Linux驱动层
/drivers/android/power.c
第六部分:使用电源管理注意事项
1. 可在onCreate时设置该界面的电源管理,在onDestroy时取消设置
2. 可在onResume时设置该界面的电源管理,在onPause时取消设置
3. 注意设置是以Activity为单位,不是以应用为单位
4. 注意在AndroidManifest.xml中声明该应用有设置电源管理的权限
5. 注意加锁解锁要成对出现
6. 注意多个用途最好用多个锁,不要一锁多用,以免出错
7. 注意对运行在后台和异常时对锁的处理
8. 注意在网络连接或传输时最好加锁,以免传输被中断
9. 注意加锁以保证程序逻辑
第七部分:参阅资料
Xplanner简介
2010-9-7 10:34:15 阅读160 评论0 72010/09 Sept7
Xplanner是为极限编程团队(XP)准备的基于网络的项目规划跟踪工具。Xplanner需要使用Java,JSP,Struts和MySQL(其他数据库的支持需用户的提供)来实现。
1、 特点
l 简单的规划模型
l 虚拟便笺
l 支持记录和跟踪项目:迭代、用户流程和任务
l 未完成流程的智能化继续(拷贝未完成任务,拷贝的流程是交联的)
l 分布式整合指令 (以email通知)
l 在个人/组级别,实现在线时间跟踪和时间表单生成
l 度量生成 (组速度, 个人时间, ...)
l 迭代速度的图表, 用Scrum控制管理方法消减任务 (http://www.controlchaos.com/burndown.htm ), 任务类型的分布, 处理, 及其他
l 能够在任务和流程中附加注释(以附件形式)。
l 迭代估计准确度的视图
l 项目与迭代信息可以导出为XML和MPX格式
l TWiki-style 文本格式化支持,可以支持外部工具集成和动态wiki word链接
l 综合的、可扩展的验证
l 对高级的XPlanner集成和扩展提供SOAP接口
l 支持英语、西班牙语、法语、德语、意大利语、巴西葡萄牙语和丹麦语
2、 XPlanner的安装
(1) 基本配置
Ant, 推荐XPlanner的相关数据库MySQL, JDK 1.44或以上版本, Servlet 2.3 (JSP 1.2) 或者更好的兼容容器(例如:Tomcat 4.x )
(2) 获得源代码
下载最新版本或者由CVS检出源码。 如果由CVS上检出, 就要判断是否选择第三方库。如果要检出所有模块,就要选择XPlanner和类子模块。否则,可以只检出源代码和第三方库. 目前如果使用自己的类库就必须编辑Ant的build.xml文件。今后这个可以通过一个外部属性文件来配置。
如果使用Tomcat,请阅读Tomcat安装注释.
(3) 配置数据库
XPlanner使用关系数据库储存数据,所以必须通过XPlanner准备好可用的关系数据库。
l 设置 JDBC 驱动:
XPlanner中带有与MySQL and HSQLDB的连接所需要的JDBC驱动。如果使用不同的数据库(参阅对其他数据库的支持),要取得所用数据库的JDBC驱动jar文件,在编译XPlanner网络应用程序前,将之放置于lib/jdbc-driver 目录下。在继续进行之前,最好确定在XPlanner之外驱动/数据库的联合能否工作。
l 配置属性:
访问xplanner.properties文件,用来设置应用于你的数据库的数据库连接属性。你需要编辑如下属性:
hibernate.connection.url
hibernate.connection.username
hibernate.connection.password
(xplanner.properties文件中有MySQL和HSQL的注释范例。)
l 安装大纲schema:
注释:如果你已经有XPlanner 0.4 MySQL数据库,那么可以使用xplanner/sql里的特定版本脚本来升级。这些脚本也可以作为升级除了MySQL外的其他数据库的示例。注意在升级前备份数据!
安装数据库schema由两个选择:自动和手动。
自动安装比较简单,但可能破坏存在的数据 (包括之前的XPlanner安装), 所以使用它时必须非常小心!只有当在专用的XPlanner数据库中进行全新的安装时,我们才推荐这个选项。对于自动安装,在XPlanner根目录中运行:
$ ant install.db.schema
这个目标将使用xplanner.properties中的信息来创建XPlanner需要的表格。如果名为‘admin’用户的不存在,它还会创建该用户‘admin’ 且密码也是‘admin’。初次进入XPlanner时要使用这个用户,注意立即更改密码。
对于手动安装,在XPlanner根目录中运行:
$ ant create.db.schema.script
这个命令将在项目根目录下将创建一个名为xplanner_schema.sql 的文件。这个文件包括了XPlanner运行xplanner.properties文件中指定的数据库时所需的所有的DDL命令。当创建了这个文件后,对可能的问题,可以检查、编辑和执行它。这个进程的细节依赖于本地配置和使用的特定数据库。
(4)编译xplanner.war文件
进入基本XPlanner目录(含build.xml)。运行
$ ant build.war
在xplanner顶层目录下将会编译xplanner.war文件。
(5)配置验证和授权
XPlanner使用申请提供方式的(application-provided)可插入的验证和授权。已经定义了三个角色:查看者(viewer) 编辑者(editor)和管理员(admin)。查看者只能对XPlanner数据进行只读访问,甚至无法关注到可编辑数据的相关链接。编辑者可以编辑大部分项目,但是只能编辑它们本身的注释和描述信息。管理角色可以进行另外的操作例如编辑其他人的描述或者增加新的用户。管理员应该具有相关的编辑者和管理者的角色。
(6)用户定制属性(可选的)
XPlanner分布式整合指令使用email通知等待的人集成它们的代码。配置email,设置xplanner.properties 中如下的属性:
xplanner.mail.smtp.host (default=localhost)
xplanner.mail.from (default=xplanner@xplanner.org)
支持简单的wiki word格式化。要激活wiki word格式化,非注释化twiki.wikiadapter属性。 默认的wiki适配器将使用TWiki wiki 方案(twiki.scheme.wiki属性)来格式化wiki word。简单的适配器无法知道是否定义了一个wiki word,所以用户无法知道定义过的和未定义的words之间不同的链接。
(7) 配置WAR文件
按照你的servlet容器的说明来配置xplanner.war 文件。对于Tomcat, 将xplanner.war文件放在 $CATALINA_HOME/webapps目录中。.
(8)为服务器端图形生成配置你的JVM (可选)
如果在类似Unix系统上运行XPlanner并且XPlanner的图表有问题,这可能是因为在服务器上运行的不是X window服务器。这会导致网络服务器日志上的错误,类似于……
java.lang.NoClassDefFoundError
at com.jrefinery.chart.axis.Axis.<init>(Unknown Source)
at com.jrefinery.chart.axis.CategoryAxis.<init>(Unknown Source)
at com.jrefinery.chart.axis.HorizontalCategoryAxis.<init>(Unknown Source)
at com.jrefinery.chart.ChartFactory.createLineChart(Unknown Source)
at de.laures.cewolf.taglib.CewolfChartFactory.getChartInstance(CewolfChartFactory.java:79)
at de.laures.cewolf.taglib.ChartDefinitionImpl.getChart(ChartDefinitionImpl.java:89)
at de.laures.cewolf.taglib.ChartImageDefinitionImpl.getChart(ChartImageDefinitionImpl.java:99)
at de.laures.cewolf.util.Renderer.renderChart(Renderer.java:97)
at de.laures.cewolf.util.Renderer.render(Renderer.java:83)
at de.laures.cewolf.taglib.ChartImageDefinitionImpl.ensureRendered(ChartImageDefinitionImpl.java
...
不过有一些解决的办法。用户可以在XPlanner主机上运行X服务系统。通常,如果主机是专门的服务器,这是不需要的。用户也可以在运行servlet引擎时加入一个参数到JVM中。
加入-Djava.awt.headless=true到JVM 命令行。除非应用程序调用询问屏幕尺寸之类的方法,否则它会工作良好的。使用JDK1.4.1_03,我曾使Cewolf在Linux下为另一个程序正常工作,所以它也应该能为XPlanner工作。
- Neal Sanche
我试过这个方法,它在我们的Linux服务器上可以工作。对于Tomcat,在启动服务器前,在JAVA_OPTS环境变量前加选项(编辑已用的startup.sh程序来启动Tomcat)。还有一个称为Xvfb (X virtual frame buffer)的程序可以用来作为完整X服务程序的替代。还有一些可能的方法,但我推荐在尝试其他方法之前使用JVM选项。
(9) 启动servlet容器,开始使用Xplanner
3、 工艺和结构
XPlanner使用分层的结构构成。在最低层,MySQL中存储着不变的数据,并使用JDBC访问。一般来说,相关的数据并不直接访问,而是类似于Java对象使用Tornado面向对象影射引擎来装载。“事物逻辑”层(操作)生成计划域对象并提供它们到表示层,表示层使用JSP(包括个别的定制标记),表单处理程序,CSS和HTML格式化它们。页面上所有的静态文本将储存在源代码包中以支持国际化。
XPlanner使用手册(收藏)
2010-9-7 10:24:06 阅读26 评论0 72010/09 Sept7
XPlanner 是一个基于Web的XP团队计划和跟踪工具。XP的开发概念如iteration、user stories等,XPlanner都提供了相对应的管理工具,XPlanner支持XP开发流程,并解决利用XP思想来开发项目所碰到的问题。
XPlanner特点包括:简单的模型规划,虚拟笔记卡(Virtual note cards),iterations、user stories与工作记录的追踪,未完成stories将自动迭代,工作时间追踪,生成团队效率,个人工时报表,SOAP界面支持。
二、 公司研发类项目XPlanner初始设定要求
1. 根据项目类型不同分为两种类型对项目在XPlanner上进行初始设定:
对于定制开发类项目,以实际项目名称作为XPlanner的项目名称。在项目下建立首次迭代,制定迭代起止时间。根据公司目前项目情况,建议每次迭代过程 不要超过一个月。在制定首次迭代后制定至少一个User Story;在User Story中至少制定一个任务。
对于短期实施类项目,以实际项目名称作为XPlanner的项目名称。一般一次迭代应该完成此项目。
2. 在XPlanner内建立人员列表包括:
公司研发部人员(项目经理设为管理员权限、其他开发人员为编辑者权限)
公司其他部门项目人员,包括项目助理或可以作为项目跟踪者的市场部门人员或者公司管理者
客户:可以作为项目客户或跟踪者
其他访问者:可看到该项目进展情况的访客。设为访客
三、 项目组各成员使用过程及要求
1. 项目负责人:鉴于目前XPlanner仅适用于软件研发类项目适用,所以定为研发部项目负责人为XPlanner项目管理员。项目负责人主要负责 XPlanner中项目、迭代、User Story、任务的设置、编辑、删除。项目管理员也可后续由项目助理担任作为执行人和跟踪者。对于项目负责人应该:
在启动新项目前依据《软件项目开发计划》在XPlanner上加入新项目,要对本项目目标加以简要说明。将《软件项目开发计划》附加入"备注/附件"
按照《软件项目开发计划》细分出本次迭代,订立迭代目标、迭代周期、完成的User Story、涉及人员、迭代完成评定标准。形成《软件项目迭代计划》(格式附后)提交审批。要求可度量的明确的迭代目标是《软件项目迭代计划》的重要内容之一。
按照迭代目标、迭代周期、涉及人员制定任务草稿,提交《迭代会议》讨论。
招集相关人员召开《迭代会议》通告《软件项目迭代计划》即迭代目标、迭代周期、完成的User Story、迭代完成评定标准。并对任务草稿进行讨论,制定出确定的任务列表。填入《软件项目迭代计划》。
按照《软件项目迭代计划》在XPlanner上添加相应的迭代、完成的User Story、任务。正式启动本次迭代
如果项目负责人是跟踪者角色,需随时在"我的资料"界面跟踪迭代执行情况。
迭代时间要求不允许调整。User Story可以变更、增加、编辑、删除或调整、延续到后续迭代中去,任务也可以变更、增加、编辑、删除或调整、延续到其他User Story中去,但需要开会讨论决定。任务里的评估工时是XP开发方式的重点控制内容。一般不允许调整(通过对评估工时和实际工时的比对可以统计精确 度)。
要及时督促项目研发人员添加、更新XPlanner上各角色负责的内容,做到每日下班前打开XPlanner监控项目进行情况。
本次迭代完成后按照《利用XPlanner对项目进行持续进度跟踪管理》的要求作出简要的《软件项目迭代总结》
2. 编辑者:定为本项目的研发人员、软件测试人员。要求对于本项目的研发人员应该:
参加《迭代会议》,充分了解《软件项目迭代计划》即迭代目标、迭代周期、完成的User Story、迭代完成评定标准。并对任务草稿进行讨论。
接受自己部份的任务列表,对任务内涵要求有清晰明确的认识。
要求每日在"我的资料"界面相应的任务列表内的"操作"表型图标上进入"编辑任务时间"界面,填写本日此任务工时。在"编辑任务时间"页面中的操作应实时进行,要填写开始时间和结束时间。要求格式为时间格式: YYYY-MM-DD HH:MM。
对于需求设计任务主要是以模块功能描述的方式说明,完成识别就是完成此User Story某部份的设计实现方案,必须有设计输出。对于开发类项目任务主要是以功能描述的方式说明,完成识别就是完成此功能描述地实现代码。对于测试类任 务主要是以测试用例列表或模块说明,完成识别就是完成此测试用例列表,或模块的全部业务路径,输出BUG单。对于Overhead(整合、管理)类任务主 要是以工作内容说明,完成识别就是完成工作说明的内容。
在"我的资料"界面下部"时间表"操作进入"时间表",此界面可统计本人的时间累计工时情况。作为个人时间管理参考。
如果实际某任务工时累计超过了评估工时,将会显示"实际的工作时间已经超过了当前任务的评估工时.请进行新的任务评估以便于 XPlanner 的状态视图可以更精确."信息。此时可以按实际工时数调整原有的评估工时。新的评估工时与原有评估工时在"精确度"界面有显示。对于此类情况要求随着评估 工时的准确程度提高而减少。
当任务完成后从"我的资料"界面相应的任务列表内的任务可进入任务管理界面,点击完成任务可以结束此任务。表示此任务已完成。
3. User Story跟踪者:一般就是项目负责人,也可以是由项目助理担任作为执行人。如果是客户直接参与研发,也可以是客户本身。可在"我的资料"界面跟踪迭代执 行情况。可及时和项目组沟通。可配合督促项目相关人员添加、更新XPlanner上各角色负责的内容,做到每日下班前打开XPlanner监控项目进行情 况。
4. 客户:可以是本公司市场部门相关人员,如果是客户直接参与研发,也可以是客户本身。可在"我的资料"界面跟踪迭代执行情况。
四、 项目组负责人利用XPlanner对项目进行持续进度跟踪管理过程
1. 在项目一次迭代完成后要求对迭代过程进行总结。主要内容有:
按照《软件项目迭代计划》总结迭代目标、迭代周期、涉及人员任务完成情况。
总结迭代目标达成情况,按照即定评定标准得出符合迭代目标程度。
总结本次迭代调整情况,找出调整原因。逐步减少调整。
在统计表上得出本次迭代趋势。
召开《迭代会议》作出简要的《软件项目迭代总结》(可以和下次迭代会议合并召开)
如果是项目软件、系统交付迭代,还需其他相关人员参加《迭代会议》,以说明刚完成的迭代输出的详细情况。
本项目全部迭代完成后向质控部门提交项目完成报告。内含此项目的质量目标总结统计。目前项目完成状态,客户投诉解决状态。
项目负责人负责各次《软件项目迭代计划》、《软件项目迭代总结》质量记录的保存。并定期提交归档。
2. 主要统计分析界面说明:
时间表:总体时间表可以通过设定时间区段、包含人员条件统计:实际工时按项目(按项目种类在此时间区段的实际工时分类比例);实际工时按迭代(按迭代种类 在此时间区段的实际工时分类比例);实际工时按用户需求User Story(按User Story种类在此时间区段的实际工时分类比例);个人时间表可以通过设定时间区段统计本人:时间区段内每日实际工时;实际工时按项目(按项目种类在此时 间区段的实际工时分类比例);实际工时按迭代(按迭代种类在此时间区段的实际工时分类比例);实际工时按用户需求User Story(按User Story种类在此时间区段的实际工时分类比例);
User Story主界面:列出该迭代下所有的User Story,所有User Story的合计评估工时(当前评估),实际完成,剩余工时合计。列表中显示所有的User Story的分类合计,可以点击列表头排序相应列(其他列表同此)。Progress条棒显示完成比例。
度量界面:在某迭代时间区段内人员的工时情况统计。上表显示时间区段内人员的实际工时排列(按降序)。下表(开发人员平均有效工时)显示在某迭代时间区段 内评估工时(当前评估)按人员排列(按降序)。如果在"编辑任务时间"界面填写时间时填入结对开发人员,则条棒区分显示。
精确度界面:表头列出:当前评估工时(括号内为原始评估工时:创建任务时首次填入),实际完成工时,剩余工时(当前评估工时-实际完成工时)。列表依次 为:User Story名称;优先级;此任务完成工时;当前评估工时;原始评估工时;符合度(此任务完成工时-原始评估工时)/ 原始评估工时 *100%;原始评估工时与当前评估工时比例;此任务完成工时与原始评估工时比例;此任务完成工时与当前评估工时比例;
统计界面:人员利用率表(缺省不使用)。迭代进度表:横轴为迭代时间区段,纵轴为工时量(长度自适应);红线为当前评估工时,蓝线为实际完成工时。 Burn Down Chart剩余工时表:横轴为迭代时间区段,纵轴为工时量(长度自适应);红线为本节点剩余工时合计。此二表由后台时间触发引擎 Quartz自动按照设定 Quartz Configuration项触发执行填入数据。
if ($ != jQuery) { $ = jQuery.noConflict(); }
使用位置服务和地图API
2010-9-3 10:14:14 阅读163 评论0 32010/09 Sept3
获取地理位置信息的方法有GPS技术和Google网络定位技术,Android将这些不同的定位技术统称为LBS(Location-Based Services,基于位置的服务)。
一. android.location.*包
1. LocationManager:是用于周期性获得当前设备位置信息。获取LocationManager实例通过Context.getSystemService(Context.LOCATION_SERVICE)方法获取。
主要常量及函数列表
a. GPS_PROVIDER:GPS位置提供者的名称;
b. NETWORK_PROVIDER:网络位置提供者的名称;
c. boolean addGpsStatusListener(GpsStatus.Listener listener):添加GPS状态监听器;
d. List<String> getAllProviders():获取所有的位置提供者的名称列表;
e. String getBestProvider(Criteria criteria,Boolean enabledOnly):根据criteria获取最优的位置提供者的名称;
f. GpsStatus getGpsStatus(GpsStatus status):获取GPS的状态信息;
g. Location getLastKnownLocation(String provider):获取最后已知的位置信息;
h. LocationProvider getProvider(String name):获取位置提供者对象;
i. List<String> getProviders(Criteria criteria,boolean enabledOnly):根据criteria获取的位置提供者名称列表;
j. List<String> getProviders(boolean enabledOnly):获取位置提供者名称列表;
k. void removeGpsStatusListener(GpsStatus.Listener listener):移除GPS状态监听器;
2. Criteria:用于描述查询LocationProvider的条件列表。主要函数列表如下。
a. void setAccuracy(int accuracy):设置经度和纬度的精确度;精确度参数有:
i. ACCURACY_FINE:高精度;
ii. ACCURACY_COARSE:低经度;
b. void setAltitudeRequired(boolean altitudeRequired):设置是否提供海拔信息;
c. void setBearingRequired(boolean bearingRequired):设置是否提供方位信息;
d. void setPowerRequired(int level):设置功率;功率参数有:
i. POWER_HIGH;
ii. POWER_LOW;
iii. POWER_MEDIUM;
e. void setSpeedRequired(boolean speedRequired):设置是否提供速度信息;
3. LocationProvider:位置信息提供者,由LocationManager的实例获得。
4. LocationListner:位置信息监听器,主要用于监听位置信息的改变。其主要的几个需要重写的函数如下:
a. abstract void onLocationChanged(Location location):当位置发生变化时调用;
b. abstract void onProviderDisabled(String provider):当LocationProvider被禁用时调用;
c. abstract void onProviderEnabled(String provider):当LocationProvider可用时调用;
d. abstract void onStatusChanged(String provider,int status,Bundle extras):当LocationProvider的硬件状态信息改变时调用;
5. GpsStatus.Listener :GPS状态监听器
a. abstract void onGpsStatusChanged(int event),event类型如下
i. GPS_EVENT_STARTED
ii. GPS_EVENT_STOPPED
iii. GPS_EVENT_FIRST_FIX
iv. GPS_EVENT_SATELLITE_STATUS
二. com.google.android.maps.*包:Google Map的API在应用中嵌入地图信息需要MapActivity和MapView控件。Android的地图相关的API的包不是系统标准的包,而是可选包,因此必须显示的在AndroidManifest.xml文件中声明应用将使用地图库。
<users-library android:name=”android.google.android.maps”/>
1. MapActivity:主要用于Google Map显示的Activity.
2. MapView:是一个能够通过网络于Google Map Service下载显示地图的控件。为了使开发人员合理的使用Google Map服务,在使用之前必须为当前应用程序签名的密钥生成一个MD5指纹,然后利用此MD5指纹在Google生成Map API密钥的网站生成一个所有用此密钥签名的应用程序通用的Map API密钥。具体操作流程如下:
a. 找到debug.keystore,WinXP系统下的位置为Documents and Settings\<user>\Local Setting\Application Data\Android目录下。也可以在eclipse->Window->Preferences->Android->Build中查看其存放路径。
b. 利用Java的keytool生成MD5密钥,keytool的参数如下:
keytool参数 |
描述 |
-list |
在终端打印出MD5指纹 |
-keystore <keystore_name>.keystore |
目标密钥所在的密钥库 |
-storepass <password> |
密钥库密码 |
-alias <alias_name> |
密钥库内生成MD5指纹的密钥别名 |
-keypass <password> |
指定密钥密码 |
c. 到http://code.google.com/android/map-api-signup.html去申请Map API密钥。需要注意的是,在应用程序发布时,需要根据为应用程序签名的密钥重新生成Map API密钥,并在程序中修改引用到Map API密钥的地方。
<com.google.android.maps.MapView
android:id="@+id/myMapView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="0dtslw4qDN01uXHvmk9wer6Zf3Ngh2Und3eYhLQ"/>
d. 主要的一些函数:
i. void setSatellite(boolean on)设置是否显示卫星视图;
ii. void setTraffic(boolean on) 设置是否显示交通视图;
iii. void setStreetView(boolean on)设置是否显示街景视图;
iv. MapController getMapController() 获取MapController实例;
3. Overlay:若要在地图上标记位置,就需要用到Overlay,它是一种专门用于在地图上用2 D图像进行标记的类。由于其默认的绘制方法draw()并不绘制任何图像,所以通常,我们会重新创建一个集成自Overlay的类,并重新实现其draw()方法。将Overlay添加到MapView的方法如下:
List<Overlay> overlays = mapView.getOverlays();
MyLocationOverlay locLay = new MyLocationOverlay(this,map);
locLay.enableMyLocation();
overlays.add(locLay);
4. MyLocationOverlay:继承自Overlay,用于绘制用户当前位置。
5. MapController:主要用于实现MapView的缩放、移动等操作。
Symbian描述符操作
2009-2-28 13:20:02 阅读36 评论0 282009/02 Feb28
Symbian OS
让一个字符串进入2进制代码
Static char hellorom[]=”hello”
_LIT(khellorom,”hello”)
在栈中获得字符串的指针
Const char* helloptr=hellorom
TPtrC helloptr=khellorom
获得在栈中字符串的指针
Char hellostack[sizeof(hellorom)];
Strcpy(hellostack,hellorom);
TBufC<5> hellostack=khellorom;
获得在堆中字符串的指针
Char* helloheap=
(char *)malloc(sizeof(hellorom));
strcpy(helloheap,hellorom);
HBufC* helloheap=
Khellorom.AllocLC();
a)TPtrC相当于不变的字符串常量.
b)TPtr相当与String类型。Tbuf相当于char[]。前者与后者的唯一区别是,后者需要指定分配的栈空间大小。
C)HBufC* 与char*类似。分配的是堆上的空间。
HBufC* textResource;
//两种字符串附值方法
textResource = StringLoader::LoadLC( R_HEWP_TIME_FORMAT_ERROR );
textResource =iEikonEnv->AllocReadResourceL(R_EXAMPLE_TEXT_HELLO);
TBuf<32> timeAsText;
timeAsText = *textResource;
/* 数据类型转换*/
TBuf 转换为 TPtrC16
TBuf<32> tText(_L("2004/11/05 05:44:00"));
TPtrC16 tPtrSecond=tText.Mid(17,2);
TPtrC16 转换为 TBufC16
TPtrC16 tPtrSecond=tText.Mid(17,2);
TBufC16<10> bufcs(tPtrSecond);
TBufC16 转换为 TPtr16
TBufC16<10> bufcs(tPtrSecond);
TPtr16 f=bufcs.Des();
TPtr16 转换为 TBuf
TBuf<10> bufSecond;
bufSecond.Copy(f);
TBuf 转换为 TPtr16
TBuf<10> bufSecond(_L("abc"));
TPtr16 f;
f.Copy(bufSecond);
TBuf 转换为 TInt
TInt aSecond;
TLex iLexS(bufSecond);
iLexS.Val(aSecond);
TInt 转换为 TBuf
TBuf<32> tbuf;
TInt i=200;
tbuf.Num(i);
1.串转换成数字
TBuf16<20> buf(_L( "123" ) );
TLex lex( buf );
TInt iNum;
lex.Val( iNum );
2.数字转换成串
TBuf16<20> buf;
TInt iNum = 20;
buf.Format( _L( "%d" ) , iNum );
3.将symbian串转换成char串
char* p = NULL;
TBuf8<20> buf( _L( "aaaaa" ) );
p = (char *)buf.Ptr();
4.UTF-8转换成UNICODE
CnvUtfConverter::ConvertToUnicodeFromUtf8( iBuf16 , iBuf8 );
5.UNICODE转换成UTF-8
CnvUtfConverter::ConvertFromUnicodeToUtf8( iBuf8 , iBuf16 );
6.将char串转换成symbian串
char* cc = "aaaa";
TPtrC8 a;
a.Set( (const TUint8*)cc , strlen(cc) );
7、将TPtrc8与TPtrc16之间转化
// Get a iBuf8 from a iBuf16 (data are not modified)
TPtrC8 ptr8(reinterpret_cast<const TUint8*>(iBuf16.Ptr()),(iBuf16.Size()*2));
iBuf8=ptr8;
// Get a iBuf16 from a iBuf8 (data are not modified)
TPtrC16 ptr16(reinterpret_cast<const TUint16*>(iBuf8.Ptr()),(iBuf8.Size()/2));
iBuf16=ptr16;
The second one takes each character and convert it to the other format. The 16-bit to 8-bit conversion may not always succeed in this case:
Code:
// Get a iBuf8 from a iBuf16 (data are modified)
CnvUtfConverter::ConvertFromUnicodeToUtf8(iBuf8,iBuf16);
// Get a iBuf16 from a iBuf8 (data are modified)
CnvUtfConverter::ConvertToUnicodeFromUtf8(iBuf16,iBuf8);
This second method requires to include the utf.h header and to link against charconv.lib.
/*memset memcpy strcpy */
memset主要应用是初始化某个内存空间。用来对一段内存空间全部设置为某个字符。
memcpy是用于COPY源空间的数据到目的空间中,用来做内存拷贝可以拿它拷贝任何数据类型的对象。
strcpy只能拷贝字符串了,它遇到'\0'就结束拷贝。
strcpy
原型:extern char *strcpy(char *dest,char *src);
用法:#include <string.h>
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
memcpy
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#include <string.h>
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
memset
原型:extern void *memset(void *buffer, int c, int count);
用法:#include <string.h>
功能:把buffer所指内存区域的前count个字节设置成字符c。
说明:返回指向buffer的指针。
Symbian图形显示
2009-1-14 14:31:57 阅读80 评论0 142009/01 Jan14
在Symbian OS 中,所有的绘图都是在窗口中进行的,窗口是与系统进行交互的基本单位。我们在进行绘图前,首先要声明一个窗口:
CreateWindowL();
然后通过SetRect()来设置窗口的大小。
SetRect(aRect);
之后我们就可以进行绘图工作了。
在Symbian 系统中,所有的绘图工作都是通过Graphics Context完成的。其中包括绘制点、绘制矩形和绘制文本等。所有的Graphics Context都由CGraphicsContent类派生。
CGraphicsContent类包括的特性有:
画笔(Pen):表示当前Graphics Context所有要绘制的线的绘图模式,包括颜色、宽度、样式等,可以通过SetPenColor(), SetPenSize(), SetPenStyle()等方法进行设置。
刷 子(Brush):表示当前Graphics Context用以填充的绘图模式,包括填充颜色,样式、背景色等,可通过 SetBrushColor(), SetBrushOrigin(), SetBrushStyle(),UseBrushPattern(), DiscardBrushPattern() 等方法进行设置。
字体(Font):表示Graphics Contex当前所使用绘制文本的字体,使用UseFont(),DiscardFont()方法来设置或取消字体。
位置(Position):表示Graphics Contex的当前位置。可以通过MoveBy(), MoveTo()等方法来改变当前位置。
原点 (Origin):定义了相对于设备的原点的偏移量,默认值为(0,0),可以通过SetOrigin()来改变。
剪辑(Clipping):定义了需要进行裁切的区域,通过SetClippingRect(),CancelClippingRect()方法进行设置或取消裁切区域。
在Symbian系统中,我们通过CGraphicsDevice实现Graphics Device,他指定了我们要操作的具体设备类的接口。
void DrawText(const TDesC& aText,const TPoint& aPosition)
void DrawText(const TDesC& aText,const TRect& aBox,TInt aBaselineOffset, TTextAlign aAlignment=ELeft,TInt aLeftMargin=0)
其中第一个直接在窗口中绘制文本,其中aText给出来要绘制的文本内容,aPosition制定了要绘制文本的起始位置。
第二个在绘制文本的同时,还要以给定的aBox绘制一个矩形外框。aAlignment参数指定了文本的对齐方向,默认为左对齐;aLeftMargin指定了间隔距离,默认值为0。
由于Symbian系统的内存受限制,所以,没有使用的字体系统是不会调入内存的,因此我们在绘制文本前,应该首先使用UseFont()设置系统的字体:
void UseFont(const CFont* aFont)
这样系统会将字体调入内存中。
在我们不使用这个字体以后,为了节省内存,要使用DiscardFont()释放掉内存中的字体。
void DiscardFont()
当画笔的宽度大于一个像素的时候,系统会以aPoint为圆心,画笔的宽度为直径绘制一个圆,并用画笔的颜色填充这个圆。
void DrawLine(const TPoint& aPoint1,const TPoint& aPoint2)
DrawLine()在aPoint1和aPonit2之间绘制一条直线。
void DrawLineTo(const TPoint& aPoint)
DrawLineTo()从当前点向aPoint绘制一条直线。
void DrawLineBy(const TPoint& aVector)
DrawLineBy()从当前点向相对当前点位置为aVector的点绘制一条直线。
void DrawPolyLine(const CArrayFix<TPoint>* aPointList)
DrawPolyLine()根据给定的位置数组从第一个点开始向第二个点绘制直线,然后以第二个点为起始点向第三个点绘制直线。。。。。。直到最后一个点。
在这里需要注意的一点是,在绘制直线的时候,系统并不绘制直线的最后一点,如果我们希望绘制一条包括最后一点的直线,我们可以使用上边的Plot()方法绘制最后一个点。
我 们可以使用系统提供的方法直接绘制五种简单图形,分别是矩形(rectangle)、圆角矩形(rounded rectangle)、多边形 (polygon)、椭圆形(ellipse)和饼型(pie slice)。绘制模式和填充模式与当前的画笔(Pen)、刷子(Brush)设置相同
void DrawRect(const TRect& aRect)
DrawRect()在屏幕上根据给定的aRect绘制矩形。
void DrawRoundRect(const TRect& aRect,const TSize& aCornerSize)
DrawRoundRect()在屏幕上根据给定的aRect绘制矩形,并根据给定的aCornerSize确定圆角的直径。
TInt DrawPolygon(const CArrayFix<TPoint>* aPointList,TFillRule aFillRule=EAlternate)
TInt DrawPolygon(const TPoint* aPointList,TInt aNumPoints,TFillRule aFillRule=EAlternate)
DrawPolygon()根据给定的点集aPointList按顺序连接并按照aFillRule规则填充多边形。
void DrawEllipse(const TRect& aRect)
DrawEllipse()在给定的aRect区域中绘制椭圆形。如果给定的区域是正方形,那么将绘制出圆形。
void DrawPie(const TRect& aRect,const TPoint& aStart,const TPoint& aEnd)
DrawPie()通过给定的起始点aStart和结束点aEnd在由aRect形成的椭圆内截取相应的饼型区域。
4.3.1读取:
首先 我们定义要读取的位图所在位置:
_LIT (KMultiBitmapFilename,"\\system\\apps\\graphics\\images.mbm");
其中images.mdm是我们的位图文件经过压缩打包的结果,是一个多位图文件。我们要在.mmp文件中作如下定义:
START BITMAP images.mbm
HEADER
TARGETPATH \system\apps\graphics
SOURCEPATH ..\bitmaps
SOURCE c12 image1.bmp
SOURCE c12 image2.bmp
END
系统产生一个位图头文件.mbg,这个头文件提供了一个访问位图的ID。例如,在Epoc32\include中的IMAGES.mbg文件包含如下内容:
enum TMbmImages{
EMbmImagesImage1,
EMbmImagesImage2,
};
接下来我们定义:
CFbsBitmap* iImage1;
CFbsBitmap* iImage2;
然后我们就可以将mdm中的位图文件读取出来:
iImage1 = new (ELeave) CFbsBitmap();
CleanupStack::PushL(iImage1);
TInt loadException = iImage1 ->Load(KMultiBitmapFilename,EMbmImagesImage1);
User::LeaveIfError(loadException);
CleanupStack::Pop(iImage1);
void DrawBitmap(const TPoint& aTopLeft,const CFbsBitmap* aSource)
这里,aTopLeft指定了要绘制的位图的左上角坐标,aSource给出了要绘制的位图的内容。
void DrawBitmap(const TRect& aDestRect,const CFbsBitmap* aSource)
将给出的位图aSource绘制在指定的矩形区域aDestRect中。
void DrawBitmap(const TRect& aDestRect,const CFbsBitmap* aSource,const TRect& aSourceRect)
在给出的位图aSource中截取aSourceRect区域,将其内容绘制在指定的矩形区域aDestRect中。
可以通过使用TBitmapUtil类的一些方法对位图进行像素级的处理。包括:
void Begin(const TPoint& aPosition):设置当前要处理的像素位置,并锁定堆。
void End():解除对堆的锁定。
void SetPos(const TPoint& aPosition):改变当前像素位置至aPosition。
void IncXPos():将当前的X坐标自增1。
void DecXPos():将当前的X坐标自减1。
void IncYPos():将当前的Y坐标自增1。
void DecYPos():将当前的Y坐标自减1。
TUint32 GetPixel() const:获取当前像素的RGB值。
void SetPixel(TUint32 aValue):设置当前像素的RGB值。
下面我们通过将一个位图反转后写入另一张位图中的操作来说明TBitmapUtil类的使用方法。
利用前面已经生成并读取的位图:CFbsBitmap* iImage1和CFbsBitmap* iImage2。这里iImage2的长宽均大于iImage1,我们将iImage1反转装入iImage2中
首先关联要操作的位图:
TBitmapUtil bitmap1Util(iBitmap1);
TBitmapUtil bitmap2Util(iBitmap2);
接下来开始对位图操作,并设置初始点为(0,0):
bitmap1Util.Begin(TPoint(0,0));
bitmap2Util.Begin(TPoint(0,0));
下面从iBitmap1逐像素读取,并写入iBitmap2中:
TSize inSize = iBitmap1->SizeInPixels();
TInt xPos;
for (TInt yPos=0;yPos<inSize.iHeight;yPos++)
{
bitmap1Util.SetPos(TPoint(0,yPos));
bitmap2Util.SetPos(TPoint(yPos,0));
for (xPos=0;xPos<inSize.iWidth;xPos++)
{
bitmap2Util.SetPixel(bitmap1Util);
bitmap1Util.IncXPos();
bitmap2Util.IncYPos();
}
}
最后结束操作,清理堆栈
bitmap1Util.End();
bitmap2Util.End();
这样,我们就完成了将iImage1反转并写入iImage2中的工作。
posted on 2011-05-25 09:35 Tiger_Dog 阅读(5689) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步