# uts for Android

This article aims to help Android developers get started with UTS quickly.

Readers are required to have Android native application development experience.

# 1 Understand what the UTS plugin is

UTS插件uni-app新型插件形式,拥有跨平台,高效率,易调试等优点。详情

For Android developers, what we need to know is:

  1. When compiling: When we save the UTS source code file, the IDE will synchronously compile it into the corresponding Kotlin code.
  2. Runtime: When the real machine is running/cloud packaging, these compiled kotlin source codes will also become part of the apk

# 2 Master UTS grammar

# 2.1 For those who master the kotlin language

因为UTS语法与kotlin很类似,建议快速阅读后,在实践中掌握这UTS语法。uts语法介绍

# 2.2 For those who only master the java language

Compared with js, the syntax of uts is more similar to java. However, there are still big differences, and you need to read the 2.3 grammar section in detail.

Although the development of UTS plug-ins does not require mastering kotlin, since UTS is currently on the android platform, it will be compiled into kotlin source code. Learn the kotlin language to facilitate troubleshooting and implementation of complex functions.

Therefore, it is recommended to learn kotlin syntax.

# 2.3 Data Type Differences

Although UTS and koltin are basically consistent in data types, there are still differences in some scenarios, which are specifically explained here

In principle:

**The data type is subject to the built-in type of UTS, and each native platform will automatically adapt to it. **

**But UTS itself is a cross-platform language. When the API of a specific platform has clear requirements, the data type clearly required by the other party shall prevail. **


# Example 1: Int and Number

By default UTS developers can use Number to override the scenario of using Int on the android platform.

But when the developer rewrites the onStartCommand method of the Service component, the Android API requires that the last two parameters must be Int

In the native development environment, it should be written like this:

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
   return super.onStartCommand(intent, flags, startId);
}

In the standard TS environment, there is only Number type and no Int type

In order to adapt to this situation, UTS allows developers to use the data type Int of the native platform to meet the data type requirements of the native API:

 override onStartCommand(intent:Intent ,flags:Int ,startId:Int):Int {
	return super.onStartCommand(intent, flags, startId);
 }

# Example 2: MutableList

MutableList is a unique data type of the android platform. In general scenarios, the built-in type Array in UTS can be used instead

However, when calling the onAppActivityRequestPermissionsResult function to monitor the result of the permission application, it is explicitly required to use this type of parameter

In the native environment, it should be written like this:


onAppActivityRequestPermissionsResult(fun(requestCode: Number, permissions: MutableList<String>, grantResults: MutableList<Number>){
      
});

In the standard TS environment, there is no MutableList type, and the similar data type is Array

In order to adapt to this situation, UTS allows developers to use the data type MutableList of the native platform to meet the data type requirements of the native platform API:

onAppActivityRequestPermissionsResult((requestCode: number,permissions: MutableList<string>,grantResults: MutableList<number>) => {
	
});

# 举例三:String[]

部分三方sdk 使用java开发,要求继承/实现的方法参数为 string[]类型,这种情况比较特殊,需要将kotlin.Array先进行别名声明,再继续使用

import KotlinArray from 'kotlin.Array';


class XXX{
 	override onCaptureFinish(p0: KotlinArray<string>){
		// do sth
	};
}

# 2.4 Differences in thread environment

In the UTS environment, there is no thread concept by default. The code is executed in a separate thread pool of uts by default.

If you need to perform asynchronous tasks, it is recommended to execute them through the built-in function setTimeOut

console.log("这里是UTS默认线程 :"  + Thread.currentThread().getName())
setTimeOut(function(){
	console.log("这里是异步任务线程 :"  + Thread.currentThread().getName())
},1000)

If it is an android native api that requires some code to be executed on the UI thread, it needs to be operated through the native api:

class AddUIRunnable extends Runnable {
	override run():void {
		// do something
		console.log("这里是android平台主线程 :"  + Thread.currentThread().getName())
    }
};
let uiRunable = new AddUIRunnable();
getUniActivity()!.runOnUiThread(uiRunable)

# 3 Android native environment configuration

For Android projects, in addition to source code, it also involves common issues such as dependencies, resources, and configurations.

This chapter will introduce how to configure these properties in the UTS plug-in development environment

注意

  • 1 本章节内的实例代码均取自Hello UTS 项目地址
  • 2 本章节涉及的配置,均需自定义基座后才能生效
  • 3 The automatic generation of R files has been supported by HBuilder X 3.6.9, please use the latest version for development

# 3.1 Configure AndroidManifest.xml

Take the configuration file in the native-page plug-in in hello UTS as an example:

Example file location in hello uts:

~\uni_modules\uts-nativepage\utssdk\app-android\AndroidManifest.xml

AndroidManifest.xml example:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" 
  // 注意:这里是插件的包名而不是应用的包名
  package="io.dcloud.uni_modules.utsNativepage">
   // configure permissions
   <!--Create foreground service permissions-->
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application>
	   // configure service / activity
	   <service android:name="uts.sdk.modules.utsNativepage.ForeService"  />
       <activity android:name="uts.sdk.modules.utsNativepage.DemoActivity"></activity>
    </application>
</manifest>

AndroidManifest.xml configuration rules are consistent with those in android.

Special Note:

Each UTS plugin corresponds to a lib module in the android project.

Unlike you manually enter the package name in android studio, if you do not have a manual package name, HX will generate one by default according to the following rules:

uts插件默认包名规则:

如果是根目录utssdk下的uts插件
	包名:uts.sdk.(插件ID转驼峰)
如果是uni_modules目录下的uts插件
	包名:uts.sdk.modules.(插件ID转驼峰)


举例:
uni-getbatteryinfo -> uts.sdk.modules.uniGetbatteryinfo;
uts-nativepage  ->  uts.sdk.modules.utsNativepage

# 3.2 Configuring res resources

Example file location in hello uts:

~\uni_modules\uts-nativepage\utssdk\app-android\res

In addition to the layout and values directories listed here, it also supports all android standard resource directories such as anim

需要注意的是:如果res资源中使用了 android appcompat库内置的资源,需要在config.json中添加下面的配置:

{
	"dependencies": [
		"androidx.appcompat:appcompat:1.0.0"
	]
}


# 3.3 Configuring asset resources

Take the uts-advance plugin in hello UTS as an example.

Directory structure

key code:

// Get the asset manager
let assetManager = getAppContext()!.getAssets();
// load free.mp3 resource
let afd = assetManager.openFd("free.mp3");
// Use the media components that come with android to play
let mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();

The location of the complete code in hello uts:

~\uni_modules\uts-advance\utssdk\app-android\assets

# 3.4 Add libs dependent resources

下面是一个config.json示例

{
	"dependencies": [
		"androidx.recyclerview:recyclerview:1.0.0"
	]
}



截止 HBuilder 3.8.2 版本内置了以下依赖

Developers need to pay attention to two points when using the dependencies in the list:

  • When the real machine is running, you can directly reference related classes without adding dependencies in the list
  • Do not introduce the same dependencies by manually adding jar/aar, otherwise cloud packaging will fail due to dependency conflicts.
+--- my-imageloader.jar
+--- my-nineoldandroids-2.4.0.jar
+--- zip4j-2.8.0.jar
+--- com.github.getActivity:XXPermissions:18.0@jar
+--- android-gif-drawable-release@1.2.23.aar
+--- msa_mdid_1.0.13.aar
+--- breakpad-build-release.aar
+--- androidx.multidex:multidex:2.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-v4:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.0@aar
+--- com.github.bumptech.glide:glide:4.9.0@aar
+--- com.alibaba:fastjson:1.1.46.android@jar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
+--- androidx.media:media:1.0.0@aar
+--- androidx.legacy:legacy-support-core-utils:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable:1.0.0@aar
+--- androidx.viewpager:viewpager:1.0.0@aar
+--- androidx.coordinatorlayout:coordinatorlayout:1.0.0@aar
+--- androidx.drawerlayout:drawerlayout:1.0.0@aar
+--- androidx.slidingpanelayout:slidingpanelayout:1.0.0@aar
+--- androidx.customview:customview:1.0.0@aar
+--- androidx.swiperefreshlayout:swiperefreshlayout:1.0.0@aar
+--- androidx.asynclayoutinflater:asynclayoutinflater:1.0.0@aar
+--- androidx.loader:loader:1.0.0@aar
+--- androidx.core:core:1.0.0@aar
+--- androidx.versionedparcelable:versionedparcelable:1.0.0@aar
+--- androidx.collection:collection:1.0.0@jar
+--- androidx.cursoradapter:cursoradapter:1.0.0@aar
+--- com.github.bumptech.glide:gifdecoder:4.9.0@aar
+--- androidx.lifecycle:lifecycle-runtime:2.0.0@aar
+--- androidx.interpolator:interpolator:1.0.0@aar
+--- androidx.documentfile:documentfile:1.0.0@aar
+--- androidx.localbroadcastmanager:localbroadcastmanager:1.0.0@aar
+--- androidx.print:print:1.0.0@aar
+--- androidx.lifecycle:lifecycle-viewmodel:2.0.0@aar
+--- androidx.lifecycle:lifecycle-livedata:2.0.0@aar
+--- androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar
+--- androidx.lifecycle:lifecycle-common:2.0.0@jar
+--- androidx.arch.core:core-runtime:2.0.0@aar
+--- androidx.arch.core:core-common:2.0.0@jar
+--- androidx.annotation:annotation:1.0.0@jar
+--- com.github.bumptech.glide:disklrucache:4.9.0@jar
\--- com.github.bumptech.glide:annotations:4.9.0@jar


# 3.5 远程依赖仓库说明

目前云打包机支持下面的仓库:


jcenter()
google()
// huawei
maven {url 'https://developer.huawei.com/repo/'}
// jitpack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }

部分场景下,开发者可能需要将本地依赖,上传到远程仓库,避免wgt提交资源过大超出打包限制。

这种情况,推荐开发者上传到 jitpack.io 这也是目前android 原生开发主流的远程仓库。 使用文档

# 4 Android built-in library

In uts, all APIs of Android can be accessed.

However, application and activity are often rewritten in Android development, and the uni-app main engine has rewritten related classes. So if you want to operate application and activity, you need to call the API encapsulated by uni-app engine.

These APIs are UTSAndroid objects under io.dcloud.uts library, see below for details.

# 4.1 application context dependent

# 4.1.1 getAppContext

HBuilderX 3.6.3+

import { UTSAndroid } from "io.dcloud.uts";

Usage instructions: Get the current application context, corresponding to the android platform Context.getApplicationContext function implementation

In Android development scenarios, this context is required to call application-level resources/capabilities. For more usage, refer to Android Official Documentation

// [Example] Get the audio under asset and play it
let assetManager = UTSAndroid.getAppContext()!.getAssets();
let afd = assetManager.openFd("free.mp3");
let mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();

** 与Application的转换 **

UTSAndroid.getAppContext() 默认返回的是 实现了 Context抽象类的Application 对象

部分场景,明确需要 Application 对象,那么直接强制类型转换即可

import Application from 'android.app.Application'


let app = UTSAndroid.getAppContext() as Application
console.log(app)

# 4.1.2 getResourcePath(resourceName:String)

HBuilderX 3.6.3+

import { UTSAndroid } from "io.dcloud.uts";

Get the runtime absolute path of the specified plugin resource

// [Example] Get the specified resource path
// Get the runtime path of the file: `/storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/__UNI__3732623/www/uni_modules/test-uts-static/static/logo.png`
UTSAndroid.getResourcePath("uni_modules/test-uts-static/static/logo.png")

# 4.1.3 onAppTrimMemory / offAppTrimMemory

# onAppTrimMemory

HBuilderX 3.6.11+

When the App memory is insufficient, the system callback function corresponds to the native API: onTrimMemory

UTSAndroid.onAppTrimMemory((level:Number) => {
	let eventName = "onAppTrimMemory - " + level;
	console.log(eventName);
});
# offAppTrimMemory

HBuilderX 3.6.11+

The anti-registration function corresponding to onAppTrimMemory

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppTrimMemory()
// Remove the specified listener
UTSAndroid.offAppTrimMemory((level:Number) => {
	
});

# 4.1.4 onAppConfigChange / offAppConfigChange

# onAppConfigChange

HBuilderX 3.6.1+

Triggered when the App configuration changes, such as switching between horizontal and vertical screens Corresponding to the native API: onConfigurationChanged

UTSAndroid.onAppConfigChange((ret:UTSJSONObject) => {
	let eventName = "onAppConfigChange - " + JSON.stringify(ret);
	console.log(eventName);
});
# offAppConfigChange

Anti-registration function corresponding to onAppConfigChange

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppConfigChange();
// Remove the specified listener
UTSAndroid.offAppConfigChange(function(ret){

});

Special note: In addition to the functions listed in this chapter, other context methods of the application in the android environment can be implemented through getAppContext()!.xxx()

For example, to get the app cache directory:

UTSAndroid.getAppContext()!.getExternalCacheDir()!.getPath()

# 4.2 Activity 上下文

# 4.2.1 getUniActivity

HBuilderX 3.6.11+

Get the activity instance to which the current plug-in belongs, corresponding to the android platform getActivity function implementation

In the Android development scenario, this context needs to be used to invoke activity-level resources/capabilities. For more usage, refer to Android Official Documentation

// [示例]获取当前activity顶层容器
let decorView = UTSAndroid.getUniActivity()!.window.decorView;
let frameContent = decorView.findViewById<FrameLayout>(android.R.id.content)

# 4.2.2 onAppActivityPause / offAppActivityPause

# onAppActivityPause

HBuilderX 3.6.3+

Triggered when App's activity onPause

UTSAndroid.onAppActivityPause(() => {
    let eventName = "onAppActivityPause - " + Date.now();
    console.log(eventName);
});
# offAppActivityPause

HBuilderX 3.6.9+

The anti-registration function corresponding to onAppActivityPause

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppActivityPause();
// Remove the specified listener
UTSAndroid.offAppActivityPause(() => {
});

# 4.2.3 onAppActivityResume / offAppActivityResume

# onAppActivityResume

HBuilderX 3.6.3+

Triggered when App's activity onResume

UTSAndroid.onAppActivityResume(() => {
     let eventName = "onAppActivityResume - " + Date.now();
     console.log(eventName);
});
# offAppActivityResume

HBuilderX 3.6.9+

The anti-registration function corresponding to onAppActivityResume

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.onAppActivityResume();
// Remove the specified listener
UTSAndroid.onAppActivityResume(() => {
});

# 4.2.4 onAppActivityDestroy / offAppActivityDestroy

# onAppActivityDestroy

HBuilderX 3.6.3+

Triggered when App's activity onDestroy

UTSAndroid.onAppActivityDestroy(() => {
     let eventName = "onAppActivityDestroy- " + Date.now();
     console.log(eventName);
});
# offAppActivityDestroy

HBuilderX 3.6.9+

Anti-registration function corresponding to onAppActivityDestroy

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppActivityDestroy();
// Remove the specified listener
UTSAndroid.offAppActivityDestroy(() => {
});

# 4.2.5 onAppActivityBack / offAppActivityBack

# onAppActivityBack

HBuilderX 3.6.3+

Triggered when the App's activity falls back to a physical button click

UTSAndroid.onAppActivityBack(() => {
     let eventName = "onAppActivityBack- " + Date.now();
     console.log(eventName);
});

# offAppActivityBack

HBuilderX 3.6.9+

The anti-registration function corresponding to onAppActivityBack

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppActivityBack();
// Remove the specified listener
UTSAndroid.offAppActivityBack(() => {
});

# 4.2.6 onAppActivityResult / offAppActivityResult

# onAppActivityResult

HBuilderX 3.6.8+

App 的 activity 启动其他activity的回调结果监听 对应原生的 onActivityResult

需要特别注意的是 requestCode 参数,这个参数用于区别 不同的请求来源,开发者应该只处理自己发起请求

let customRequestCode = 12000

UTSAndroid.onAppActivityResult((requestCode: Int, resultCode: Int, data?: Intent) => {
	if(requestCode == 12000){
		// 我们发起的请求
		let eventName = "onAppActivityResult  -  requestCode:" + requestCode + " -resultCode:"+resultCode + " -data:"+JSON.stringify(data);
    	console.log(eventName);
	}else{
		// 别的代码发起的请求,不要处理
	}
	
});
# offAppActivityResult

HBuilderX 3.6.9+

The anti-registration function corresponding to onAppActivityResult

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppActivityResult();
// Remove the specified listener
UTSAndroid.offAppActivityResult(() => {
});

# 4.2.7 onAppActivityRequestPermissionsResult / offAppActivityRequestPermissionsResult

已废弃,请使用 4.4章节系统权限管理替代此api

# onAppActivityRequestPermissionsResult

HBuilderX 3.6.3+

App's activity gets the callback of permission request result

UTSAndroid.onAppActivityRequestPermissionsResult((requestCode: number,
                                                     permissions: Array<string>,
                                                     grantResults: Array<number>) => {
		
		console.log(grantResults);
		console.log(permissions);   
		console.log(requestCode);
	});

//发起定位权限申请
let permission = [Manifest.permission.ACCESS_COARSE_LOCATION]
ActivityCompat.requestPermissions(getUniActivity()!,
	    permission, 1001);

# offAppActivityRequestPermissionsResult

HBuilderX 3.6.9+

The anti-registration function corresponding to onAppActivityRequestPermissionsResult

If the function passed in can be empty, if it is empty, it will be regarded as removing all listeners

// remove all monitors
UTSAndroid.offAppActivityRequestPermissionsResult();
// Remove the specified listener
UTSAndroid.offAppActivityRequestPermissionsResult(() => {
});

-----------------------------


特别说明:除了本章节列出的函数外,android环境下 activity 其他上下文方法都可以通过 getUniActivity()!.xxx()的方式实现

比如获取当前activity的顶层View容器

```ts
UTSAndroid.getUniActivity()!.getWindow().getDecorView();

# 4.3 UTS插件开发中Activity生命周期注意事项

即使在android原生开发中,应用的生命周期管理也是十分重要的。 android生命周期

UTS环境中对原生的生命周期进行了封装和简化,用户不需要关心 除了activity 以外的更多原生细节。

只需要了解本章节中列出的 activity相关生命周期即可。即 在UTS环境中 开发者可以认为 activity 的生命周期 就是应用的生命周期。

其中最为常见的场景,要数onAppActivityDestroy中释放系统资源了:

举个例子,以Hello UTS 用户截屏插件为例。

在注册监听回调时,添加了下列代码。

UTSAndroid.onAppActivityDestroy(function(){
	screenOB?.stopWatching()
	screenOB = null
})

这段代码的意思是当宿主activity被销毁时,主动回收屏幕监听的FileObserver

这是因为除了正常的用户注册/注册 之外,还存在一种情况:用户没有反注册,便关闭了应用。 此时FileObserver 并没有被反注册回收。就会导致应用关闭后继续持有上一个uni-app js引擎实例的引用,从而导致下一次启动时出现引擎回调找不到的情况。

开发者在开发UTS插件时,如果遇到了类似使用系统组件的情况,也需要特别关注资源释放情况。

# 4.4 系统权限管理

HBuilder X 3.8.2版本之后支持

系统权限管理使用了 https://github.com/getActivity/XXPermissions 工具库

如果开发者使用了相同依赖,可能打包冲突。需要修改为 complileOnly 或者 修改为本章节内置API

# 4.4.1 requestSystemPermission

请求系统权限,对应的两个参数: 1 请求的权限列表 2 请求结果回调

let permission = ["android.permission.ACCESS_FINE_LOCATION","android.permission.ACCESS_FINE_LOCATION"]
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!,permission,function(allRight:boolean,grantedList:string[]){
		if(allRight){
			// 用户同意了全部权限
		}else{
			// 用户仅同意了 grantedList中的权限
		}
	},function(doNotAskAgain:boolean,grantedList:string[]){
		// 用户拒绝了部分权限,仅允许了grantedList中的权限
		if(doNotAskAgain){
			// 用户拒绝了权限,并且选择不再询问
		}
	})
# 4.4.2 gotoSystemPermissionActivity

跳转至系统设置权限设置界面,一般是用户选择了不再继续询问选项后

let permissionWifi = ["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]
UTSAndroid.gotoSystemPermissionActivity(UTSAndroid.getUniActivity()!,permissionWifi)
# 4.4.3 getSystemPermissionDenied

判断权限是否已经被用户禁止

let permission = ["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]
let denied = UTSAndroid.getSystemPermissionDenied(UTSAndroid.getUniActivity()!, permission)
// 执行结果
[android.permission.ACCESS_FINE_LOCATION, android.permission.ACCESS_FINE_LOCATION]
# 4.4.4 checkSystemPermissionGranted

判断权限是否已经被用户授予

let permission = ["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"]
let grant = UTSAndroid.checkSystemPermissionGranted (UTSAndroid.getUniActivity()!, permission)
// 执行结果
false

# 5 Kotlin与UTS差异重点介绍 (持续更新)

通过上面的章节的阅读。

至此我们认为你已经掌握了UTS语法,掌握了基本的Kotlin语法,掌握了UTS对于android资源的支持。

但是对于一个熟悉android开发的kotlin语言者来说,有很多常用的习惯发生了改变,我们会在这个章节特别指出,便于开发者加深认识。

# 5.1 语法差异


# 5.1.1 可为空的语法标识

kotlin中可为空的语法统一为类型后加?,以下面的代码为例

// A nullable string variable named user
var user:String? = null

但是ts中分两种情况,如果是全局变量,可为空,需要这样写

let user:string | null

如果是成员变量,与kotlin类似,但是区别在于?写在变量后,而非类型后

let user?:string

# 5.1.2 let和var

kotlin中 可变变量修饰为 varval。 区别在于 val 不可变,var可变。

uts中对应var的变量类型为 var/let

推荐使用let 因为只会在作用域内生效,需要慎用var,因为它具备有更大的作用范围

# 5.1.3 方法定义

方法定义 kotlin里的方法只有一种定义方式

 fun startListener():void{
	 
 }

uts中,需要区分全局方法、成员方法

 // member method
 startListener():void{
	 
 }
 // global method
 function startListener():void{
	 
 }

# 5.1.4 extends

kotlin中的: 继承操作符,需要用extends取代

语法 kotlin uts
继承类 : extends
实现type接口 : extends
实现接口 : implements
// 使用UTS 实现 OnClickListener接口
class StartServiceListener extends OnClickListener{
	
    override onClick(v?: View):void{
		// 执行点击逻辑
    }
}

# 5.1.5 非空断言

kotlin中的非空断言是!!,ts中是一个!

user!.sayHello();
user!!.sayHello();

# 5.1.6 快速调用父类实现

//kotlin 中快速实现super
constructor() : super() {
}
	
//uts 中快速实现super
constructor (){
	super();
}

# 5.1.7 匿名内部类

kotlin中可以使用匿名内部类

// kotlin new event listener
user.setListener(Listener(){
	//todo
});

目前版本UTS还不支持匿名内部类,需要显性的声明再新建

// Declare a new class that implements Listener
class MyListener extends Listener{
	// todo
}
// Create a new instance
let myListener = new MyListener();
user.setListener(myListener);

# 5.1.8 可为空函数调用

有一种特殊场景,我们需要定义一些可为空的函数变量,比如下面的 success,fail:

type Option = {
	success?: (res: object) => void;
	fail?: (res: object) => void;
};

这个时候我们需要这样调用

options.success?.(res)

这样的调用方式在kotlin中是非法的,属于TS中的特有语法,需要特别注意。

# 5.1.9 一个类只能有一个构造函数

Kotlin/java中允许一个函数有多个构造器,但是UTS中是不被允许的

# 5.1.10 界面跳转写法

android开发中场景的 intent跳转需要传入 目标界面的class对象,目前UTS中仅支持一种写法

let intent = new Intent(getUniActivity(),DemoActivity().javaClass);
getUniActivity()!.startActivity(intent);

# 5.1.11 指定double数据类型

某些场景下开发者需要获得 指定double数据类型的数据

开发者下意识的写法可能是:

// this is wrong
let a:Int =3
let b:Int =4
let c:Double  = a/b

但是Android原生环境中,数据类型的精度是向下兼容的,如果想要获得一个double类型,必须要有一个double类型参与运算:

// This is correct
let a:Int =3
let b:Int =4
let c:Double  = a * 1.0 / b

# 5.2 警告优化

下面的内容不会影响功能使用,但是在UTS环境中,有合适的解决办法

# 5.2.1 java lang包的引入问题

kotlin 或者java 中java.lang.*是被特殊处理的,可以直接使用而不需要引入。

// Get the current timestamp
System.currentTimeMillis()

UTS环境中,lang包没有被特殊对待,需要手动引入。

// Manually import the classes under the lang package
import System from 'java.lang.System';

// Get the current timestamp
System.currentTimeMillis()

# 5.2.2 UTS 不建议使用 快捷构造

kotlin 中 支持通过()的方式,快速实现无参构造器的声明

// Get the current timestamp
class ScreenReceiver extends BroadcastReceiver(){
  
}

UTS环境中,不建议这样做(虽然目前这样做不会影响编译),建议使用手动声明无参构造

class ScreenReceiver extends BroadcastReceiver{
	
	constructor (){
		super();
	}

}

# 5.2.3 UTS 中下划线前缀的变量,有屏蔽未使用警告的含义

// IDE will prompt that name, status, desc variables are not used
onStatusUpdate(name:string, status:Int, desc:string){
	
}

// don't warn about variable unused
onStatusUpdate(_name:string, _status:Int, _desc:string){
	
}

# 6 常见问题(持续更新)

# 6.1 如何在UTS环境中,新建一个activity

参考Hello UTS项目中的uts-nativepage插件

路径:

~\uni_modules\uts-nativepage

# 6.2 如何在UTS环境中,新建一个service

参考Hello UTS项目中的uts-nativepage插件

路径:

~\uni_modules\uts-nativepage

# 6.3 如何在UTS环境中,新建一个Thread

简单示例

class CustomThread extends Thread{
	
	constructor(){
		super();
	}
	
	override run(){
		Thread.sleep(1000)
		console.log("CustomThread = " + Thread.currentThread().getName())
	}
}

完整示例参考Hello UTS项目中的uts-nativepage插件

路径:

~\uni_modules\uts-nativepage

# 6.4 如果我要实现一个官方已有的三方SDK功能,比如微信支付,如何处理?

因为android中,每个UTS插件都对应一个gradle 子项目,所以类似的情况不能简单复用 自定义基座中的官方依赖。

需要: 不要勾选官方的依赖,然后在uts插件中,按照文档配置依赖

# 6.5 UTSCallback 和 UTSJSONObject 是什么?

UTSCallback 和 UTSJSONObject 是UTS内置专门用于UTS环境和前端交互的特定类型。

uni环境与UTS环境交互时,除了基本数据类型之外,涉及function的需要使用UTSCallback替代,涉及复杂对象object需要用UTSJSONObject 替代

# 6.6 如何生成android平台Array对象

UTS环境中,默认的数组写法[] / Array() 对应到 android平台的数据结构是 UTSArray

理论上来说 UTSArray确实更加灵活强大,但是部分android 平台api 明确要求了 Array格式的数据(比如请求权限)

类似场景下,我们就要使用 toTypedArray() 函数进行转换,以便将MutableList 转换为对应的Array


// 得到一个UTSArray
let permissionArray :String[] = []
// 得到一个Array
console.log(permissionArray.toArray())
// 得到一个MutableList
console.log(permissionArray.toMutableList())

另外还存在一种特殊情况,即开发者 在UTS中使用了 kotlin编写的依赖,这个时候情况稍微复杂些

UTS中只有一种 数组结构相比,kotlin中的数组结构要多很多,比如 IntArray,Array,MutableList等,

对于情况,开发者需要注意两点:

1 UTS具备类型推导功能,调用第三方依赖是不需要声明类型

// 建议的写法
let a:IntArray = xxx.getInfo()

// 这样是没必要的,如果一定要这样写,必须要明确了解到kotlin依赖返回的数据结构,否能可能会因为类型错误,导致编译报错
let a:IntArray = xxx.getInfo()


2 各种数组类型的转换说明

// IntArray 转 MutableList
val a = intArrayOf(1,2,3)
val b = a.toMutableList()


// MutableList 转 Array<Int>
val c = b.toTypedArray()

// Array<Int> 转 IntArray
val d = c.toIntArray()


# 6.7 如何生成byte[]对象

在java平台中,二进制操作一般采用字节数组实现。

UTS在android平台编译后的语言为Kotlin,对应的语法对象是ByteArray.

使用这个类不需要额外引入包,直接运行即可

下面是一个简单的示例

let byteTest = new ByteArray(5)
console.log(byteTest)

# 6.8 如何向UTS环境中传递数组参数

在 uni-app 1.0 平台,js环境与原生环境的交互都是经过js引擎桥接

js引擎除了 string,number,boolean 等基本数据结构外,仅支持JSONObject,JSONArray两种。

  • JSONObject 比较常见,基本所有的接口参数都会 对应一个uts中定义的 type 类
  • JSONArray 一般在uts中采用Array数组来承接

下面是一个Array的使用示例:

// UTS插件,声明数组参数
export function callWithoutParam(filterArray : Array<string>,success: () => void) {
	console.log(filterArray)
	success();
	return { name: "doSthWithCallback" };
}

// 前端传递数组参数
UTSHello.callWithoutParam(
	["system","optionB"]
	,
	()=>{
		uni.showToast({
			icon:'none'
		});
	}
);

# 6.9 编译报错:unresolved reference R (R资源无法识别)

UTS插件支持使用android的原生资源,比如动画,布局,字符串等。 详细说明

如果提示 R资源无法找到:unresolved reference R

1 需要排查资源是否符合android原生格式

2 检查R资源引入的包名是否正确,参考hello uts nativepage插件

import R from 'io.dcloud.uni_modules.uts_nativepage.R';

# 6.10 UTSJSONObject 转 Map

val extraParam = UTSJSONObject()
val extraMap = extraParam.toMap()

# 6.11 synchronized / Lock 等线程同步概念,在UTS里怎么写?

前端领域里线程安全的解决思路 与java的不同。 他们提供了 async/await 等关键字来实现异步任务处理

  • 如果业务代码中有需要多线程、异步任务,建议切换到 async/await 等 uts 语法

  • 如果是要翻译原有的java代码到 UTS,可以选择打成AAR来处理。

# 6.12 UTS 如何判断对象数据类型

简单类型判断,返回结果 string/number/object/function 等


let param = "123"
UTSAndroid.typeOf(param) // string

复杂的对象类名判断

let aa = 12
// 可以获取到具体的类名
console.log(aa.javaClass.toString()) // int

# 6.13 UTS 如何进行遍历操作

相比于for in / 下标计数等写法, UTS 推荐更现代化的foreach语法 实现集合的遍历。

数组:

let arrayObj = utsArrayOf("111","222","333")
arrayObj.forEach(function(e:any){
	console.log(e)
})
let arrayObj2 = [10,20,30]
arrayObj2.forEach(function(e:any){
	console.log(e)
})

遍历Map:

let mapObj = new Map<string,any>()
mapObj.put("name","zhangsan")
mapObj.put("age",12)
mapObj.forEach(function(value:any,key:string){
	console.log(key)
	console.log(value)
})

遍历UTSJSONObject:(暂未公开)

let utsJsonObj = {
	name:"zhangsan",
	age:"22",
}
utsJsonObj.forEach(function(perField:any){
	console.log(perField)
})

# 6.14 UTS 如何实现一个接口

以HelloUTS nativepage插件 部分代码为例:

import OnClickListener from 'android.view.View.OnClickListener';
// 实现 OnClickListener 接口
class User {
   name:string = "name"
}

class StartBroadcastListener extends User implements OnClickListener{
   
   override onClick(v?: View):void{
   	
   	let myReceiver = new ScreenReceiver();
   	let filter = new IntentFilter();
   	filter.addAction(Intent.ACTION_SCREEN_OFF);
   	filter.addAction(Intent.ACTION_SCREEN_ON);
   	UTSAndroid.getUniActivity()!.registerReceiver(myReceiver, filter);
   	
   	// 提示屏幕状态监听已经注册
   	Toast.makeText(UTSAndroid.getAppContext(),"屏幕状态监听已注册,注意观察控制台日志",Toast.LENGTH_LONG).show();
   	
   }
}


// 使用
let btn_start_screen_listen = this.findViewById<Button>(R.id.btn_start_screen_listen);
btn_start_screen_listen.setOnClickListener(new StartBroadcastListener());

如果要同时实现多个接口,采用的也是 implements 和 , 分隔来实现

class Person{
	name:string = ""
}
class User extends Person implements android.view.View.OnClickListener,Cloneable{
	constructor(){
		
	}
	
	override onClick(v?: android.view.View):void{
		console.log(v)
	}
	
	override equals(other?: any):boolean{
		return true
	}
}

编译后的kotlin代码

open class Person {
    open var name: String = "";
}
open class User : Person, android.view.View.OnClickListener, Cloneable {
    constructor(){}
    override fun onClick(v: android.view.View?): Unit {
        console.log(v, " at uni_modules/uts-helloworld/utssdk/index.uts:37");
    }
    override fun equals(other: Any?): Boolean {
        return true;
    }
}

其中需要注意的是

  • 目前暂不支持匿名声明,需要先定义一个 StartBroadcastListener 声明实现 OnClickListener 后再显性的创建

# 7 已知待解决问题(持续更新)

# 7.1 结构入参 boolean 参数默认为true

当以type 结构体为参数时,其内部boolean字段 默认值为false,不支持指定。

# 7.2 目前尚不支持 Math内置

HBuilderX 3.7.1 版本已支持

# 7.3 目前尚不支持 8.x 版本gradle

建议先使用7.X版本,这个问题后续会处理

# 7.4 android原生资源文件,暂不支持三方库依赖

比如xml布局文件中暂时只支持 linearlayout等官方标签,不支持 appcompat等三方库标签。这个问题后续会被处理

On This Page