gpt4 book ai didi

java - 在我的例子中,启动服务(按类)返回 null

转载 作者:搜寻专家 更新时间:2023-11-01 08:01:32 24 4
gpt4 key购买 nike

我使用 DexClassLoaderMyActivityonCreate() 回调中从外部 dex 文件动态加载 Android Service 类:

public class MyActivity extends Activity {

private Class<Object> myServiceClass;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

String dexFile = "path/to/dexFile.dex";
DexClassLoader classLoader = new DexClassLoader(dexFile, getDir("tmp", 0).getAbsolutePath(), null, this.getClass().getClassLoader());

myServiceClass = (Class<Object>) classloader.loadClass("com.test.MyService");
//Here, I am able to use java relfection to successfully get those methods in myServiceClass.
//so, no problem here!
}

@Override
protected void onStart() {
super.onStart();
//PROBLEM HERE: I get null, failed to start service, why?
ComponentName name = startService(new Intent(this, myServiceClass.getClass()));
}

我还在 AndroidManifest.xml 中声明了 MyService。

<service
android:name="com.test.MyService"
/>

为什么在MyActivityonStart() 回调中启动我的服务 时得到空值?

================更新(startService()现在返回组件名称)==========

在我改为使用 ComponentName name = startService(new Intent(this, myServiceClass));

上面的 startService(...) 返回以下组件名称:

ComponentInfo{com.project.myapp/com.test.MyService}

但是我的 logcat 也显示了错误:

No content provider found for permission revoke: file:///data/local/tmp/myapp.apk
...
Unable to start service Intent { cmp=com.project.myapp/java.lang.Class }: not found
...
...

java.lang.RuntimeException: Unable to instantiate service com.test.MyService: java.lang.ClassNotFoundException: com.test.MyService
12-23 13:50:44.040: E/AndroidRuntime(7959): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2380)
12-23 13:50:44.040: E/AndroidRuntime(7959): at android.app.ActivityThread.access$1600(ActivityThread.java:138)
12-23 13:50:44.040: E/AndroidRuntime(7959): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1286)

最佳答案

尽管您通过自定义 ClassLoader 成功加载了 Service 类,Android API 调用很快就忘记了实际的 Class 实例:Intent 只是删除 Class 并保留其名称:

来自 android.content.Intent(API 18):

public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}

来自android.content.ComponentName:

public ComponentName(Context pkg, Class<?> cls) {
mPackage = pkg.getPackageName();
mClass = cls.getName();
}

android.app.ActivityThread 然后创建 Service 实例,如下所示:

LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}

包类加载器将无法加载由自定义加载的类 DexClassLoader .问题是缓存 - 如果我没记错的话,自从我必须实现自己的 ClassLoader 以来已经有一段时间了 - 由每个 ClassLoader 执行,例如子级 ClassLoader 缓存它加载的 Class,父级无权访问它。

更新:

解决方法是一个包装器服务,它将所有调用委托(delegate)给一个 (Service) 实例,该实例可以使用任何 ClassLoader 加载。这可能会变得非常困惑,但它也可以完全解决问题。

假设您有包装器服务 class WrapperService extends Serviceinterface ExtServiceclass MyServiceClass implements ExtService (其中 MyServiceClass 可以从您的 dex 文件加载):

  • 构造函数 public WrapperService() 实例化 DexClassLoader,加载 MyServiceClass 并将实例转换为 ExtService它存储在实例字段中。
  • MyServiceClass 获取对 WrapperService 的引用,作为构造函数参数或通过 ExtService 中定义的方法。
  • ExtService 声明了所有必须由 WrapperService 调用的方法。
  • WrapperService 的实例将必要的方法调用委托(delegate)给 ExtService 的方法。

更新 2:

我刚刚阅读了 Android 应用程序 ClassLoader 的某些部分(PathClassLoader extends BaseDexClassLoader),它使用 final DexPathList 本身包含一个 final 固定大小的数组。这使得常见的 JAVA 技巧(例如将 URL 添加到 URLClassLoader)变得不可能,即使 SecurityManager 允许这样做。

关于java - 在我的例子中,启动服务(按类)返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20708995/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com