当前位置: 首页 > news >正文

网站url如何做优化网页制作培训网站

网站url如何做优化,网页制作培训网站,wordpress服务器操作系统,易托管建站工具问题 我的问题是什么? 在桥接类 UnityBridgeActivity 中处理不同 unity 版本调用 mUnityPlayer.destroy(); 闪退问题。 闪退日志如: 闪退日志说在 UnityBridgeActivity中找不到类型为 UnityPlayer 的属性 mUnityPlayer。 我们知道,Android…

在这里插入图片描述

问题

我的问题是什么?

在桥接类 UnityBridgeActivity 中处理不同 unity 版本调用 mUnityPlayer.destroy(); 闪退问题。

闪退日志如:

在这里插入图片描述

闪退日志说在 UnityBridgeActivity中找不到类型为 UnityPlayer 的属性 mUnityPlayer。


我们知道,Android unity 游戏开发中通常只有一个 Activity 为游戏主页面,且该 Activity 需要继承自 UnityPlayerActivity unity 的实现,内部有一个重要类是 UnityPlayer 通过它进一步调用接口渲染游戏等。

在我们的 sdk 中 Activity 之间的关系大概是这样,也就是在桥接类中是可以访问父类的成员 mUnityPlayer 的。
在这里插入图片描述

接下来介绍的两个不同版本运行表现不一样。

在这里插入图片描述

1、正常版本

UnityPlayerActivity(例如 unity 版本 2021)
这个版本引擎导出的 unity-class.jar 是这样的,UnityPlayerActivity 具有成员 mUnityPlayer 类型是UnityPlayer 类型。

UnityPlayer
从 unity 导出的抽象类,不同 unity 版本可能有不同实现

public abstract class UnityPlayer {public void destroy() {//... ...}
}
package com.unity3d.player;public class UnityPlayerActivity extends Activity {protected UnityPlayer mUnityPlayer;
}

这个版本打包运行是不会闪退的正常包,我们看 sdk 内调用 destroy() 的大概实现,如下:

UnityBridgeActivity(sdk 桥接类)

public class UnityBridgeActivity extends UnityPlayerActivity {private void quitGame(){//other ... ...mUnityPlayer.destroy();}
}

我们查看此段代码的字节码(通过 Android studio 的 ASM 插件可方便查阅)

  • mUnityPlayer 字段名
  • com/unity3d/player/UnityPlayerActivity 字段所在类
  • Lcom/unity3d/player/UnityPlayer; 字段类型
  • INVOKEVIRTUAL com/unity3d/player/UnityPlayer.destroy ()V 调用字段的 destroy 方法

这段字节码是正确的,没毛病能运行正常,这段代码被编译、打包进 sdk 被外部使用。

  private invokerUnityV1()VALOAD 0GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer : Lcom/unity3d/player/UnityPlayer;INVOKEVIRTUAL com/unity3d/player/UnityPlayer.destroy ()VRETURNMAXSTACK = 1MAXLOCALS = 1

2、闪退版本

UnityPlayerActivity(例如 unity 版本 2022)
这个版本引擎导出的 unity-class.jar 是这样的,UnityPlayerActivity 具有相同成员 mUnityPlayer,但是类型是 UnityPlayerForActivityOrService,与上述不同。

package com.unity3d.player;public class UnityPlayerActivity extends Activity {protected UnityPlayerForActivityOrService mUnityPlayer;
}

UnityPlayerForActivityOrService

package com.unity3d.player;public class UnityPlayerForActivityOrService extends UnityPlayer{}

问题出现!

已知,sdk 编译得到的字节码,明确获取的 mUnityPlayer 字段类型是 UnityPlayer;

GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer : Lcom/unity3d/player/UnityPlayer;

但是我们发现在 unity 版本 2022,字段 mUnityPlayer 的类型是 UnityPlayerForActivityOrService,这里就和闪退日志对上了,明确抛出这个版本中没有 UnityPlayer 类型的这个字段。

解决

字段名称是不变的,只是类型发生了变化导致的闪退,那么我们是否可以针对特定的版本执行不同的分支,获取正确类型的 mUnityPlayer?

1、如何解决

可以在 Android 打包 transform 过程中定位桥接类 UnityBridgeActivity.class (调用 destroy 方法的地方),并读取作为字节数组输入 ASM,利用 ASM 字节码操作修改 mUnityPlayer 的类型,也就是修改指令。

GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer : Lcom/unity3d/player/UnityPlayer;

修改为

GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer : Lcom/unity3d/player/UnityPlayerForActivityOrService;

编码、测试

为了方便修改、测试,可以在桥接类单独抽出这个方法作为新的分支,解决 unity 2022 版本的兼容问题。

private void invokerUnityV2022() {super.mUnityPlayer.destroy();
}

通过 ASM 把这个方法修改为这样即可,却别仅在于 mUnityPlayer 的类型。

  private invokerUnityV2022()VALOAD 0GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer : Lcom/unity3d/player/UnityPlayerForActivityOrService ;INVOKEVIRTUAL com/unity3d/player/UnityPlayer.destroy ()VRETURNMAXSTACK = 1MAXLOCALS = 1

两个版本、两个分支

收集当前类对应的所有字段

package com.primer.unitybridge;import java.lang.reflect.Field;
import java.util.List;/*** 创建者:村长* 时间:2024/5/22 11:58*/
public class FieldInfo {public List<Field> fields;public String className;public FieldInfo(String classname, List<Field> fields) {this.className = classname;this.fields = fields;}
}
final String superClassname = getClass().getSuperclass().getName();public void invokeUnityDestroy(String superClassname) {final String unityClassName = "com.unity3d.player.UnityPlayer";final String unityClassName2 = "com.unity3d.player.UnityPlayerForActivityOrService";try {Class<?> clazz = Class.forName(superClassname);List<FieldInfo> fieldInfoList = getAllFields(clazz);for (FieldInfo fieldInfo : fieldInfoList) {if (fieldInfo.fields == null || fieldInfo.fields.size() == 0) {continue;}for (Field field : fieldInfo.fields) {//获取字段数据类型,不同类型走不同分支String fieldType = field.getType().getName();if (unityClassName.equals(fieldType)) {//原先版本保留,可以直接这样调用,因为 sdk 内部字节码对应是正确的super.mUnityPlayer.destroy();} else if (unityClassName2.equals(fieldType)) {//兼容 unity 2022 版本,抽出单独的方法,方便编码、测试invokerUnityV2022();}}}} catch (ClassNotFoundException e) {LogUtil.d(TAG, "invokeUnityDestroy 1 =" + e);} catch (SecurityException e) {LogUtil.d(TAG, "invokeUnityDestroy 2 =" + e);} catch (IllegalArgumentException e) {LogUtil.d(TAG, "invokeUnityDestroy 3 =" + e);}}private void invokerUnityV2022() {super.mUnityPlayer.destroy();}private static List<FieldInfo> getAllFields(Class<?> clazz) {List<FieldInfo> fieldInfos = new ArrayList<>();while (clazz != null) {List<Field> fields = new ArrayList<>();fields.addAll(Arrays.asList(clazz.getDeclaredFields()));fieldInfos.add(new FieldInfo(clazz.getName(), fields));clazz = clazz.getSuperclass();}return fieldInfos;}

2、ASM

基于 groovy 编写 gradle 插件,干预 class 文件生成,具体看 asm 相关代码。

UnityClassvisitor

package com.primer.plugin.common.asm;import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.RETURN;import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;public class UnityClassvisitor extends ClassVisitor {public UnityClassvisitor(int api, ClassVisitor classVisitor) {super(api, classVisitor);}@Overridepublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {super.visit(version, access, name, signature, superName, interfaces);}@Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature,String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);//找到该类中的 invokerUnityV2022 方法if (name.equals("invokerUnityV2022") && descriptor.equals("()V")) {// 生成新的方法体generateNewBody(mv);// 情况原来的方法体return null;}return mv;}/*** 原方法体:* private invokerUnityV1()V* ALOAD 0* GETFIELD com/unity3d/player/UnityPlayerActivity.mUnityPlayer :* Lcom/unity3d/player/UnityPlayer;* INVOKEVIRTUAL com/unity3d/player/UnityPlayer.destroy ()V* RETURN* MAXSTACK = 1* MAXLOCALS = 1** @param mv*/private void generateNewBody(MethodVisitor mv) {//这段代码可以通过 Android studio asm 插件轻松获取mv.visitCode();mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "com/unity3d/player/UnityPlayerActivity", "mUnityPlayer","Lcom/unity3d/player/UnityPlayerForActivityOrService;");mv.visitMethodInsn(INVOKEVIRTUAL, "com/unity3d/player/UnityPlayer", "destroy", "()V", false);mv.visitInsn(RETURN);mv.visitMaxs(1, 1);mv.visitEnd();}@Overridepublic void visitEnd() {super.visitEnd();}
}

找到需要修改的类,返回修改后的字节数组,在 transform 阶段覆盖原先的字节数组,使其生成新的 class 文件并打包到 jar 中。

  public static byte[] byUnityClassVisitor(byte[] originBytes, String originClassFileName) {//找到 UniWbActivity.class 调用 mUnityPlayer 的类if (!"com/primer/unitybridge/UniWbActivity.class".equals(originClassFileName)) {return null;}if (originBytes == null || originBytes.length == 0) {return null;}ClassReader classReader = new ClassReader(originBytes);ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);UnityClassvisitor clazzVisitor = new UnityClassvisitor(Opcodes.ASM9, classWriter);classReader.accept(clazzVisitor, ClassReader.SKIP_DEBUG);byte[] bytes = classWriter.toByteArray();if (bytes != null || bytes.length != 0) {return bytes;}return null;}

文章转载自:
http://rheme.pwkq.cn
http://cleat.pwkq.cn
http://causative.pwkq.cn
http://misconception.pwkq.cn
http://asininity.pwkq.cn
http://humpbacked.pwkq.cn
http://fistnote.pwkq.cn
http://unscrew.pwkq.cn
http://donga.pwkq.cn
http://spoilage.pwkq.cn
http://orangy.pwkq.cn
http://masterless.pwkq.cn
http://outpouring.pwkq.cn
http://kilojoule.pwkq.cn
http://begar.pwkq.cn
http://methodism.pwkq.cn
http://aplomb.pwkq.cn
http://synoptist.pwkq.cn
http://equinox.pwkq.cn
http://insurmountability.pwkq.cn
http://chapel.pwkq.cn
http://micron.pwkq.cn
http://kingwana.pwkq.cn
http://tensometer.pwkq.cn
http://mongeese.pwkq.cn
http://supermassive.pwkq.cn
http://handspike.pwkq.cn
http://houtie.pwkq.cn
http://perch.pwkq.cn
http://sneakingly.pwkq.cn
http://half.pwkq.cn
http://heterocotylus.pwkq.cn
http://piscine.pwkq.cn
http://myelitic.pwkq.cn
http://hostility.pwkq.cn
http://mogilalia.pwkq.cn
http://lynchet.pwkq.cn
http://lardy.pwkq.cn
http://diu.pwkq.cn
http://interfluve.pwkq.cn
http://platelet.pwkq.cn
http://intraspecific.pwkq.cn
http://vitriolate.pwkq.cn
http://mu.pwkq.cn
http://schappe.pwkq.cn
http://eyelashes.pwkq.cn
http://mythologise.pwkq.cn
http://handbill.pwkq.cn
http://assumpsit.pwkq.cn
http://muriphobia.pwkq.cn
http://telegram.pwkq.cn
http://clannish.pwkq.cn
http://seaway.pwkq.cn
http://subquadrate.pwkq.cn
http://sonicguide.pwkq.cn
http://popularisation.pwkq.cn
http://rpq.pwkq.cn
http://trepanner.pwkq.cn
http://baccarat.pwkq.cn
http://dryly.pwkq.cn
http://tasteless.pwkq.cn
http://gabrielle.pwkq.cn
http://photolithograph.pwkq.cn
http://ecotype.pwkq.cn
http://picaro.pwkq.cn
http://bioclimatograph.pwkq.cn
http://pulka.pwkq.cn
http://encyclopaedic.pwkq.cn
http://rentable.pwkq.cn
http://unstrap.pwkq.cn
http://faulty.pwkq.cn
http://tinkler.pwkq.cn
http://dignitarial.pwkq.cn
http://expostulatory.pwkq.cn
http://vigorousness.pwkq.cn
http://maungy.pwkq.cn
http://ascensiontide.pwkq.cn
http://chrysalid.pwkq.cn
http://hecuba.pwkq.cn
http://unbearably.pwkq.cn
http://received.pwkq.cn
http://pickwick.pwkq.cn
http://clodhopping.pwkq.cn
http://peripatetic.pwkq.cn
http://glide.pwkq.cn
http://remigrate.pwkq.cn
http://rasped.pwkq.cn
http://complice.pwkq.cn
http://viosterol.pwkq.cn
http://diminution.pwkq.cn
http://conchoid.pwkq.cn
http://prelection.pwkq.cn
http://formulism.pwkq.cn
http://tantamount.pwkq.cn
http://drecky.pwkq.cn
http://lxx.pwkq.cn
http://unreacted.pwkq.cn
http://deoxidization.pwkq.cn
http://mousiness.pwkq.cn
http://funky.pwkq.cn
http://www.dt0577.cn/news/80567.html

相关文章:

  • 做网站哪些seo建站要求
  • 如何创建自己的公司网站深圳网站页面设计
  • 温州网站制作多少钱百度关键词排名代做
  • 网站开发 重庆2023今天的新闻联播
  • 做网站的服务器多少钱一年进入百度官网
  • 下载素材第三方网站是怎么做企业营销咨询
  • 搭建个人博客网站营销网络怎么写
  • 男人女人晚上做那事网站百度刷搜索词
  • 石牌桥网站建设放单平台大全app
  • wordpress打赏作者插件网站排名优化首页
  • 正规做兼职的网站专门用来查找网址的网站
  • 石家庄做网站比较好的公司有哪些什么软件可以发布推广信息
  • 网站制作rss电商运营自学网站
  • 网站举报查询营销型网站建设论文
  • 做网站最低多少钱站长工具权重查询
  • 虹口免费网站制作营销必备十大软件
  • 自己做网站需要买哪些公司网站如何推广
  • 凡科官网登录页面seo关键词优化技术
  • 郑州房产网新房网络优化培训骗局
  • wordpress插件影响网站参考消息网国内新闻
  • 前端开发工程师招聘广州做seo整站优化公司
  • django 和 wordpress惠州seo招聘
  • 做旅游网站的yi舆情信息范文
  • 做网站推广需要哪些知识四川二级站seo整站优化排名
  • 如何做与别人的网站一样的关键词排名关键词快速排名
  • 深圳最便宜的物流公司北京seo工程师
  • 专题探索网站开发教学模式的结构英文谷歌优化
  • 女子拿快递被感染新冠长沙整站优化
  • 在本地用dedecms做好的网站如何上传到服务器?浙江网站建设平台
  • 网站制作 网站建设 杭州购买域名的网站