2013-12-20 4 views
2

я использую DexClassLoader динамически загрузить Android Service класса от внешнего Dex файла в onCreate() обратного вызова MyActivity:Начало службы (по классу) возвращает нуль в моем случае

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())); 
} 

Я также объявил MyService в AndroidManifest.xml.

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

Почему я получаю нулевой, когда начать свою службу в onStart() обратного вызова MyActivity?

================ Update (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) 
+0

«Этот классный загрузчик требует приложения, доступного для записи, каталога ...», что такое «путь/to/dexFile.dex»? Также любые конкретные ошибки или следы? Можете ли вы добавить вызовы журнала в методы [lifecycle] (http://developer.android.com/reference/android/app/Service.html#ServiceLifecycle), чтобы узнать, какой из них вызывается? Включите его и в конструктор, посмотрите, насколько он загружается и где он останавливается. – soulseekah

+0

Привет, «путь/to/dexFile.dex» находится в моем каталоге sdcard. Я также получил сообщение об ошибке «Не предоставление разрешения android.permission.READ_LOGS» – Mellon

+0

sdcard не является закрытым приложением. Попробуйте getDir для него частный каталог, аналогичный тому, как вы для него 'tmp'. – soulseekah

ответ

4

Хотя вы успешно загрузили Service класс через обычай ClassLoader, андроид вызовы 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 и загружается, и родитель не имеет к нему доступа.

Обновление:

Обходной является службой обертки, которое передает все вызовы к (в) Service экземпляра, который может быть загружен с помощью любого ClassLoader. Это может стать довольно грязным, но это может также решить проблему полностью.

Предполагая, что служба оберткиclass WrapperService extends Service, interface ExtService и class MyServiceClass implements ExtService (где MyServiceClass может быть загружено из файла DEX):

  • Конструктор public WrapperService() конкретизирует в DexClassLoader, грузы MyServiceClass и бросает экземпляр на ExtService, который он хранит в поле экземпляра.
  • MyServiceClass получает ссылку на WrapperService либо в качестве параметра конструктора, либо посредством метода, определенного в ExtService.
  • ExtService объявляет все методы, которые должны быть вызваны WrapperService.
  • Экземпляры WrapperService делегировать необходимые вызовы методов методам ExtService.

Update 2:

Я только что прочитал некоторые части Android ClassLoader приложений (PathClassLoader расширяет BaseDexClassLoader), который использует final DexPathList, что само по себе содержит final массив фиксированного размера. Это делает обычные трюки JAVA (например, добавление URL-адресов в URLClassLoader s) невозможным, даже если это допустимо SecurityManager.

+0

Привет, похоже, вы не проверяли мои обновления в своем сообщении, я заметил уже упомянутую ошибку и исправил ее и успешно загрузил класс службы, проблема в том, что я не могу запустить службу. Я получил «Не удалось создать службу». Пожалуйста, проверьте мои обновления в моем сообщении. Благодарю. – Mellon

+0

Ваш прогноз правилен. Я получил исключение «Невозможно создать экземпляр службы», хотя я успешно загрузил класс службы. Вы не знаете, как избавиться от этой проблемы? – Mellon

+0

@Mellon Мне очень жаль, я случайно пропустил ваши обновления. Я обновил свой ответ и предложил обходной путь, хотя в настоящее время я работаю над «настоящим» решением. –

Смежные вопросы