Google Play ์คํ ์ด ์ฑ (์ด์ ์๋ Android ๋ง์ผ์ด๋ผ๊ณ ํจ) ์ โ์ด๊ธฐโ ๋ฒํผ์ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ ๋ ๋ด ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค . Play ์คํ ์ด Intent
์์ ์คํํ๋ ๊ฒ์ ์ ํ์ ์์ฉ ํ๋ก๊ทธ๋จ ๋ฉ๋ด ์์ด์ฝ์์ ์คํ ํ๋ ๊ฒ๊ณผ ๋ค๋ฅธ ๊ฒ์ ์ฌ์ฉํ๋ ๊ฒ ๊ฐ์ต๋๋ค . ์ด๋ก ์ธํด ์๋ก ์ถฉ๋ํ๋ ๋์ผํ ํ๋์ โโ์ฌ๋ฌ ๋ณต์ฌ๋ณธ์ด ์คํ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด ๋ด ์ฑ์ด ํ๋ ABC๋ก ๊ตฌ์ฑ๋ ๊ฒฝ์ฐ์ด ๋ฌธ์ ๋ก ์ธํด ABCA ์คํ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
android:launchMode="singleTask"
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ชจ๋ ํ๋์ ์ฌ์ฉํ๋ ค๊ณ ์๋ํ์ง๋ง ํ ๋ฒํผ์ ๋๋ฅผ ๋๋ง๋ค ํ๋ ์คํ์ ๋ฃจํธ๋ก ์ง์ฐ๋ ์์น ์๋ ๋ถ์์ฉ์ด ์์ต๋๋ค.
์์๋๋ ๋์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ABC-> HOME-> ์ฑ์ด ๋ณต์๋๋ฉด ABC-> HOME-> ABC๊ฐ ํ์ํฉ๋๋ค.
HOME ๋ฒํผ์ ์ฌ์ฉํ ๋ ๋ฃจํธ ํ๋์ผ๋ก ์ฌ์ค์ ํ์ง ์๊ณ ๋์ผํ ์ ํ์ ์ฌ๋ฌ ํ๋์ด ์์๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
๋ต๋ณ
์ด๊ฒ์ onCreate์ ์ถ๊ฐํ๋ฉด ์ข์ต๋๋ค.
// Possible work around for market launches. See https://issuetracker.google.com/issues/36907463
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {
Log.w(LOG_TAG, "Main Activity is not the root. Finishing Main Activity instead of launching.");
finish();
return;
}
}
๋ต๋ณ
์คํจํ ์ด์ ์์ด ๋ฒ๊ทธ๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ฌํํ์ฌ ํ ์คํธ ์ค์ํธ์ ํตํฉ ํ ์์๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
-
Eclipse ๋๋ Market App์ ํตํด ์ฑ์ ์์ํ๋ฉด FLAG_ACTIVITY_NEW_TASK ์ธ ํ ํธ ํ๋๊ทธ์ ํจ๊ป ์์๋ฉ๋๋ค.
-
๋ฐ์ฒ (ํ)๋ฅผ ํตํด ์์ํ ๋ ๋ค์ ํ๋๊ทธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, ์์ โ MAIN โ๋ฐ ๋ฒ์ฃผ โ LAUNCHER โ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ ์คํธ ์ผ์ด์ค์์์ด๋ฅผ ์ฌํํ๋ ค๋ฉด ๋ค์ ๋จ๊ณ๋ฅผ ์ฌ์ฉํ์ญ์์ค.
adb shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity
๊ทธ๋ฐ ๋ค์ ๋ค๋ฅธ ํ๋์ ์ํํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ํ์ญ์์ค. ์ ๋ชฉ์ ์ ์ํด ๋ค๋ฅธ ํ๋์ ์์ํ๋ ๋ฒํผ์ ๋ฐฐ์นํ์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ๋ค์์ ์ฌ์ฉํ์ฌ ๋ฐ์ฒ (ํ)๋ก ๋์๊ฐ๋๋ค.
adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN
๋ค์๊ณผ ๊ฐ์ด ์คํ๊ธฐ๋ฅผ ํตํด ์คํ์ ์๋ฎฌ๋ ์ด์ ํฉ๋๋ค.
adb shell am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity
isTaskRoot () ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํตํฉํ์ง ์์ ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ์ฌํ๋ฉ๋๋ค. ์ด ๋ฒ๊ทธ๊ฐ ๋ค์ ๋ฐ์ํ์ง ์๋๋ก ์๋ ํ ์คํธ์์์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค!
๋ต๋ณ
singleTop ์คํ ๋ชจ๋ ๋ฅผ ์ฌ์ฉํด ๋ณด์ จ์ต๋๊น ?
๋ค์์ http://developer.android.com/guide/topics/manifest/activity-element.html์ ์ผ๋ถ ์ค๋ช ์ ๋๋ค .
โฆ ์๋ก์ด ์ธ ํ ํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด โsingleTopโํ๋์ ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค ์๋ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋์ ์์ ์ ์คํ ๋งจ ์์ ์ด๋ฏธ ํ๋์ ๊ธฐ์กด ์ธ์คํด์ค๊ฐ์๋ ๊ฒฝ์ฐ ํด๋น ์ธ์คํด์ค๋ onNewIntent () ํธ์ถ์์ ์ ์ธ ํ ํธ๋ฅผ ์์ ํฉ๋๋ค. ์ ์ธ์คํด์ค๊ฐ ์์ฑ๋์ง ์์ต๋๋ค. ๋ค๋ฅธ ์ํฉ์์ (์ : โsingleTopโํ๋์ ๊ธฐ์กด ์ธ์คํด์ค๊ฐ ๋์ ์์ ์ ์์ง๋ง ์คํ์ ๋งจ ์์ ์์ง ์์ ๊ฒฝ์ฐ ๋๋ ์คํ์ ๋งจ ์์ ์์ง๋ง ๋์ ์์ ์๋์๋ ๊ฒฝ์ฐ) ์ ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ณ ์คํ์ ํธ์๋ฉ๋๋ค.
๋ต๋ณ
์๋ง๋์ด ๋ฌธ์ ์ผ๊น์? ์๋๋ฉด ๊ฐ์ ๋ฒ๊ทธ์ ๋ค๋ฅธ ํํ?
๋ต๋ณ
๋ฐ์ ๋ค์ธ ๋ต๋ณ ( Duane Homick )์๋ ์ฒ๋ฆฌ๋์ง ์์ ์ฌ๋ก๊ฐ ์๋ค๊ณ ์๊ฐํฉ๋๋ค .
๋ค๋ฅธ ์ถ๊ฐ ๊ธฐ๋ฅ์ด ์์ผ๋ฉฐ ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฑ์ด ์ค๋ณต๋ฉ๋๋ค.
- ๋ง์ผ ๋๋ ํ ํ๋ฉด ์์ด์ฝ (๋ง์ผ์์ ์๋์ผ๋ก ๋ฐฐ์น๋จ)์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ ๋
- ๋ฐ์ฒ ๋๋ ์๋์ผ๋ก ๋ง๋ ํ ํ๋ฉด ์์ด์ฝ์ผ๋ก ์์ฉ ํ๋ก๊ทธ๋จ์ ์์ํ ๋
๋ค์์ ์ด๋ฌํ ์ฌ๋ก์ ์ํ ํ์ ์ค ์๋ฆผ๋ ์ฒ๋ฆฌ ํ ์์๋ ์๋ฃจ์ (SDK_INT> = 11 ์๋ฆผ)์ ๋๋ค.
๋งค๋ํ์คํธ :
<activity
android:name="com.acme.activity.LauncherActivity"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name="com.acme.service.LauncherIntentService" />
๋ฐ์ฒ ํ๋ :
public static Integer lastLaunchTag = null;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = LayoutInflater.from(this);
View mainView = null;
mainView = mInflater.inflate(R.layout.act_launcher, null); // empty layout
setContentView(mainView);
if (getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().containsKey(Consts.EXTRA_ACTIVITY_LAUNCH_FIX)) {
Intent serviceIntent = new Intent(this, LauncherIntentService.class);
if (getIntent() != null && getIntent().getExtras() != null) {
serviceIntent.putExtras(getIntent().getExtras());
}
lastLaunchTag = (int) (Math.random()*100000);
serviceIntent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_TAG, Integer.valueOf(lastLaunchTag));
startService(serviceIntent);
finish();
return;
}
Intent intent = new Intent(this, SigninActivity.class);
if (getIntent() != null && getIntent().getExtras() != null) {
intent.putExtras(getIntent().getExtras());
}
startActivity(intent);
}
์๋น์ค :
@Override
protected void onHandleIntent(final Intent intent) {
Bundle extras = intent.getExtras();
Integer lastLaunchTag = extras.getInt(Consts.EXTRA_ACTIVITY_LAUNCH_TAG);
try {
Long timeStart = new Date().getTime();
while (new Date().getTime() - timeStart < 100) {
Thread.currentThread().sleep(25);
if (!lastLaunchTag.equals(LauncherActivity.lastLaunchTag)) {
break;
}
}
Thread.currentThread().sleep(25);
launch(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void launch(Intent intent) {
Intent launchIintent = new Intent(LauncherIntentService.this, LauncherActivity.class);
launchIintent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIintent.setAction(Intent.ACTION_MAIN);
launchIintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIintent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (intent != null && intent.getExtras() != null) {
launchIintent.putExtras(intent.getExtras());
}
launchIintent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_FIX, true);
startActivity(launchIintent);
}
์๋ฆผ :
ComponentName actCN = new ComponentName(context.getPackageName(), LauncherActivity.class.getName());
Intent contentIntent = new Intent(context, LauncherActivity.class);
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 11) {
contentIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // if you need to recreate activity stack
}
contentIntent.addCategory(Intent.CATEGORY_LAUNCHER);
contentIntent.setAction(Intent.ACTION_MAIN);
contentIntent.putExtra(Consts.EXTRA_CUSTOM_DATA, true);
๋ต๋ณ
๋๋ ์ง๋ฌธ์ด Xamarin Android์ ๊ด๋ จ์ด ์๋ค๋ ๊ฒ์ ์๊ณ ์์ง๋ง ๋ค๋ฅธ ๊ณณ์์๋ ๋ณด์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ฒ์ํ๊ณ ์ถ์์ต๋๋ค.
Xamarin Android์์์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด @DuaneHomick์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ณ MainActivity.OnCreate()
. Xamarin๊ณผ์ ์ฐจ์ด์ ์ Xamarin.Forms.Forms.Init(this, bundle);
๋ฐ LoadApplication(new App());
. ๊ทธ๋์ ๋ด OnCreate()
๋ชจ์ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
if(!IsTaskRoot) {
Intent intent = Intent;
string action = intent.Action;
if(intent.HasCategory(Intent.CategoryLauncher) && action != null && action.Equals(Intent.ActionMain, System.StringComparison.OrdinalIgnoreCase)) {
System.Console.WriteLine("\nIn APP.Droid.MainActivity.OnCreate() - Finishing Activity and returning since a second MainActivity has been created.\n");
Finish();
return; //Not necessary if there is no code below
}
}
}
* ํธ์ง : Android 6.0 ์ดํ๋ก ์์ ์๋ฃจ์
์ ํน์ ์ํฉ์ ์ถฉ๋ถํ์ง ์์ต๋๋ค. ๋๋ ์ด์ ์ค์ LaunchMode
์ SingleTask
์ผ์ ๋ค์ ํ ๋ฒ ์ ๋๋ก ์๋ ๋ง๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค. ๋ถํํ๋ ์ด๊ฒ์ด ๋ค๋ฅธ ๊ฒ๋ค์ ์ด๋ค ์ํฅ์ ๋ฏธ์น ์ง ํ์คํ์ง ์์ต๋๋ค.
๋ต๋ณ
๋๋ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๊ณ ๋ค์ ์๋ฃจ์ ์ ์ฌ์ฉํ์ฌ ์์ ํ์ต๋๋ค.
์ฃผ์ ํ๋์์ onCreate
๋ฉ์๋ ์๋จ์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค .
ActivityManager manager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
List<RunningTaskInfo> tasks = manager.getRunningTasks(Integer.MAX_VALUE);
for (RunningTaskInfo taskInfo : tasks) {
if(taskInfo.baseActivity.getClassName().equals(<your package name>.<your class name>) && (taskInfo.numActivities > 1)){
finish();
}
}
์ด ๊ถํ์ ๋งค๋ํ์คํธ์ ์ถ๊ฐํ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค.
< uses-permission android:name="android.permission.GET_TASKS" />
๋์์ด๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.