# uts for Android

本文旨在帮助Android开发者快速上手UTS。

需要阅读者具备Android原生应用开发经验。

# 1 了解UTS插件是什么

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

对于Android开发者来说,我们需要了解的是:

  1. 编译时:当我们在保存UTS源码文件时,IDE会同步将其编译为对应的Kotlin代码。
  2. 运行时:在真机运行/云打包时,这些编译后的kotlin源码也会成为apk的一部分

# 2 掌握UTS语法

# 2.1 对于掌握kotlin语言者

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

# 2.2 对于仅掌握java语言者

与js相比,uts的语法和java更加类似。但是依然存在较大的差异,需要详细阅读2.3语法部分。

尽管开发UTS插件,并不要求一定掌握kotlin,但是鉴于UTS目前在android平台上,会编译为kotlin源码。学会kotlin语言,方便排查问题和复杂功能实现。

因此建议学习一下kotlin语法。

# 2.3 数据类型差异

虽然 UTS 和 koltin 在数据类型上基本保持了一致,但是在部分场景下,还是会有差异,在此特别说明

原则上:

数据类型以UTS 内置的类型为准, 各原生平台都会对其自动适配。

但是 UTS本身是跨平台语言,当具体平台的api 有明确要求时,需要以对方明确要求的数据类型为准。


# 举例一: Int 和Number

默认情况下UTS 开发者可以使用 Number 覆盖android 平台上使用 Int的场景。

但是当开发者重写 Service 组件onStartCommand 方法时,Android API要求 明确要求后两个参数 必须为Int

原生开发环境中,应该这样写:

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

标准的TS环境中,只有Number类型而没有Int类型

为了适应这种情况,UTS 允许开发者使用原生平台的数据类型Int,来满足原生API对数据类型的要求:

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

# 举例二:MutableList

MutableListandroid平台 特有的数据类型,一般场景下,可以使用UTS中内置类型 Array 替代

但是在 调用onAppActivityRequestPermissionsResult 函数监听权限申请结果时,明确要求使用此类型的参数

在原生环境中,应该这样写:


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

});

标准的TS环境中,没有MutableList类型,与之相近的数据类型是 Array

为了适应这种情况,UTS 允许开发者使用原生平台的数据类型MutableList,来满足原生平台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 线程环境差异

UTS环境中,默认是没有线程概念的。

如果需要执行异步任务,建议通过内置函数UTSAndroid.getDispatcher("io")执行

文档地址

UTSAndroid.getDispatcher("main").async(function(_){
	if(Thread.currentThread().name != 'main'){
		callback(false,"main thread error")
		return
	}
	UTSAndroid.getDispatcher("dom").async(function(_){
		/**
		 * dom 参数,只在2.0生效,1.0会自动切换到main线程
		 */
		if(Thread.currentThread().name != 'main' && Thread.currentThread().name != 'io_dcloud_uniapp_dom'){
			callback(false,"dom thread error")
			return
		}
		UTSAndroid.getDispatcher("io").async(function(_){
			/**
			 * dom 参数,只在2.0生效,1.0会自动切换到main线程
			 */
			if(!Thread.currentThread().name.contains("DefaultDispatcher")){
				callback(false,"io thread error")
				return
			}
			callback(true,"pass")
		},null)

	},null)
},null)

# 3 Android原生环境配置

对于Android项目来说,除了源码之外,还会涉及依赖,资源,配置等常见问题

本章节将会介绍,UTS插件开发环境中如何配置这些属性

注意

  • 1 本章节内的实例代码均取自Hello UTS 项目地址
  • 2 本章节涉及的配置,均需自定义基座后才能生效

# 3.1 配置AndroidManifest.xml

以hello UTS中的native-page插件中的配置文件为例:

文件位置: ~\uni_modules\uts-nativepage\utssdk\app-android\AndroidManifest.xml

AndroidManifest.xml示例:

<?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">
   // 配置权限
   <!--创建前台服务权限-->
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

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

UTS插件中的 AndroidManifest.xmlandroid 中的 AndroidManifest.xml 规则完全一致。

特别提示

每一个UTS插件对应android项目中的一个 lib module.

与你在android studio中手动输入包名不同的是,如果你没有手动包名,HX会按照下面的规则默认生成一个:

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 配置res资源

示例文件在hello uts中的位置:

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

除了这里列出的layout、values目录外,还支持anim等所有android标准资源目录

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

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


# 3.3 配置asset资源

以hello UTS中的uts-advance插件为例。

目录结构

关键代码:

// 获取asset管理器
let assetManager = getAppContext()!.getAssets();
// 加载free.mp3 资源
let afd = assetManager.openFd("free.mp3");
// 使用android 自带的媒体组件进行播放
let mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();

完整的代码在hello uts中的位置:

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

# 3.4 增加libs依赖资源

远端插件可以通过配置 config.json添加依赖 ,下面是一个config.json示例

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



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

开发者在使用列表中的依赖时,需要注意两点:

  • 真机运行时,不需要添加列表中的依赖,即可直接引用相关类
  • 请勿通过 手动添加jar/aar 等方式引入相同的依赖,否则会因依赖冲突导致云打包失败。
+--- 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


如果是本地依赖, 参考 hello uts 将jar/aar 添加到 utssdk/app-android/libs 目录下即可使用

对于gradle 配置的高级写法,可以参考文档

# 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内置库

在uts里,Android的所有api都可以访问。

但是对于Android开发中高频使用applicationactivity等,UTS通过内置对象UTSAndroid 进行了包裹和封装。具体见下:

# 4.1 application 上下文相关

# 4.1.1 getAppContext

HBuilderX 3.6.3+

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

用法说明:获取当前应用Application上下文,对应android平台 Context.getApplicationContext 函数实现

Android开发场景中,调用应用级别的资源/能力,需要使用此上下文。更多用法,参考Android官方文档

// [示例]获取asset下的音频,并且播放
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";

获取指定插件资源的运行期绝对路径

// [示例]获取指定资源路径
// 得到文件运行时路径: `/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+

App 内存不足时,系统回调函数 对应原生的API: onTrimMemory

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

HBuilderX 3.6.11+

onAppTrimMemory 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除所有监听
UTSAndroid.offAppTrimMemory()
// 移除指定监听
UTSAndroid.offAppTrimMemory((level:Number) => {

});

# 4.1.4 onAppConfigChange / offAppConfigChange

# onAppConfigChange

HBuilderX 3.6.1+

App 配置发生变化时触发,比如横竖屏切换 对应原生的API: onConfigurationChanged

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

与onAppConfigChange 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除所有监听
UTSAndroid.offAppConfigChange();
// 移除指定监听
UTSAndroid.offAppConfigChange(function(ret){

});

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

比如获取app缓存目录:

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

# 4.2 Activity 上下文

# 4.2.1 getUniActivity

HBuilderX 3.6.11+

获取当前插件所属的activity实例,对应android平台 getActivity 函数实现

Android开发场景中,调用活动的级别的资源/能力,需要使用此上下文。更多用法,参考Android官方文档

// [示例]获取当前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+

App的activity onPause时触发

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

HBuilderX 3.6.9+

onAppActivityPause 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.offAppActivityPause();
// 移除指定监听
UTSAndroid.offAppActivityPause(() => {
});

# 4.2.3 onAppActivityResume / offAppActivityResume

# onAppActivityResume

HBuilderX 3.6.3+

App的activity onResume时触发

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

HBuilderX 3.6.9+

onAppActivityResume 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.onAppActivityResume();
// 移除指定监听
UTSAndroid.onAppActivityResume(() => {
});

# 4.2.4 onAppActivityDestroy / offAppActivityDestroy

# onAppActivityDestroy

HBuilderX 3.6.3+

App 的 activity onDestroy时触发

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

HBuilderX 3.6.9+

onAppActivityDestroy 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.offAppActivityDestroy();
// 移除指定监听
UTSAndroid.offAppActivityDestroy(() => {
});

# 4.2.5 onAppActivityBack / offAppActivityBack

# onAppActivityBack

HBuilderX 3.6.3+

App 的 activity 回退物理按键点击时触发

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

# offAppActivityBack

HBuilderX 3.6.9+

onAppActivityBack 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.offAppActivityBack();
// 移除指定监听
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+

onAppActivityResult 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.offAppActivityResult();
// 移除指定监听
UTSAndroid.offAppActivityResult(() => {
});

# 4.2.7 onAppActivityRequestPermissionsResult / offAppActivityRequestPermissionsResult

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

# onAppActivityRequestPermissionsResult

HBuilderX 3.6.3+

App 的 activity 获得权限请求结果的回调

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+

onAppActivityRequestPermissionsResult 对应的反注册函数

如果传入的函数可为空,如果为空,则视为移除所有监听

// 移除全部监听
UTSAndroid.offAppActivityRequestPermissionsResult();
// 移除指定监听
UTSAndroid.offAppActivityRequestPermissionsResult(() => {
});

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

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

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

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

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

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 语法差异


# let和var

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

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

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

# 方法定义

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

 fun startListener():void{

 }

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

 // 成员方法
 startListener():void{

 }
 // 全局方法
 function startListener():void{

 }

# extends

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

语法 kotlin uts
继承类 : extends
实现type接口 : extends
实现接口 : implements
// 使用UTS 实现 OnClickListener接口
class StartServiceListener extends OnClickListener{

    override onClick(v?: View):void{
		// 执行点击逻辑
    }
}

# 非空断言

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

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

# 快速调用父类实现

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

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

# 匿名内部类

kotlin中可以使用匿名内部类

// kotlin 新建事件监听
user.setListener(Listener(){
	//todo
});

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

// 声明一个新的类,实现Listener
class MyListener extends Listener{
	// todo
}
// 新建实例
let myListener = new MyListener();
user.setListener(myListener);

# 可为空函数调用

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

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

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

options.success?.(res)

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

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

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

# 界面跳转写法

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

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

# 指定double数据类型

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

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

// 这样是错误的
let a:Int =3
let b:Int =4
let c:Double  = a/b

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

// 这样才是正确的
let a:Int =3
let b:Int =4
let c:Double  = a * 1.0 / b

# 警告优化

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

# java lang包的引入问题

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

// 获取当前时间戳
System.currentTimeMillis()

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

// 手动引入lang包下的类
import System from 'java.lang.System';

// 获取当前时间戳
System.currentTimeMillis()

# UTS 不建议使用 快捷构造

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

// 获取当前时间戳
class ScreenReceiver extends BroadcastReceiver(){

}

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

class ScreenReceiver extends BroadcastReceiver{

	constructor (){
		super();
	}

}

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

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

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

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

参考Hello UTS项目中的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插件

如果只是想要简单的开启一个异步任务,建议使用:

UTSAndroid.getDispatcher("io").async(function(_){

}

详细用法

# 如何生成byte[]对象

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

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

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

下面是一个简单的示例

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

# 编译报错: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.4 如何实现一个接口

以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 后再显性的创建

# UTS 如何访问静态实例方法

kotlin编译产出的AAR,会将访问方法修改为私有,不能以下面的方法访问

ScancodeConfig.setShowLine(false);

应该使用属性访问:

ScancodeConfig.showLine = false;

# 6.5 Android原生API过时警告处理

调用原生过时的API插件编译时产生警告,可以使用@Suppress("DEPRECATION")添加注解到使用的方法上忽略警告,例:

@Suppress("DEPRECATION")
function getAppName(context : Context) : string {
	let appName = ""
	try {
		const packageManager = context.getPackageManager()
		const applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0)
		appName = packageManager.getApplicationLabel(applicationInfo) as string
	} catch (e : Exception) {
		e.printStackTrace()
	}
	return appName
}

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

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

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

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

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

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

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

本页导读