Android in_app billing: java.lang.NullPointerException

本文记录了一个在实现in_appbillingfeature过程中遇到的问题:在添加信用卡界面时程序崩溃。崩溃原因是尝试在未授权支付的国家使用支付功能,提供了解决此问题的方法。

今天做in_app billing feature时发现一个问题,当进行到 添加 Credit Card界面里程序会crash (下图是在play store里支持付费app时的界面,会遇到同样的问题,所以用在此处作为参照)


点击 Continue之后 就会Crash... Crash 信息如下:


Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@40942068

Starting: Intent { cmp=com.android.vending/com.google.android.finsky.activities.FinskyCreateInstrumentActivity (has extras) } from pid 746
Could not find method android.app.Activity.getActionBar, referenced from method com.google.android.finsky.layout.CustomActionBarFactory.getInstance
VFY: unable to resolve virtual method 115: Landroid/app/Activity;.getActionBar ()Landroid/app/ActionBar;
VFY: replacing opcode 0x6e at 0x0006
VFY: dead code 0x0009-0010 in Lcom/google/android/finsky/layout/CustomActionBarFactory;.getInstance (Landroid/app/Activity;)Lcom/google/android/finsky/layout/CustomActionBar;
VFY: dead code 0x0012-0017 in Lcom/google/android/finsky/layout/CustomActionBarFactory;.getInstance (Landroid/app/Activity;)Lcom/google/android/finsky/layout/CustomActionBar;
GC_EXTERNAL_ALLOC freed 223K, 42% free 3778K/6471K, external 1017K/1028K, paused 39ms
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0x401a3568)


FATAL EXCEPTION: main

java.lang.NullPointerException

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.getAddressSpec(AddCreditCardFragment.java:249)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.setupWidgets(AddCreditCardFragment.java:349)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.onBillingCountriesLoaded(AddCreditCardFragment.java:286)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.access$400(AddCreditCardFragment.java:55)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment$3.run(AddCreditCardFragment.java:271)

at com.google.android.finsky.billing.GetBillingCountriesAction.run(GetBillingCountriesAction.java:40)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.loadBillingCountries(AddCreditCardFragment.java:268)

at com.google.android.finsky.billing.creditcard.AddCreditCardFragment.onCreateView(AddCreditCardFragment.java:243)

at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:870)

at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1080)

at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:622)

at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1416)

at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420)

at android.os.Handler.handleCallback(Handler.java:587)

at android.os.Handler.dispatchMessage(Handler.java:92)

at android.os.Looper.loop(Looper.java:130)

at android.app.ActivityThread.main(ActivityThread.java:3694)

at java.lang.reflect.Method.invokeNative(Native Method)

at java.lang.reflect.Method.invoke(Method.java:507)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)

at dalvik.system.NativeStart.main(Native Method)


产生这个错误的原因是:Google 发现你处于没有被授权支付的国度。这好像是无法躲过检查的,SIM卡里的地理信息好像是伪装不了的。

When an invalid country (one not authorized/enabled for purchases like China) code is read from the SIM and not found in the list mCountries  ( BillingPreferences.BILLING_COUNTRIES ), the mSelectedCountry is set to  NULL:

private void setupWidgets(Bundle bundle)
{
     mSelectedCountry =
BillingUtils.findCountry(BillingUtils.getDefaultCountry(getActivity(), null),
mCountries);
     ...
     mBillingAddress.setAddressSpec(mSelectedCountry,
getAddressSpec(mSelectedCountry));
     ...

}

SUGGESTED FIX:
com.google.android.finsky.billing.creditcard.AddCreditCardFragment.getAddressSpecroutine should check parameter NULL condition.



上网搜索,发现了有人与我遇到了同样的问题:

参考资料:

http://code.google.com/p/android/issues/detail?id=27188这里面详细说明了问题出现的原因

http://code.google.com/p/android/issues/detail?id=28162

python sqlalchemy mysql,以下根据表名 动态生成模型对象,如用sqlalchemy,表中已经有索引和联合索引,是否需要在模型对象中再次声明。 如何检查索引和联合索引是否正确建立 table_name_send = f't_s_sendlist{table_suffix}' model_attributes = { '__tablename__': table_name_send, '__table_args__ ': {'extend_existing': True,'idx_s_Send_phone_msgid_finishedtime':db.Index('phone', 'sms_msgid', 's_finished_time_this'),}, 'id': db.Column(db.Integer, primary_key=True, autoincrement=True), # user:db.Column(db.String(20), nullable=False), #从 s_Task_list 获取 'phone': db.Column(db.String(11), nullable=False), # 号码 'phone_area': db.Column(db.String(50), nullable=True), # 区域 'phone_operator': db.Column(db.String(20), nullable=True), # 运营商 # sms_text:db.Column(db.String(1000), nullable=True) , #从 s_Task_list 获取 'sms_billing_this': db.Column(db.Integer, nullable=True), # 计费条数/条 'sms_msgid': db.Column(db.String(20), nullable=True), # 用于判断 对应关系 # s_channel:db.Column(db.String(20), nullable=True) , #从 s_Task_list 获取 's_back_channel': db.Column(db.String(20), nullable=True, index=True), # 后台通道 # s_bottom_account:db.Column(db.String(20), nullable=True) , #底层帐号,不需要在此,应在通道配置时用到 's_send_time_this': db.Column(db.DateTime, index=True), # 发送时间 's_finished_time_this': db.Column(db.DateTime), # 完成时间 # s_finished_time_this':db.Column(db.DateTime(timezone=True)) , # 完成时间 's_state_time': db.Column(db.DateTime), # 状态获取时间,状态获取时的时间点 # s_submit_state':db.Column(db.String(20), nullable=True) , #提交状态,对任务 's_state_gateway': db.Column(db.String(20), nullable=True), # 网关状态 's_state': db.Column(db.String(20), nullable=True), # 最后状态 成功、失败、无 's_state_for_more': db.Column(db.String(20), nullable=True), # 状态更多描述 'sms_text': db.Column(db.String(1000), nullable=True), # 从 s_Task_list 获取 's_submit_time_this': db.Column(db.DateTime, index=True), # 提交时间,提交时填上 'discontinue': db.Column(db.Integer), # 中止。 当用户已提交后,在任务清单界面,点击按键进行中止任务 'resend_backup': db.Column(db.Integer), # 标识 None:原始状态; 1: 为 重发前备份的记录 2: 重发前备份的发送记录 3: 新增一条的记录,用于触发SRS.sms_send 'status_read': db.Column(db.Integer), # 状态已读 /未读, 1 为已读 model_send = type(table_name_send.capitalize(), (db.Model,), model_attributes)
09-18
python sqlalchemy mysql,以下语句,根据已存在的表名,动态生成对应的Model。出现错误提示sqlalchemy.exc.InvalidRequestError: Table 't_s_sendlist' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object. def generate_sendlistmodel_from_table_name(table_name): model_attributes={ '__tablename__': table_name, 'id':db.Column(db.Integer, primary_key=True, autoincrement=True), # user:db.Column(db.String(20), nullable=False), #从 s_Task_list 获取 'phone':db.Column(db.String(11), nullable=False), # 号码 'phone_area':db.Column(db.String(50), nullable=True), # 区域 'phone_operator':db.Column(db.String(20), nullable=True), # 运营商 # sms_text:db.Column(db.String(1000), nullable=True) , #从 s_Task_list 获取 'sms_billing_this':db.Column(db.Integer, nullable=True) , # 计费条数/条 'sms_msgid':db.Column(db.String(20), nullable=True), # 用于判断 对应关系 # s_channel:db.Column(db.String(20), nullable=True) , #从 s_Task_list 获取 's_back_channel':db.Column(db.String(20), nullable=True, index=True) , # 后台通道 # s_bottom_account:db.Column(db.String(20), nullable=True) , #底层帐号,不需要在此,应在通道配置时用到 's_send_time_this':db.Column(db.DateTime, index=True) , # 发送时间 's_finished_time_this':db.Column(db.DateTime), # 完成时间 # s_finished_time_this':db.Column(db.DateTime(timezone=True)) , # 完成时间 's_state_time':db.Column(db.DateTime), # 状态获取时间,状态获取时的时间点 # s_submit_state':db.Column(db.String(20), nullable=True) , #提交状态,对任务 's_state_gateway':db.Column(db.String(20), nullable=True), # 网关状态 's_state':db.Column(db.String(20), nullable=True), # 最后状态 成功、失败、无 's_state_for_more':db.Column(db.String(20), nullable=True) , # 状态更多描述 'sms_text':db.Column(db.String(1000), nullable=True), # 从 s_Task_list 获取 's_submit_time_this':db.Column(db.DateTime, index=True), # 提交时间,提交时填上 'discontinue':db.Column(db.Integer) ,# 中止。 当用户已提交后,在任务清单界面,点击按键进行中止任务 'resend_backup':db.Column(db.Integer) , # 标识 None:原始状态; 1: 为 重发前备份的记录 2: 重发前备份的发送记录 3: 新增一条的记录,用于触发SRS.sms_send 'status_read':db.Column(db.Integer), # 状态已读 /未读, 1 为已读 # 屏蔽。需要时让动态计入。 cmpp_sendlists':db.relationship("cmpp_Send_list", backref='s_sendlist') # 不会在数据库出现 } model = type(table_name.capitalize(), (db.Model,), model_attributes) print("在 generate_sendlistmodel_from_table_name里面") print(model.__name__) print(model.__tablename__) return model tablelist = ['t_s_senst'] print("打印模型的表名") model_conditions_list=[] for tablename in tablelist: model_conditions=[] model = generate_sendlistmodel_from_table_name(tablename) print(model.__tablename__) print(model) model_conditions.append(model) conditions=[] model_conditions.append(conditions) model_conditions_list.append(model_conditions)
09-17
class SocketStatus(Enum): """连接状态枚举,提高代码可读性""" OFFLINE = "Offline" CONNECTING = "Connecting" RECONNECT = "ReConnect" RECONNECT_ERROR = "ReConnect_error" CONNECT_WAIT_FOR_FLAG = "CONNECT_WAIT_FOR_FLAG" CONNECT_WAIT_FOR_0X05 = "Connect_wait_for_0x05" CONNECT_WAIT_FOR_0X06 = "Connect_wait_for_0x06" CONNECT_WAIT_FOR_0X09 = "Connect_wait_for_0x09" CONNECT_WAIT_FOR_0X0A = "Connect_wait_for_0x0A" CONNECT_WAIT_FOR_0X13 = "Connect_wait_for_0x13" CONNECT_WAIT_FOR_0X55 = "Connect_wait_for_0x55" CONNECT_WAIT_FOR_0X56 = "Connect_wait_for_0x56" ONLINE = "Online" class AsyncSocketWriter: """异步Socket发送器 - 专门负责数据发送""" def __init__(self, writer, pileCode, message_id, total, connections, logger): self.writer = writer self.pileCode = pileCode self.message_id = message_id self.start = " " self.dataLen = " " self.serialNum = "" self.encryption = " " self.Type = " " self.pileType = " " self.chargingGunNum = " " self.protocolVersion = " " self.programVersion = "" self.netType = " " self.SIMCard = " " self.carrier = " " self.modelCode = " " self.charging = False self.running = True self.total = total self.time_count = 0 self.logger = logger self.connections = connections[pileCode] self._write_lock = asyncio.Lock() self._status_handlers = { SocketStatus.OFFLINE: self._handle_offline_status, SocketStatus.CONNECTING: self._handle_connecting_status, SocketStatus.RECONNECT: self._handle_reconnect_status, SocketStatus.RECONNECT_ERROR: self._handle_reconnect_error_status, SocketStatus.CONNECT_WAIT_FOR_FLAG: self._handle_wait_for_flag, SocketStatus.CONNECT_WAIT_FOR_0X05: self._handle_wait_for_0x05, SocketStatus.CONNECT_WAIT_FOR_0X06: self._handle_wait_for_0x06, SocketStatus.CONNECT_WAIT_FOR_0X09: self._handle_wait_for_0x09, SocketStatus.CONNECT_WAIT_FOR_0X0A: self._handle_wait_for_0x0A, SocketStatus.CONNECT_WAIT_FOR_0X13: self._handle_wait_for_0x13, SocketStatus.CONNECT_WAIT_FOR_0X55: self._handle_wait_for_0x55, SocketStatus.CONNECT_WAIT_FOR_0X56: self._handle_wait_for_0x56, SocketStatus.ONLINE: self._handle_online_status } self.send_0x13_flag = False async def start_write(self): self.logger.info(f'✅Socket {self.message_id} 桩号: {self.pileCode} 正在等待发送报文序列') while self.running: try: await self._process_status() await asyncio.sleep(0.5) except asyncio.CancelledError: break except Exception as e: raise e class AsyncSocketReader: """异步Socket读取器 - 专门负责数据接收""" def __init__(self, reader, pileCode, message_id, connections, logger): self.reader = reader self.pileCode = pileCode self.message_id = message_id self.running = True self.connections = connections[pileCode] self._reader_lock = asyncio.Lock() self.logger = logger # 报文处理器映射 self._message_handlers = { 0x02: self._handle_login_message, 0x04: self._handle_status_message, 0x06: self._handle_billing_model_message, 0x56: self._handle_unknown_message_56, 0x0A: self._handle_price_model_message, 0x9C: self._handle_unknown_message_9C } self.flag_0x0A = False self.flag_0x56 = False self.flag_0x13 = False async def start_reading(self): global logger while self.running: try: await self._read_and_process() await asyncio.sleep(0.5) except Exception as e: raise e class AsyncSocketManager: """异步Socket管理器 - 协调Reader和Writer线程""" def __init__(self): self.running = True self._shutdown_event = asyncio.Event() self._cleanup_lock = asyncio.Lock() self.shutdown_requested = asyncio.Event() self.original_sigint_handler = None self.shutdown_requested = asyncio.Event() self.server_config = {'ip': 'xxx.com', 'port': 5455} self.total = 0 self.writer = None self.reader = None self._active_tasks = set() self.connections: Dict[str, Dict] = {} self.tasks: List[asyncio.Task] = [] self._read_lock = asyncio.Lock() # 读取操作专用锁 self._write_lock = asyncio.Lock() # 写入操作专用锁 self._state_lock = asyncio.Lock() # 状态变更锁 self.logger = logging.getLogger(__name__) self.setup_logging() self.connections_count = 0 async def socket_worker(self, message_id: str, pileCode: str, total: int): global logger """异步Socket工作协程""" current_times = 0 while self.running and not self._shutdown_event.is_set(): try: writer, reader = await self.create_socket_connection(self.server_config, message_id, pileCode) if writer is not None and reader is not None: self.writer, self.reader = AsyncSocketWriter(writer, pileCode, message_id, self.total, self.connections, self.logger), AsyncSocketReader( reader, pileCode, message_id, self.connections, self.logger) if current_times == 0: activeNum = len(list(filter(lambda conn: conn.get('isLogin', False), self.connections.values()))) self.logger.info(f'🌍 现有连接 {activeNum} / {self.total}') current_times = time.time() elif time.time() - current_times > 6: activeNum = len(list(filter(lambda conn: conn.get('isLogin', False), self.connections.values()))) self.logger.info(f'🌍 现有连接 {activeNum} / {self.total}') current_times = time.time() if self.writer is not None and self.reader is not None: # 并发启动读写任务 read_task = asyncio.create_task( self.reader.start_reading(), name=f"reader_{pileCode}_{message_id}" ) write_task = asyncio.create_task( self.writer.start_write(), name=f"writer_{pileCode}_{message_id}" ) # 添加到实例级别的活跃任务集合 self._active_tasks.add(read_task) self._active_tasks.add(write_task) await asyncio.gather( read_task, write_task, return_exceptions=True ) except asyncio.CancelledError: self.logger.info("Socket工作协程被取消") except Exception as e: self.logger.error(f"❌Socket {message_id} 桩号: {pileCode} 工作线程异常: {e}") if pileCode in self.connections: if self.connections[pileCode]['writer'] is not None: try: self.connections[pileCode]['writer'].close() await self.connections[pileCode]['writer'].wait_closed() except Exception as close_error: self.logger.error( f"❌ 发生错误 {e} 而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}") del self.connections[pileCode] self.logger.info(f"✅发生错误 {e} 原因Socket {message_id} 桩号: {pileCode} 连接已关闭") await asyncio.sleep(20) # 异常后等待20秒重试 except KeyboardInterrupt: self.logger.error("接收到中断信号...") break async def start_multiple_connections(self, piles_list_config): """异步启动多个Socket连接""" for i, pileCode in enumerate(piles_list_config): task = asyncio.create_task( self.socket_worker( f"conn_{i + 1}", pileCode, len(piles_list_config)) ) self.total = len(piles_list_config) self.tasks.append(task) self.logger.info( f"启动异步Socket连接 {i + 1} 到 {self.server_config['ip']}:{self.server_config['port']} 桩号: {pileCode}") async def create_socket_connection(self, server_config: Dict, message_id: str, pileCode: str): global logger """创建单个socket连接并处理通信""" try: """创建异步Socket连接""" reader, writer = await asyncio.wait_for( asyncio.open_connection(server_config['ip'], server_config['port']), timeout=60.0 ) self.logger.info( f"异步Socket {message_id} 桩号: {pileCode} " f"成功连接到 {server_config['ip']}:{server_config['port']}" ) # 存储连接信息 self.connections[pileCode] = { 'reader': reader, 'writer': writer, 'pileCode': pileCode, 'message_id': message_id, 'config': server_config, 'last_heartbeat1': 0, 'last_heartbeat2': 0, 'last_time_stamp': 0, 'isLogin': False, 'timeout_count_login': 0, 'timeout_count': 0, 'heart_serialNum': "00 00", 'status': 'Offline', 'charging': False, 'priceModelList': [] } return writer, reader except asyncio.TimeoutError: if pileCode in self.connections: if self.connections[pileCode]['writer'] is not None: try: self.connections[pileCode]['writer'].close() await self.connections[pileCode]['writer'].wait_closed() except Exception as close_error: self.logger.error(f"❌ 超时而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}") if pileCode in self.connections: del self.connections[pileCode] self.logger.info(f"✅超时原因 Socket {message_id} 桩号: {pileCode} 连接已关闭") self.logger.error(f"⛔Socket {message_id} 桩号: {pileCode} 连接超时") except ConnectionRefusedError: if pileCode in self.connections: if self.connections[pileCode]['writer'] is not None: try: self.connections[pileCode]['writer'].close() await self.connections[pileCode]['writer'].wait_closed() except Exception as close_error: self.logger.error(f"❌ 连接拒绝而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}") if pileCode in self.connections: del self.connections[pileCode] self.logger.info(f"✅连接拒绝原因 Socket {message_id} 桩号: {pileCode} 连接已关闭") self.logger.error(f"🚫Socket {message_id} 桩号: {pileCode} 连接被拒绝") except Exception as e: if pileCode in self.connections: if self.connections[pileCode]['writer'] is not None: try: self.connections[pileCode]['writer'].close() await self.connections[pileCode]['writer'].wait_closed() except Exception as close_error: self.logger.error( f"❌ 发生错误 {e} 而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}") if pileCode in self.connections: del self.connections[pileCode] self.logger.info(f"❌发生错误 {e} 原因Socket {message_id} 桩号: {pileCode} 连接已关闭") self.logger.error(f"❌Socket {pileCode} 桩号: {pileCode} 发生错误: {e}") except KeyboardInterrupt: self.logger.info("接收到中断信号...") def read_to_array(file_path, remove_empty=True, encoding='utf-8'): """ 读取txt文件到数组 参数: file_path: 文件路径 remove_empty: 是否移除空行 encoding: 文件编码 """ try: with open(file_path, 'r', encoding=encoding) as file: if remove_empty: lines = [line.strip() for line in file if line.strip()] else: lines = [line.strip() for line in file] # 对每行数据进行处理 processed_lines = [] for line in lines: if len(line) == 14 and line.isdigit(): # 将14位数字分割为每2位一组 parts = [line[i:i + 2] for i in range(0, 14, 2)] processed_line = ' '.join(parts) processed_lines.append(processed_line) else: processed_lines.append(line) return processed_lines except FileNotFoundError: print(f"❌错误: 文件 '{file_path}' 不存在") return [] except UnicodeDecodeError: print(f"❌错误: 使用 {encoding} 编码读取文件失败") return [] except Exception as e: print(f"❌未知错误: {e}") return [] async def async_main(): # 定义多个桩号配置 piles_list_config = read_to_array('pileList.txt') if not piles_list_config: print("⚠️ 桩号配置文件为空或无有效数据") return # 创建多socket客户端实例 manager = AsyncSocketManager() # 创建优雅关闭管理器 manager.install_signal_handlers() try: # 启动多个连接 print("启动多个socket连接...") await manager.start_multiple_connections(piles_list_config) # 使用事件等待替代while循环 logger.info("✅ 所有连接已启动,等待工作完成或关闭信号...") await manager.shutdown_requested.wait() logger.info("🔄 接收到关闭信号,开始清理流程...") except asyncio.CancelledError: logger.info("异步任务被取消") # 重新抛出以便上层处理 raise "异步任务被取消" except KeyboardInterrupt: logger.info("接收到键盘中断信号") # 不直接处理,让信号处理器处理 except Exception as e: logger.error(f"❌ 异步Socket客户端异常: {e}") finally: # 确保资源清理 logger.info("开始最终资源清理...") await manager.close_all_connections() manager.restore_signal_handlers() logger.info("✅ 异步Socket客户端已完全关闭") def main(): """主入口函数""" try: asyncio.run(async_main()) except KeyboardInterrupt: logger.info("程序被用户中断") except Exception as e: logger.error(f"程序异常退出: {e}") return 1 return 0 if __name__ == "__main__": exit(main()) 优化以上代码,给出完整代码
最新发布
11-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值