์นดํ…Œ๊ณ ๋ฆฌ ๋ณด๊ด€๋ฌผ: Android

Android

๋‹ค๋ฅธ ์ธ ํ…ํŠธ๋กœ ์‹œ์ž‘๋  ๋•Œ ํ™œ๋™์˜ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ• ํ”„๋กœ๊ทธ๋žจ ๋ฉ”๋‰ด ์•„์ด์ฝ˜์—์„œ

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;
    }
}

๋‹ต๋ณ€

์‹คํŒจํ•œ ์ด์œ ์™€์ด ๋ฒ„๊ทธ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ์žฌํ˜„ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์— ํ†ตํ•ฉ ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. Eclipse ๋˜๋Š” Market App์„ ํ†ตํ•ด ์•ฑ์„ ์‹œ์ž‘ํ•˜๋ฉด FLAG_ACTIVITY_NEW_TASK ์ธ ํ…ํŠธ ํ”Œ๋ž˜๊ทธ์™€ ํ•จ๊ป˜ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

  2. ๋Ÿฐ์ฒ˜ (ํ™ˆ)๋ฅผ ํ†ตํ•ด ์‹œ์ž‘ํ•  ๋•Œ ๋‹ค์Œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 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" />

๋„์›€์ด๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.


์ด ๊ธ€์€ Android ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋ถ„๋ฅ˜๋˜์—ˆ๊ณ  ๋‹˜์— ์˜ํ•ด ์— ์ž‘์„ฑ๋์Šต๋‹ˆ๋‹ค.