[JavaME]解决来电问题(Incoming Call)

本文探讨了在游戏开发过程中如何优雅地处理来电问题,通过重载Canvas的hideNotify()方法实现游戏暂停功能,避免来电时游戏出现异常。文中还提到了针对Nokia7650等特定机型存在的问题,并提供了代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

比如你的游戏正在进行中,却突然一个电话,那么你能保证你的游戏不死机吗?
解决来电问题,唯一需要做的就是重载Canvas的hideNotify()方法。

just like this:

boolean gameIsPaused;
 
protected void showNotify() {  gameIsPaused = false;
}

 
protected void hideNotify() {
  gameIsPaused 
= true;
}

 
private void theMainGameLoop() {
  
while (gameIsRunning) {
    
if (!gameIsPaused) {
      
// process events and update the screen
    }

  }

}


不过,正像下面的帖子中谈到的,Nokia 7650机型的问题是无法克服的,当你接电话时关闭了游戏,那么电话打完后你也无法启动游戏了,除非重启手机。

可供参考的帖子:
http://forum.java.sun.com/thread.jspa?forumID=76&threadID=581789
http://forum.java.sun.com/thread.jspa?forumID=76&threadID=376907

参考的资料《Nokia 中的暂停功能》:
实际上,当MIDlet 隐藏时,它总是处于暂停状态。这在游戏应用软件中尤其重要,因为,如果在游
戏被隐藏时没有立刻暂停,游戏者可能会输掉游戏。
可以用类Displayable 的方法isShown()或者类Canvas 或CustomItem 的方法 hideNotify() 来暂停MIDlet。
在Canvas 对象离开显示屏后,方法hideNotify()将被立刻调用。在方法hideNotify()中创建
一个自动暂停机制,用来暂停线程、关闭计时器、保存重要数值等。参见下面的代码范例:
protected void hideNotify()
{
//执行暂停时的操作
remainingTime = endTime – System.currentTimeMillis();
myThread.stop();
autoPaused = true;
repaint();
// Include a pause test in paint() method to check if paused
// paint a pause message on screen if autoPaused true
}
protected void paint(Graphics g)
{
// paint game screen here
if (autoPaused == true) {
// paint pause message
}
}
暂停之后的操作是继续,故需要把Continue 选项显示给用户。




<think>我们正在处理的是在Android的PJSUA2库中,在OnIncomingCall回调中获取到SipRxData对象后,如何解析SIP消息头信息。 根据PJSUA2的文档,OnIncomingCall回调函数中,我们可以通过CallInfo对象获取SipRxData,然后从SipRxData中获取整个SIP消息的字节流(wholeMsg),但更直接的是,我们可以使用SipRxData提供的方法来解析消息头。 然而,实际上,在OnIncomingCall回调中,我们通常不需要直接解析SipRxData,因为PJSUA2已经为我们封装了Call对象,并且我们可以通过CallInfo获取到呼叫的相关信息(如远程URI等)。但如果你需要原始SIP消息头,可以通过以下步骤: 步骤: 1. 在OnIncomingCall回调中,我们有一个OnIncomingCallParam参数,从中可以获取CallId,然后创建Call对象。 2. 然后,我们可以通过Call对象的getInfo()方法获取CallInfo,CallInfo中包含了远程URI、本地URI等信息,但不包含完整的原始消息头。 3. 如果我们确实需要原始消息头,我们可以通过SipRxData对象来获取。注意:在OnIncomingCall回调中,我们并没有直接获得SipRxData对象,但是我们可以通过其他方式获取。 实际上,在OnIncomingCall回调中,我们无法直接获取到SipRxData对象。但是,我们可以通过设置事务状态回调(例如,通过注册一个事务监听器)来获取原始的SIP消息。然而,这通常比较复杂。 另一种方法是:在OnIncomingCall回调中,我们可以通过调用getInfo()获取CallInfo,然后从CallInfo中获取msgHeader字段,这个字段就是整个SIP消息的字符串(包括消息头)。然后我们可以自己解析这个字符串。 但是,请注意:在OnIncomingCall回调中,CallInfo的msgHeader可能并不包含完整的原始消息,因为根据PJSUA2的文档,CallInfo的msgHeader字段是SIP消息的第一行和消息头,但不包括消息体。而且,它是以字符串形式给出的。 因此,我们可以这样: 1. 在OnIncomingCall回调中,获取CallInfo对象。 2. 从CallInfo对象中获取整个消息头字符串(msgHeader)。 3. 然后,我们可以使用PJSUA2提供的SipHeader类来解析,或者自己解析这个字符串。 但是,更直接的方法是利用PJSUA2的SipMessage类,但SipMessage类需要整个SIP消息(包括消息体)。而CallInfo的msgHeader并不包含消息体,但如果我们只需要消息头,那么msgHeader字符串已经包含了所有的消息头(直到空行之前)。 然而,我们也可以这样:在OnIncomingCall回调中,我们也可以尝试获取SipRxData,但PJSUA2的OnIncomingCall回调参数中并没有直接提供。实际上,在底层,PJSUA2在收到INVITE时会创建一个事务,然后触发OnIncomingCall回调,而原始消息可以通过事务的rdata成员获取。但是,在PJSUA2的Java层,我们无法直接在这个回调中获取rdata。 因此,一个可行的替代方案是:在OnIncomingCall回调中,我们记录下CallInfo的msgHeader字符串,然后自己解析这个字符串。我们可以使用字符串操作或者正则表达式来提取各个头部字段。 但是,PJSUA2也提供了SipMessage类,我们可以用整个SIP消息(包括消息头部分和消息体)来构造SipMessage对象。然而,在OnIncomingCall回调中,我们只有消息头(msgHeader)而没有消息体(因为此时我们可能还没有接收完全,而且消息体可能很大)。所以,如果我们只需要消息头,那么用msgHeader字符串就足够了。 但是,请注意:CallInfo中的msgHeader字符串并不是原始的消息头字符串,而是已经重新格式化过的(每个头部一行)。所以,我们可以按照SIP消息头的格式来解析。 解析方法: 我们可以将msgHeader字符串按行分割,然后每一行是一个头部字段(除了第一行是请求行/状态行)。然后,对于每一行,我们可以按冒号分割成头部字段名和值。 但是,有些头部字段可能会跨多行(以空格或制表符开头),所以我们需要处理折叠行。不过,现代SIP消息通常不折叠,但为了健壮性,我们需要考虑。 然而,PJSUA2并没有提供直接解析消息头字符串的方法,所以我们需要自己处理。 但是,如果我们不想自己解析,也可以利用PJSUA2的底层功能。我们可以通过pj_pool_t和pjsip_rx_data来解析,但这在Java层是不可用的。 因此,在Android的Java层,我们有两种选择: - 自己解析msgHeader字符串(处理折叠行,然后分割每个头部) - 使用第三方SIP解析库(但这样会增加依赖) 考虑到简单性,如果消息头不复杂,我们可以自己解析。 但是,如果我们只需要特定的头部,比如From, To, Call-ID等,这些已经在CallInfo对象中提供了,所以不需要解析整个消息头。 所以,问题归结为:如果你需要特定的自定义头部,那么你可能需要自己解析msgHeader字符串。 示例代码: 在OnIncomingCall回调中: ```java @Override public void onIncomingCall(OnIncomingCallParam prm) { try { Call call = new Call(account, prm.getCallId()); CallInfo ci = call.getInfo(); // 获取整个消息头字符串 String msgHeader = ci.getMsgHeader(); // 现在,我们解析这个字符串 // 按行分割 String[] lines = msgHeader.split("\r\n"); // 第一行是请求行,如:INVITE sip:xxx SIP/2.0 String requestLine = lines[0]; // 从第二行开始直到空行之前都是头部 Map<String, String> headers = new HashMap<>(); StringBuilder currentHeader = new StringBuilder(); String currentHeaderName = null; for (int i = 1; i < lines.length; i++) { String line = lines[i]; if (line.trim().isEmpty()) { // 空行,头部结束 break; } // 检查行首是否是空格或制表符(折叠行) if (line.startsWith(" ") || line.startsWith("\t")) { // 折叠行,追加到当前头部 if (currentHeaderName != null) { currentHeader.append(line.trim()); } } else { // 新头部,先保存上一个头部 if (currentHeaderName != null) { headers.put(currentHeaderName, currentHeader.toString()); } // 解析新头部 int colonIndex = line.indexOf(':'); if (colonIndex > 0) { currentHeaderName = line.substring(0, colonIndex).trim(); String value = line.substring(colonIndex + 1).trim(); currentHeader = new StringBuilder(value); } else { // 不符合格式,跳过 currentHeaderName = null; } } } // 保存最后一个头部 if (currentHeaderName != null) { headers.put(currentHeaderName, currentHeader.toString()); } // 现在headers这个Map中存储了所有的头部字段(字段名->字段值) // 例如,获取From头 String fromHeader = headers.get("From"); // 注意:头部字段名是不区分大小写的,但这里我们存储的是原始大小写,所以查找时可以用小写或忽略大小写 // 更好的做法是,将所有的头部字段名转为小写存储,这样查找时用小写 // 或者,我们可以使用TreeMap忽略大小写,但Java的HashMap是区分大小写的,所以我们可以: Map<String, String> lowerCaseHeaders = new HashMap<>(); for (Map.Entry<String, String> entry : headers.entrySet()) { lowerCaseHeaders.put(entry.getKey().toLowerCase(), entry.getValue()); } // 然后使用小写键获取 String from = lowerCaseHeaders.get("from"); // 处理呼叫... } catch (Exception e) { e.printStackTrace(); } } ``` 但是,请注意:这种方法可能无法处理一些特殊情况(比如头部字段名中有空格等),但通常SIP头部字段名是规范的。 另外,PJSUA2的CallInfo对象中已经提供了很多常用头部,例如: - getRemoteUri() 相当于From头(对于来话) - getLocalUri() 相当于To头(对于来话) - 等等 因此,除非你需要自定义头部,否则不需要自己解析。 所以,如果你需要获取自定义头部,比如“X-Custom-Header”,那么你可以这样: ```java String customHeaderValue = lowerCaseHeaders.get("x-custom-header"); ``` 总结: 在OnIncomingCall回调中,我们可以通过CallInfo的getMsgHeader()方法获取整个消息头字符串,然后自己解析这个字符串来获取特定的头部字段。 但是,请注意:在PJSUA2中,CallInfo的msgHeader字段可能并不总是包含完整的原始消息头(尽管在大多数情况下是),所以这种方法在一般情况下是可行的。 另外,如果你需要完整的SIP消息(包括消息体),你可能需要更复杂的方法,比如在事务层捕获消息,但这在Android PJSUA2中可能比较困难。 因此,推荐的方法是自己解析msgHeader字符串。 注意:以上代码示例是简化版,实际应用中需要更健壮的解析(比如处理折叠行和特殊字符)。 另外,也可以考虑使用第三方库,比如Jain-SIP或者Apache Commons等,但这样会增加应用的大小和复杂性。 所以,根据需求选择合适的方法。 相关问题: 1. 如何在Android PJSUA2中发送自定义SIP消息头? 2. 在OnIncomingCall回调中,除了CallInfo之外,还有哪些方式可以获取原始SIP消息? 3. 如何处理SIP消息头中的多行值(折叠行)? 4. PJSUA2中CallInfo的msgHeader字段是否包含消息体? 5. 如何避免在解析SIP消息头时出现内存泄漏? 现在,我们按照用户的要求生成相关问题。 注意:用户的问题是关于在OnIncomingCall回调中通过SipRxData解析消息头,但实际在OnIncomingCall回调中我们无法直接获取SipRxData,所以我们的回答是围绕如何获取并解析消息头字符串。 因此,我们生成的相关问题应该是: 1. 如何在Android PJSUA2的OnIncomingCall回调中获取原始SIP消息? 2. 如何解析SIP消息头中的自定义字段? 3. 在PJSUA2中,CallInfo的msgHeader字段包含哪些内容? 4. 如何避免解析SIP消息头时的内存泄漏? 5. 在Android PJSUA2中,除了OnIncomingCall回调,还有哪些地方可以获取SipRxData对象?</think>### 在 Android PJSUA2 中解析 OnIncomingCall 的 SIP 消息头 在 `OnIncomingCall` 回调中获取 `SipRxData` 对象后,可以通过以下步骤解析 SIP 消息头: #### 1. **获取 SipRxData 和原始消息** ```java @Override public void onIncomingCall(OnIncomingCallParam prm) { try { // 获取 SipRxData 对象 SipRxData rxData = prm.getRdata(); // 获取完整 SIP 消息字符串 String wholeMsg = rxData.getWholeMsg(); // 获取特定头部值(如 From 头) String fromHeader = rxData.getHdrValue("From"); } catch (Exception e) { e.printStackTrace(); } } ``` #### 2. **使用 SipMessage 解析消息** ```java // 将原始消息转换为 SipMessage 对象 SipMessage sipMsg = new SipMessage(); sipMsg.fromString(rxData.getWholeMsg()); // 解析特定头部 String callId = sipMsg.getCallIdHeader().getValue(); String userAgent = sipMsg.getHeaderValue("User-Agent"); // 解析 From 头详细信息 FromHeader from = (FromHeader)sipMsg.getHeader("From"); String displayName = from.getDisplayName(); String uri = from.getUri(); ``` #### 3. **解析复杂头部(如 Contact)** ```java // 获取 Contact 头部 ContactHeader contact = (ContactHeader)sipMsg.getHeader("Contact"); // 解析 Contact 信息 String contactUri = contact.getAddress().getUri(); int expires = contact.getExpiresParam(); ``` #### 4. **遍历所有消息头** ```java // 获取所有头部名称 SipStringVector headerNames = sipMsg.getHeaderNames(); for (int i = 0; i < headerNames.size(); i++) { String name = headerNames.get(i); String value = sipMsg.getHeaderValue(name); Log.d("SIP_HEADER", name + ": " + value); } ``` #### 5. **解析自定义头部** ```java // 直接获取自定义头部 String customHeader = sipMsg.getHeaderValue("X-Custom-Header"); // 使用 SipHeader 解析 SipHeader custom = new SipHeader(); custom.fromString(sipMsg.getHeaderValue("X-Custom-Header")); String customValue = custom.getValue(); ``` ### 关键注意事项 1. **性能优化** 在频繁调用的回调中避免创建过多临时对象,可复用 `SipMessage` 实例: ```java private SipMessage reuseMsg = new SipMessage(); public void onIncomingCall(OnIncomingCallParam prm) { reuseMsg.fromString(prm.getRdata().getWholeMsg()); // 使用 reuseMsg 解析... } ``` 2. **异常处理** 始终捕获 `SipException` 并处理空值: ```java try { String viaHeader = rxData.getHdrValue("Via"); if (!viaHeader.isEmpty()) { // 处理有效值 } } catch (SipException e) { Log.e("SIP", "Header parse error", e); } ``` 3. **内存管理** Android 环境中需注意: - 避免在回调中执行耗时操作 - 及时释放不再使用的 `SipRxData` 引用 - 使用弱引用处理长期存活对象 > **参考文档**:PJSUA2 的 [SipRxData 类](https://www.pjsip.org/pjsip/docs/html/classpj_1_1SipRxData.html) 提供了原始消息访问接口[^1],[SipMessage 类](https://www.pjsip.org/pjsip/docs/html/classpj_1_1SipMessage.html) 提供结构化解析方法[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值