Skip to main content

加载apk、aar、jar

AS 可以将一个apk,arr,jar 文件导入,加载并执行其中的类

如果apk,arr中包含so库,也会一并加载.

复杂嵌套加载须知

如果您加载的apk中包含arr,arr中包含so 那么需要将arr中的so拷贝一份放在apk的libs中即可正常加载.

传统的jar包无法加载,需要android工具进行Dex化转换.

将apk,arr,jar注入到运行环境

此方法会将apk,arr,jar等文件注入到当前运行环境,注入后可以像使用普通Java类一样直接 import 使用。

from ascript.android import plug
from ascript.android.system import R

# 注入工程res目录下的包
plug.inject(R.res("demo.apk"))

# 注入绝对路径的包
plug.inject("/sdcard/libs/utils.jar")

支持的格式: .apk .jar .dex

特性:

  • 注入后可直接用 from / import 导入包中的Java类
  • 同一个文件重复调用无副作用
  • 如果包中含有 .so 原生库,也会一并加载

使用案例

案例源码下载

  • 假如在你的工程res目录下包含 一个 app-release.apk 文件. 且apk中包含 一个Person类
// Java Person类结构
package com.aojoy.plug.testloader;

public class Person {
public static String a = "a_public";
private static String b = "b_private";
public String name;
private int age;

public Person(){}

public Person(String name,int age){
this.age = age;
this.name = name;
}

public String eat(){
return this.name +"吃饭了";
}

public String eat(String what){
return this.name +"吃的是"+what;
}

public static Person create(){
return new Person();
}

public static Person create(String name,int age){
return new Person(name,age);
}

}
  • 用 python 注入apk后直接使用 Person 类
from ascript.android.system import R
from ascript.android import plug
from java import jint

# 注入工程res目录下的 app-release.apk
plug.inject(R.res("app-release.apk"))

# 注入后可直接 import apk 中的类
from com.aojoy.plug.testloader import Person

# 创建无参对象
person = Person()

# 创建有参对象 (python传递给java的数字默认是long类型,这里用jint转换)
person = Person("张三", jint(18))

# 访问公共属性
print(person.name) # 张三

# 调用对象方法
print(person.eat()) # 张三吃饭了
print(person.eat("包子")) # 张三吃的是包子

# 访问静态属性
print(Person.a) # a_public

# 调用静态方法
person2 = Person.create("李四", jint(20))
print(person2.eat()) # 李四吃饭了
对比旧方式

注入后无需再使用 Reflect 反射调用,直接 import 即可,代码更简洁、更符合 Python 习惯。

访问私有属性/方法

注入后直接 import 的方式只能访问公共成员。如果需要访问私有属性或方法,仍可配合 Reflect 使用:

from ascript.android import plug
from ascript.android.system import R
from org.joor import Reflect
from java import jint

plug.inject(R.res("app-release.apk"))
from com.aojoy.plug.testloader import Person

person = Person("张三", jint(18))

# 访问私有属性 age
age = Reflect.on(person).field("age").get()
print(age) # 18

Python类型转Java实参类型

一个Python的布尔型(bool)、整型(int)、浮点型(float)或字符串(str)对象可以直接传递给任何兼容的Java方法参数或字段。

然而,Java拥有比Python更多的基本数据类型,因此,当方法调用存在多个适用的整型或浮点型重载时,将使用参数类型最长的那个。类似地,当将一个单字符字符串传递给一个同时拥有String和char类型重载的方法时,将使用String类型的重载。

  • 如果上述规则不能得到期望的结果,可以使用以下包装类来选择您想要使用的Java基本数据类型:
# 转java boolean
class java.jboolean(value)

# 转java byte
class java.jbyte(value, truncate=False)

# 转java short
class java.jshort(value, truncate=False)

# 转java int
class java.jint(value, truncate=False)

# 转java long (python int 默认会转为java long)
class java.jlong(value, truncate=False)

# 转java float
class java.jfloat(value, truncate=False)

# 转java double
class java.jdouble(value, truncate=False)

# 转java jchar
class java.jchar(value)

# 转java jvoid 无法被实例化,但在定义静态代理时可以作为返回类型使用。
class java.jvoid

# java的数组
class java.jarray

数值类型包装器接受一个可选的truncate参数。如果设置了该参数,那么给定值的任何多余的高位将被丢弃,就像在Java中进行类型转换一样。否则,向包装器类传递一个超出范围的值将导致OverflowError。

当使用这些包装器时,将应用Java的重载解析规则来处理被包装的参数。例如,一个jint只适用于Java的int或更大的类型,并且会使用最短的可适用重载。

  • 案例
from java import jint,jboolean,jlong,jdouble,jarray

a = jint(10)

b = jboolean(True)

c = jlong(10)

d = jdouble(10.2222)

# Python code // Java equivalent
jarray(jint)([1, 2, 3]) new int[] {1, 2, 3}
jarray(jarray(jint))([[1, 2], [3, 4]]) new int[][] {{1, 2}, {3, 4}}
jarray(String)(["Hello", "world"]) new String[] {"Hello", "world"}
jarray(jchar)("hello") new char[] {'h', 'e', 'l', 'l', 'o'}

易错点

类型转换