구현하고 싶은 상황

  1. 현재 웹 뷰로 웹 컨텐츠를 보고있는 상황
  2. FCM을 통해 알림이 온다.
  3. 알림을 누르면 웹 뷰 안에서 새로운 웹 페이지로 이동하게 하고싶다.

구현 방법

  1. Intent만들기 + Flag 설정
  2. 웹뷰를 띄우는 Activity에서 onNewIntnet 오버라이드하기

 

val intent = Intent(this, WebViewActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    putExtra("url", "www.example.com")
}

val pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)

val notification = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle(title)
    .setContentText(body)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)
    .build()

notificationManager.notify(CHAT_NOTIFICATION_ID, notification)

이런 식으로 PendingIntent와 Notification을 만든다.

 

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    webView.loadUrl(intent.getExtras().getString("url"));
}

그리고 WebViewActivity 안에서 onNewIntent를 오버라이딩하면된다.

문제 상황

Notification을 클릭하면 Intent에 담긴 Extra의 url을 웹뷰로 띄우는 작업이다.

Intent로 전달하는 url을 바꾸어도 이전 url이 계속 로드되는 현상이 발생했다.

해결

pending intent의 request code때문이었다.

PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)

request code인 2번째 파라미터 값을 변경해주면 된다. 

문제

에러코드

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라고 불렀던 것같다.

 

 

문제상황

안드로이드 7.1이하에서는 알림이 status bar에만 표시되고, head up (pop up) 알림은 안나오는 상황이였다.

원인

AOS 7.1 (SDK25) 이하에서 알림의 중요도는 알림의 priority에 따라 결정된다.

8.0 부터는 channel의 important에 따라 결정된다. 

Priority는 다음 4 종류가 있다.

  • 긴급: 알림음이 울리며 헤드업 알림으로 표시됩니다.
  • 높음: 알림음이 울립니다.
  • 중간: 알림음이 없습니다.
  • 낮음: 알림음이 없고 상태 표시줄에 표시되지 않습니다.

해결

AOS 8.0 미만에서는 채널은 설정할 필요가 없기 때문에 NotificationBuilder에 priority설정을 해주면 된다.

NotificationCompat.Builder(this, CHANNEL_ID)
    .setPriority(NotificationCompat.PRIORITY_HIGH)
    .setDefaults(NotificationCompat.DEFAULT_ALL)

 

8.0 이상에서는 채널에 important 설정을 해주면된다.

val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)

+ Recent posts