Recently I have been doing some memory analysis on one of my application. The application is made for Android platform. For two of the screens, I am observing that there is a leak.
No validate, I removed all of my code and kept only $.screenName.open() call on both the controllers and they way of calling them is:
Alloy.createController(screenToLaunch, payloadJson);
Inside the respective controllers, the open() call was present. I use DDMS and do a heap analysis.
Before opening Controller A, I pressed the cause GC several time to get a stable allocated reading. Once done I launch Controller A and press the back button to close it. Now when I click on cause GC several times, there is a difference of 60KB, everytime.
I am not storing the createController reference in any global variable. Any idea as to why it is behaving in such manner?
Updated:
Above is the HPROF difference between opening and closing of the controller. I am not using any DB calls, yet I see there are many DB related calls being made. I think it might be that the framework is using those calls for its internal functioning.
I'm using this architecture for controllers (UI.Windows) and works very well.
Open window : Alloy.createController('name_controller').getView().open();
Cleaning up Alloy controllers
This is my cleanup method for all controllers and widgets:
someview.js
var args = arguments[0] || {},
data = {};
data.button = Alloy.createController('button',{
title:'button'
}).getView();
$.view.cleanup = function() {
$.destroy();
$.off();
data.button.cleanup();
$ = data = args = null;
};
button.js
var args = arguments[0] || {},
data = {};
data.click = function() { ... };
$.view.addEventListener('click',data.click);
$.view.cleanup = function() {
$.destroy();
$.off();
$.view.removeEventListener('click',data.click);
$ = data = args = null;
};
I've created a global function, to call the cleanup method and remove it's content on every children of the controllers:
UPDATE
added delete operator and try statement
exports.unset = function(view) {
if(view) {
if(view.children && view.children.length) {
for(var i in view.children) try { util.unset(view.children[i]);
} catch(e) {}
view.removeAllChildren();
}
if(view.views && view.views.length) for(var i = view.views.length; i > 0; i--) if(view.views[i-1]) {
if(view.removeView) view.removeView(i-1);
try { util.unset(view.views[i-1]);
} catch(e) {}
}
if(view.cleanup) try { view.cleanup();
} catch(e) {}
view = null;
delete view;
}
};
Related
I have the following code using Akavache in a Xamarin app and it's not behaving the way I would think it should. Probably my misunderstanding of how it should be but it's driving me crazy.
So in my viewmodel I'm making the call to FetchNewsCategories and specifying a cache of 5 minutes for the item. What I'd expect to happen is that if the cache item is not there, it would make a call to the fetchFunc (ie. FetchNewsCategoriesAsync) but if I call the service any number of times inside the cache timeout of 5 minutes, it should just give me the cached item and not do the server call. In all cases that I've tried, it keeps doing the rest call and never gives me the cached item. I've also tried this with GetAndFetchLatest and if there is a cached item, it doesn't make the rest call but it also doesn't make the call in the subscribe event in the viewmodel.
Any ideas what I'm doing wrong here?
EDIT: I tested this same code on Android (Nexus 5 KitKat API19) and it's working flawlessly. I'm going to reset my IOS emulator and see if something was just out of whack.
NewsService.cs
public static async Task<ServiceResponse<List<ArticleCategoryInfo>>> FetchNewsCategoriesAsync(BlogSourceType blogSource)
{
return await ServiceClient.POST<List<ArticleCategoryInfo>>(Config.ApiUrl + "news/categories", new
{
ModuleId = Int32.Parse(Config.Values[blogSource == BlogSourceType.News ? ConfigKeys.KEY_NEWS_MODULE_ID : ConfigKeys.KEY_BLOG_MODULE_ID])
});
}
public static IObservable<ServiceResponse<List<ArticleCategoryInfo>>> FetchNewsCategories(BlogSourceType blogSource)
{
var cache = BlobCache.LocalMachine;
var cachedCategories = cache.GetOrFetchObject("categories" + blogSource,
async () => await FetchNewsCategoriesAsync(blogSource),
DateTime.Now.AddMinutes(5));
return cachedCategories;
}
NewsViewModel.cs
public async Task LoadCategories()
{
var cachedCategories = NewsService.FetchNewsCategories(blogSource);
cachedCategories.Subscribe((obj) => { Device.BeginInvokeOnMainThread(() => DisplayCategories(obj.Result,"Subscribe"));});
return;
}
private void DisplayCategories(IList<ArticleCategoryInfo> categories, string source)
{
Categories.Clear();
System.Diagnostics.Debug.WriteLine("Redisplaying categories from " + source);
foreach (var item in categories)
{
Categories.Add(item);
}
}
Just wanted to add my resolution to the issue I experienced above for reference to others with this problem.
The ServiceResponse object that I was trying to cache had an HttpResponseMessage in it which I suspect was causing a serialization error, probably a cyclical reference, so it never did get cached and ended up calling the endpoint every time. I ended up putting an [IgnoreDataMemberAttribute] on that property so it wasn't serialized and the problems went away.
I ended up handling the subscribe in the following manner to handle errors and to make sure the activity indicator bound to the IsBusy property was updated properly.
public async Task LoadActivities(bool refresh)
{
IsBusy = true;
if (refresh) OlderThanJournalId = int.MaxValue;
var cached = ActivityService.FetchJournalItems(GroupId, OlderThanJournalId, refresh);
cached.Subscribe((result) => { Device.BeginInvokeOnMainThread(() => {
DisplayActivities(result);
}); }, (err) => HandleError(err), () => IsBusy = false);
}
public void HandleError(Exception ex) {
IsBusy = false;
DialogService.ShowErrorToast(AppResources.ErrorMessage, "Unable to load activity stream.");
System.Diagnostics.Debug.WriteLine(ex.Message);
}
private void DisplayActivities(ServiceResponse<List<JournalItem>> response)
{
if (!response.IsConnected) {
DialogService.ShowInfoToast(AppResources.ErrorMessage, AppResources.NotConnected);
return;
}
if (!response.Authorized) {
App.LoginManager.Logout();
}
Activities.Clear();
foreach (var item in response.Result)
{
Activities.Add(item);
}
}
BeginInvokeOnMainThread is used to make sure that the updates to the ObservableCollection in DisplayActivities are seen in the UI.
When I try to debug react native, it usually looks like this.
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (ReactCompositeComponent.js:785)
at ReactCompositeComponentWrapper._renderValidatedComponent (ReactCompositeComponent.js:811)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:735)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:715)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:634)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:534)
at Object.receiveComponent (ReactReconciler.js:131)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:737)
There are no useful information and if I use debugger command to see what initiated the 50-depth call stack, then it almost always comes down to the onmessage method in `debuggerWorker.js', and it's almost zero useful.
How do you really debug your react native app?
onmessage = function(message) {
var object = message.data;
var sendReply = function(result) {
postMessage({replyID: object.id, result: result});
};
var handler = messageHandlers[object.method];
if (handler) {
// Special cased handlers
handler(object, sendReply);
} else {
// Other methods get called on the bridge
var returnValue = [[], [], [], 0];
try {
if (typeof __fbBatchedBridge === 'object') {
returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments);
}
} finally {
sendReply(JSON.stringify(returnValue));
}
}
};
I tend to use the stack traces to figure out the general area where the error occurred and then use the web debugger to actually find out what when wrong.
https://facebook.github.io/react-native/docs/debugging.html#chrome-developer-tools
Its actually pretty nice, you can open all your js files in the sources tab of the chrome dev tools and then put debug points in and just step through your file and view your variables.
In testing out Firebase with AngularFire, I was surprised at how slow it is. After further testing, I discovered that it isn't Firebase that is slow, but AngularFire that is slow (incredibly slow in Firefox v26.0).
My use case is where I need to access a number of children for a given parent. The total number of children will potentially be in the thousands, so fetching them all at once is not an option. In addition, they will need to be accessed from grandparents, so querying by priority is not always an option.
Is there something I'm doing wrong in this sample with AngularFire (slow):
http://plnkr.co/edit/eML3HF3RtchIU26EGVaw?p=preview
Gist of accessing children with AngularFire:
function getChild(childID) {
recordCount++;
myC.children[childID] = $firebase(new Firebase(childrenUrl + childID));
myC.children[childID].$on('loaded', function () {
returnCount++;
checkReturnCount();
});
}
function checkReturnCount() {
if (recordCount != 0 && recordCount == returnCount) {
var diff = (new Date).getTime() - start;
myC.log.push("Loaded " + parent.FirstName + "'s children in " + diff + "ms.");
$scope.$apply();
}
}
For comparison, see this sample which isn't using any Angular plugin (fast):
http://plnkr.co/edit/GA17FEnHu7p8wAiDXA5b?p=preview
Gist of accessing children without AngularFire
function getChild(childID) {
recordCount++;
var tempRef = new Firebase(childrenUrl + childID);
tempRef.on('value', function (data) {
myC.children[childID] = data.val();
returnCount++;
checkReturnCount();
});
}
function checkReturnCount() {
if (recordCount != 0 && recordCount == returnCount) {
var diff = (new Date).getTime() - start;
myC.log.push("Loaded " + parent.FirstName + "'s children in " + diff + "ms.");
$scope.$apply();
}
}
OK, I may have found a solution. Apparently Firefox used to add random times to it's setTimeouts, but it doesn't any longer (see https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout). However, Firefox (as well as other browsers) apparently still have a minimum timeout delay (which in FF is apparently 4ms).
This page proposes a solution: http://dbaron.org/log/20100309-faster-timeouts
Here is the setZeroTimeout method from that blog post:
// Only add setZeroTimeout to the window object, and hide everything
// else in a closure.
(function() {
var timeouts = [];
var messageName = "zero-timeout-message";
// Like setTimeout, but only takes a function argument. There's
// no time argument (always zero) and no arguments (you have to
// use a closure).
function setZeroTimeout(fn) {
timeouts.push(fn);
window.postMessage(messageName, "*");
}
function handleMessage(event) {
if (event.source == window && event.data == messageName) {
event.stopPropagation();
if (timeouts.length > 0) {
var fn = timeouts.shift();
fn();
}
}
}
window.addEventListener("message", handleMessage, true);
// Add the one thing we want added to the window object.
window.setZeroTimeout = setZeroTimeout;
})();
When I use this setZeroTimeout method, using AngularFire doesn't seem to be noticeably slower than using the base API.
For comparison, I've created a new Plnkr using it instead of the $timeout service.
AngularFire with setZeroTimout: http://plnkr.co/edit/nywEJpLcPwEJjXzipS4n?p=preview
AngularFire - http://plnkr.co/edit/nywEJpLcPwEJjXzipS4n?p=preview
Base Firebase API - http://plnkr.co/edit/GA17FEnHu7p8wAiDXA5b?p=preview
Could this be included in AngularFire? Or should I just modify my version for now?
OK, I think I've come up with a further improvement on the solution I started to come up with above, which also triggers the angular digest cycle as needed:
I overwrote the _timeout function in the AngularFire function as follows:
this._timeout = function (fn) {
fn();
throttledApply();
};
throttledApply is defined in the $firebase factory as:
var throttledApply = _.throttle(apply, 100);
function apply() {
$rootScope.$apply();
}
and is then passed to the AngularFire function instead of the $timeout service. It is making use of underscore's throttle function to call $apply immediately, and then at most once every 100ms thereafter. For my purposes, this is sufficient. It could easily be reduced to something more like 50ms, or 25ms though.
Are there any repercussions of these modifications that I'm not seeing?
private var csv:URLLoader = new URLLoader();
private var array:Array = new Array();
private var urlr:URLRequest = new URLRequest();
public function loadRecipe(path:String):void
{
try
{
csv.dataFormat = URLLoaderDataFormat.TEXT;
urlr = new URLRequest(path);
csv.addEventListener(Event.COMPLETE, finishRecipe);
csv.load(urlr);
}
catch (e:SecurityErrorEvent)
{
trace("1");
}
catch (e:IOErrorEvent)
{
trace("2");
}
}
public function finishRecipe(e:Event):void
{
var csvString:String = csv.data as String;
array = csvString.split(",");
}
My code that I'm working with is above. I can't get the completion event to ever trigger, that is, my array is never populated. Can anyone give me insight as to why?
EDIT:
I changed to get rid of all the weak references and check for errors. I don't get any errors.
I've run into this bug frequently over the years. When certain browsers (FireFox, Chrome) retrieve the file from cache instead of network, the loader will dispatch a PROGRESS event but never COMPLETE.
Try clearing your browser cache and see whether the file loads correctly next time. If so, you can do one of two things as a workaround:
Break the cache by adding a random string to the end of your request URL.
urlr = new URLRequest(path + "?cachebust=" + Math.floor(100000+900000*Math.random()));
This is simple to code, but has obvious disadvantages, forcing unnecessary reloads.
Listen for both COMPLETE and PROGRESS events. In the PROGRESS handler, check to see if bytesLoaded matches bytesTotal. If it does, remove all handlers and continue as if it were a COMPLETE event.
... somewhere in your code ...
loader.addEventListener(Event.COMPLETE, handleComplete);
loader.addEventListener(ProgressEvent.PROGRESS, handleProgress);
... somewhere else
private function handleProgress(evt:ProgressEvent):void
{
checkLoadComplete();
}
private function handleComplete(evt:Event):void
{
checkLoadComplete();
}
private function checkLoadComplete():void
{
if(loader.bytesTotal > 0 && loader.bytesLoaded == loader.bytesTotal) {
loader.removeEventListener(Event.COMPLETE, handleComplete);
loader.removeEventListener(ProgressEvent.PROGRESS, handleProgress);
... your code here
}
}
Ummm just looking at the code you provided it seems like you actually try to catch the errors using try/catch. In order to find errors, you have to start listening to them on the actual loader. Like this:
public function Foobar() {
var loader:URLLoader;
loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
loader.addEventListener(ProgressEvent.PROGRESS, onProgress);
loader.addEventListener(Event.COMPLETE, onComplete);
}
private function onComplete(e:Event):void {}
private function onProgress(e:ProgressEvent):void {}
private function onSecurityError(e:SecurityErrorEvent):void {}
private function onIOError(e:IOErrorEvent):void {}
I'm trying to make two Ajax calls to get data to populate different bits of a web page, and as you'll already know, only the second happens.
So I thought I'd do this:
callAjax1('a'); callAjax2('b');
function callAjax1(data) {
ajax(data);
}
function callAjax2(data) {
ajax(data);
}
function ajax(data) {
// calls XMLHttpRequestObject etc
}
The idea was that instead of calling ajax() twice, now, I'd have two independent instances of ajax that would run independently.
It works .. but only if I put in an alert at the top of ajax() to let me know I've arrived.
So I'm thinking that alert gives the first request time to finish before the second is called. Therefore, I've not managed to separate them properly into separate instances. Is that not possible?
What am I missing?
All the best
J
UPDATE:
I'm thinking this, do I stand a chance?
tParams = new Array (2); // we intend to call ajax twice
tParams[0] = new Array('ajaxGetDataController.php', 'PROJECT', 'id');
tParams[1] = new Array('ajaxGetFileController.php', 'FILE', 'projectId');
<select name='projectSelector' onchange=\"saveData(tParams, this.value);\">\n";
// gets called, twice
function saveData(pParams, pData) // pParams are: PageToRun, Table, Field
{
if (XMLHttpRequestObject)
{
tPage = pParams[0][0]+'?table='+pParams[0][1]+'&pField='+pParams[0][2]+'&pData='+pData;
XMLHttpRequestObject.open('GET', tPage);\n
XMLHttpRequestObject.onreadystatechange = callAjax(pParams, pData);
XMLHttpRequestObject.send(null);
}
}
function callAjax(pParams, pData)
{
if (XMLHttpRequestObject.readyState == 4 && XMLHttpRequestObject.status == 200)
{
var tReceived = XMLHttpRequestObject.responseXML;
options = tReceived.getElementsByTagName('option'); // fields and their values stored in simplest XML as options
popForm(options, pParams[0][1]); // goes off to use the DOM to populate the onscreen form
pParams.shift(); // cuts off pParams[0] and moves all elements up one
if (pParams.length>0)
{
saveData(pParams, pData);
}
}
}
I would create a ready state variable for the AJAX function:
function ajax(data) {
readyState = false;
// calls XMLHttpRequestObject etc
}
And then check for the ready state before executing the second call:
function callAjax2(data) {
if(readyState == true) {
ajax(data);
readyState = true;
}
}
And make sure to change the readyState back to false after the AJAX calls have executed. This will ensure the first AJAX call has finished executing before the second one tries to fire.