문제

에러코드

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: , PID: 25378
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter activity
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764)

발생코드

class MainActivity extends AppCompatActivity {
    ...
    public void method() {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected void onPreExecute() {
            }

            @Override
            protected Void doInBackground(Void... params) {
            	getParent(); // null
                MainActivity.this // ok
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
            }
        };
    }
}

 

Activity 안의 메소드에서 익명클래스로 AsyncTask를 구현하고 있었고, AsyncTask의 메소드 안에서 getParent() 를 호출하고 있었다.

Activity의 context를 얻고싶어 했던 것같다.

하지만 getParent()는 null로 전달되고 있었다. 

getParent()의 값을 사용하는 메소드의 callee를 kotlin으로 변환하며, Nullable이던 자바의 타입에서 NotNull로 바뀌면서 에러가 생겼다.

해결

코드가 포함되어있는 액티비티의 context를 참조하도록 바꾸었다.

getParent() -> MainActivity.this

의문점

getParent()는 뭐하는 메소드이고, 왜 null이 나오는가?

 

getParent()는 뭐하는 메소드인가?

Activity.java

/** Return the parent activity if this view is an embedded child. */
public final Activity getParent() {
    return mParent;
}
  • mParent를 반환하고 있다.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final void setParent(Activity parent) {
    mParent = parent;
}

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
        IBinder shareableActivityToken) {
    ...
    
    mParent = parent;
    
    ...
}
  • mParent는 Activity.java의 setParent와 attach에서 할당된다.
  • 이 메소드들은 어디서 호출되는 걸까?
  • ActivityThread의 performLaunchActivity() 에서 attach()를 호출한다.
  • performLaunchActivity()는 ActivityThread의 startActivityNow()와 handleLaunchActivity()에서 호출된다. 

intent할 때 호출되는 콜스택을 보면 handleLaunchActivity()가 호출되고 있다.

startActivityNow()같은 경우는 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) 어노테이션이 있다. 
P(AOS9, SDK28)이하에서 접근가능한 메소드인 것이다. (테스트 기기: AOS13)

parent 액티비티의 정보는 ActivityClientRecord 라는 클래스의 parent속성값으로, handleLaunchActivity()의 파라미터를 통해 들어온다. 

하지만 ActivityClientRecord의 Parent를 설정해주는 코드는 startActivityNow() 함수에만 있다. 이는 intent상에서 실행되지 않는 함수이기때문에 parent를 불렀을 때, null이 나왔던 것이다.

그럼 parent는 무엇일까?

getParent()의 설명을 보면 "Return the parent activity if this view is an embedded child."라고 나와있다. 

embedded child라는 것은, 현재 뷰 안에 속해있는 다른 뷰를 말하는 것으로 보인다.
한 액티비티가 다른 액티비티를 호출했을때, Caller를 Parent, Callee를 Child라고 하는 것과는 약간 달라보인다.

ActivityGroup의 설명을 보면 

"A screen that contains and runs multiple embedded activities"라고 되어있는데,

Fragment가 도입되기 전에(API11 에서 추가됨) 한 화면에 뷰를 여러개 띄울 때, 다른 뷰에 속해있는 뷰를 embedded child라고 불렀던 것같다.

 

 

+ Recent posts