Google Playコンソールでは、Dagger 2の使用を開始してから非常に多くのクラッシュレポートが表示されますが、Android 7.0および主にSamsungデバイス、HuawaiおよびMotorolaデバイス、まれなXperiaデバイスでのみ:
Java.lang.RuntimeException:
at Android.app.ActivityThread.performLaunchActivity (ActivityThread.Java:2984)
at Android.app.ActivityThread.handleLaunchActivity (ActivityThread.Java:3045)
at Android.app.ActivityThread.-wrap14 (ActivityThread.Java)
at Android.app.ActivityThread$H.handleMessage (ActivityThread.Java:1642)
at Android.os.Handler.dispatchMessage (Handler.Java:102)
at Android.os.Looper.loop (Looper.Java:154)
at Android.app.ActivityThread.main (ActivityThread.Java:6776)
at Java.lang.reflect.Method.invoke (Method.Java)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.Java:1518)
at com.Android.internal.os.ZygoteInit.main (ZygoteInit.Java:1408)
Caused by: Java.lang.RuntimeException:
at dagger.Android.AndroidInjection.inject (AndroidInjection.Java:48)
at dagger.Android.support.DaggerAppCompatActivity.onCreate (DaggerAppCompatActivity.Java:43)
at com.package.MainActivity.onCreate (MainActivity.Java:83)
at Android.app.Activity.performCreate (Activity.Java:6956)
at Android.app.Instrumentation.callActivityOnCreate (Instrumentation.Java:1126)
at Android.app.ActivityThread.performLaunchActivity (ActivityThread.Java:2927)
影響を受けるデバイスが手元にないため、この問題を再現できません。また、特定の種類のすべてのデバイスが、ランダムな起動エラーのように影響を受けるわけではないようです。
調査を通じて学んだことから、アクティビティが実際にアプリケーションにアタッチされる前に、アクティビティのonCreateが呼び出される可能性が高いことがわかります。しかし、私はこの声明を証明することはできません...
GoogleのMVP + Daggerのアーキテクチャ設計図に従っています。
私のアプリケーションクラス:
public class App extends DaggerApplication {
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}
私のMainActivityクラス:
public class MainActivity extends DaggerAppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
関連する短剣2コード:
DaggerAppCompatActivity: https://github.com/google/dagger/blob/e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8/Java/dagger/Android/support/DaggerAppCompatActivity.Java#L42-L45
protected void onCreate(@Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
AndroidInjection: https://github.com/google/dagger/blob/e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8/Java/dagger/Android/AndroidInjection.Java#L43-L52
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(
String.format(
"%s does not implement %s",
application.getClass().getCanonicalName(),
HasActivityInjector.class.getCanonicalName()));
}
このクラッシュを解決する方法はわかりませんが、クラッシュの量は無視できないほど重要です。私のDagger 2の使用は、他のすべてのAndroidバージョンとデバイスで完全に機能するため、Dagger 2の使用方法によるものではなく、ベンダー固有の7.0実装によるものと思われます。同じ問題と解決策を見つけてください、お願い、助けてください!
このエラーが私を夢中にさせているので、この全体がどこでうまくいかないかを理解しようとしている10万人のユーザーにテスト版を公開しました。
public abstract class TestDaggerAppCompatActivity extends AppCompatActivity implements HasFragmentInjector, HasSupportFragmentInjector {
@Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject DispatchingAndroidInjector<Android.app.Fragment> frameworkFragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
inject();
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
@Override
public AndroidInjector<Android.app.Fragment> fragmentInjector() {
return frameworkFragmentInjector;
}
private void inject() {
Application application = getApplication();
if(application == null) {
injectWithNullApplication();
return;
}
if (!(application instanceof HasActivityInjector)) {
injectWithWrongApplication();
return;
}
// Everything seems ok...
injectNow(application);
}
private void injectWithNullApplication() {
Application application = (Application) getApplicationContext();
injectNow(application);
}
private void injectWithWrongApplication() {
Application application = (Application) getApplicationContext();
injectNow(application);
}
private void injectNow(Application application) {
checkNotNull(application, "Application must not be null");
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(String.format("%s does not implement %s", application.getClass().getCanonicalName(), HasActivityInjector.class.getCanonicalName()));
}
AndroidInjector<Activity> activityInjector = ((HasActivityInjector) application).activityInjector();
checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass().getCanonicalName());
activityInjector.inject(this);
}
}
アクティビティは、インラインAndroidInjectionコードを使用したDaggerのアクティビティに基づいています。私の考えでは、getApplication()
の代わりにApplicationContextを使用してこの問題を解決できない場合、スタックトレースで何が起こっているのかを詳しく説明する必要があります。
getApplication()
の場合、スタックトレースにはinjectWithNullApplication()
またはinjectWithWrongApplication()
が含まれます。getApplicationContext()
がnullを返したことを示します。getApplicationContext()
が私のアプリケーションではないことを示しますgetApplication()
またはgetApplicationContext()
がアプリケーションを返し、実際に問題を解決したものを気にしませんスタックトレースは次のとおりです。
Caused by: Java.lang.RuntimeException:
at com.package.di.TestDaggerAppCompatActivity.inject (TestDaggerAppCompatActivity.Java:49)
at com.package.di.TestDaggerAppCompatActivity.onCreate (TestDaggerAppCompatActivity.Java:31)
at com.package.MainActivity.onCreate (MainActivity.Java:83)
at Android.app.Activity.performCreate (Activity.Java:6942)
at Android.app.Instrumentation.callActivityOnCreate (Instrumentation.Java:1126)
at Android.app.ActivityThread.performLaunchActivity (ActivityThread.Java:2880)
したがって、!(application instanceof HasActivityInjector)
のif節inject()
はinjectWithWrongApplication()
に再ルーティングしませんでしたが、同じif節によって同じアプリケーションインスタンスのinjectNow(Application application)
でRuntimeExceptionが発生しました。 WTF?私は自分のコードで100回のように見えましたが、そこにエラーがある場合はお知らせください!そうでなければ、7.0のベンダー実装では、おそらく修正できないかもしれない、本当に奇妙なことが起こっていると思います...
https://github.com/google/dagger/issues/748 の議論に基づいて、getApplicationContext()
ではなくgetApplication()
のみを使用するテストバージョンも展開しました。 ]すべてのDaggerコンポーネントに違いはありません。
マニフェストからの私のアプリケーションタグ
<application
Android:name=".App"
Android:allowBackup="true"
Android:icon="@mipmap/ic_launcher"
Android:label="@string/app_name"
Android:theme="@style/SplashScreenTheme"
Android:fullBackupContent="false">
<meta-data Android:name="com.google.Android.gms.version" Android:value="@integer/google_play_services_version" />
<meta-data Android:name="com.google.Android.gms.games.APP_ID" Android:value="@string/app_id" />
<meta-data Android:name="Android.max_aspect" Android:value="2.1" />
<activity
Android:name="com.package.MainActivity"
Android:label="@string/app_name">
<intent-filter>
<action Android:name="Android.intent.action.MAIN" />
<category Android:name="Android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service Android:name="com.package.GeneratorService" Android:exported="false"/>
</application>
最後に、アプリケーションでAndroid 7.0の下でDagger 2を使用することによって引き起こされるクラッシュを解決する方法を見つけました。これは、カスタムアプリケーションがAndroid 7.0の下で適切に使用されていません。私の場合、Dagger 2を実装する以外にカスタムアプリケーションに重要なロジックがありませんでした。 DaggerApplication
ベースの実装を以下のApplicationlessInjection
に置き換えました。
既知の問題
DaggerAppCompatActivity
、DaggerIntentService
、およびDaggerFragment
のみを置き換えました。 DaggerDialogFragment
やDaggerBroadcastReceiver
などの他のコンポーネントを使用している場合は、独自の実装を作成する必要がありますが、それほど難しくないはずです:)実装
DaggerApplication
の使用を停止します。標準のApplication
からカスタムアプリケーションを再度拡張するか、カスタムアプリケーションを完全に削除します。 Dagger 2を使用した依存性注入では、もう必要ありません。たとえば、拡張するだけですFixedDaggerAppCompatActivity
そして、アクティビティにはDagger 2 DIを使用するのがよいでしょう。
ApplicationlessInjection.getInstance()
にまだアプリケーションコンテキストを渡しています。依存性注入自体にはコンテキストはまったく必要ありませんが、アプリケーションコンテキストを他のコンポーネントやモジュールに簡単に注入できるようにしたいと思います。そして、アプリケーションコンテキストがカスタムアプリなのか、それがコンテキストである限り、Android 7.0のその他のクレイジーなものなのかは気にしません。
ApplicationlessInjection
public class ApplicationlessInjection
implements
HasActivityInjector,
HasFragmentInjector,
HasSupportFragmentInjector,
HasServiceInjector,
HasBroadcastReceiverInjector,
HasContentProviderInjector {
private static ApplicationlessInjection instance = null;
@Inject DispatchingAndroidInjector<Activity> activityInjector;
@Inject DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;
@Inject DispatchingAndroidInjector<Android.app.Fragment> fragmentInjector;
@Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject DispatchingAndroidInjector<Service> serviceInjector;
@Inject DispatchingAndroidInjector<ContentProvider> contentProviderInjector;
public ApplicationlessInjection(Context applicationContext) {
AppComponent appComponent = DaggerAppComponent.builder().context(applicationContext).build();
appComponent.inject(this);
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityInjector;
}
@Override
public DispatchingAndroidInjector<Android.app.Fragment> fragmentInjector() {
return fragmentInjector;
}
@Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
@Override
public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
return broadcastReceiverInjector;
}
@Override
public DispatchingAndroidInjector<Service> serviceInjector() {
return serviceInjector;
}
@Override
public AndroidInjector<ContentProvider> contentProviderInjector() {
return contentProviderInjector;
}
public static ApplicationlessInjection getInstance(Context applicationContext) {
if(instance == null) {
synchronized(ApplicationlessInjection.class) {
if (instance == null) {
instance = new ApplicationlessInjection(applicationContext);
}
}
}
return instance;
}
}
FixedDaggerAppCompatActivity
public abstract class FixedDaggerAppCompatActivity extends AppCompatActivity implements HasFragmentInjector, HasSupportFragmentInjector {
@Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject DispatchingAndroidInjector<Android.app.Fragment> frameworkFragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
inject();
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
@Override
public AndroidInjector<Android.app.Fragment> fragmentInjector() {
return frameworkFragmentInjector;
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
AndroidInjector<Activity> activityInjector = injection.activityInjector();
if (activityInjector == null) {
throw new NullPointerException("ApplicationlessInjection.activityInjector() returned null");
}
activityInjector.inject(this);
}
}
FixedDaggerFragment
public abstract class FixedDaggerFragment extends Fragment implements HasSupportFragmentInjector {
@Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
@Override
public void onAttach(Context context) {
inject();
super.onAttach(context);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return childFragmentInjector;
}
public void inject() {
HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector();
AndroidInjector<Fragment> fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector();
if (fragmentInjector == null) {
throw new NullPointerException(String.format("%s.supportFragmentInjector() returned null", hasSupportFragmentInjector.getClass().getCanonicalName()));
}
fragmentInjector.inject(this);
}
private HasSupportFragmentInjector findHasFragmentInjector() {
Fragment parentFragment = this;
while ((parentFragment = parentFragment.getParentFragment()) != null) {
if (parentFragment instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) parentFragment;
}
}
Activity activity = getActivity();
if (activity instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) activity;
}
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(activity.getApplicationContext());
if (injection != null) {
return injection;
}
throw new IllegalArgumentException(String.format("No injector was found for %s", getClass().getCanonicalName()));
}
}
FixedDaggerIntentService
public abstract class FixedDaggerIntentService extends IntentService {
public FixedDaggerIntentService(String name) {
super(name);
}
@Override
public void onCreate() {
inject();
super.onCreate();
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
AndroidInjector<Service> serviceInjector = injection.serviceInjector();
if (serviceInjector == null) {
throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
}
serviceInjector.inject(this);
}
}
私のAppComponent
@Singleton
@Component(modules = {
AppModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class
})
public interface AppComponent extends AndroidInjector<ApplicationlessInjection> {
@Override
void inject(ApplicationlessInjection instance);
@Component.Builder
interface Builder {
@BindsInstance
AppComponent.Builder context(Context applicationContext);
AppComponent build();
}
}
私のAppModule
@Module
public abstract class AppModule {
@Binds
@ApplicationContext
abstract Context bindContext(Context applicationContext);
}
そして完全を期すために、私の@ApplicationContextアノテーション
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {}
うまくいけば、私のコードで他の誰かを助けることもできます。私にとっては、Dagger 2と奇妙なAndroid 7.0バージョンの導入に関連するすべてのクラッシュを解決できました。
さらに説明が必要な場合は、お知らせください!
私は私のアプリで同じ問題に遭遇し、以下のコードを使用してそれを解決しました:
Application app = activity.getApplication();
if(app == null) {
app = (Application)activity.getApplicationContext();
}