## uts for Android 本文旨在帮助Android开发者,快速上手UTS。 需要阅读者具备Android原生应用开发经验。 ## 1 了解UTS插件是什么 `UTS插件`是`uni-app`新型插件形式,拥有跨平台,高效率,易调试等优点。[详情](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#) 对于Android开发者来说,我们需要了解的是: 1. 编译时:当我们在保存`UTS`源码文件时,IDE会同步将其编译为对应的Kotlin代码。 2. 运行时:在真机运行/云打包时,这些编译后的kotlin源码也会成为apk的一部分 ## 2 掌握UTS语法 ### 2.1 对于掌握kotlin语言者 因为UTS语法与kotlin很类似,建议快速阅读后,在实践中掌握这UTS语法。[uts语法介绍](https://uniapp.dcloud.net.cn/tutorial/syntax-uts)。 ### 2.2 对于仅掌握java语言者 与js相比,uts的语法和java更加类似。但是依然存在较大的差异,需要详细阅读2.3语法部分。 尽管开发UTS插件,并不要求一定掌握kotlin,但是鉴于`UTS`目前在android平台上,会编译为kotlin源码。学会kotlin语言,方便排查问题和复杂功能实现。 因此建议学习一下kotlin语法。 + kotlin [https://kotlinlang.org/](https://kotlinlang.org/) + kotlin for android [https://developer.android.com/kotlin](https://developer.android.com/kotlin) ### 2.3 数据类型差异 虽然 UTS 和 koltin 在数据类型上基本保持了一致,但是在部分场景下,还是会有差异,在此特别说明 原则上: **数据类型以UTS 内置的类型为准, 各原生平台都会对其自动适配。** **但是 UTS本身是跨平台语言,当具体平台的api 有明确要求时,需要以对方明确要求的数据类型为准。** ------------------------- #### 举例一: Int 和Number 默认情况下`UTS` 开发者可以使用 `Number` 覆盖`android` 平台上使用 `Int`的场景。 但是当开发者重写 `Service` 组件`onStartCommand` 方法时,`Android` API要求 明确要求后两个参数 必须为Int 原生开发环境中,应该这样写: ```kotlin override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { return super.onStartCommand(intent, flags, startId); } ``` 标准的TS环境中,只有`Number`类型而没有`Int`类型 为了适应这种情况,UTS 允许开发者使用原生平台的数据类型Int,来满足原生API对数据类型的要求: ```ts override onStartCommand(intent:Intent ,flags:Int ,startId:Int):Int { return super.onStartCommand(intent, flags, startId); } ``` #### 举例二:`MutableList` `MutableList`是`android`平台 特有的数据类型,一般场景下,可以使用UTS中内置类型 `Array` 替代 但是在 调用`onAppActivityRequestPermissionsResult` 函数监听权限申请结果时,明确要求使用此类型的参数 在原生环境中,应该这样写: ```kotlin onAppActivityRequestPermissionsResult(fun(requestCode: Number, permissions: MutableList, grantResults: MutableList){ }); ``` 标准的TS环境中,没有`MutableList`类型,与之相近的数据类型是 `Array` 为了适应这种情况,UTS 允许开发者使用原生平台的数据类型`MutableList`,来满足原生平台API对数据类型的要求: ```ts onAppActivityRequestPermissionsResult((requestCode: number,permissions: MutableList,grantResults: MutableList) => { }); ``` #### 举例三:`String[]` 部分三方sdk 使用java开发,要求继承/实现的方法参数为 string[]类型,这种情况比较特殊,需要将kotlin.Array先进行别名声明,再继续使用 ```uts import kotlin.Array as KotlinArray; class XXX{ override onCaptureFinish(p0: KotlinArray){ // do sth }; } ``` ### 2.4 线程环境差异 UTS环境中,默认是没有线程概念的。 代码默认执行在uts 单独的线程池中。 如果需要执行异步任务,建议通过内置函数`setTimeOut`执行 ```ts console.log("这里是UTS默认线程 :" + Thread.currentThread().getName()) setTimeOut(function(){ console.log("这里是异步任务线程 :" + Thread.currentThread().getName()) },1000) ``` 如果是android原生api 需要部分代码必须执行在UI线程,则需要通过原生api进行操作: ```ts class AddUIRunnable extends Runnable { override run():void { // do something console.log("这里是android平台主线程 :" + Thread.currentThread().getName()) } }; let uiRunable = new AddUIRunnable(); getUniActivity()!.runOnUiThread(uiRunable) ``` ## 3 Android原生环境配置 对于Android项目来说,除了源码之外,还会涉及依赖,资源,配置等常见问题 本章节将会介绍,UTS插件开发环境中如何配置这些属性 注意: + 1 本章节内的实例代码均取自Hello UTS [项目地址](https://gitcode.net/dcloud/hello-uts) + 2 本章节设计的配置,均需自定义基座后才能生效 + 3 R文件的自动生成,已经在HBuilder X 3.6.9 版本支持,请使用最新版本开发 ### 3.1 配置AndroidManifest.xml 以hello UTS中的native-page插件中的配置文件为例: 示例文件在hello uts中的位置: ~\uni_modules\uts-nativepage\utssdk\app-android\AndroidManifest.xml AndroidManifest.xml示例: ```xml // 配置权限 // 配置service / activity ``` AndroidManifest.xml配置规则与android中的规则是一致的。 特别提示: 每一个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资源 ![](https://native-res.dcloud.net.cn/images/uts/forAndroid/uts_android_res_folder.jpg) 示例文件在hello uts中的位置: ~\uni_modules\uts-nativepage\utssdk\app-android\res 除了这里列出的layout、values目录外,还支持anim等所有android标准资源目录 **需要注意的是:如果res资源中使用了 android appcompat库内置的资源,需要在config.json中添加下面的配置:** ```json { "dependencies": [ "androidx.appcompat:appcompat:1.0.0" ] } ``` ### 3.3 配置asset资源 以hello UTS中的uts-advance插件为例。 ![目录结构](https://native-res.dcloud.net.cn/images/uts/forAndroid/uts_android_assets_folder.jpg) 关键代码: ```ts // 获取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依赖资源 以Hello UTS项目下的uts-tencentgeolocation 插件为例 ![](https://native-res.dcloud.net.cn/images/uts/forAndroid/uts_android_libs_folder.jpg) 示例文件在hello uts中的位置: ~\uni_modules\uts-tencentgeolocation\utssdk\app-android\libs ------ 截止 HBuilder 3.8.2 版本内置了以下依赖 开发者在使用列表中的依赖时,需要注意两点: + 真机运行时,不需要添加列表中的依赖,即可直接引用相关类 + 请勿通过 手动添加jar/aar 等方式引入相同的依赖,否则会因依赖冲突导致云打包失败。 ```gradle +--- 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 ``` ## 4 Android内置库@iodcloudutsandroid 在uts里,Android的所有api都可以访问。 但Android开发中经常要复写application和activity,uni-app主引擎已经复写了相关类。所以想要操作application和activity,需要调用uni-app引擎封装的API。 这些api在`io.dcloud.uts`库下 UTSAndroid对象,具体见下。 ### 4.1 application 上下文相关 #### 4.1.1 getAppContext > HBuilderX 3.6.3+ ```ts import { UTSAndroid } from "io.dcloud.uts"; ``` 用法说明:获取当前应用Application上下文,对应android平台 Context.getApplicationContext 函数实现 Android开发场景中,调用应用级别的资源/能力,需要使用此上下文。更多用法,参考[Android官方文档](https://developer.android.google.cn/docs) ```ts // [示例]获取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` 对象,那么直接强制类型转换即可 ```ts import Application from 'android.app.Application' val app = UTSAndroid.getAppContext() as Application console.log(app) ``` #### 4.1.2 getResourcePath(resourceName:String) > HBuilderX 3.6.3+ ```ts import { UTSAndroid } from "io.dcloud.uts"; ``` 获取指定插件资源的运行期绝对路径 ```ts // [示例]获取指定资源路径 // 得到文件运行时路径: `/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 ```ts UTSAndroid.onAppTrimMemory((level:Number) => { let eventName = "onAppTrimMemory - " + level; console.log(eventName); }); ``` ##### offAppTrimMemory > HBuilderX 3.6.11+ onAppTrimMemory 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除所有监听 UTSAndroid.offAppTrimMemory() // 移除指定监听 UTSAndroid.offAppTrimMemory((level:Number) => { }); ``` #### 4.1.4 onAppConfigChange / offAppConfigChange ##### onAppConfigChange > HBuilderX 3.6.1+ App 配置发生变化时触发,比如横竖屏切换 对应原生的API: onConfigurationChanged ```ts UTSAndroid.onAppConfigChange((ret:UTSJSONObject) => { let eventName = "onAppConfigChange - " + JSON.stringify(ret); console.log(eventName); }); ``` ##### offAppConfigChange 与onAppConfigChange 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除所有监听 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官方文档](https://developer.android.google.cn/docs) ```ts // [示例]获取当前activity顶层容器 let decorView = UTSAndroid.getUniActivity()!.window.decorView; let frameContent = decorView.findViewById(android.R.id.content) ``` #### 4.2.2 onAppActivityPause / offAppActivityPause ##### onAppActivityPause > HBuilderX 3.6.3+ App的activity onPause时触发 ```ts UTSAndroid.onAppActivityPause(() => { let eventName = "onAppActivityPause - " + Date.now(); console.log(eventName); }); ``` ##### offAppActivityPause > HBuilderX 3.6.9+ onAppActivityPause 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.offAppActivityPause(); // 移除指定监听 UTSAndroid.offAppActivityPause(() => { }); ``` #### 4.2.3 onAppActivityResume / offAppActivityResume ##### onAppActivityResume > HBuilderX 3.6.3+ App的activity onResume时触发 ```ts UTSAndroid.onAppActivityResume(() => { let eventName = "onAppActivityResume - " + Date.now(); console.log(eventName); }); ``` ##### offAppActivityResume > HBuilderX 3.6.9+ onAppActivityResume 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.onAppActivityResume(); // 移除指定监听 UTSAndroid.onAppActivityResume(() => { }); ``` #### 4.2.4 onAppActivityDestroy / offAppActivityDestroy ##### onAppActivityDestroy > HBuilderX 3.6.3+ App 的 activity onDestroy时触发 ```ts UTSAndroid.onAppActivityDestroy(() => { let eventName = "onAppActivityDestroy- " + Date.now(); console.log(eventName); }); ``` ##### offAppActivityDestroy > HBuilderX 3.6.9+ onAppActivityDestroy 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.offAppActivityDestroy(); // 移除指定监听 UTSAndroid.offAppActivityDestroy(() => { }); ``` #### 4.2.5 onAppActivityBack / offAppActivityBack ##### onAppActivityBack > HBuilderX 3.6.3+ App 的 activity 回退物理按键点击时触发 ```ts UTSAndroid.onAppActivityBack(() => { let eventName = "onAppActivityBack- " + Date.now(); console.log(eventName); }); ``` ##### offAppActivityBack > HBuilderX 3.6.9+ onAppActivityBack 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.offAppActivityBack(); // 移除指定监听 UTSAndroid.offAppActivityBack(() => { }); ``` #### 4.2.6 onAppActivityResult / offAppActivityResult ##### onAppActivityResult > HBuilderX 3.6.8+ App 的 activity 启动其他activity的回调结果监听 对应原生的 onActivityResult ```ts UTSAndroid.onAppActivityResult((requestCode: Int, resultCode: Int, data?: Intent) => { let eventName = "onAppActivityResult - requestCode:" + requestCode + " -resultCode:"+resultCode + " -data:"+JSON.stringify(data); console.log(eventName); }); ``` ##### offAppActivityResult > HBuilderX 3.6.9+ onAppActivityResult 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.offAppActivityResult(); // 移除指定监听 UTSAndroid.offAppActivityResult(() => { }); ``` #### 4.2.7 onAppActivityRequestPermissionsResult / offAppActivityRequestPermissionsResult ##### onAppActivityRequestPermissionsResult > HBuilderX 3.6.3+ App 的 activity 获得权限请求结果的回调 ```ts UTSAndroid.onAppActivityRequestPermissionsResult((requestCode: number, permissions: MutableList, grantResults: MutableList) => { console.log(grantResults); console.log(permissions); console.log(requestCode); }); //发起定位权限申请 ActivityCompat.requestPermissions(getUniActivity()!, arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 1001); ``` ##### offAppActivityRequestPermissionsResult > HBuilderX 3.6.9+ onAppActivityRequestPermissionsResult 对应的反注册函数 如果传入的函数可为空,如果为空,则视为移除所有监听 ```ts // 移除全部监听 UTSAndroid.offAppActivityRequestPermissionsResult(); // 移除指定监听 UTSAndroid.offAppActivityRequestPermissionsResult(() => { }); ----------------------------- 特别说明:除了本章节列出的函数外,android环境下 activity 其他上下文方法都可以通过 getUniActivity()!.xxx()的方式实现 比如获取当前activity的顶层View容器 ```ts UTSAndroid.getUniActivity()!.getWindow().getDecorView(); ``` ### 4.3 UTS插件开发中Activity生命周期注意事项 即使在android原生开发中,应用的生命周期管理也是十分重要的。 [android生命周期](https://developer.android.com/guide/components/activities/activity-lifecycle?hl=zh_cn) UTS环境中对原生的生命周期进行了封装和简化,用户不需要关心 除了activity 以外的更多原生细节。 只需要了解本章节中列出的 activity相关生命周期即可。即 在UTS环境中 开发者可以认为 activity 的生命周期 就是应用的生命周期。 其中最为常见的场景,要数`onAppActivityDestroy`中释放系统资源了: 举个例子,以Hello UTS [用户截屏插件](https://ext.dcloud.net.cn/plugin?id=9897)为例。 在注册监听回调时,添加了下列代码。 ``` 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 请求结果回调 ```ts let permissionWifi:string[] = mutableListOf("android.permission.ACCESS_FINE_LOCATION","android.permission.ACCESS_FINE_LOCATION"); UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!,permissionWifi,function(allRight:boolean,grantedList:string[]){ if(allRight){ // 用户同意了全部权限 }else{ // 用户仅同意了 grantedList中的权限 } },function(doNotAskAgain:boolean,grantedList:string[]){ // 用户拒绝了部分权限,仅允许了grantedList中的权限 if(doNotAskAgain){ // 用户拒绝了权限,并且选择不再询问 } }) ``` ##### 4.4.2 gotoSystemPermissionActivity 跳转至系统设置权限设置界面,一般是用户选择了不再继续询问选项后 ``` var permissionWifi: MutableList = mutableListOf("android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"); UTSAndroid.gotoSystemPermissionActivity(UTSAndroid.getUniActivity()!!,permissionWifi) ``` ##### 4.4.3 getSystemPermissionDenied 判断权限是否已经被用户禁止 ```ts var permissionWifi: MutableList = mutableListOf("android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"); val denied = UTSAndroid.getSystemPermissionDenied(UTSAndroid.getUniActivity()!!, permissionWifi) // 执行结果 [android.permission.ACCESS_FINE_LOCATION, android.permission.ACCESS_FINE_LOCATION] ``` ##### 4.4.4 checkSystemPermissionGranted 判断权限是否已经被用户授予 ```ts var permissionWifi: MutableList = mutableListOf("android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"); val grant = UTSAndroid.checkSystemPermissionGranted (UTSAndroid.getUniActivity()!!, permissionWifi) // 执行结果 false ``` ## 5 Kotlin与UTS差异重点介绍 (持续更新) 通过上面的章节的阅读。 至此我们认为你已经掌握了UTS语法,掌握了基本的Kotlin语法,掌握了UTS对于android资源的支持。 但是对于一个熟悉android开发的kotlin语言者来说,有很多常用的习惯发生了改变,我们会在这个章节特别指出,便于开发者加深认识。 ### 5.1 语法差异 ------------------------------- #### 5.1.1 可为空的语法标识 kotlin中可为空的语法统一为类型后加`?`,以下面的代码为例 ```ts // 一个可为空的字符串变量,变量名为user var user:String? = null ``` 但是ts中分两种情况,如果是全局变量,可为空,需要这样写 ```ts let user:string | null ``` 如果是成员变量,与kotlin类似,但是区别在于?写在变量后,而非类型后 ```ts let user?:string ``` #### 5.1.2 let和var `kotlin`中 可变变量修饰为 `var`、`val`。 区别在于 val 不可变,var可变。 `uts`中对应`var`的变量类型为 `var/let` 推荐使用`let` 因为只会在作用域内生效,需要慎用`var`,因为它具备有更大的作用范围 #### 5.1.3 方法定义 方法定义 `kotlin`里的方法只有一种定义方式 ```kotlin fun startListener():void{ } ``` uts中,需要区分全局方法、成员方法 ```ts // 成员方法 startListener():void{ } ``` ```ts // 全局方法 function startListener():void{ } ``` #### 5.1.4 extends `kotlin`中的: 继承操作符,需要用`extends`取代 |语法|kotlin|uts| |---|-------|---| |继承类|:|extends| |实现type接口|:|extends| |实现接口|:|implements| ```ts // 使用UTS 实现 OnClickListener接口 class StartServiceListener extends OnClickListener{ override onClick(v?: View):void{ // 执行点击逻辑 } } ``` #### 5.1.5 非空断言 kotlin中的非空断言是`!!`,ts中是一个`!` ```ts user!.sayHello(); ``` ```kotlin user!!.sayHello(); ``` #### 5.1.6 快速调用父类实现 ```ts //ts 中快速实现super constructor() : super() { } ``` ```kotlin //kotlin 中快速实现super constructor (){ super(); } ``` #### 5.1.7 匿名内部类 `kotlin`中可以使用匿名内部类 ```kotlin // kotlin 新建事件监听 user.setListener(Listener(){ //todo }); ``` 目前版本UTS还不支持匿名内部类,需要显性的声明再新建 ```ts // 声明一个新的类,实现Listener class MyListener extends Listener{ // todo } // 新建实例 let myListener = new MyListener(); user.setListener(myListener); ``` #### 5.1.8 可为空函数调用 有一种特殊场景,我们需要定义一些可为空的函数变量,比如下面的 success,fail: ```ts type Option = { success?: (res: object) => void; fail?: (res: object) => void; }; ``` 这个时候我们需要这样调用 ```ts options.success?.(res) ``` 这样的调用方式在kotlin中是非法的,属于TS中的特有语法,需要特别注意。 #### 5.1.9 一个类只能有一个构造函数 在`Kotlin`/`java`中允许一个函数有多个构造器,但是UTS中是不被允许的 #### 5.1.10 界面跳转写法 android开发中场景的 intent跳转需要传入 目标界面的class对象,目前UTS中仅支持一种写法 ```ts let intent = new Intent(getUniActivity(),DemoActivity().javaClass); getUniActivity()!.startActivity(intent); ``` #### 5.1.11 指定double数据类型 某些场景下开发者需要获得 指定double数据类型的数据 开发者下意识的写法可能是: ```ts // 这样是错误的 let a:Int =3 let b:Int =4 let c:Double = a/b ``` 但是Android原生环境中,数据类型的精度是向下兼容的,如果想要获得一个double类型,必须要有一个double类型参与运算: ```ts // 这样才是正确的 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.*是被特殊处理的,可以直接使用而不需要引入。 ```kotlin // 获取当前时间戳 System.currentTimeMillis() ``` UTS环境中,lang包没有被特殊对待,需要手动引入。 ```ts // 手动引入lang包下的类 import System from 'java.lang.System'; // 获取当前时间戳 System.currentTimeMillis() ``` #### 5.2.2 `UTS` 不建议使用 快捷构造 `kotlin` 中 支持通过()的方式,快速实现无参构造器的声明 ```kotlin // 获取当前时间戳 class ScreenReceiver extends BroadcastReceiver(){ } ``` UTS环境中,不建议这样做(虽然目前这样做不会影响编译),建议使用手动声明无参构造 ```ts class ScreenReceiver extends BroadcastReceiver{ constructor (){ super(); } } ``` #### 5.2.3 `UTS` 中下划线前缀的变量,有屏蔽未使用警告的含义 ```ts // IDE会提示 name,status,desc 变量未使用 onStatusUpdate(name:string, status:Int, desc:string){ } // 不会警告变量未使用 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`? 简单示例 ```ts 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平台的数据结构是 `MutableList` 理论上来说 `MutableList`确实更加灵活强大,但是部分android 平台api 明确要求了 Array格式的数据(比如请求权限) 类似场景下,我们就要使用 toTypedArray() 函数进行转换,以便将`MutableList` 转换为对应的`Array` ```typescript // 得到一个MutableList let permissionArray :String[] = [] // 得到一个Array console.log(permissionArray.toTypedArray()) ``` 另外还存在一种特殊情况,即开发者 在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 val c = b.toTypedArray() // Array 转 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,success: () => void) { console.log(filterArray) success(); return { name: "doSthWithCallback" }; } ``` ```js // 前端传递数组参数 UTSHello.callWithoutParam( ["system","optionB"] , ()=>{ uni.showToast({ title:'成功调用', icon:'none' }); } ); ``` ### 6.9 编译报错:unresolved reference R (R资源无法识别) UTS插件支持使用android的原生资源,比如动画,布局,字符串等。 [详细说明](!https://uniapp.dcloud.net.cn/plugin/uts-for-android.html#_3-1-%E9%85%8D%E7%BD%AEandroidmanifest-xml) 如果提示 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() ``` ## 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等三方库标签。这个问题后续会被处理