[Workaround] AIR randomly crash on Mac
这几天很郁闷, AIR 会时不时的罢工:

这个错误属于 AIR 内部 crash,没有任何 actionscript error 抛出来,我一句一句 trace 最后发现发生在 sqlite 的 sql 语句调用。但。。。它又不一定每次都会 crash。。。。
基本上如果最后的产品在 Mac 上跑然后撞到这个问题就会很欢乐。。。
不幸的是,crash 的 workaround 又被我我无情的发现了。

根本原因是 AIR 在 de-serialize 数据库中的 column 数据时,如果多个 column 都是复杂对象,而且对象的 ref 又纠结在一起的时候就会 boom。
解决办法就是在写入 complex object 的时候先手动 serialize 到
1 | ByteArray |
里,然后再塞给 db,取数据的时候再手动把
1 | ByteArray |
de-serialize 还原成 complex object。
详细的 crash reproduce 以及 workaround 实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | <?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()"> <mx:Script> <![CDATA[ private namespace safe; private namespace normal; private function init():void { use namespace safe; //use namespace normal; //如果把 namespace 换成 normal 在 mac 上会造成 crash var dbFile:File = new File("app-storage:/test.db"); if(dbFile.exists) { dbFile.deleteFile(); } var connection:SQLConnection = new SQLConnection(); connection.open(dbFile); connection.begin(); executeSql(connection,"CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar0 OBJECT NOT NULL, bar1 OBJECT NOT NULL);"); connection.commit(); //喂点复杂的 object graph 给 sqlite... var obj0:Object = {prop0:"blah"}; var obj1:Object = {prop0:obj0, prop1:"test"}; var obj2:Object = {prop0:obj1, prop2: obj0}; connection.begin(); executeSql(connection,"INSERT INTO foo (bar0, bar1) VALUES(:bar0, :bar1);",{bar0:obj1,bar1:obj2}); for(var i:int=0;i<1000;i++) { obj0.prop0=Math.random(); executeSql(connection,"UPDATE foo SET bar0=:bar0, bar1=:bar1;",{bar0:obj1,bar1:obj2}); executeSql(connection,"SELECT * FROM foo"); } connection.commit(); } normal function executeSql(connection:SQLConnection,str:String,param:Object=null,mapping:Class=null):SQLResult { var s:SQLStatement = new SQLStatement(); s.sqlConnection = connection; s.text = str; if(param!=null) { for(var k:String in param) { var p:Object = param[k]; s.parameters[":"+k] = p; } } if(mapping!=null) { s.itemClass = mapping; } s.execute(); return s.getResult(); } safe function executeSql(connection:SQLConnection,str:String,param:Object=null,mapping:Class=null):SQLResult { var s:SQLStatement = new SQLStatement(); s.sqlConnection = connection; s.text = str; if(param!=null) { for(var k:String in param) { var p:Object = param[k]; if(!isPlainType(p)) {//将 复杂 Object 先 write 到 byteArray 里 p = objectToByteArray(p); } s.parameters[":"+k] = p; } } if(mapping!=null) { s.itemClass = mapping; } s.execute(); var result:SQLResult = s.getResult(); if(result.data!=null) { var c:int = result.data.length; for(var i:int=0;i<c;i++) { var row:Object = result.data[i]; for(var j:String in row) { var val:Object = row[j]; if(val is ByteArray) {// 还原 byteArray 里面的 object val = objectFromByteArray(val as ByteArray); row[j]=val; } } } } return result; } private function isPlainType(obj:Object):Boolean { var type:String = typeof(obj); switch (type) { case "number": case "string": case "boolean": { return true; } case "object": { return (obj is Date); } } return false; } private function objectToByteArray(obj:Object):ByteArray { var bytes:ByteArray = new ByteArray(); bytes.writeObject(obj); bytes.position=0; return bytes; } private function objectFromByteArray(bytes:ByteArray):Object { return bytes.readObject(); } ]]> </mx:Script> </mx:WindowedApplication> |

好多中文注释 >_< … 用啥编辑器呢?
对了, 我建议Pawaca老师给blog装一个Gravatar~~