標籤:異常 android ios unity 崩潰
最近 做些準備性得工作和有意思的事情。所以最近做了一個適合ios和android 錯誤資訊捕捉的unity外掛程式。
兩個功能,app崩潰也就是閃退 是開發人員 很頭疼的一件事,還有就是一些莫名得錯誤 有時候也會困擾著我們。現在,unity已經封裝得挺好了,及時出現數組越界,和Null 物件這樣嚴重得錯誤也不會崩潰,聽著挺好,但是這給開發人員帶了很多煩惱啊。因為有時候可能出錯了 你要跟就不知道 ,在什麼地方出得錯誤啊。所以我們要想辦法去解決這個問題。
我們都知道及時app崩潰,其實後台還是在運行得 只不過是 到了另一個線程去處理崩潰得一些問題。那好我們就可以去捕捉到錯誤,供我們去理解這個問題。
首先 我們先針對 ios閃退得錯誤得問題。我們知道 現在ios還是才去得OC編程,身為一個編譯性得語言,一般很小得一個錯誤都會造成整個app得崩潰。
接下來 我們說下原理。 對於 IOS的機制是,它專門有一個類來處理異常錯誤。那就是NSException類,來處理各種錯誤得一個類。我們要的就是這個類中得一個通知,就是 在app出現異常崩潰的事後會通知得一個方法C語言的NSSetUncaughtExceptionHandler。我們就是用這個方法來進行 異常註冊。
NSSetUncaughtExceptionHandler(&caughtExceptionHandler);
其中 caughtExceptionHandler是自己寫得類中得一個C語言得方法。
void caughtExceptionHandler(NSException *e){}
可以看到 在這個方法中我們就可以拿到NSException得錯誤得指標。就可以得到所有得錯誤得資訊。
這裡知道了一半,資訊我們是得到了,但是app現在是處於崩潰中,我們想讓我們的錯誤資訊傳到伺服器或者返回給開發人員得處理平台。那我們得線程必須等待才可以,這樣才能給我們時間把錯誤資訊上傳到處理平台。那怎麼才能把線程阻塞調那 正好 ios有線程進入等待得方法CFRunLoopRunInMode()
好了想好這個多 我們開始 進行實施了。
這是 異常擷取得方法,把此方法 進行註冊 也就是
-(void) _registerException{ NSSetUncaughtExceptionHandler(&caughtExceptionHandler);}
void caughtExceptionHandler(NSException *e){ NSString *currentTime = [[VKCatchCrash shareCatchCrash] getCurrentTime]; CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); NSArray *arr = [e callStackSymbols]; NSString *reason = [e reason]; NSString *name = [e name]; NSMutableDictionary *dic = [[NSMutableDictionary alloc]init]; [dic setValue:[[VKCatchCrash shareCatchCrash] _getDeviceInfo] forKey:@"device"]; [dic setValue:arr forKey:@"callStackSymbols"]; [dic setValue:reason forKey:@"reason"]; [dic setValue:name forKey:@"name"]; [dic setObject:currentTime forKey:@"time"]; NSError *error = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error]; if ([jsonData length] > 0 && error == nil){ NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSString *path = [[VKCatchCrash shareCatchCrash] fullScreenshots:currentTime]; //if([[VKCatchCrash shareCatchCrash] checkNetwork]){ [Upload startUpload:[Upload initUpBackBlock:^(NSData *data, NSURLResponse *response, NSError *error) { NSFileManager *manager = [NSFileManager defaultManager]; if([manager fileExistsAtPath:path]){ [manager removeItemAtPath:path error:nil]; } } upUrl:HOST upDelegate:nil formName:@"fileField" filePath:path contentKey:[[NSArray alloc] initWithObjects:@"bug", nil] contentValue:[[NSArray alloc] initWithObjects:jsonString, nil]]];// } NSLog(@"%@",jsonString); [[VKCatchCrash shareCatchCrash] performSelectorOnMainThread:@selector(alertUploadBug) withObject:nil waitUntilDone:YES]; while (!dismiss) { for (NSString *mode in (__bridge NSArray *)allModes) { CFRunLoopRunInMode((__bridge CFStringRef)mode, 0, false); } } CFRelease(allModes); NSSetUncaughtExceptionHandler(NULL); }else{ NSLog(@"nil"); }}警告框提示app閃退。
-(void)alertUploadBug{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"產生莫名得崩潰,報告正在發送伺服器!" message:nil delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil, nil]; [alert show];}
目前時間
-(NSString *)getCurrentTime{ NSDate * senddate=[NSDate date]; NSDateFormatter *dateformatter=[[NSDateFormatter alloc] init]; [dateformatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; NSString * locationString=[dateformatter stringFromDate:senddate]; return locationString;}
這裡還用到了一個 自己寫的額一個上傳得類VKHttpManager,回來我會上傳 源碼。
基本資料就這麼多。詳情可以看下 源碼。
下面我們 我們來說下Android得閃退得異常捕獲。
Android相對於ios比較簡單,只要使用一個類來介面UncaughtExceptionHandler就可以了
然後有借口得方法,會自動調用該回調。
public class MyCrashHandler implements UncaughtExceptionHandler{@Overridepublic void uncaughtException(Thread thread, Throwable ex) {}}
其中 Throwable得異常對象就是我們需要得異常對象。
其中這裡也有 和ios相似得地方 那就是 線程阻塞
Looper.prepare();
Looper.loop();
這裡有一個地方要注意得就是 在安卓中 unity調用 AndroidUI得東西 要使用 UI線程不然是無法顯示。詳情見下面得源碼
UnityPlayer.currentActivity.runOnUiThread(new Runnable() { @Override public void run() { }};
然後 unity裡我們還要做些東西來捕獲unity得異常和對ios和Android進行對接得一些東西。
using UnityEngine;using System.Collections;using System.Runtime.InteropServices;public class CatchCrash : MonoBehaviour {private static string HOST ="http://192.168.1.240/game/";private static string currentTime = "";#if UNITY_IPHONE[DllImport ("__Internal")]private static extern void registerException (string host);[DllImport ("__Internal")]private static extern void createBug ();[DllImport ("__Internal")]private static extern void unityBugAlert (string msg);[DllImport ("__Internal")]private static extern string getDeviceInfo ();#elif UNITY_ANDROIDAndroidJavaClass jc = null;AndroidJavaObject jo = null;#endif// Use this for initializationvoid Awake () {#if UNITY_EDITOR#elif UNITY_IPHONE || UNITY_ANDROIDDontDestroyOnLoad (gameObject);Application.RegisterLogCallback(OnLog);#endif#if UNITY_IPHONEregisterException (HOST);#elif UNITY_ANDROIDjc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");jo = jc.GetStatic<AndroidJavaObject>("currentActivity");jo.Call("registerException",HOST);#endif}void OnLog (string message, string stacktrace, LogType type) { if (type == LogType.Exception) { currentTime = getcurrentTime().ToString();string butMsg = "{\n"+"\"message\":" +"\""+message.Replace("\n","")+"\""+",\n\"stacktrace\":"+"\""+stacktrace.Replace("\n","")+"\""+",\n\"time\":"+"\""+currentTime+"\""+"\n" +"\"device\":" +"\""+#if UNITY_IPHONEgetDeviceInfo().Replace("\n","")+"\""+#elif UNITY_ANDROIDjo.CallStatic<string>("getDeviceInfo").Replace("\n","")+"\""+#endif"\n}";StartCoroutine(uploadBug(butMsg));#if UNITY_IPHONEunityBugAlert (butMsg);#elif UNITY_ANDROIDjo.CallStatic("unityBugAlert",butMsg);#endif} } IEnumerator uploadBug(string butMsg){yield return new WaitForEndOfFrame();int width = Screen.width;int height = Screen.height;Texture2D tex = new Texture2D (width,height,TextureFormat.RGB24, false);tex.ReadPixels(new Rect(0, 0, width, height), 0, 0 );tex.Apply();byte [] bytes = tex.EncodeToPNG ();WWWForm form = new WWWForm();form.AddField ("bug",butMsg);form.AddBinaryData ("fileField",bytes,currentTime+".png","image/png");WWW w = new WWW (HOST,form);yield return w;if (w.error != null) {Debug.Log (" upload bug erro");} else {Debug.Log (" upload bug finish");}}public static string getcurrentTime(){System.DateTime now = System.DateTime.Now;return now.Year + "-" + now.Month + "-" + now.Day + " " + now.Hour + ":" + now.Minute + ":" + now.Second;}}
工程再次哦