Unity与Android交互(3)——需要了解的Andorid基础知识

news2024/12/23 14:10:51

【前言】

在上一篇文章中只是说了如何实现unity和android交互的问题,要了解其中的原理还必须要先了解一些Android的基础知识,了解后也能搞清楚如何接入SDK或者写Native插件。

(以下只是简要介绍,详细的内容需要自己去看链接)

【Android四大组件】

Activitiy

它提供了一个窗口,可以包含用户界面相关的组件,主要用于和用户进行交互,这个窗口通常会填满整个屏幕(可以将Activity理解为Unity中的Canvas)。Android应用程序通常由多个彼此松散绑定的Activity组成,通常,会有一个Activity被指定为MainActivity,启动应用程序时该Activity被激活呈现画面给用户(类似游戏中的主界面),每个Activity可以启动另一个Activity(类似在一个Canvas中打开另一个Canvas),Activity之间的切换基于堆栈机制(这个机制由Android系统实现,游戏中一般是在UIManager中自己实现),Activity之间通过Intent通信。这些Activity需要在AndroidM

在Unity游戏中,通常只有一个Activity,即UnityPlayerActivity。这个Activity会作为游戏的主界面,在启动游戏时被创建并显示,而且在游戏运行期间一直保持活跃状态。UnityPlayerActivity负责加载Unity引擎,并协调游戏界面和游戏逻辑的交互。在Unity中,所有的UI元素、游戏场景、特效等都是通过Unity引擎进行渲染和展示的,因此不需要像原生Android应用那样创建多个Activity来管理多个窗口或界面。

Activity有自己的生命周期,在这个生命周期中进行调用,与Unity的生命周期中的部分关联起来,即可让游戏运行起来。

Service

Service非常适用于去执行那些不需要和用户交互而且还要长期运行在后台的任务,例如播放音效、下载文件等。在Android中,后台的运行是完全不依赖UIService一般默认是后台的,其运行时不依赖任何用户界面,即使Activity被销毁,程序被切换到后台或者打开另外一个应用程序,Service仍然可以保存正常运行(这时Acvivity已经停止运行了)。只有当应用程序进程被杀掉时,所有依赖于该进程的Service才会停止运行。

注意,Service是运行在主线程中的,也即生命周期由主线程控制,所有不能在Service中做耗时长的操作,这会导致主线程阻塞,引发引发ANRApplication Not Responding)异常。耗时操作,通常在Service中开子线程来完成的。

在Android系统中,Service的优先级较低,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。如果希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。前台Service会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

Content Provider

Android的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到ContentProvider。其以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。而且Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。

如果自己的应用程序里的数据需要其他应用程序访问,需要自己实现ContentProvider,通过uri(统一资源定位符,Universal Resource Identifier)来标识哪些数据其他应用程序可以访问,每个资源都有一个uri。其他应用程序通过ContentResolver来访问ContentProvider提供的数据,通过uri来定位自己要访问的数据,还可以通过ContentProvider来监听ContentProvider的数据变化。(UnityAddressable有和这套机制相似的概念)

Broadcast Receiver

看到广播,不管是在哪,我们都知道是用于做跨模块通信的,在Android中用于应用内多个不同组件之间、不同应用组件之间的消息通信。广播分为有序广播,粘性广播、本地广播、系统广播等。像手机开启、网络状态改变、开始充电、屏幕开启关闭等都会发出系统广播 ,注册了相关广播的app就能知道了,广播被封装在Intent里面,会包含广播类型、广播参数等必要信息。

在发布到Android平台的Unity游戏中,是否需要使用Service、ContentProvider、broadcast receiver,取决于游戏的具体需求。Unity提供了Android Java插件的机制,让我们可以使用Java代码来创建和管理这些组件以实现更加复杂的功能。

【AndroidManifest.xml】

每个Android的应用程序都必须包含一个 AndroidManifest.xml,且文件名是固定的,不能修改。应用程序需要通过它向Android系统提供一些必需的信息,且需要在运行前提供给系统。这些信息包括:

  1. 应用的软件包名称,其通常与代码的命名空间相匹配
  2. 应用的组件,即所需使用的四大组件。每个Activity、Service、Content Provider都需要在文件中进行配置,未配置则不会启动,也即用不了。而Broadcast Receiver的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。静态注册会随系统的启动而一直处于活跃状态,即使程序未运行,只要接收到感兴趣的广播就会触发。
  3. 应用为访问系统或其他应用的受保护部分所需的权限
  4. 应用需要的硬件和软件功能

AndroidManifest文件结构

文件结果官网上说的很详细,不懂的直接在官网上搜即可,这点是必须要看的

Unity如何生成AndroidManifest

首先,Unity有一个默认的AndroidManifest文件,位于:

Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk

其次,自己可以根据需要重写AndroidManifest文件:

再次,所有接入的插件或SDK可能以后自己的的AndroidManifest文件

最后,在构建应用时,Unity会将上述所有的文件合成一个文件,修改清单,自动向清单添加权限、配置选项、使用的特性和其他信息,生成最终的AndroidManifest文件。

实例AndroidManifest解析

以之前的例子打包出来的apk的文件并添加些其他东西作为例子进行解析

<?xml version="1.0" encoding="utf-8" standalone="no"?>                                                                                      <!-- 安装位置是外部存储 -->                 <!-- 包名,在unity中填写的 --> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:compileSdkVersion="32" android:compileSdkVersionCodename="12" android:installLocation="preferExternal" package="com.test.UnityTest" platformBuildVersionCode="32" platformBuildVersionName="12">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 读取外部存储权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 写入外部存储权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 挂载文件系统权限 -->
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/> <!-- 写入媒体存储权限 -->
    <uses-feature android:glEsVersion="0x00030000"/>
    <uses-feature android:name="android.hardware.vulkan.version" android:required="false"/>  <!-- uses-feature表示需要使用的硬件,使用vulkan渲染 --> 
    <uses-permission android:name="android.permission.INTERNET"/> <!-- 网络权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- 定位权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- 定位权限 -->
    <uses-feature android:name="android.hardware.location.gps" android:required="false"/> <!-- 需要使用gps定位 -->
    <uses-feature android:name="android.hardware.location" android:required="false"/> <!-- 使用定位 -->
    <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> <!-- 使用触摸屏 -->
    <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false"/> <!-- 使用多点触摸 -->
    <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false"/>   <!-- 使用多点触摸 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <!-- 读取媒体图片权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>   <!-- 读取媒体视频权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>   <!-- 读取媒体音频权限 -->
    <application android:debuggable="true" android:extractNativeLibs="true" android:icon="@mipmap/app_icon" android:label="@string/app_name" android:requestLegacyExternalStorage="true">
        <activity android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode" android:exported="true" android:hardwareAccelerated="false" android:launchMode="singleTask" android:name="com.unity3d.player.UnityPlayerActivity" android:resizeableActivity="false" android:screenOrientation="fullSensor" android:theme="@style/UnityThemeSelector"> 
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/> <!-- activity中有这一行表示其被设置为Main Activity 启动应用时首先显示哪一个Activity --> 
                <category android:name="android.intent.category.LAUNCHER"/><!-- 表示activity应该被列入系统的启动器(launcher)(允许用户启动它)。Launcher是安卓系统中的桌面启动器,是桌面UI的统称。 -->
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true"/> <!-- meta-data是一种键值对,用于向组件提供配置信息,这里表示该activity是unity的activity -->
            <meta-data android:name="android.notch_support" android:value="true"/> <!-- notch_support表示是否支持刘海屏 -->
        </activity>
        <meta-data android:name="unity.splash-mode" android:value="0"/>     <!-- 0表示不显示splash screen -->
        <meta-data android:name="unity.splash-enable" android:value="true"/> <!-- true表示显示splash screen -->
        <meta-data android:name="unity.allow-resizable-window" android:value="false"/> <!-- false表示不允许改变窗口大小 -->
        <meta-data android:name="notch.config" android:value="portrait|landscape"/> <!-- notch.config表示刘海屏的配置,portrait表示竖屏,landscape表示横屏 -->
        <meta-data android:name="unity.build-id" android:value="2dd52062-f22a-4914-98d2-35cfe8574afc"/>     <!-- build-id表示构建id -->
    </application>
</manifest>

【UI线程】

在Android中UI线程是一个应用程序的主线程, 其在app启动时就会被创建(即每个app都有一个UI线程),Activity中涉及到UI组件的更新必须在主线程中进行(这和UnityAPI只能在主线程中使用是一个道理)。从其他线程的调用如果最终涉及到UI组件,就会报错甚至崩溃。其他线程调用UI线程的组件实际上就是多线程之间的通信,Android 提供了几种途径来从其他线程访问 UI 线程,在Unity中一般只用到Activity.runOnUiThread(Runnable)

runOnUiThread()方法是Activity类的一个方法,可以通过当前Activity对象来调用。该方法接受一个Runnable对象作为参数,该Runnable对象中的run()方法会在主线程中执行。如果当前线程时UI线程,那么会立即执行,如果不是那会发到UI线程的一个事件队列里等待执行。其源码如下:

public final void runOnUiThread(Runnable action) {
    if(Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

前文说过,Unity并不使用Android来渲染UI界面,所以Unity有自己的主线程,和Android的UI线程不是同一个线程,但我们可以在UI线程中通过UnitySendMessage发消息给Unity主线程(如上一篇文章所示)。像播放广告、浏览网页等,一般用的是Android UI组件是实现,注意要在UI线程中进行一些处理,需要用到runOnUiThread。

【arr与jar】

Android工程中, app最终被编译打包成能在Android设备上运行的APK文件,Android library在目录结构上与Android App相同。

其包含构建APP所需的一切(如源代码、资源文件、Android Manifest),但在构建时被编译成供其它Android App依赖的Android Archive (AAR)文件,也即多个app可以使用同一个arr文件(类似Unity中的Package)。

一个arr文件包含了源代码、资源文件、Android Manifest等,将arr文件解压出来,可以得到只包含源代码的Java Archive(JAR)文件。

【静态链接库与动态链接库】 

静态库和动态库都是从其他工程里build出来的可重定位目标文件,里面都是二进制的代码,可以被链接生成可执行文件。

在构建形成可执行文件的链接步骤中,会将自己的代码和从静态链接库拷贝出来的代码(仅需要拷贝自己代码使用的那部分库代码,没用的不拷贝)形成一个整体的代码,对动态链接库只会拷贝一些重定位和符号表信息。

在程序运行时,静态链接库中的代码和自己的代码作为一个整体被加载到内存中,而动态链接库在使用时根据重定位和符号表信息加载库并找到需要调用的函数。

因此,对于同样的一份代码,使用静态库构建出来的可执行文件比用动态库的大,多个不同的可执行文件使用了同样的静态库,那么内存中会有多份静态库的代码,而动态库只会有一份。

Linux/Unix 系统里静态库扩展名一般是 .a(archive),动态库扩展名一般是 .so(share object)

Windows 系统里 VC 编译器用的静态库扩展名一般是 .lib,动态库扩展名一般是 .dll(dynamic link library)

Android so文件

Android基于Linux Kernl,开发Android应用时,有时候Java层的编码不能满足实现需求,就需要到C/C++实现后生成的so文件,再用System.loadLibrary()加载进行调用。常见的场景如:加解密算法,音视频编解码等。不同的CPU架构对所执行的二进制文件的规范是不同的,所以在生成so文件时,需要考虑适配市面上不同手机CPU架构。

【SDK的生成】

  1. 编写源代码:一般使用C/C++编写需要实现的功能代码,方便跨平台
  2. 编译源代码:使用编译器将源代码编译成库文件,如.so或.a文件
  3. 创建调用接口:如果是面向Android平台,需要Java Native Interface(JNI)创建Java接口,以便Java代码能够使用库文件中的函数和方法
  4. 打包SDK:如果是面向Android平台,将库文件和写好的Java接口打包成Android SDK,以便其他开发者使用
  5. 测试SDK:对生成的SDK进行测试,确保其能够正常运行
  6. 撰写SDK开发、API接口说明等文档
  7. 发布SDK

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/652729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle 查询优化改写(第四章)

第四章 插入、更新与删除 1.阻止对某几列插入 2. 用with check option 限制数据录入 当约束条件比较简单时&#xff0c;可以直接加在表中&#xff0c;如工资必须大于0&#xff1a; alter table emp add constraint ch_sal check(sal>0) 但有些复杂或特殊的约束条件是不能…

MySQL高级篇-索引分析与优化、数据库锁机制与主从复制

MySQL高级 基于MySQL版本5.5 MySQL的架构介绍 MySQL简介 概述 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;目前属于Oracle公司。 MySQL是一种关联数据库管理系统&#xff0c;将数据保存在不同的表中&#xff0c;而不是将所有的数据放在…

化工企业针对消防安全的具体防火对策介绍 安科瑞 许敏

摘要&#xff1a;我们国家对化工企业的消防安全问题是非常关注的&#xff0c;而且化工企业当中生产的产品都是具有危险性的&#xff0c;极容易发生爆炸等危险事故&#xff0c;所以化工企业出现泄漏就会产生严重后果。下面这篇文章我们就对化工企业的消防安全问题进行具体的探讨…

机器人视觉梳理(下)

原创 | 文 BFT机器人 03 机器人3D视觉应用案例 1.波士顿动力Atlas Atlas 使用 TOF 深度相机以每秒 15 帧的速度生成环境的点云&#xff0c;点云是测距的大规模集合。Atlas 的感知软件使用一种名为多平面分割的算法从点云中提取平面。多平面分割算法的输入馈入到一个映射系统…

Linux运维监控学习笔记3

通过Zabbix-agent监控远程主机 1、在agent1上安装abbix-agent包&#xff1a; yum install -y zabbix-agent 2、配置zabbix-agent端的配置文件&#xff0c;启动服务器并进行开机自启动配置&#xff1a; vim /etc/zabbix/zabbix-agentd.confServer192.168.17.11 # 被动模式s…

【数据分析】如何使用docker部署程序并移植(算法、接口)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、Docker的基本使用1.安装Docker2.列出本地镜像3.获取镜像,创建本地ubuntu:13.10镜像4.查找镜像5.删除本地镜像6.创建自定义镜像7.镜像…

数学专业毕业生做高性能计算工程师合适吗?

一般人看到数学专业都会很奇怪&#xff0c;学数学有什么用呢&#xff0c;能就业吗&#xff1f;其实不然。像数学、应用数学、物理、地球物理、化工等传统工科专业&#xff0c;在当今形式多变的就业环境下&#xff0c;反而是通用的香饽饽。 拿数学专业来举例&#xff0c;就有有许…

MATLAB | 如何使用MATLAB获取顶刊cell全部绘图(附带近3年全部图像)

众所周知&#xff0c;学习科研绘图、配色最好的办法就是去观摩顶级期刊上的作图&#xff0c;本来想着收集各个顶级期刊绘图提供给大家&#xff0c;但是每年的图像加起来大约3-5G&#xff0c;撑死也就能提供三四年的图像&#xff0c;再多内存可能不太够了&#xff0c;于是我写了…

【python】pandas-DataFrame类型数据重命名列表头

目录 0.环境 1.将DataFrame类型数据某一列重命名 0.环境 windows jupyter notebook python 使用场景&#xff1a; 在处理数据对齐的问题时&#xff0c;两个表格的对齐列名不相同&#xff08;一个数据集是DataFrame类型&#xff0c;一个数据集是geopandas类型&#xff09;&…

加速金融营销科技价值发现,神策数据双引擎赋能数字化客户经营

6 月 9 日&#xff0c;「开放融合&#xff0c;引领营销 5.0 新纪元——暨 2023 年金融营销科技价值发现论坛」在中国杭州成功举办。 神策数据创始人 & CEO 桑文锋发表了《双引擎赋能数字化客户经营》的主题演讲&#xff0c;并与数十位来自银行、证券、保险、互联网金融等领…

基于Eclipse+Java+Swing+Mysql实现进销存管理系统

基于EclipseJavaSwingMysql实现进销存管理系统 一、系统介绍二、功能展示1.登陆2.商品列表3.购买清单4.个人中心5.商品采购6、采购记录7、商品预警 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 用户&#xff1a;商品列表、购买清单、个人中心 管理员&#xff1…

腾讯安全升级三大原子能力,助力企业构建数字安全免疫力

数字化时代&#xff0c;企业如何治好“发展”与“安全”失调症&#xff1f;近日,腾讯安全、IDC、腾讯研究院等联合举办研讨论坛&#xff0c;发布“数字安全免疫力”模型框架&#xff0c;提出用免疫思维应对新时期下安全建设与企业发展难以协同的挑战。 (腾讯安全联合IDC发布“…

Redis入门 - Redis概念和基础

原文首更地址&#xff0c;阅读效果更佳&#xff01;Redis入门 - Redis概念和基础 https://www.codermast.com/database/redis/introduce-redis.html 什么是NoSQL NoSQL&#xff0c;泛指非关系型的数据库。NoSQL最常见的解释是 "non-relational"&#xff0c; 很多人…

Spring Boot整合Redis

文章目录 一、Redis概述二、使用Spring Boot 整合 Redis&#xff08;一&#xff09;搭建Redis环境&#xff08;二&#xff09;下载和安装Redis可视化管理工具&#xff08;三&#xff09;创建Spring Boot项目RedisDemo&#xff08;四&#xff09;创建实体类1、创建地址实体类 - …

建立和使用Python自定义模块

文章目录 &#xff08;一&#xff09;现状以及问题&#xff08;二&#xff09;Python模块&#xff08;2.1&#xff09;包的结构&#xff08;2.2&#xff09;包的位置&#xff08;2.2.1&#xff09;site-packages目录&#xff08;2.2.2&#xff09;dist-packages目录 &#xff0…

更新阅读量时的常见错误:脏读

最近在对开发完的项目进行测试&#xff0c;于是用到了Jmeter来进行压力测试…… 这里准备压测的是一个获取详情的方法&#xff0c;在获取详情的通知更新阅读量&#xff01; 上图中代码的逻辑是&#xff1a; 1. 根据ID获取实体 2. 判断实体是否存在 3. 使用三目运算获…

【c++11】c++1特性

c11 c11简介列表初始化std::initializer_list autodecltypenullptr 结语 c11简介 从C0x到C11&#xff0c;C标准10年磨一剑&#xff0c;第二个真正意义上的标准珊珊来迟。相比于C98/03&#xff0c;C11则带来了数量可观的变化&#xff0c;其中包含了约140个新特性&#xff0c;以…

datax-web

【DataX-Web的搭建&#xff08;Windows/Linux环境&#xff09;_datax源码部署_zxj19880502的博客-CSDN博客 mirrors / WeiYe-Jing / datax-web GitCode https://github.com/WeiYe-Jing/datax-web/blob/master/userGuid.md 安装使用 这里只记录关键点 1、先安装datax、参考…

LED太阳能感应庭院灯开发方案

在庭院装修的时候&#xff0c;常会使用一些外部美观的照明灯&#xff0c;提供户外照明。为此&#xff0c;宇凡微推出了LED太阳能感应庭院灯方案&#xff0c;庭院灯控制芯片采用宇凡微YF单片机。 一、LED太阳能感应庭院灯方案的原理 太阳能感应庭院灯根据不同的设计需求&#xf…

dp算法篇Day4

"我把每一天都当做新开始&#xff0c;新革命。" 16、买卖股票的最佳时机含手续费 (1) 题目分析 完成一笔交易才算达成交易。但其实你可以将手续费加在任意一处上。 (2) 算法原理 class Solution { public:int maxProfit(vector<int>& prices, int fee)…