mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
fix(devtools-ios): Ensure UI modifications run on main thread
Modifications to the UI can only be made from the main thread. Since {N} 5.3.0 all debugger protocol messages are processed by the worker thread that receives them in iOS. refs #7219, https://github.com/NativeScript/ios-runtime/pull/1101
This commit is contained in:
@ -18,6 +18,38 @@ export function test_releaseNativeObject_canBeCalledWithNativeObject() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function test_executeOnMainThread_Works(done: Function) {
|
||||||
|
utils.executeOnMainThread(() => {
|
||||||
|
try {
|
||||||
|
TKUnit.assertTrue(utils.isMainThread());
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_mainThreadify_PassesArgs(done: Function) {
|
||||||
|
const expectedN = 434;
|
||||||
|
const expectedB = true;
|
||||||
|
const expectedS = "string";
|
||||||
|
const f = utils.mainThreadify(function (n: number, b: boolean, s: string) {
|
||||||
|
try {
|
||||||
|
TKUnit.assertTrue(utils.isMainThread());
|
||||||
|
TKUnit.assertEqual(n, expectedN);
|
||||||
|
TKUnit.assertEqual(b, expectedB);
|
||||||
|
TKUnit.assertEqual(s, expectedS);
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
f(expectedN, expectedB, expectedS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function test_releaseNativeObject_canBeCalledWithNativeObject_iOS() {
|
function test_releaseNativeObject_canBeCalledWithNativeObject_iOS() {
|
||||||
let deallocated = false;
|
let deallocated = false;
|
||||||
const obj = new ((<any>NSObject).extend({
|
const obj = new ((<any>NSObject).extend({
|
||||||
|
@ -2,6 +2,7 @@ import { getNodeById } from "./dom-node";
|
|||||||
|
|
||||||
// Needed for typings only
|
// Needed for typings only
|
||||||
import { ViewBase } from "../ui/core/view-base";
|
import { ViewBase } from "../ui/core/view-base";
|
||||||
|
import { mainThreadify } from "../utils/utils";
|
||||||
|
|
||||||
// Use lazy requires for core modules
|
// Use lazy requires for core modules
|
||||||
const frameTopmost = () => require("../ui/frame").topmost();
|
const frameTopmost = () => require("../ui/frame").topmost();
|
||||||
@ -11,7 +12,7 @@ function unsetViewValue(view, name) {
|
|||||||
if (!unsetValue) {
|
if (!unsetValue) {
|
||||||
unsetValue = require("../ui/core/properties").unsetValue;
|
unsetValue = require("../ui/core/properties").unsetValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
view[name] = unsetValue;
|
view[name] = unsetValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,10 +31,10 @@ export function getDocument() {
|
|||||||
if (!topMostFrame) {
|
if (!topMostFrame) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
topMostFrame.ensureDomNode();
|
topMostFrame.ensureDomNode();
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("ERROR in getDocument(): " + e);
|
console.log("ERROR in getDocument(): " + e);
|
||||||
}
|
}
|
||||||
@ -49,7 +50,7 @@ export function getComputedStylesForNode(nodeId): Array<{ name: string, value: s
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeNode(nodeId) {
|
export const removeNode = mainThreadify(function removeNode(nodeId) {
|
||||||
const view = getViewById(nodeId);
|
const view = getViewById(nodeId);
|
||||||
if (view) {
|
if (view) {
|
||||||
// Avoid importing layout and content view
|
// Avoid importing layout and content view
|
||||||
@ -63,9 +64,9 @@ export function removeNode(nodeId) {
|
|||||||
console.log("Can't remove child from " + parent);
|
console.log("Can't remove child from " + parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
export function setAttributeAsText(nodeId, text, name) {
|
export const setAttributeAsText = mainThreadify(function setAttributeAsText(nodeId, text, name) {
|
||||||
const view = getViewById(nodeId);
|
const view = getViewById(nodeId);
|
||||||
if (view) {
|
if (view) {
|
||||||
// attribute is registered for the view instance
|
// attribute is registered for the view instance
|
||||||
@ -93,4 +94,4 @@ export function setAttributeAsText(nodeId, text, name) {
|
|||||||
|
|
||||||
view.domNode.loadAttributes();
|
view.domNode.loadAttributes();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as types from "./types";
|
import * as types from "./types";
|
||||||
|
import { executeOnMainThread } from "./utils"
|
||||||
|
|
||||||
export const RESOURCE_PREFIX = "res://";
|
export const RESOURCE_PREFIX = "res://";
|
||||||
export const FILE_PREFIX = "file:///";
|
export const FILE_PREFIX = "file:///";
|
||||||
@ -154,3 +155,10 @@ export function hasDuplicates(arr: Array<any>): boolean {
|
|||||||
export function eliminateDuplicates(arr: Array<any>): Array<any> {
|
export function eliminateDuplicates(arr: Array<any>): Array<any> {
|
||||||
return Array.from(new Set(arr));
|
return Array.from(new Set(arr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mainThreadify(func: Function): (...args: any[]) => void {
|
||||||
|
return function () {
|
||||||
|
const argsToPass = arguments;
|
||||||
|
executeOnMainThread(() => func.apply(this, argsToPass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -372,3 +372,14 @@ Please ensure you have your manifest correctly configured with the FileProvider.
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function executeOnMainThread(func: () => void) {
|
||||||
|
new android.os.Handler(android.os.Looper.getMainLooper())
|
||||||
|
.post(new java.lang.Runnable({
|
||||||
|
run: func
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMainThread(): Boolean {
|
||||||
|
return android.os.Looper.myLooper() === android.os.Looper.getMainLooper();
|
||||||
|
}
|
||||||
|
20
tns-core-modules/utils/utils.d.ts
vendored
20
tns-core-modules/utils/utils.d.ts
vendored
@ -269,6 +269,26 @@ export function GC();
|
|||||||
*/
|
*/
|
||||||
export function releaseNativeObject(object: any /*java.lang.Object | NSObject*/);
|
export function releaseNativeObject(object: any /*java.lang.Object | NSObject*/);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches the passed function for execution on the main thread
|
||||||
|
* @param func The function to execute on the main thread.
|
||||||
|
*/
|
||||||
|
export function executeOnMainThread(func: Function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function wrapper which executes the supplied function on the main thread.
|
||||||
|
* The wrapper behaves like the original function and passes all of its arguments BUT
|
||||||
|
* discards its return value.
|
||||||
|
* @param func The function to execute on the main thread
|
||||||
|
* @returns The wrapper function which schedules execution to the main thread
|
||||||
|
*/
|
||||||
|
export function mainThreadify(func: Function): (...args: any[]) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Boolean value indicating whether the current thread is the main thread
|
||||||
|
*/
|
||||||
|
export function isMainThread(): boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the specified path points to a resource or local file.
|
* Returns true if the specified path points to a resource or local file.
|
||||||
* @param path The path.
|
* @param path The path.
|
||||||
|
@ -159,6 +159,14 @@ export function openUrl(location: string): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function executeOnMainThread(func: () => void) {
|
||||||
|
NSOperationQueue.mainQueue.addOperationWithBlock(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMainThread(): Boolean {
|
||||||
|
return NSThread.isMainThread;
|
||||||
|
}
|
||||||
|
|
||||||
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
|
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
|
||||||
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
|
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user