Uniapp 引入 Android aar 包 和 Android 离线打包

需求:

原生安卓 apk 要求嵌入到 uniapp 中,并通过 uniapp 前端调起 app 的相关组件。

下面手把手教你,从 apk 到 aar,以及打包冲突到如何运行,期间我所遇到的问题都会 一 一 进行说明,相关版本以我文章内为例子,其他版本如果存在差异请自行解决(不过一般也不会有很大差别啦)

官方文档传送门:uni原生插件开发教程

一、uniapp(这里以新建为主演示)

直接在 HBuilder 中新建即可

新建完成后,uniapp 工作台会创建对应的项目,等会生成 appkey 会用到(没有 dcloud 账号就创建一个)

二、Android环境(离线基座项目)搭建

因为我们是 Android 开发的,所以我们只看 Android 部分就可以了,开发环境如下:

首先下载对应 HBuilderX 对应版本的 SDK

1. 查看我们当前 HBuilder 的版本(建议升级到最新):


2. 点击以下红框部分,跳转到查下 Android sdk 界面,选择对应的版本下载,如果HBuilder 不是最新的,就到历史版本中下载对应的即可


3. 在 Android Studio 中,新建项目(选 java,当前项目是我们离线基座的apk,不是我们的项目的),记录下包名(以下我的相关配置):



4. 添加相关配置:

复制代码
app 下 build.gradle:
groovy 复制代码
    implementation 'androidx.appcompat:appcompat:1.0.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.alibaba:fastjson:1.2.83'
    implementation 'androidx.webkit:webkit:1.3.0'
    implementation 'com.facebook.fresco:fresco:2.5.0'
    implementation "com.facebook.fresco:animated-gif:2.5.0"
    implementation 'androidx.recyclerview:recyclerview:1.0.0'

    implementation 'com.squareup.okio:okio:1.15.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.12'
    implementation 'com.github.bumptech.glide:glide:4.9.0'

下载后的sdk放进来(我们自己的aar包打包后直接放进来,然后同步即可 ):

可以看到,这个基座是一个 androidX 的项目 ,如果我们要引进来的项目是 support 的也没关系(我的就是 support 的)

5. 搞一个签名,签名网上有很多教程,这里我就不贴出来了,可以自行搜索,这里我贴一下我的配置,这个签名的作用,是为后面我们打包 apk 和申请 appkey 的时候用到的(离线打包自签名,咋搞都可以,只要签名能用):

6. AndroidManifest.xml

注意点:

(1)application 添加:tools:replace="android:icon,android:allowBackup"

(2)provider 的 authorities需要改成你当前的 包名...dc.fileprovider,例如:android:authorities="com.android.myapplication2.dc.fileprovider"

(3) 增加 dcloud_appkey,value 就是等会我们要去 uniapp 工作台申请的 appkey

html 复制代码
<meta-data
            android:name="dcloud_appkey"
            android:value="xxx" />

完整示例如下

html 复制代码
<?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="com.android.myapplication2">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
    <uses-permission android:name="zy.permission.OUT_ENTER" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:supportsRtl="true"
        tools:replace="android:icon,android:allowBackup">

        <activity
            android:name="io.dcloud.PandoraEntry"
            android:configChanges="orientation|keyboardHidden|keyboard|navigation"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:hardwareAccelerated="true"
            android:theme="@style/TranslucentTheme"
            android:screenOrientation="user"
            android:windowSoftInputMode="adjustResize" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <action android:name="android.intent.action.VIEW" />
                <data android:scheme=" " />
            </intent-filter>
        </activity>
        <activity
            android:name="io.dcloud.PandoraEntryActivity"
            android:launchMode="singleTask"
            android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc|fontScale|keyboard|smallestScreenSize|screenLayout|screenSize|uiMode"
            android:hardwareAccelerated="true"
            android:permission="com.miui.securitycenter.permission.AppPermissionsEditor"
            android:screenOrientation="user"
            android:theme="@style/DCloudTheme"
            android:windowSoftInputMode="adjustResize">
        </activity>

        <provider
            android:name="io.dcloud.common.util.DCloud_FileProvider"
            android:authorities="com.android.myapplication2.dc.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/dcloud_file_provider" />
        </provider>

        <meta-data
            android:name="dcloud_appkey"
            android:value="xxx" />
    </application>

</manifest>

三、申请离线打包的 appkey

1. 登录 dcloud 后,进入工作台,在"应用管理------我的应用"中,找到我们在 HBuilder 中创建的项目

2. 点击 "应用名称" 后,跳转到应用信息,选择 "各平台信息",点击 "新增" 按钮


3. 将我们刚才创建的基座 Android 项目的包名,输入到 "包名" 文本框中,对于 SHA1 和 SHA256 获取,回到我们的 Android Studio,右键我们的签名,然后选择 Terminal 打开

输入命令:keytool -list -v -keystore 签名文件(注意后缀也需要) -storepass 密码

这样就得到了我们的 sha1 和 sha256

最终平台信息如下

4. 上面平台信息填写完成后,点击提交,平台信息就会变化了

5. 这里我已经创建了离线打包 key,没有创建显示的是创建按钮,点击创建生成我们的 appkey,至此,appkey 创建完成

6. 将 appkey,配置到我们刚才 AndroidManifest.xml 中的 dcloud_appkey

四、基座 Android 项目的完善

1. 回到 HBuilder,打开我们的项目,在项目根下,创建以下目录结构(dir1看package.json 的配置):

其中,package.json 配置结构如下,建议红框中的三个部分和dir1都填写一样(插件名称),避免出错,而 class 部分,是用于暴露给 uniapp 调用的类路径,该路径是你 aar 下的,不是签名的基座的(关于 aar 文件,参考后面 aar 打包部分

html 复制代码
{
    "id": "插件名称",
	"name": "插件名称",
    "version": "1.0.0",
    "description": "",
	"_dp_type": "nativeplugin",
	"_dp_nativeplugin": {
		"android": {
			"plugins": [{
				"type": "module",
				"name": "插件名称",
				"class": "aar中提供的类路径"
			}],
			"integrateType": "aar"
		}
	}
}

2. 这里配置正确后,打开 manifest.json,找到 App 原生插件配置,选择本地插件

前面配置正确的话,弹出框就能看到我们的插件了

3. 添加调起我们 aar 的方法(需要和 Android 端约定方法名和参数)

插件名称,参考前面 package.json 的配置,里面 dir1 部分,这里 startActivity(obj),是调起 pageckage.json 中配置的 class 对应类的方法,具体可以看后面打包部分,关于该类的创建和方法定义

4. 在 HBuilder 中,将我们的 uniapp 项目打包成静态资源出来

打包完成后,会在项目根下生成 unpackage下,生成 resources 目录,resources 目录下的目录,存放的就是我们需要的静态资源,该目录名称就是我们项目的应用标识,复制该目录,粘贴到基座 app 的 assets/app 下,该资源目录不存在就创建一个

5. 在 assets 资源目录下,创建 data 目录,然后将 sdk 中提供的三个 dcloud_ 文件放进来,打开 dcloud_control.xml,将其改下面的样子,其中,appid 就是你 uniapp 项目的应用标识,注意要给 hbuilder 添加 debug 和 syncDebug

html 复制代码
<hbuilder debug="true" syncDebug="true">
    <apps>
        <app appid="__UNI__XXXX" appver="1.0.0"/>
    </apps>
</hbuilder>

6. 将我们的的目标 aar 放到基座项目的 libs 下,

前面我们依赖已经配置了 implementation fileTree(dir: 'libs', include: ['.aar', ' .jar']) ,所以手动同步一下

7. 接下来就可以直接运行到手机了,而安装在手机的,就是离线基座了,或者打包成 apk,放在 uniapp unpackage/debug 目录下,基座apk名称修改成 android_debug.apk (一定要这个名字),Hbuilder 选择运行到设备的时候,选择自定义基座也是可以的

五、AAR 打包

PS:以下打包的过程,都是你自己项目的,跟基座项目没关系

PPS:我的项目是 kotlin ,且是 support,可以正常打包和调起的

1、添加打包工具

在项目的 build.gradle 中添加 fat-aar 打包工具:

classpath 'com.github.kezong:fat-aar:1.3.8'

2、修改应用 build.gradle配置

将 'com.android.application' 修改成 'com.android.library'

添加 apply plugin: 'com.kezong.fat-aar'

将所有依赖改成 embed 方式,例如:

如果项目有依赖内部的其他 module 模块,那么也是需要修改成 embed,但是,该模块内的依赖的是不会被打包进去的,为了更好的调整,建议将该模块下的所有依赖,搬到当前应用的依赖下,然后修改成 embed,方便后面修改,也不用去修改所依赖内部模块的内容

引入 uniapp 依赖包,用 compileOnly,该模块用于创建我们前面 package.json 中的 class,该 class 依赖 uniapp 包中的类:

以下是我的项目的部分依赖例子(关于我遇到的包冲突部分,在后面 ):

3、添加 dcloud_uniplugins.json 配置

在 assets 中,创建 dcloud_uniplugins.json,添加如下配置:

其中,name和class 和前面 package.json 中的保持一致,类名可以自己定义

html 复制代码
{
  "nativePlugins": [
    {
      "plugins": [
        {
          "type": "module",
          "name": "插件名称",
          "class": "包名.ZyModule"
        }
      ]
    }
  ]
}

4、创建我们的入口类 ZyModule

这里我只暴露了一个方法 startActivity,且可以传入一个参数,具体根据自己需求修改,我这里方法用于调起我的 MyActivity,这里定义好后,就可以将方法名和 uniapp 的 package.json 中进行配置和调用了

java 复制代码
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.common.UniDestroyableModule;

public class ZyModule extends UniDestroyableModule {

    private static final String TAG = "ZyModule";

    @UniJSMethod(uiThread = true)
    public void startActivity(Object data) {
        Log.e(TAG, "传递参数:" + (JSONObject) data);
        Context context = mUniSDKInstance.getContext();
        try {
            Intent intent = new Intent(context, MyActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
            Log.d(TAG, "启动成功");
        } catch (Exception e) {
            Log.e(TAG, "启动失败: " + e.getMessage());
        }
    }

    @Override
    public void destroy() {

    }
}

5、AndroidManifest.xml 的修改

  1. 去掉 application 中的 name
  2. 去掉 mian 入口的配置
  3. FileProvider 一定要写完整的包名,不能用 ${applicationId} ,否则会调起不起我们的类
  4. 点击 Android Studio 的 gradle(一般在右边),然后找到我们的项目,在 Tasks/ build 或者 other 或者某个目录下,找到 assembleDebug 或者 assembleRelease,双击运行即可生成 aar 包
  5. 将 aar 复制到两个地方(aar包名名称不用管,但最好修改成统一的格式)
    (1)uniapp插件中:nativeplugins\插件名称\android

    (2)直接放在基座的libs目录下,然后同步一下即可

六、问题

1、appkey或者配置无效问题

在申请 appkey 时,其实就是新增平台信息的时候,填错包名或者 SHA1、SHA256,然后重新回来修改成正确的,接着如果去重新生成 appkey,但是发现 appkey 没有改变(如果有改变就用这个新的试试看,我遇到的是没有改变),就重新创建一条平台,原先这条就可以删掉了。

我是一开始包名写错,然后修改成正确的包名后,发现 appkey 没有变化,使用该 appkey 总是包标题错误,就删掉了重新生成才可以的。

2、基座打包冲突和缺包处理

首先,建议直接离线打包,在线打包每个账号每天有次数限制,且需要排队,很慢

其次,包冲突可以在本地快速处理

在打包过程中,有几个包是需要我们自己过滤的,我们的 aar 项目需要改成 compileOnly:

  • fastjson
  • zip4j
  • glide
  • com.squareup.okio:okio:1.15.0 和 com.squareup.okhttp3:okhttp:3.12.12

这几个包在在线打包的时候都会出现包冲突报错的,所以我们不能打包进我们的 aar 中,当然,离线下我们需要引入,具体看一看前面基座项目应用的依赖配置

zip4j在基座的libs下aar中有

fastjson需要基座申明依赖

glide 需要的版本是 4.9.0,如果是其他版本,请将自己的项目配置成该版本,然后自行修改代码

缺包和部分冲突

关于缺包,是因为 embed 的依赖方式,是不会将第三方依赖中的依赖打包进去的,这个需要自己在 External Libraries 中找到相关的包添加到你 aar 应用的依赖中,例如,我这里用到的 rx:

或者我项目本身 kotlin 部分:

当然,在线打包,如果我们的是 kotlin 项目,那么我们需要去掉以下三个包:

其他包冲突和缺包都可以按照上面的方式来处理,建议先离线来处理,一般会把所有冲突先罗列出来,运行的时候才会报缺包,缺包就缺啥补啥即可

3、aar 已经放在 uniapp 项目下,为啥运行的时候,会报插件 undefined

原因是没有制作基座!!!

一定要有基座,基座其实就是将 aar 打包进 apk,运行的时候才会被调用到,基座 apk 路径可以参考前面

七、测试

uniapp 要调起 aar,无论是在线打包还是离线打包,都是生成自定义基座:项目/unpackage/debug/android_debug.apk,然后运行的时候选择自定义基座的,如果没有这个基座,而是默认标准基座,那么 aar 是掉不起来的

相关推荐
TechMerger1 小时前
Android 17 重磅重构!服役 20 年的 MessageQueue 迎来无锁改造,卡顿大幅优化!
android·性能优化
yuhuofei20214 小时前
【Python入门】Python中字符串相关拓展
android·java·python
dalancon4 小时前
Android Input Spy Window
android
dalancon5 小时前
InputDispatcher派发事件,查找目标窗口
android
我命由我123455 小时前
Android Framework P3 - MediaServer 进程、认识 ServiceManager 进程
android·c语言·开发语言·c++·visualstudio·visual studio·android runtime
天才少年曾牛7 小时前
Android14 新增系统服务后,应用调用出现 “hidden api” 警告的原因与解决方案
android·frameworks
赏金术士7 小时前
Jetpack Compose 底部导航实战教程(完整版)
android·kotlin·compose
随遇丿而安7 小时前
第5周:XML 资源、样式和主题,真正解决的是“页面以后还改不改得动”
android
zh_xuan8 小时前
Android 获取系统内存页大小:sysconf(_SC_PAGESIZE) 与 JNI 实现
android·jni·ndk·内存页大小
fundroid9 小时前
Google I/O 2026 | Android 全面进化:从操作系统到“智能中枢”
android·jetpack compose·google i/o 2026