{"id":277,"date":"2016-03-17T18:03:45","date_gmt":"2016-03-17T18:03:45","guid":{"rendered":"http:\/\/fluentreports.com\/blog\/?p=277"},"modified":"2016-03-17T18:39:11","modified_gmt":"2016-03-17T18:39:11","slug":"nativescript-deep-dive-into-activityapplication-overrides","status":"publish","type":"post","link":"https:\/\/fluentreports.com\/blog\/?p=277","title":{"rendered":"NativeScript: Deep dive into Activity\/Application overrides"},"content":{"rendered":"<figure id=\"attachment_281\" aria-describedby=\"caption-attachment-281\" style=\"width: 307px\" class=\"wp-caption alignright\"><a href=\"http:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving.jpg\" rel=\"attachment wp-att-281\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-281\" src=\"http:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving.jpg\" alt=\"ManDiving\" width=\"307\" height=\"471\" srcset=\"https:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving.jpg 2486w, https:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving-195x300.jpg 195w, https:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving-768x1179.jpg 768w, https:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving-667x1024.jpg 667w, https:\/\/fluentreports.com\/blog\/wp-content\/uploads\/2016\/03\/ManDiving-624x958.jpg 624w\" sizes=\"auto, (max-width: 307px) 100vw, 307px\" \/><\/a><figcaption id=\"caption-attachment-281\" class=\"wp-caption-text\">(c) Cody Hough (http:\/\/www.flickr.com\/people\/IvanTortuga\/)<\/figcaption><\/figure>\n<p>NativeScript 1.7.0 released a new feature that several people have been waiting for with baited breath.\u00a0\u00a0 Well 1.7.0 is out, and so I just had to see how this new feature worked.<\/p>\n<p>Unfortunately its implementation does leave a bit to be desired; but I fully understand the constraints so at the moment we will over look the bad to see what we get in return for the new ability.<\/p>\n<p>First of all you now have access to the raw JAVA code that makes up the Activity and Application class.\u00a0 WooHoo!\u00a0\u00a0 This will allow you to extend the app easily in the cases you actually need to add something to the onCreate or add some missing callback like onRequestPermissions (Android 6+ permissions).<\/p>\n<p>There are THREE way to extend your NativeScriptApplication or NativeScriptActivity<\/p>\n<ol>\n<li>Directly modify the Java File<\/li>\n<li>Directly modify the JS file<\/li>\n<li>Create a new JS file (and modify the Java File to point to new JS file)<\/li>\n<\/ol>\n<p>1 &amp; 2 are typically the easiest, as all you do is modify it and you are done.\u00a0\u00a0\u00a0 So lets look into the Java part of this first...<\/p>\n<h2>Method 1:<\/h2>\n<p>The NativeScriptActivity.java and NativeScriptApplication.java are located at:<\/p>\n<p><strong>\/YourAppFolder\/platforms\/android\/src\/main\/java\/com\/tns<\/strong><\/p>\n<p>As you might have guessed this means it will be replaced and or deleted when you do destructive things with your platforms folder, and of course they aren't under version control like the rest of your app.\u00a0 So sad, we now have another file outside of version control when we finally got all the others under version control in 1.6...<\/p>\n<p>But with that annoyance, comes the GOOD! You now have FULL access to the FULL Java classes that make up the two core startup items which are where you normally need to put any special initialization items.\u00a0 And in all reality most the time most people won't have to change these files ever!<\/p>\n<p>Lets look at parts of the <em>NativeScriptApplication.java<\/em> file:<br \/>\n<pre>@JavaScriptImplementation(javaScriptFile = &quot;app\/tns_modules\/application\/application.js&quot;)\npublic class NativeScriptApplication extends android.app.Application implements com.tns.NativeScriptHashCodeProvider {\n\n&nbsp;&nbsp;public void onCreate() {\n&nbsp;&nbsp;&nbsp;&nbsp;java.lang.Object[] params = null;\n&nbsp;&nbsp;&nbsp;&nbsp;com.tns.Platform.callJSMethod(this, &quot;onCreate&quot;, void.class, params);\n&nbsp;&nbsp;}\n\n&nbsp;&nbsp;public void onTrimMemory(int level) {\n\u00a0\u00a0\u00a0 java.lang.Object[] params = new Object[1];\n\u00a0\u00a0\u00a0 params[0] = level;\n\u00a0\u00a0\u00a0 com.tns.Platform.callJSMethod(this, &quot;onTrimMemory&quot;, void.class, params);\n\u00a0 }\n}\n<\/pre><br \/>\nYou see that line @JavaScriptImplementation(javaScriptFile = ...) this is where the JavaScript side of the class exists.\u00a0\u00a0 As you can see, it is located in your \"Application.js\" file in the common core folder.\u00a0 That file now contains the JS side of the class also.\u00a0\u00a0 You can point this to another location if you want; BUT you will need to make sure that you implement everything that already exists in the existing class in the Application.js file.<\/p>\n<p>If you are only planning on adding a startup item, then you probably just need to add it to the onCreate and you are done.<\/p>\n<p>Now if you are planning on adding a missing feature; this is where it gets a bit more complicated.\u00a0\u00a0 You have two choices, pure Java, or you can have the Java call into your JavaScript class and you can do all your stuff in JavaScript.\u00a0\u00a0 If it is pure Java, then you just need to add your new function to the file, and again you are done.\u00a0\u00a0 If you want to implement it so that you can manage all your new code on the JavaScript side; then you create your function in Java; but you need to add a couple lines of boilerplate to make everything work.<\/p>\n<p>Lets look at this boiler plate code:<br \/>\n<pre>java.lang.Object[] params = null;\n\/\/ OR\njava.lang.Object[] params = new Object[1];<\/pre><br \/>\nThe params is how many parameters you are passing onto your JS side.\u00a0 Typically it will be the same number that this function received.\u00a0 So if your function received no parameters, then you are going to pass null in.\u00a0 If your function received one parameter you use the <em>new Object[1]<\/em>, then the following lines you assign each of the params to the values they need to be for example: <em>params[0] = &lt;your parameter&gt;<\/em>.<\/p>\n<p>The key line is the last line you see in both function above:<br \/>\n<pre>com.tns.Platform.callJSMethod(this, &quot;onTrimMemory&quot;, void.class, params);<\/pre><br \/>\nThis is what calls into the JS runtime, the first parameter should always be <em>this<\/em>.\u00a0 The second parameter is the name of the function you will be running on the JavaScript side.\u00a0 So in this case it will be looking for the \"onTrimMemory\" function in the javascript side of the application class.\u00a0 The third parameter is what you are expecting in return.\u00a0 void.class means no return value.\u00a0 But you can do things like boolean.class to say you are expecting a boolean value back.\u00a0\u00a0 And the final parameter is the object array of parameters you are passing into the JavaScript side.\u00a0 Please note; if you do NOT implement this function on the JavaScript side now; your application WILL crash and burn when it goes to try to call it.\u00a0 So, after you add it here, you need to add it into the JavaScript side of things.<\/p>\n<h2>Method 2:<\/h2>\n<p>Now that we have the Java side of the class done, lets look at the JavaScript side of the class.\u00a0 For the <em>NativeScriptApplication<\/em> the file is located at: <strong>\/YourAppFolder\/node_modules\/tns-core-modules\/application\/application.android.js<\/strong> and for the <em>NativeScriptActivity<\/em> it is located at <strong>\/YourAppFolder\/node_modules\/tns-core-modules\/ui\/frame\/frame.android.js<\/strong>.<\/p>\n<p>Now as you might have guessed; these are in the core modules; do you know what my number one rule with NativeScript is?\u00a0 <em><strong>DON'T EVER CHANGE THE CODE INSIDE THE CORE MODULES!!!<\/strong><\/em> \u00a0 Ugh, Ugh, Ugh, the only way to add your code is to change the core modules.\u00a0 :very very sad face:<\/p>\n<p>Mihail, I love the new feature but I hate where it resides by default!\u00a0 Can we please, change this...<\/p>\n<p>I now need a 10 minute break to compose myself.\u00a0\u00a0\u00a0 I can't bear to face myself in the mirror as I actually tell you; \"you have to change the code in the core modules\" to make this work.\u00a0\u00a0\u00a0 So very very sad...\u00a0\u00a0 But in all other cases <em><strong>NEVER<\/strong> <\/em>change the code in the core modules.<\/p>\n<p>! WARNING !<br \/>\nBy changing any code in the core modules you risk your application totally creating massive worm holes to suck your phone and computer to another dimension and\/or maybe just\u00a0 crashing.\u00a0 At the point you update or reinstall the core modules all your changes will be LOST!<br \/>\n! WARNING !<\/p>\n<p>Ok, now with that warning maybe I can face myself again, you will find in the application.js file the following class toward the top:<br \/>\n<pre>var NativeScriptApplication = (function (_super) {\n&nbsp;&nbsp;&nbsp;&nbsp;__extends(NativeScriptApplication, _super);\n&nbsp;&nbsp;&nbsp;&nbsp;function NativeScriptApplication() {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_super.call(this);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return global.__native(this);\n&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;NativeScriptApplication.prototype.onCreate = function () {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;androidApp.init(this);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setupOrientationListener(androidApp);\n&nbsp;&nbsp;&nbsp;&nbsp;};\n&nbsp;&nbsp;&nbsp;&nbsp;NativeScriptApplication.prototype.onLowMemory = function () {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gc();\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.System.gc();\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_super.prototype.onLowMemory.call(this);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedExports.notify({ eventName: typedExports.lowMemoryEvent, object: this, android: this });\n&nbsp;&nbsp;&nbsp;&nbsp;};\n&nbsp;&nbsp;&nbsp;&nbsp;NativeScriptApplication.prototype.onTrimMemory = function (level) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gc();\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.System.gc();\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_super.prototype.onTrimMemory.call(this, level);\n&nbsp;&nbsp;&nbsp;&nbsp;};\n&nbsp;&nbsp;&nbsp;&nbsp;NativeScriptApplication = __decorate([\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JavaProxy(&quot;com.tns.NativeScriptApplication&quot;)\n&nbsp;&nbsp;&nbsp;&nbsp;], NativeScriptApplication);\n&nbsp;&nbsp;&nbsp;&nbsp;return NativeScriptApplication;\n}(android.app.Application));<\/pre><br \/>\nAs you can see the calls are called into the the function of the same name, and depending on what that function requires you might need to call the _super version of the function as part of your function.<\/p>\n<p>Now to be clear; your best bet is to do only the very very minimal work in this class and have all your code actually in a function inside another file that you use require to load.\u00a0 So for example say I needed to do some initialization at startup; I would do this:<\/p>\n<p>Line 1: var myAppInitCode = require('myAppInitCode.js');<br \/>\ninside the onCreate() function I would add:\u00a0 MyAppInitCode.onCreate();<\/p>\n<p>Pretty simple, as an additional safety, mechanism I would also add a dead-mans-switch to this file.\u00a0 So basically it goes something like this:<\/p>\n<p>var hasRan = false;<br \/>\nexports.onCreate() {<br \/>\nhasRan = true;<br \/>\n\/\/ Do Init code<br \/>\n}<\/p>\n<p>setTimeout(function() {<br \/>\nif (hasRan) return;<br \/>\n\/\/ ... require dialog code ...<br \/>\ndialog ('Hey stupid, you must have overwrote the application.js file again!'); }<br \/>\n, 1000);<\/p>\n<p>And then you include this file in your app.js file also.\u00a0\u00a0 hasRan will be set to true during initialization if your code is getting ran by the init process.\u00a0 If you messed up and lost your changes; then the setTimeout code will be triggered by the inclusion in the app.js file and of course you will hit your head against the wall and say thank God you thought to verify your code is still running instead of spending hours trying to figure out why feature x broke...\u00a0 \ud83d\ude00<\/p>\n<p>This way in the event the application code is overwritten, it is now fairly simple for you to recover; if you add all your init code directly to the application.js file; you are SOL when you accidentally update to try the 1.7.1 fixes.<\/p>\n<p>So now you can see how to do the first two ways of modifying your application; the third way is \"potentially\" the better way to do it as it currently stands. However it requires a bit more work; and creates a different way for bad bugs to be introduced.\u00a0\u00a0 But then it also allows you keeps the majority of the code under your control and eliminates the majority of the overwriting risks.<\/p>\n<h2>Method 3:<\/h2>\n<p>The third way is to create your own NativeScriptApplication and NativeScriptActivity class in JavaScript files you have in your project folder.\u00a0\u00a0\u00a0\u00a0 So the steps to do this are:<\/p>\n<ol>\n<li>Open up the Application.js file and COPY EVERYTHING for the NativeScriptApplication class as it exists in the application.js file; you CAN NOT extend the existing one.\u00a0\u00a0 You must have your own version and it MUST implement all the same functionality of the one that you will be \"replacing\".\u00a0 (Do you see the risk yet?)<\/li>\n<li>Create a new file in your project to contain the NativeScriptApplication class; you can either have one file for both classes or you can have separate files for both classes; this is really up to you.\u00a0 (Do you see the risk yet?)<\/li>\n<li>Change the source Java file, than line we spoke about towards the top where it says<\/li>\n<\/ol>\n<p><pre>@JavaScriptImplementation(javaScriptFile = &quot;app\/tns_modules\/application\/application.js&quot;)<\/pre><br \/>\nAnd point it to where your file resides.\u00a0\u00a0 This will cause the runtimes to load your version of the NativeScriptApplication and\/or NativeScriptActivity as the primary classes.\u00a0 If you mess up; it will use the original versions.\u00a0 So this might throw you off, if you put the path incorrectly.<\/p>\n<p>Please note that if you prefer TypeScript you can copy the TypeScript code from the frame\/application directly from the nativescript repo.\u00a0 Just make sure when you copy it you include the line:<br \/>\n<pre>@JavaProxy(&quot;com.tns.NativeScriptActivity&quot;)<\/pre><br \/>\nwith it as it is required for the fixing up to make it become the correct class.<\/p>\n<h2>Risks:<\/h2>\n<p>So now that you see the three ways let me outline the risks for each method:<\/p>\n<p>Method 1: It requires changes to the .JAVA files; these WILL be overwritten and\/or deleted when doing anything with the TNS platforms command.\u00a0\u00a0 So back them up!\u00a0\u00a0\u00a0 HOWEVER NEVER use the backup file as-is.\u00a0\u00a0 When you install a new version of the Runtimes, these original Java files may CHANGE, which means you really need to copy your code out of your old backup version into the new version.\u00a0\u00a0 For a real example; I just committed a pull request yesterday to deal with OnRequestPermssions (for android 6).\u00a0 This means that when\/if they accept it, the NativeScriptActivity.java file will CHANGE to include the new code needed to make it work.\u00a0 If you blindly copy your old backup NativeScriptActivity.java file over the new one you risk crashing, inconstant behavior and\/or the feature not working.\u00a0 So you must \"patch\" the .JAVA file each time.<\/p>\n<p>Method 2: Has the same issue as #1, other than it is with the JavaScript files.\u00a0 Make a backup but DO NOT use the backup as-is.\u00a0 You must copy your code out of the backup and apply it to the new version, otherwise you REALLY risk major issues.\u00a0 The application &amp; frame files have a lot more changes to them each patch than the .JAVA counterparts.\u00a0\u00a0 So if you were to copy your backup from 1.6.0 over the 1.7.0 version your application probably would crash at startup.\u00a0 Not a good idea.\u00a0\u00a0 So to re-iterate; you must copy your changes from the backup to the new version of the file.<\/p>\n<p>Method 3: You not only have the same risks as method 1, but your additional risk is that you don't get a change to the JavaScript NativeScriptActivity or NativeScriptApplication.\u00a0\u00a0 Remember, you are pointing the Java to use your version of the JS classes.\u00a0\u00a0 Taking my example above; once my pull request is accepted, the Java and JS parts of the Activity class will change.\u00a0 If you get the new Java changes; and then fix it to point to your awesome version of the JS side of the class; guess what happens?\u00a0\u00a0\u00a0 CRASH at startup!!!\u00a0\u00a0 You be missing the all new OnRequestPermission code in your awesome version.\u00a0\u00a0\u00a0 In addition you will also be missing any bug fixes that were done to the classes in the original copies.<\/p>\n<p>So you kinda are deciding which poison pill you want to take.\u00a0 Me personally, I think three is fraught with more land mines as you have to keep your \"versions\" of the classes up to date, you mess up here and its game over.\u00a0 But on the other hand it is safer (for version control) in that none of your code is in the core modules which I REALLY hate having to change...<\/p>\n<h2>Some notes:<\/h2>\n<p>With the current design is that plugins have no ability to tie in or change these files.\u00a0 If I create a plugin that replaces any of these files, then I risk overwriting your changes, or a newer version, so these files are really for all practical purposes off limits to plugins.\u00a0 \ud83d\ude41<\/p>\n<p>For anyone wanting to get the new API 23+ permissions; I do have a pull request in for them to be put into the core files, but if you want it today; go look at the changes I did and you can easily add it to these files I mentioned above.\u00a0\u00a0\u00a0 I also have a NativeScript-permissions plugin that will come out shortly that will make requesting perms easy for everybody including other plugins.\u00a0 It will return a promise; that promise will be resolved either true or false and then you can choose what to do after that.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>NativeScript 1.7.0 released a new feature that several people have been waiting for with baited breath.\u00a0\u00a0 Well 1.7.0 is out, and so I just had to see how this new feature worked. Unfortunately its implementation does leave a bit to be desired; but I fully understand the constraints so at the moment we will over&hellip; <a class=\"more-link\" href=\"https:\/\/fluentreports.com\/blog\/?p=277\">Continue reading <span class=\"screen-reader-text\">NativeScript: Deep dive into Activity\/Application overrides<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":""},"categories":[15],"tags":[61,62,16,63,64],"class_list":["post-277","post","type-post","status-publish","format-standard","hentry","category-nativescript","tag-activity","tag-application","tag-nativescript","tag-nativescriptactivity","tag-nativescriptapplication","entry"],"_links":{"self":[{"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/277","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=277"}],"version-history":[{"count":4,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/277\/revisions"}],"predecessor-version":[{"id":282,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/277\/revisions\/282"}],"wp:attachment":[{"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=277"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fluentreports.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}