개요
Unreal 로 개발을 마치고 플레이 스토어에 aab 파일을 올렸는데 경고 메시지가 나타났습니다. 무시해도 당분간은 문제가 없습니다. 하지만 업데이트는 계속해야 하고 결국 해결해야 할 문제라는 생각이 들어 조치하기로 했습니다.
첫 번째 경고 메시지
첫 번째 경고 메시지는 다음과 같습니다.
androidx.fragment:fragment (androidx.fragment:fragment) 개발자가 1.0.0 버전이 오래되었다고 신고했습니다. 신작을 게시하기 전에 다음 버전 중 하나로 업그레이드하는 것이 좋습니다.1.1.0+ 사용 중인 SDK에 대해 자세히 알아보고 Google Play SDK 색인 정보를 바탕으로 SDK를 선택하세요.
내용 그대로 오래된 버전으로 지정되어 발생한 경고입니다. Engine\Source\ThirdParty\AndroidPermission\permission_library\additions.gradle 파일을 열어서 다음의 내용을 추가합니다.
dependencies {
...
constraints.implementation 'androidx.fragment:fragment:1.3.6'
}
Unreal Engine 5.3.2 기준으로 fragment 버전을 최신(1.6.2)으로 하면 빌드 오류가 발생했습니다. 1.3.6 으로 지정하면 정상적으로 빌드됩니다.
나머지 경고 메시지
앱 화면 하단에 배너 광고가 나타나도록 했습니다. 다른 경고 메시지는 애드몹(Admob)에 관련된 것 이었습니다.
Google Mobile Ads (GMA) SDK (com.google.android.gms:play-services-ads) 개발자가 18.1.0 버전이 오래되었다고 신고했습니다. 이 버전의 앱을 출시한 지 90일이 지나면 새 버전(20.0.0+)으로 업그레이드할 때까지 이 SDK를 포함한 새 버전을 출시할 수 없습니다. 사용 중인 SDK에 대해 자세히 알아보고 Google Play SDK 색인 정보를 바탕으로 SDK를 선택하세요.
Google Mobile Ads (GMA) SDK (com.google.android.gms:play-services-ads) 개발자가 SDK 버전 18.1.0에 다음 메모를 추가했습니다. As of June 30th 2023, this version is sunset. For more information, please visit https://developers.google.com/admob/android/deprecation. 이 버전의 앱을 출시한 지 90일이 지나면 새 버전으로 업그레이드할 때까지 이 SDK를 포함한 새 버전을 출시할 수 없습니다. 사용 중인 SDK에 대해 자세히 알아보고 Google Play SDK 색인 정보를 바탕으로 SDK를 선택하세요.
Google Mobile Ads (GMA) SDK (com.google.android.gms:play-services-ads-lite) 개발자가 18.1.0 버전이 오래되었다고 신고했습니다. 이 버전의 앱을 출시한 지 90일이 지나면 새 버전(20.0.0+)으로 업그레이드할 때까지 이 SDK를 포함한 새 버전을 출시할 수 없습니다. 사용 중인 SDK에 대해 자세히 알아보고 Google Play SDK 색인 정보를 바탕으로 SDK를 선택하세요.
나머지 경고 메시지 해결 방법
이 글에서 안내하는 해결 방법은 배너 광고에만 해당합니다. Engine\Source\Runtime\Advertising\Android\AndroidAdvertising\AndroidAdvertising_APL.xml 파일을 열어 implementation(‘com.google.android.gms:play-services-ads:18.0.1’) 을 implementation(‘com.google.android.gms:play-services-ads:22.6.0’) 로 변경합니다.
이 상태에서 빌드하면 InterstitialAd 관련 오류가 발생합니다. 원인은 com.google.android.gms.ads.InterstitialAd 이 20.0.0+ 에서 deprecated 되어 AndroidAdvertising_APL.xml 파일내의 삽입광고 구현부분과 라이브러리 버전 20.0.0+ 과의 불일치 때문입니다.
삽입광고를 사용하시는 분들은 새로 변경된 라이브러리 버전에 맞추어 수정하셔야 합니다. <![CDATA[ , ]]>로 감싸진 부분이 애드몹 관련 자바 코드 부분입니다. 필자는 삽입 광고 부분을 모두 주석 처리 했습니다. 추가로 더 이상 지원되지 않는 메소드의 @Override 부분도 다음과 같이 주석 처리 했습니다.
/*
@Override
public void onAdFailedToLoad(int errorCode)
{
adIsAvailable = false;
adIsRequested = false;
// don't immediately request a new ad on failure, wait until the next show
updateAdVisibility(false);
}
*/
AndroidAdvertising_APL.xml 파일을 수정한 경우 Unreal Engine이 실행 중이면 바로 반영되지 않습니다. 다시 엔진을 시작하고 빌드한 후 aab 파일을 올리면 위의 경고 메시지가 나타나지 않는 것을 알 수 있습니다.
삽입 광고 관련한 부분을 변경된 라이브러리에 맞추어 수정된 파일이 있지 않을까 해서 검색을 좀 해보았는데 발견하지 못하였습니다. 엔진이 업데이트 되면 이 불일치 부분도 해결되었으면 좋겠습니다.
참고로 AndroidAdvertising_APL.xml 파일의 수정된 부분 전체의 내용입니다.
/* 위쪽 import 부분 */
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdListener;
//import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.ads.interstitial.InterstitialAd;
import com.google.android.gms.ads.RequestConfiguration;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
/** AdMob support */
private PopupWindow adPopupWindow;
private AdView adView;
private boolean adInit = false;
private LinearLayout adLayout;
private int adGravity = Gravity.TOP;
//private InterstitialAd interstitialAd;
//private boolean isInterstitialAdLoaded = false;
//private boolean isInterstitialAdRequested = false;
//private AdRequest interstitialAdRequest;
private String advertisingID = null;
/** true when the application has requested that an ad be displayed */
private boolean adWantsToBeShown = false;
/** true when an ad is available to be displayed */
private boolean adIsAvailable = false;
/** true when an ad request is in flight */
private boolean adIsRequested = false;
// handle ad popup visibility and requests
private void updateAdVisibility(boolean loadIfNeeded)
{
if (!adInit || (adPopupWindow == null))
{
return;
}
// request an ad if we don't have one available or requested, but would like one
if (adWantsToBeShown && !adIsAvailable && !adIsRequested && loadIfNeeded)
{
AdRequest adRequest = new AdRequest.Builder().build(); // add test devices here
_activity.adView.loadAd(adRequest);
adIsRequested = true;
}
if (adIsAvailable && adWantsToBeShown)
{
if (adPopupWindow.isShowing())
{
return;
}
adPopupWindow.showAtLocation(activityLayout, adGravity, 0, 0);
// don't call update on 7.0 to work around this issue: https://code.google.com/p/android/issues/detail?id=221001
if (ANDROID_BUILD_VERSION != 24) {
adPopupWindow.update();
}
}
else
{
if (!adPopupWindow.isShowing())
{
return;
}
adPopupWindow.dismiss();
adPopupWindow.update();
}
}
public void AndroidThunkJava_ShowAdBanner(String AdMobAdUnitID, boolean bShowOnBottonOfScreen)
{
Log.debug("In AndroidThunkJava_ShowAdBanner");
Log.debug("AdID: " + AdMobAdUnitID);
adGravity = bShowOnBottonOfScreen ? Gravity.BOTTOM : Gravity.TOP;
if (adInit)
{
// already created, make it visible
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
if ((adPopupWindow == null) || adPopupWindow.isShowing())
{
return;
}
adWantsToBeShown = true;
updateAdVisibility(true);
}
});
return;
}
// init our AdMob window
adView = new AdView(this);
adView.setAdUnitId(AdMobAdUnitID);
adView.setAdSize(AdSize.BANNER);
if (adView != null)
{
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
adInit = true;
final DisplayMetrics dm = getResources().getDisplayMetrics();
final float scale = dm.density;
adPopupWindow = new PopupWindow(_activity);
adPopupWindow.setWidth((int)(320*scale));
adPopupWindow.setHeight((int)(50*scale));
adPopupWindow.setClippingEnabled(false);
adLayout = new LinearLayout(_activity);
final int padding = (int)(-5*scale);
adLayout.setPadding(padding,padding,padding,padding);
MarginLayoutParams params = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);;
params.setMargins(0,0,0,0);
adLayout.setOrientation(LinearLayout.VERTICAL);
adLayout.addView(adView, params);
adPopupWindow.setContentView(adLayout);
// set up our ad callbacks
_activity.adView.setAdListener(new AdListener()
{
@Override
public void onAdLoaded()
{
adIsAvailable = true;
adIsRequested = false;
updateAdVisibility(true);
}
/*
@Override
public void onAdFailedToLoad(int errorCode)
{
adIsAvailable = false;
adIsRequested = false;
// don't immediately request a new ad on failure, wait until the next show
updateAdVisibility(false);
}
*/
});
adWantsToBeShown = true;
updateAdVisibility(true);
}
});
}
}
public void AndroidThunkJava_HideAdBanner()
{
Log.debug("In AndroidThunkJava_HideAdBanner");
if (!adInit)
{
return;
}
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
adWantsToBeShown = false;
updateAdVisibility(true);
}
});
}
public void AndroidThunkJava_CloseAdBanner()
{
Log.debug("In AndroidThunkJava_CloseAdBanner");
if (!adInit)
{
return;
}
// currently the same as hide. should we do a full teardown?
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
adWantsToBeShown = false;
updateAdVisibility(true);
}
});
}
public void AndroidThunkJava_LoadInterstitialAd(String AdMobAdUnitID)
{
/*
interstitialAdRequest = new AdRequest.Builder().build();
interstitialAd = new InterstitialAd(this);
isInterstitialAdLoaded = false;
isInterstitialAdRequested = true;
interstitialAd.setAdUnitId(AdMobAdUnitID);
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
interstitialAd.loadAd(interstitialAdRequest);
}
});
interstitialAd.setAdListener(new AdListener()
{
@Override
public void onAdFailedToLoad(int errorCode)
{
Log.debug("Interstitial Ad failed to load, errocode: " + errorCode);
isInterstitialAdLoaded = false;
isInterstitialAdRequested = false;
}
@Override
public void onAdLoaded()
{
//track if the ad is loaded since we can only called interstitialAd.isLoaded() from the uiThread
isInterstitialAdLoaded = true;
isInterstitialAdRequested = false;
}
});
*/
}
public boolean AndroidThunkJava_IsInterstitialAdAvailable()
{
//return interstitialAd != null && isInterstitialAdLoaded;
return false;
}
public boolean AndroidThunkJava_IsInterstitialAdRequested()
{
//return interstitialAd != null && isInterstitialAdRequested;
return false;
}
public void AndroidThunkJava_ShowInterstitialAd()
{
/*
if(isInterstitialAdLoaded)
{
_activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
interstitialAd.show();
}
});
}
else
{
Log.debug("Interstitial Ad is not available to show - call LoadInterstitialAd or wait for it to finish loading");
}
*/
}
private GetAdvertisingIdTask AdTask = null;
private class GetAdvertisingIdTask extends android.os.AsyncTask<String, Integer, String>
{
@Override
protected String doInBackground(String... values)
{
AdvertisingIdClient.Info adInfo = null;
try
{
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(GameActivity.Get().getApplicationContext());
if (adInfo.isLimitAdTrackingEnabled())
{
Log.debug("GetAdvertisingId: User opted out of ad tracking");
adInfo = null;
}
Log.debug("GetAdvertisingID: success");
}
catch (Exception e) {
Log.debug("GetAdvertisingId failed: " + e.getMessage());
}
return (adInfo == null) ? "" : adInfo.getId();
}
@Override
protected void onPostExecute(String s)
{
advertisingID = s;
}
}
public String AndroidThunkJava_GetAdvertisingId()
{
try
{
AdTask.get();
}
catch (Exception e)
{
advertisingID = null;
}
return advertisingID;
}
]]>
별 내용이 없어 보이는데 생각보다 해결에 시간이 많이 소요되었습니다. 필자와 같은 문제를 겪고 계신 분들께 도움이 되었으면 좋겠습니다.