簡體   English   中英

在我的情況下,啟動服務(按類)返回null

[英]Start service (By class) returns null in my case

我使用DexClassLoaderMyActivity 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 Serviceinterface ExtServiceclass 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM