5부 - 실제 코드 공유 전략
이 섹션에서는 일반적인 애플리케이션 시나리오에 대한 코드를 공유하는 방법에 대한 예제를 제공합니다.
데이터 계층
데이터 계층은 스토리지 엔진과 정보를 읽고 쓰는 메서드로 구성됩니다. 성능, 유연성 및 플랫폼 간 호환성을 위해 SQLite 데이터베이스 엔진은 Xamarin 플랫폼 간 애플리케이션에 권장됩니다. Windows, Android, iOS 및 Mac을 비롯한 다양한 플랫폼에서 실행됩니다.
SQLite
SQLite는 오픈 소스 데이터베이스 구현입니다. 원본 및 설명서는 SQLite.org 찾을 수 있습니다. SQLite 지원은 각 모바일 플랫폼에서 사용할 수 있습니다.
- iOS – 운영 체제에 기본 제공.
- Android – Android 2.2(API 수준 10) 이후 운영 체제에 기본 제공됩니다.
- Windows – 유니버설 Windows 플랫폼 확장에 대한 SQLite를 참조하세요.
모든 플랫폼에서 사용할 수 있는 데이터베이스 엔진이 있더라도 데이터베이스에 액세스하는 네이티브 메서드는 다릅니다. iOS와 Android는 모두 Xamarin.iOS 또는 Xamarin.Android에서 사용할 수 있는 SQLite에 액세스하기 위한 기본 제공 API를 제공하지만 네이티브 SDK 메서드를 사용하면 코드를 공유할 수 없습니다(SQL 쿼리 자체가 문자열로 저장되어 있다고 가정). 네이티브 데이터베이스 기능에 대한 CoreData
자세한 내용은 iOS 또는 Android 클래스 SQLiteOpenHelper
에서 검색합니다. 이러한 옵션은 플랫폼 간이 아니므로 이 문서의 범위를 벗어 난다.
ADO.NET
Xamarin.iOS 및 Xamarin.Android 지원 System.Data
및 Mono.Data.Sqlite
(자세한 내용은 Xamarin.iOS 설명서 참조)
이러한 네임스페이스를 사용하면 두 플랫폼에서 작동하는 ADO.NET 코드를 작성할 수 있습니다. 프로젝트의 참조를 편집하여 다음 using 문을 포함하고 System.Data.dll
Mono.Data.Sqlite.dll
코드에 추가합니다.
using System.Data;
using Mono.Data.Sqlite;
그런 다음, 다음 샘플 코드가 작동합니다.
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"items.db3");
bool exists = File.Exists (dbPath);
if (!exists)
SqliteConnection.CreateFile (dbPath);
var connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
if (!exists) {
// This is the first time the app has run and/or that we need the DB.
// Copy a "template" DB from your assets, or programmatically create one like this:
var commands = new[]{
"CREATE TABLE [Items] (Key ntext, Value ntext);",
"INSERT INTO [Items] ([Key], [Value]) VALUES ('sample', 'text')"
};
foreach (var command in commands) {
using (var c = connection.CreateCommand ()) {
c.CommandText = command;
c.ExecuteNonQuery ();
}
}
}
// use `connection`... here, we'll just append the contents to a TextView
using (var contents = connection.CreateCommand ()) {
contents.CommandText = "SELECT [Key], [Value] from [Items]";
var r = contents.ExecuteReader ();
while (r.Read ())
Console.Write("\n\tKey={0}; Value={1}",
r ["Key"].ToString (),
r ["Value"].ToString ());
}
connection.Close ();
ADO.NET 실제 구현은 분명히 다양한 메서드와 클래스로 분할됩니다(이 예제는 데모용으로만 사용됨).
SQLite-NET – 플랫폼 간 ORM
ORM(또는 개체 관계형 매퍼)은 클래스에서 모델링된 데이터의 스토리지를 간소화하려고 시도합니다. 클래스 필드 및 속성에서 수동으로 추출된 TABL 또는 SELECT, INSERT 및 DELETE 데이터를 만드는 SQL 쿼리를 수동으로 작성하는 대신 ORM은 이를 수행하는 코드 계층을 추가합니다. ORM은 리플렉션을 사용하여 클래스의 구조를 검사하여 클래스와 일치하는 테이블 및 열을 자동으로 만들고 데이터를 읽고 쓰는 쿼리를 생성할 수 있습니다. 이를 통해 애플리케이션 코드는 개체 인스턴스를 ORM으로 보내고 검색할 수 있습니다. 이 경우 내부적으로 모든 SQL 작업을 처리합니다.
SQLite-NET은 SQLite에서 클래스를 저장하고 검색할 수 있는 간단한 ORM 역할을 합니다. 컴파일러 지시문 및 기타 요령의 조합으로 플랫폼 간 SQLite 액세스의 복잡성을 숨깁니다.
SQLite-NET의 기능:
- 테이블은 모델 클래스에 특성을 추가하여 정의됩니다.
- 데이터베이스 인스턴스는 SQLite-Net 라이브러리의
SQLiteConnection
주 클래스인 서브클래스로 표시됩니다. - 개체를 사용하여 데이터를 삽입, 쿼리 및 삭제할 수 있습니다. 필요한 경우 SQL 문을 작성할 수 있지만 SQL 문은 필요하지 않습니다.
- 기본 Linq 쿼리는 SQLite-NET에서 반환된 컬렉션에서 수행할 수 있습니다.
SQLite-NET에 대한 소스 코드 및 설명서는 github의 SQLite-Net에서 사용할 수 있으며 두 사례 연구에서 모두 구현되었습니다. 아래는 Tasky Pro 사례 연구에서 SQLite-NET 코드의 간단한 예제를 보여 줍니다.
먼저 클래스는 TodoItem
특성을 사용하여 데이터베이스 기본 키로 필드를 정의합니다.
public class TodoItem : IBusinessEntity
{
public TodoItem () {}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public bool Done { get; set; }
}
이렇게 하면 인스턴스에서 TodoItem
다음 코드 줄(및 SQL 문 없음) SQLiteConnection
을 사용하여 테이블을 만들 수 있습니다.
CreateTable<TodoItem> ();
테이블의 데이터는 SQL 문을 요구하지 않고도 다른 메서드 SQLiteConnection
를 사용하여 조작할 수도 있습니다.
Insert (TodoItem); // 'task' is an instance with data populated in its properties
Update (TodoItem); // Primary Key field must be populated for Update to work
Table<TodoItem>.ToList(); // returns all rows in a collection
전체 예제는 사례 연구 소스 코드를 참조하세요.
파일 액세스
파일 액세스는 모든 애플리케이션의 핵심 부분이 될 수 있습니다. 애플리케이션의 일부일 수 있는 파일의 일반적인 예는 다음과 같습니다.
- SQLite 데이터베이스 파일.
- 사용자가 생성한 데이터(텍스트, 이미지, 소리, 비디오).
- 캐싱을 위해 다운로드한 데이터(이미지, html 또는 PDF 파일).
직접 액세스 System.IO
Xamarin.iOS와 Xamarin.Android 모두 네임스페이스의 클래스를 사용하여 파일 시스템 액세스를 허용합니다 System.IO
.
각 플랫폼에는 고려해야 할 다양한 액세스 제한이 있습니다.
- iOS 애플리케이션은 파일 시스템 액세스가 매우 제한된 샌드박스에서 실행됩니다. Apple은 백업되는 특정 위치(및 그렇지 않은 위치)를 지정하여 파일 시스템을 사용하는 방법을 추가로 설명합니다. 자세한 내용은 Xamarin.iOS의 파일 시스템 작업 가이드를 참조하세요.
- 또한 Android는 애플리케이션과 관련된 특정 디렉터리에 대한 액세스를 제한하지만 외부 미디어(예: SD 카드) 및 공유 데이터 액세스
- Windows Phone 8(Silverlight)은 직접 파일 액세스를 허용하지 않습니다. 파일은 .를 사용하여
IsolatedStorage
조작할 수 있습니다. - Windows 8.1 WinRT 및 Windows 10 UWP 프로젝트는 다른 플랫폼과 다른 API를 통해
Windows.Storage
비동기 파일 작업만 제공합니다.
iOS 및 Android의 예
텍스트 파일을 쓰고 읽는 간단한 예제는 다음과 같습니다.
사용하면 Environment.GetFolderPath
iOS 및 Android에서 동일한 코드를 실행할 수 있으며, 각 코드는 파일 시스템 규칙에 따라 유효한 디렉터리를 반환합니다.
string filePath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"MyFile.txt");
System.IO.File.WriteAllText (filePath, "Contents of text file");
Console.WriteLine (System.IO.File.ReadAllText (filePath));
iOS 관련 파일 시스템 기능에 대한 자세한 내용은 파일 시스템 문서로 작업하는 Xamarin.iOS를 참조하세요. 플랫폼 간 파일 액세스 코드를 작성할 때 일부 파일 시스템은 대/소문자를 구분하며 디렉터리 구분 기호가 다릅니다. 파일 또는 디렉터리 경로를 생성할 때 항상 파일 이름과 메서드에 Path.Combine()
동일한 대/소문자를 사용하는 것이 좋습니다.
Windows 8 및 Windows 10용 Windows.Storage
Xamarin.Forms 를 사용하여 Mobile Apps 만들기 책20장. 비동기 및 파일 I/O에는 Windows 8.1 및 Windows 10용 샘플이 포함되어 있습니다.
지원되는 API를 DependencyService
사용하여 이러한 플랫폼에서 파일을 읽고 파일을 파일화할 수 있습니다.
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
IStorageFile storageFile = await localFolder.CreateFileAsync("MyFile.txt",
CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(storageFile, "Contents of text file");
자세한 내용은 책 20장을 참조하세요.
Windows Phone 7 및 8의 격리된 스토리지(Silverlight)
격리된 스토리지는 모든 iOS, Android 및 이전 Windows Phone 플랫폼에서 파일을 저장하고 로드하기 위한 일반적인 API입니다.
일반적인 파일 액세스 코드를 작성할 수 있도록 Xamarin.iOS 및 Xamarin.Android에서 구현된 Windows Phone(Silverlight)의 파일 액세스에 대한 기본 메커니즘입니다. 공유 System.IO.IsolatedStorage
프로젝트의 세 플랫폼 모두에 걸쳐 클래스를 참조할 수 있습니다.
자세한 내용은 Windows Phone의 격리된 스토리지 개요를 참조하세요.
이식 가능한 클래스 라이브러리에서는 격리된 스토리지 API를 사용할 수 없습니다. PCL의 한 가지 대안은 PCLStorage NuGet입니다 .
PCL의 플랫폼 간 파일 액세스
Xamarin 지원 플랫폼 및 최신 Windows API에 대한 플랫폼 간 파일 액세스를 지원하는 PCL 호환 NuGet 인 PCLStorage 도 있습니다.
네트워크 작업
대부분의 모바일 애플리케이션에는 네트워킹 구성 요소가 있습니다. 예를 들면 다음과 같습니다.
- 이미지, 비디오 및 오디오 다운로드(예: 썸네일, 사진, 음악).
- 문서 다운로드(예: HTML, PDF).
- 사용자 데이터 업로드(예: 사진 또는 텍스트).
- 웹 서비스 또는 타사 API(SOAP, XML 또는 JSON 포함)에 액세스
.NET Framework는 네트워크 리소스에 액세스하기 위한 몇 가지 클래스를 제공합니다. HttpClient
WebClient
HttpWebRequest
HttpClient
네임스페이스의 System.Net.Http
클래스는 HttpClient
Xamarin.iOS, Xamarin.Android 및 대부분의 Windows 플랫폼에서 사용할 수 있습니다. 이 API를 이식 가능한 클래스 라이브러리(및 Windows Phone 8 Silverlight)로 가져오는 데 사용할 수 있는 Microsoft HTTP 클라이언트 라이브러리 NuGet 이 있습니다.
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://xamarin.com");
var response = await myClient.SendAsync(request);
WebClient
이 클래스는 WebClient
원격 서버에서 원격 데이터를 검색하는 간단한 API를 제공합니다.
Xamarin.iOS 및 Xamarin.Android가 동기 작업을 지원하더라도(백그라운드 스레드에서 수행할 수 있는) 유니버설 Windows 플랫폼 작업은 비동기여야 합니다.
간단한 비동 WebClient
기 작업의 코드는 다음과 같습니다.
var webClient = new WebClient ();
webClient.DownloadStringCompleted += (sender, e) =>
{
var resultString = e.Result;
// do something with downloaded string, do UI interaction on main thread
};
webClient.Encoding = System.Text.Encoding.UTF8;
webClient.DownloadStringAsync (new Uri ("http://some-server.com/file.xml"));
WebClient
이 DownloadFileCompleted
DownloadFileAsync
진 데이터를 검색할 때도 있습니다.
HttpWebRequest
HttpWebRequest
는 더 많은 사용자 지정을 WebClient
제공하므로 더 많은 코드를 사용해야 합니다.
간단한 동기 HttpWebRequest
작업에 대한 코드는 다음과 같습니다.
var request = HttpWebRequest.Create(@"http://some-server.com/file.xml ");
request.ContentType = "text/xml";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Console.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
// do something with downloaded string, do UI interaction on main thread
}
}
웹 서비스 설명서에 예제가 있습니다.
연결
모바일 디바이스는 빠른 Wi-Fi 또는 4G 연결에서 열악한 수신 영역 및 느린 EDGE 데이터 링크에 이르기까지 다양한 네트워크 조건에서 작동합니다. 따라서 원격 서버에 연결하기 전에 네트워크를 사용할 수 있는지 여부와 사용 가능한 네트워크 유형을 검색하는 것이 좋습니다.
이러한 상황에서 모바일 앱이 수행할 수 있는 작업은 다음과 같습니다.
- 네트워크를 사용할 수 없는 경우 사용자에게 조언합니다. 수동으로 사용하지 않도록 설정한 경우(예: 비행기 모드 또는 Wi-Fi를 해제) 그들은 문제를 해결할 수 있습니다.
- 연결이 3G이면 애플리케이션이 다르게 동작할 수 있습니다(예: Apple은 3G를 통해 20Mb보다 큰 앱을 다운로드하는 것을 허용하지 않음). 애플리케이션은 이 정보를 사용하여 대용량 파일을 검색할 때 과도한 다운로드 시간에 대해 사용자에게 경고할 수 있습니다.
- 네트워크를 사용할 수 있더라도 다른 요청을 시작하기 전에 대상 서버와의 연결을 확인하는 것이 좋습니다. 이렇게 하면 앱의 네트워크 작업이 반복적으로 시간 초과되는 것을 방지하고 사용자에게 더 많은 정보 오류 메시지를 표시할 수 있습니다.
WebServices
Xamarin.iOS를 사용하여 REST, SOAP 및 WCF 엔드포인트에 액세스하는 방법에 대한 웹 서비스 작업에 대한 설명서를 참조하세요. 웹 서비스 요청을 직접 작성하고 응답을 구문 분석할 수 있지만 Azure, RestSharp 및 ServiceStack을 포함하여 훨씬 간단하게 만들 수 있는 라이브러리가 있습니다. 기본 WCF 작업도 Xamarin 앱에서 액세스할 수 있습니다.
Azure
Microsoft Azure는 데이터 스토리지 및 동기화, 푸시 알림을 포함하여 모바일 앱에 다양한 서비스를 제공하는 클라우드 플랫폼입니다.
azure.microsoft.com 방문하여 무료로 사용해 보세요.
RestSharp
RestSharp는 웹 서비스에 대한 액세스를 간소화하는 REST 클라이언트를 제공하기 위해 모바일 애플리케이션에 포함할 수 있는 .NET 라이브러리입니다. 데이터를 요청하고 REST 응답을 구문 분석하는 간단한 API를 제공하여 도움이 됩니다. RestSharp는 유용할 수 있습니다.
RestSharp 웹 사이트에는 RestSharp를 사용하여 REST 클라이언트를 구현하는 방법에 대한 설명서가 포함되어 있습니다. RestSharp는 github에서 Xamarin.iOS 및 Xamarin.Android 예제를 제공합니다.
웹 서비스 설명서에는 Xamarin.iOS 코드 조각도 있습니다.
ServiceStack
RestSharp와 달리 ServiceStack은 웹 서비스를 호스트하는 서버 쪽 솔루션과 해당 서비스에 액세스하기 위해 모바일 애플리케이션에서 구현할 수 있는 클라이언트 라이브러리입니다.
ServiceStack 웹 사이트에서는 프로젝트의 목적과 문서 및 코드 샘플에 대한 링크를 설명합니다. 예제에는 웹 서비스의 전체 서버 쪽 구현뿐만 아니라 웹 서비스에 액세스할 수 있는 다양한 클라이언트 쪽 애플리케이션이 포함됩니다.
WCF
Xamarin 도구는 일부 WCF(Windows Communication Foundation) 서비스를 사용하는 데 도움이 될 수 있습니다. 일반적으로 Xamarin은 Silverlight 런타임과 함께 제공되는 WCF의 동일한 클라이언트 쪽 하위 집합을 지원합니다. 여기에는 WCF의 가장 일반적인 인코딩 및 프로토콜 구현, 즉 HTTP 전송 프로토콜 BasicHttpBinding
을 통해 텍스트로 인코딩된 SOAP 메시지가 포함됩니다.
WCF 프레임워크의 크기와 복잡성으로 인해 Xamarin의 클라이언트 하위 집합 도메인에서 지원하는 범위를 벗어나는 현재 및 향후 서비스 구현이 있을 수 있습니다. 또한 WCF 지원을 사용하려면 Windows 환경에서만 사용할 수 있는 도구를 사용하여 프록시를 생성해야 합니다.
스레딩
애플리케이션 응답성은 모바일 애플리케이션에 중요합니다. 사용자는 애플리케이션을 빠르게 로드하고 수행할 것으로 예상합니다. 사용자 입력 수락을 중지하는 '고정' 화면은 애플리케이션이 충돌했음을 나타내기 때문에 네트워크 요청 또는 느린 로컬 작업(예: 파일 압축 풀기)과 같은 장기 실행 차단 호출과 UI 스레드를 연결하지 않는 것이 중요합니다. 특히 시작 프로세스에는 장기 실행 작업이 포함되어서는 안 됩니다. 모든 모바일 플랫폼은 로드하는 데 너무 오래 걸리는 앱을 종료합니다.
즉, 사용자 인터페이스는 빠르게 표시할 수 있는 '진행률 표시기' 또는 '사용할 수 있는' UI 및 백그라운드 작업을 수행하기 위한 비동기 작업을 구현해야 합니다. 백그라운드 작업을 실행하려면 스레드를 사용해야 합니다. 즉, 백그라운드 작업은 주 스레드와 다시 통신하여 진행률을 나타내거나 완료된 시기를 나타내는 방법이 필요합니다.
병렬 작업 라이브러리
병렬 작업 라이브러리를 사용하여 만든 작업은 비동기적으로 실행되어 호출 스레드에서 반환할 수 있으므로 사용자 인터페이스를 차단하지 않고 장기 실행 작업을 트리거하는 데 매우 유용합니다.
간단한 병렬 작업 작업은 다음과 같습니다.
using System.Threading.Tasks;
void MainThreadMethod ()
{
Task.Factory.StartNew (() => wc.DownloadString ("http://...")).ContinueWith (
t => label.Text = t.Result, TaskScheduler.FromCurrentSynchronizationContext()
);
}
키는 TaskScheduler.FromCurrentSynchronizationContext()
해당 스레드에 대한 호출을 다시 마샬링하는 방법으로 메서드를 호출하는 스레드의 SynchronizationContext.Current(실행 중인 MainThreadMethod
기본 스레드)를 다시 사용하는 것입니다. 즉, 메서드가 UI 스레드에서 호출되면 UI 스레드에서 ContinueWith
작업을 다시 실행합니다.
코드가 다른 스레드에서 작업을 시작하는 경우 다음 패턴을 사용하여 UI 스레드에 대한 참조를 만들고 태스크는 여전히 다시 호출할 수 있습니다.
static Context uiContext = TaskScheduler.FromCurrentSynchronizationContext();
UI 스레드에서 호출
병렬 작업 라이브러리를 활용하지 않는 코드의 경우 각 플랫폼에는 작업을 다시 UI 스레드로 마샬링하는 고유한 구문이 있습니다.
- iOS –
owner.BeginInvokeOnMainThread(new NSAction(action))
- Android –
owner.RunOnUiThread(action)
- Xamarin.Forms –
Device.BeginInvokeOnMainThread(action)
- Windows –
Deployment.Current.Dispatcher.BeginInvoke(action)
iOS 및 Android 구문 모두 'context' 클래스를 사용할 수 있어야 합니다. 즉, 코드가 UI 스레드에서 콜백이 필요한 메서드에 이 개체를 전달해야 합니다.
공유 코드에서 UI 스레드를 호출하려면 IDispatchOnUIThread 예제(@follesoe 제공)를 따릅니다. 공유 코드의 인터페이스를 IDispatchOnUIThread
선언하고 프로그래밍한 다음, 다음과 같이 플랫폼별 클래스를 구현합니다.
// program to the interface in shared code
public interface IDispatchOnUIThread {
void Invoke (Action action);
}
// iOS
public class DispatchAdapter : IDispatchOnUIThread {
public readonly NSObject owner;
public DispatchAdapter (NSObject owner) {
this.owner = owner;
}
public void Invoke (Action action) {
owner.BeginInvokeOnMainThread(new NSAction(action));
}
}
// Android
public class DispatchAdapter : IDispatchOnUIThread {
public readonly Activity owner;
public DispatchAdapter (Activity owner) {
this.owner = owner;
}
public void Invoke (Action action) {
owner.RunOnUiThread(action);
}
}
// WP7
public class DispatchAdapter : IDispatchOnUIThread {
public void Invoke (Action action) {
Deployment.Current.Dispatcher.BeginInvoke(action);
}
}
Xamarin.Forms 개발자는 공통 코드(공유 프로젝트 또는 PCL)에서 사용해야 Device.BeginInvokeOnMainThread
합니다.
플랫폼 및 디바이스 기능 및 성능 저하
플랫폼 기능 설명서에는 다양한 기능을 다루는 구체적인 예제가 제공됩니다. 앱이 최대한 작동할 수 없는 경우에도 다양한 기능을 검색하고 애플리케이션을 정상적으로 저하하여 좋은 사용자 환경을 제공하는 방법을 다룹니다.