Special Report

ในตอนที่ผ่านมา เราได้สร้างหน้าตาของแอพลิเคชัน Text Editor กันแล้ว ในตอนนี้เราจะทำความเข้าใจ Lifecycle ของแอพลิเคชันบน Windows 8 App Store กันให้มากขึ้น ก่อนจะเพิ่มความสามารถหลักของแอพลิเคชันนี้ นั่นคือ อ่านและเขียนไฟล์นั่นเอง

วงจรชีวิต (Lifecycle) ของแอพลิเคชันสำหรับ Windows 8 App Store

เดิมทีเมื่อเราพัฒนาแอพลิเคชันสำหรับพีซี แอพลิเคชันของเราจะเริ่มทำงานเมื่อผู้ใช้ (หรือระบบ) เรียกมันขึ้นมา จากนั้นแอพลิเคชันก็จะทำงานไปเรื่อยๆ จนกว่าผู้ใช้จะปิดแอพลิเคชัน หรือปิดเครื่องไป

อย่างไรก็ตาม การทำงานลักษณะดังกล่าวไม่เหมาะสมสำหรับแอพลิเคชันบน Windows 8 App Store ซึ่งต้องทำงานได้ดีทั้งบนพีซีและอุปกรณ์พกพาเช่นแท็บเล็ต เนื่องจากจะกระทบต่อประสิทธิภาพโดยรวมของระบบ และใช้พลังงานมากเกินไป

แอพลิเคชันสำหรับ Windows 8 App Store จะมีสามสถานะหลักๆ คือ

  • Running คือสถานะที่แอพลิเคชันทำงานตามปกติ
  • Suspended คือสถานะที่แอพลิเคชันไม่ได้ทำงานเพราะถูกพักการทำงาน แต่ยังเก็บข้อมูลของแอพลิเคชันไว้ในหน่วยความจำหลักอยู่
  • Not running คือสถานะที่แอพลิเคชันไม่ได้ทำงาน และไม่มีข้อมูลใดๆ อยู่ในหน่วยความจำเลย

พฤติกรรมหลักๆ ของแอพลิเคชันจะเป็นดังนี้

![](https://lh3.googleusercontent.com/-BZGWhiuf58c/UOZyitGsqbI/AAAAAAAAmFI/HTGS5kpogvo/s615/MSFT+Tutorial+Part+3_html_m5413940c.png)

  • เมื่อผู้ใช้เปิดแอพลิเคชัน แอพลิเคชันจะเข้าสู่สถานะ Running
  • เมื่อผู้ใช้สลับไปใช้งานแอพลิเคชันอื่น แอพลิเคชันของเราจะยังอยู่ในสถานะ Running ประมาณ 10 วินาที หากผู้ใช้ไม่ได้สลับกลับมายังแอพลิเคชันของเรา แอพลิเคชันของเราจะเข้าสู่สถานะ Suspended
  • เมื่อผู้ใช้ปิดแอพลิเคชัน หรือระบบปิดแอพลิเคชันของเราที่อยู่ในสถานะ Suspended เพื่อเรียกหน่วยความจำคืน แอพลิเคชันก็จะเข้าสู่สถานะ Not running

ปัญหาของการเขียนแอพลิเคชันภายใต้ Lifecycle แบบใหม่

สังเกตว่าเมื่อผู้ใช้สลับออกจากแอพลิเคชันของเราไปยังแอพลิเคชันอื่น มีโอกาสที่แอพลิเคชันของเราจะถูกระบบปิดได้โดยที่ผู้ใช้ไม่ได้สั่งปิดด้วยตนเอง อย่างไรก็ตามแอพลิเคชันของเราจะยังคงปรากฎอยู่ในแถบด้านซ้าย ดังนั้นผู้ใช้จะยังสลับมาใช้แอพลิเคชันของเรา (ซึ่งจริงๆ ถูกปิดไปแล้ว) ได้

เมื่อเกิดเหตุการณ์ลักษณะนี้ ผู้ใช้จะได้รับประสบการณ์ที่ไม่ดีนัก เนื่องจากแอพลิเคชันจะกลับไปเริ่มที่หน้าจอแรกใหม่ แทนที่จะเป็นหน้าล่าสุดที่ผู้ใช้เปิดไว้ ปัญหาที่แย่กว่านั้นคือหากผู้ใช้ทำงานค้างไว้ (เช่น กรอกแบบฟอร์ม) งานที่ผู้ใช้ทำค้างไว้ก็จะหายไปด้วย

เพื่อไม่ให้ผู้ใช้ต้องประสบปัญหาดังกล่าว เราจะต้องพัฒนาแอพลิเคชันให้เก็บสถานะสำคัญๆ ไว้ใน WinJS.Application.sessionState เมื่อผู้ใช้สลับออกไปใช้แอพลิเคชันอื่น จากนั้นเมื่อผู้ใช้กลับมาเปิดแอพลิเคชันของเราอีกครั้ง หากเราตรวจพบว่าครั้งล่าสุดแอพลิเคชันถูกปิดโดยระบบ ให้แอพลิเคชันอ่านสถานะที่เก็บไว้กลับมาเพื่อทำให้แอพลิเคชันอยู่ในสถานะเดิม (หรือใกล้เคียง)

ตัวอย่างการเก็บสถานะของแอพลิเคชัน

ในแอพลิเคชัน Text Editor ของเรา ผู้ใช้คงโวยวายเราเป็นแน่แท้หากพบว่าหลังจากเขาพิมพ์งาน แล้วสลับไปทำอย่างอื่นสักพัก จากนั้นกลับมาที่แอพลิเคชันของเราแล้วพบว่า ทุกสิ่งที่เขาได้พิมพ์ไปนั้นสูญหายไปแล้ว

ดังนั้นเราควรจะเก็บสถานะต่างๆ ของแอพลิเคชันไว้ โดยสิ่งที่เราจะเก็บมีดังนี้

  • หน้าปัจจุบันที่ผู้ใช้เปิด
  • กรณีที่หน้าปัจจุบันเป็น Editor เก็บข้อมูลต่อไปนี้
    • ข้อความใน Textarea
    • ตำแหน่ง Caret หรือ Selection

อย่างไรก็ตาม โค้ดตั้งต้นของโปรเจกต์แบบ Navigation App ซึ่งเราสร้างไว้ตั้งแต่ต้นตอนที่แล้วนั้น ได้สร้างโค้ดสำหรับเก็บหน้าปัจจุบันที่ผู้ใช้เปิด รวมถึง Navigation Stack ทั้งหมด และคืนค่ากลับมาให้เมื่อผู้ใช้กลับมาใช้แอพลิเคชันหลังจากแอพลิเคชันถูกระบบปิดไปแล้วด้วย (ดูไฟล์ /js/default.js) ดังนั้นส่วนนี้ถือว่าเสร็จแล้วโดยที่เราไม่ต้องทำอะไรเพิ่ม

ส่วนหน้า editor นั้นเราต้องทำเอง โดยแก้ไขไฟล์ /pages/editor/editor.js เพื่อรอฟังอีเวนต์ checkpoint จาก WinJS.Application ซึ่งจะถูกส่งออกมาในเวลาที่ผู้ใช้สลับแอพลิเคชัน ดังนี้

{syntaxhighlighter brush: jscript highlight:[9,10,11,12,13,14,15,21]}(function () {
"use strict";
var contentArea;
var bottomAppBar;
var saveButton;
var onClickSaveButton = function (event) {
bottomAppBar.winControl.hide();
};
var onCheckpoint = function () {
WinJS.Application.sessionState.editorState = {
innerText: contentArea.innerText,
selectionStart: contentArea.selectionStart,
selectionEnd: contentArea.selectionEnd
}
};
WinJS.UI.Pages.define("/pages/editor/editor.html", {
ready: function (element, options) {
contentArea = element.querySelector("#contentArea");
bottomAppBar = element.querySelector("#bottomAppBar");
saveButton = element.querySelector("#saveButton");
WinJS.Application.addEventListener("checkpoint", onCheckpoint);
saveButton.addEventListener("click", onClickSaveButton);
}
});
})();{/syntaxhighlighter}

โค้ดที่เพิ่มเข้ามาจะหาอีลิเมนต์ที่มีไอดี contentArea (ซึ่งก็คือ Textarea ที่เราสร้างไว้ใน /pages/editor/editor.html ตอนที่แล้ว) มาเก็บไว้ในตัวแปร contentArea จากนั้นรอฟังอีเวนต์ checkpoint จาก WinJS.Application เมื่อเกิดอีเวนต์ขึ้นให้ทำงานในฟังก์ชัน onCheckpoint ซึ่งจะเก็บค่าต่างๆ ไว้ใน property editorState (เรากำหนดชื่อ property เอง) ให้กับวัตถุ WinJS.Application.sessionState เท่านี้ก็เป็นอันเสร็จเรียบร้อย

จากนั้นเราจึงเพิ่มโค้ดในส่วน ready ให้ดึงข้อมูลจาก sessionState กลับมา โดยแก้ไขเพิ่มเติมฟังก์ชัน ready ในไฟล์ /pages/editor/editor.js ดังนี้

{syntaxhighlighter brush:jscript highlight:[6,7,8,9,10,11]} WinJS.UI.Pages.define("/pages/editor/editor.html", {
ready: function (element, options) {
contentArea = element.querySelector("#contentArea");
bottomAppBar = element.querySelector("#bottomAppBar");
saveButton = element.querySelector("#saveButton");
if (WinJS.Application.sessionState.editorState) {
var editorState = WinJS.Application.sessionState.editorState;
contentArea.innerText = editorState.innerText;
contentArea.setSelectionRange(editorState.selectionStart, editorState.selectionEnd);
delete WinJS.Application.sessionState.editorState;
}
saveButton.addEventListener("click", onClickSaveButton);
WinJS.Application.addEventListener("checkpoint", onCheckpoint);
}
});{/syntaxhighlighter}

จากนั้นทดสอบการเก็บสถานะของแอพลิเคชัน โดย

  1. ทดลองรันแอพลิเคชัน โดยคลิกปุ่ม Start Debuging (ปุ่มไอคอน play สีเขียวบนแถบเครื่องมือ)
  2. คลิกปุ่ม New เพื่อเข้าไปยัง editor และพิมพ์ข้อความลงไป
  3. สลับมายัง Visual Studio แล้วเลือก Suspend and Shutdown เพื่อสมมติสถานการณ์ว่าแอพลิเคชันถูกระบบปิดไป
  4. ทดลองรันแอพลิเคชันอีกครั้ง

หากทุกอย่างถูกต้อง จะพบว่าแอพลิเคชันของเราจะเปิดขึ้นมาในหน้า editor พร้อมข้อความและ selection ที่เราทำไว้ก่อนหน้านี้แล้ว

การอ่านและเขียนไฟล์

สำหรับแอพลิเคชันบน Windows 8 App Store การทำงานกับส่วนต่างๆ ของระบบปฏิบัติการจะถูกควบคุมมากขึ้นกว่าเดิม โดยการใช้ฟีเจอร์ต่างๆ ของระบบปฏิบัติการนั้น จะต้องระบุฟีเจอร์ที่จะขออนุญาตใช้ไว้ในไฟล์ package.appmanifest ด้วย เหตุผลหลักๆ ก็คือเพื่อความปลอดภัยของผู้ใช้ ทำให้ผู้ใช้ทราบได้ตั้งแต่ก่อนติดตั้งว่าแอพลิเคชันนั้นจะทำอะไรบ้างนั่นเอง

สำหรับการอ่านและเขียนไฟล์ในแอพลิเคชันบน Windows 8 App Store ก็จะถูกจำกัดมากขึ้นเช่นกัน โดยเราจะไม่สามารถอ่านหรือเขียนไฟล์ใดๆ ก็ได้ในเครื่องตราบเท่าที่รู้ URI ของไฟล์ได้อีกต่อไป แต่จะอ่านและเขียนไฟล์ในเงื่อนไขต่อไปนี้ได้เท่านั้น:

  1. ไฟล์ในไดเรกทอรี Application Data ของแอพลิเคชันตนเอง (ดูเพิ่มจากเอกสารหน้า Application Data)
  2. ไลบรารีของผู้ใช้ (Documents Library, Pictures Library, Music Library, Video Library) ที่แอพลิเคชันได้ขออนุญาตไว้ในไฟล์ package.appmanifest
  3. ไฟล์ใดๆ ในเครื่อง ที่ผู้ใช้เป็นผู้เลือกเองผ่าน File Picker

ในแอพลิเคชันตัวอย่างของเราจะใช้เฉพาะการอ่านและเขียนไฟล์ผ่าน File Picker เท่านั้น

การใช้ File Picker เพื่ออ่านไฟล์

เราจะเริ่มจากแก้ไขหน้าแรกของแอพลิเคชันให้เปิดไฟล์ได้ โดยแก้ไขไฟล์ /pages/home/home.html ดังนี้

{syntaxhighlighter brush:jscript highlight:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,28,29]} (function () {
"use strict";
var onNewDocument = function (event) {
WinJS.Navigation.navigate("/pages/editor/editor.html");
};
var onOpenDocument = function (event) {
var currentState = Windows.UI.ViewManagement.ApplicationView.value;
if (currentState === Windows.UI.ViewManagement.ApplicationViewState.snapped) {
if (!Windows.UI.ViewManagement.ApplicationView.tryUnsnap()) {
return;
}
}
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
openPicker.fileTypeFilter.replaceAll(["*"]);
openPicker.pickSingleFileAsync().then(function (selectedFile) {
if (selectedFile) {
WinJS.Navigation.navigate("/pages/editor/editor.html", {
file: selectedFile
});
}
});
};
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function (element, options) {
var newButton = element.querySelector("#newButton");
newButton.addEventListener("click", onNewDocument);
var openButton = element.querySelector("#openButton");
openButton.addEventListener("click", onOpenDocument);
}
});
})();{/syntaxhighlighter}

เนื่องจาก File Picker จะไม่สามารถทำได้หากหน้าจอแอพลิเคชันอยู่ใน Snapped View ดังนั้นเราจะต้องเขียนโค้ดเพื่อควบคุมตามลำดับดังนี้

  1. ลองให้แอพลิเคชันดึงตัวเองออกมาเป็นขนาดหน้าจอปกติเสียก่อน
  2. สร้าง FileOpenPicker และตำแหน่งเริ่มต้นที่จะแสดงไฟล์ และนามสกุลที่จะอนุญาตให้เปิดได้
  3. สั่งเปิด File Picker ด้วยฟังก์ชัน pickSingleFileAsync() (ในกรณีให้เลือกหลายไฟล์เปลี่ยนเป็นเมธอด pickMultipleFilesAsync()) พร้อมกำหนดฟังก์ชัน callback ที่จะถูกเรียกเมื่อผู้ใช้เลือกหรือยกเลิกการเลือกไฟล์ และนำวัตถุ StorageFile ที่ได้ไปทำงานต่อไป

ในตัวอย่างนี้เราจะไม่ได้หน้าแรกเปิดไฟล์ด้วยตัวเอง แต่จะให้ navigate ไปที่หน้า editor พร้อมทั้งส่งไฟล์ที่ผู้ใช้เลือกไปเป็น options ของหน้า editor เพื่อให้ทำหน้าที่เปิดไฟล์ต่อไป โดยการกำหนด options ของการ navigate นั้นทำได้โดยการส่งอาร์กิวเมนต์ตัวที่สองเพิ่มไปในฟังก์ชัน WinJS.Navigation.navigate

อย่างไรก็ตาม แอพลิเคชันของเราก็ยังไม่ได้เปิดไฟล์จริงๆ อยู่ดี เราจะต้องไปแก้ไขหน้า editor ให้ตรวจดูด้วยว่า หากหน้า editor ถูกเปิดโดยส่งไฟล์มาด้วย ก็ให้อ่านเนื้อหาของไฟล์นั้นมาแสดงผล ทำได้โดยแก้ไขฟังก์ชัน ready ของไฟล์ /pages/editor/editor.js ดังนี้ (ส่วนที่เพิ่มเติมคือบรรทัดที่เน้น)

{syntaxhighlighter brush:jscript highlight:[3,7,20,29,30,31,32,33,34,35]}(function () {
"use strict";
var pageTitle;
var contentArea;
var bottomAppBar;
var saveButton;
var currentFile;
var onClickSaveButton = function (event) {
bottomAppBar.winControl.hide();
}
var onCheckpoint = function () {
WinJS.Application.sessionState.editorState = {
innerText: contentArea.innerText,
selectionStart: contentArea.selectionStart,
selectionEnd: contentArea.selectionEnd
}
};
WinJS.UI.Pages.define("/pages/editor/editor.html", {
ready: function (element, options) {
pageTitle = element.querySelector("#pageTitle");
contentArea = element.querySelector("#contentArea");
bottomAppBar = element.querySelector("#bottomAppBar");
saveButton = element.querySelector("#saveButton");
if (WinJS.Application.sessionState.editorState) {
var editorState = WinJS.Application.sessionState.editorState;
contentArea.innerText = editorState.innerText;
contentArea.setSelectionRange(editorState.selectionStart, editorState.selectionEnd);
}
else if (options && options.file) {
Windows.Storage.FileIO.readTextAsync(options.file).then(function (fileContent) {
currentFile = options.file;
pageTitle.innerText = options.file.name;
contentArea.innerText = fileContent;
});
}
saveButton.addEventListener("click", onClickSaveButton);
WinJS.Application.addEventListener("checkpoint", onCheckpoint);
}
});
})();{/syntaxhighlighter}

โค้ดส่วนที่เพิ่มขึ้นมาได้เช็คว่าหากมีการส่งไฟล์มาด้วยขณะเรียกหน้านี้ (if (options && options.file)) ให้อ่านเนื้อหาของไฟล์ด้วยเอพีเอ Windows.Storage.FileIO เมื่อเสร็จแล้วแสดงเนื้อหา นอกจากนี้ยังอัพเดทชื่อของหน้านี้ให้ตรงกับชื่อไฟล์ และเก็บวัตถุ StorageFile ที่ส่งมาไว้ในตัวแปร currentFile ด้วย เพื่อใช้ในการบันทึกไฟล์ต่อไป

การบันทึกไฟล์

การบันทึกไฟล์ไม่ได้แตกต่างจากการอ่านไฟล์เท่าไหร่นัก เราจะมาเพิ่มฟีเจอร์ให้ปุ่ม Save สามารถบันทึกไฟล์ที่เปิดไว้ได้ หรือถ้ายังไม่เคยบันทึกไฟล์ไว้ก่อนเลยก็จะเรียก File Picker ขึ้นมาให้เลือกว่าจะบันทึกที่ใดได้ โดยแก้ไขฟังก์ชัน onClickSaveButton เดิม และเพิ่มฟังก์ชัน save ในไฟล์ /pages/editor/editor.js ดังนี้ (ส่วนที่เพิ่มเติมคือบรรทัดที่เน้น)

{syntaxhighlighter brush:jscript highlight:[1,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28]}var save = function (file, content) {
Windows.Storage.FileIO.writeTextAsync(file, content).done(function () {
currentFile = file;
pageTitle.innerText = file.name;
});
};
var onClickSaveButton = function (event) {
var content = contentArea.innerText;
if (currentFile) {
save(currentFile, content);
}
else {
var currentState = Windows.UI.ViewManagement.ApplicationView.value;
if (currentState === Windows.UI.ViewManagement.ApplicationViewState.snapped) {
if (!Windows.UI.ViewManagement.ApplicationView.tryUnsnap()) {
return;
}
}
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
savePicker.fileTypeChoices.insert("Plain Text", [".txt"]);
savePicker.suggestedFileName = "New Document";
savePicker.pickSaveFileAsync().then(function (selectedFile) {
if (selectedFile) {
save(selectedFile, content);
}
});
}
bottomAppBar.winControl.hide();
}{/syntaxhighlighter}

อย่างไรก็ตาม หากเราให้ผู้ใช้เลือกตำแหน่งบันทึกไฟล์เอง ผู้ใช้อาจจะเลือกบันทึกไฟล์ในเครื่องหรือบันทึกไฟล์บนกลุ่มเมฆอย่าง SkyDrive ก็ได้ ซึ่งโค้ดในการอ่านและเขียนไฟล์ทั้งหมดที่เราเขียนมานั้นสามารถให้ผู้ใช้บันทึกไฟล์บน SkyDrive ได้อยู่แล้ว แต่การบันทึกไฟล์อาจจะยังมีประสิทธิภาพไม่ดีเท่าที่ควร เนื่องจากโดยปกติวินโดวส์จะอัพโหลดไฟล์ขึ้นบน SkyDrive ทันทีที่เราเขียนไฟล์ ซึ่งอาจเกิดการอัพโหลดหลายครั้งหากการเขียนไฟล์ใช้เวลานานมากๆ ทำให้การบันทึกเสร็จช้า

ดังนั้น เราจะใช้ Windows.Storage.CachedFileManager ช่วยป้องกันไม่ให้ระบบอัพเดทไฟล์บน SkyDrive เพียงครั้งเดียวตอนที่เราเขียนทุกอย่างเสร็จแล้ว โดยเรียกฟังก์ชัน deferUpdates ก่อนเริ่มเขียนไฟล์ จากนั้นเมื่อเขียนไฟล์เสร็จจึงเรียกฟังก์ชัน completeUpdatesAsync เพื่อให้ระบบเริ่มอัพโหลดไฟล์ได้

จากแอพลิเคชันตัวอย่าง เราจะแก้ไขฟังก์ชัน save เป็นดังนี้

{syntaxhighlighter brush:jscript}var save = function (file, content) {
Windows.Storage.CachedFileManager.deferUpdates(file);
Windows.Storage.FileIO.writeTextAsync(file, content).done(function () {
Windows.Storage.CachedFileManager.completeUpdatesAsync(file).done(function (updateStatus) {
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
currentFile = file;
pageTitle.innerText = file.name;
}
});
});
};{/syntaxhighlighter}

การอ่านและเขียนไฟล์และ Lifecycle

ถึงตอนนี้แอพลิเคชันของเราสามารถอ่านและเขียนไฟล์ได้แล้ว อย่างไรก็ตามเรายังต้องปรับปรุงการเก็บสถานะของแอพลิเคชันอีก เนื่องจากหากผู้ใช้เปิดไฟล์ขึ้นมา แล้วสลับไปใช้แอพลิเคชันอื่นจนกระทั่งแอพลิเคชันของเราถูกปิด เมื่อผู้ใช้กลับมาแก้ไขต่อจะบันทึกไฟล์ไม่ได้ เนื่องจากแอพลิเคชันของเราไม่ได้จำไว้ว่าไฟล์ที่เปิดคือไฟล์อะไร

ถึงตอนนี้เราอาจจะคิดว่าก็แก้ไขได้โดยการให้จำไฟล์นั้นไว้ใน SessionState ดังนี้

{syntaxhighlighter brush:jscript highlight:3}var onCheckpoint = function () {
WinJS.Application.sessionState.editorState = {
currentFile: currentFile
innerText: contentArea.innerText,
selectionStart: contentArea.selectionStart,
selectionEnd: contentArea.selectionEnd
};
};
{/syntaxhighlighter}

อย่างไรก็ตาม หากลองทดสอบดูจะพบว่าไม่ได้ผล โดยเมื่อเราอ่าน SessionState กลับมา จะพบว่าวัตถุ StorageFile ที่เก็บไว้กลายเป็นเพียงวัตถุธรรมดาไปแล้ว นั่นคือเราไม่สามารถถือวัตถุ StorageFile ข้ามเซสชันได้นั่นเอง

ในกรณีนี้ วิธีที่ถูกต้องคือให้เราเก็บวัตถุ SessionState ไว้กับ Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList ลิสต์นี้จะช่วยเรารักษาสิทธิ์การเข้าถึงไฟล์ข้ามเซสชันได้ ดังนี้

  1. เมื่อเรานำวัตถุ StorageFile มาเก็บในลิสต์นี้ เราจะได้ token มาสำหรับอ้างอิง ให้เราเก็บ token นี้ไว้
  2. จากนั้นเมื่อต้องการวัตถุ StorageFile จึงนำ token ไปขอ StorageFile กลับมาจากลิสต์

เราจะแก้ไขแอพลิเคชัน โดยเริ่มจากการให้เก็บวัตถุ StorageFile ลงใน futureAccessList เมื่อผู้ใช้จะสลับไปแอพลิเคชันอื่น ในฟังก์ชัน onCheckpoint ในไฟล์ /pages/editor/editor.html ดังนี้

{syntaxhighlighter brush:jscript highlight:[2,3,4]}var onCheckpoint = function () {
if (currentFile) {
Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.addOrReplace("currentFile", currentFile);
}
WinJS.Application.sessionState.editorState = {
innerText: contentArea.innerText,
selectionStart: contentArea.selectionStart,
selectionEnd: contentArea.selectionEnd
}
};
{/syntaxhighlighter}

เนื่องจากแอพลิเคชันตัวอย่างนี้แก้ไขไฟล์ได้ทีละไฟล์เท่านั้น เราจึงกำหนด token ไว้ในซอร์สโค้ดเลยเพื่อความง่าย หากต้องการเก็บหลายๆ ไฟล์ เราอาจเปลี่ยนจากเมธอด addOrReplace ไปใช้เมธอด add แทน ซึ่งจะสร้าง token แบบสุ่มมาให้เรา จากนั้นจึงเอา token นั้นไปเก็บไว้ใน SessionState
จากนั้นแก้ไขแอพลิเคชันเพิ่มเติมให้เอาวัตถุ StorageFile กลับมาด้วย โดยแก้ไขฟังก์ชัน ready ในไฟล์ /pages/editor/editor.html ดังนี้

{syntaxhighlighter brush:jscript highlight:[11,12,13,14,15,16]}WinJS.UI.Pages.define("/pages/editor/editor.html", {
ready: function (element, options) {
contentArea = element.querySelector("#contentArea");
bottomAppBar = element.querySelector("#bottomAppBar");
saveButton = element.querySelector("#saveButton");
if (WinJS.Application.sessionState.editorState) {
var editorState = WinJS.Application.sessionState.editorState;
contentArea.innerText = editorState.innerText;
contentArea.setSelectionRange(editorState.selectionStart, editorState.selectionEnd);
delete WinJS.Application.sessionState.editorState;
if (Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.containsItem("currentFile")) {
Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync("currentFile").then(function (file) {
currentFile = file;
Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.remove("currentFile");
});
}
}
else if (options && options.file) {
Windows.Storage.FileIO.readTextAsync(options.file).then(function (fileContent) {
contentArea.innerText = fileContent;
});
}
saveButton.addEventListener("click", onClickSaveButton);
WinJS.Application.addEventListener("checkpoint", onCheckpoint);
}
});
{/syntaxhighlighter}

เท่านี้ผู้ใช้ก็จะสามารถบันทึกไฟล์ได้ตามปกติถึงแม้แอพลิเคชันจะถูกระบบปิดได้แล้ว

ตอนต่อไป

ในตอนนี้เราได้พัฒนาฟีเจอร์ต่างๆ ให้แอพลิเคชันโดยคำนึงถึงข้อกำหนดต่างๆ สำหรับแอพลิเคชันบน Windows 8 App Store กันแล้ว ในตอนหน้าเราจะเพิ่มฟีเจอร์พิเศษต่างๆ เช่น การใช้ Charm Bar เพื่อให้แอพลิเคชันของเราสมบูรณ์ขึ้นต่อไป

สารบัญบทความ

บทความชุด การเขียนแอพลิเคชันสำหรับ Windows 8 App Store

Hiring! บริษัทที่น่าสนใจ

Carmen Software company cover
Carmen Software
Hotel Financial Solutions
Next Innovation (Thailand) Co., Ltd. company cover
Next Innovation (Thailand) Co., Ltd.
We are web design with consulting & engineering services driven the future stronger and flexibility.
KKP Dime company cover
KKP Dime
KKP Dime บริษัทในเครือเกียรตินาคินภัทร
Kiatnakin Phatra Financial Group company cover
Kiatnakin Phatra Financial Group
Financial Service
Fastwork Technologies company cover
Fastwork Technologies
Fastwork.co เว็บไซต์ที่รวบรวม ฟรีแลนซ์ มืออาชีพจากหลากหลายสายงานไว้ในที่เดียวกัน
Thoughtworks Thailand company cover
Thoughtworks Thailand
Thoughtworks เป็นบริษัทที่ปรึกษาด้านเทคโนโยลีระดับโลกที่คว้า Great Place to Work 3 ปีซ้อน
Iron Software company cover
Iron Software
Iron Software is an American company providing a suite of .NET libraries by engineer for engineers.
CLEVERSE company cover
CLEVERSE
Cleverse is a Venture Builder. Our team builds several tech companies.
Nipa Cloud company cover
Nipa Cloud
#1 OpenStack cloud provider in Thailand with our own data center and software platform.
Bangmod Enterprise company cover
Bangmod Enterprise
The leader in Cloud Server and Hosting in Thailand.
CIMB THAI Bank company cover
CIMB THAI Bank
MOVING FORWARD WITH YOU - CIMB is the leading ASEAN Bank
Bangkok Bank company cover
Bangkok Bank
Bangkok Bank is one of Southeast Asia's largest regional banks, a market leader in business banking
MuvMi (Urban Mobility Tech Co.,Ltd.) company cover
MuvMi (Urban Mobility Tech Co.,Ltd.)
Shape the future of urban mobility towards affordable, clean, and safe solutions
T.N. Digital Solution Co., Ltd. company cover
T.N. Digital Solution Co., Ltd.
TNDS has been involving in every first move of banking’s major digital transformation.
KBTG - KASIKORN Business-Technology Group company cover
KBTG - KASIKORN Business-Technology Group
KBTG - "The Technology Company for Digital Business Innovation"
Siam Commercial Bank Public Company Limited company cover
Siam Commercial Bank Public Company Limited
"Let's start a brighter career future together"
Icon Framework co.,Ltd. company cover
Icon Framework co.,Ltd.
Global Standard Platform for Real Estate แพลตฟอร์มสำหรับธุรกิจอสังหาริมทรัพย์ครบวงจร มาตรฐานระดับโลก
REFINITIV company cover
REFINITIV
The Financial and Risk business of Thomson Reuters is now Refinitiv
H LAB company cover
H LAB
Re-engineering healthcare systems through intelligent platforms and system design.
The Gang Technology Co., Ltd. company cover
The Gang Technology Co., Ltd.
We're a Digital Agency that helps our customers transform their business into digital with ease.
LTMH company cover
LTMH
LTMH มุ่งเน้นการพัฒนาผลิตภัณฑ์ที่สามารถช่วยพันธมิตรของเราให้บรรลุเป้าหมาย
Seven Peaks company cover
Seven Peaks
We Drive Digital Transformation
Wisesight (Thailand) Co., Ltd. company cover
Wisesight (Thailand) Co., Ltd.
The Best Choice For Handling Social Media · High Expertise in Social Data · Most Advanced and Secure
MOLOG Tech company cover
MOLOG Tech
We are Modern Logistic Platform, Specialize in WMS, OMS and TMS.
Data Wow Co.,Ltd company cover
Data Wow Co.,Ltd
We enable our clients to realize increased productivity by solving their most complex issues by Data
LINE Company Thailand company cover
LINE Company Thailand
LINE, the world's hottest mobile messaging platform, offers free text and voice messaging + Call
LINE MAN Wongnai company cover
LINE MAN Wongnai
Join our journey to becoming No.1 food platform in Thailand

ข้อมูลเพิ่มเติมครับ

มีวิธีทำให้แอพทำงานเป็น Background task ได้นะครับ กรณีที่ต้องการให้งานบางอย่างยังคงทำอยู่หลังจากผู้ใช้สลับไปแอพอื่นแล้ว (ข้อมูลเพิ่มเติม [1][2])

เป็น "ธรรมชาติ" ของ OS สมัยใหม่ครับ ไม่อย่างนั้นมันจัดการทรัพยากรลำบาก ยิ่งคำนึงเรื่องประหยัดไฟด้วยแล้วเรื่องนี้จำเป็นมาก

ตรงนี้เข้าใจครับ แต่แม้กระทั่ง Skype ผมยังต้องใช้เวลาโหลดเพื่อเปิดและเข้าระบบร่วม 10 วินาที แอพปกติก็เกือบจะ 5+ วินาทีทั้งหมด ประสบการณ์ทำงานผมร่วงหมดเลยครับ อันนี้ไม่แน่ใจว่าเพราะคอมผมเก่าไปหรือเปล่า? (5 ปี+) ดังนั้นผมจึงแทบไม่ได้แตะแอพเท่าไหร่เลย แต่อย่างน้อยหน้า Desktop มันก็เร็วกว่าและแบตอึดกว่า Windows 7

SSD ด้วยหรือเปล่าครับ? ผมเปิด gMap เมื่อกี้ เกิน 15 วินาทีเลยทีเดียว -*- ทั้ง ๆ ที่หน้า Desktop นี่เร็วปกติเลย

จริง ๆ ต่ำกว่า 5 วิผมก็ว่าช้ากว่า iPad แล้วมั้งครับ (ไม่แน่ใจ อยากจับเล่นสองตัวเทียบดูเหมือนกัน)