[英]Start service (By class) returns null in my case
我使用DexClassLoader從MyActivity
onCreate()
回調中的外部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"
/>
為什么在MyActivity
onStart()
回調中啟動服務時得到null?
===============更新(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
,而父Class
無權訪問它。
更新:
解決方法是包裝器服務 ,該服務將所有調用委派給( Service
)實例,該實例可以使用任何ClassLoader
。 這可能會變得很混亂,但也可能完全解決問題。
假設您具有包裝程序服務 class WrapperService extends Service
, interface ExtService
和class MyServiceClass implements ExtService
(可以從dex文件中加載MyServiceClass
):
public WrapperService()
實例化DexClassLoader
,加載MyServiceClass
並將一個實例強制轉換為一個ExtService
,並將其存儲在實例字段中。 MyServiceClass
獲取到的基准WrapperService
,無論是作為一個構造符參數,或者通過在所定義的方法ExtService
。 ExtService
聲明必須由WrapperService
調用的所有方法。 WrapperService
實例將必要的方法調用委托給ExtService
方法。 更新2:
我剛剛閱讀了Android應用程序ClassLoader的某些部分( PathClassLoader
擴展了BaseDexClassLoader
),該部分使用了final DexPathList
本身包含了固定大小的final
數組。 即使SecurityManager
允許,這也使常見的JAVA技巧(如將URL添加到URLClassLoader
)變得不可能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.