Introduction
Quite recently, Xamarin announced the release of a new version of its toolkit for cross-platform development of mobile applications, but there are still no sane articles in Russian on this topic. A small
overview appeared on Habré, not related to coding, there were a couple of
attempts to tell about it in a little more detail, but it didn’t go beyond the process of creating the Hello World application. It's a pity. This time we will try to correct this unfortunate misunderstanding.
Beginning of work
The installer Xamarin installs a plug-in to Visual Studio, which allows you to develop applications for popular mobile platforms in a familiar developer environment. A separate development environment Xamarin Studio is also being installed, which, apparently, is a modified version of MonoDevelop. I’m used to working in Visual Studio so the examples in this article will be shown using this particular development environment.
After installation, project templates for mobile applications for Android and iOS are added to Visual Studio (it supports the creation of both specialized applications for iPad and iPhone, universal applications, and applications using OpenGL). To create applications for iOS in Visual Studio will have to, however, pay. This functionality is available either in trial mode or in the business version of the toolkit, and it costs $ 999 per year.
After creating the project, we get the same API for each platform that we have in native development, but the syntax will be in C #, besides, it is possible to use the basic types of the .NET Framework, syntax sugar and other .NET buns.
')
Android development
After creating the Android project, we get a set of files in which there is a class of the main window and a set of resource files. After long work in Eclipse, the name of the folders in PascalCase is a bit annoying, but you can get used to it rather quickly. There are also differences in working with resource files. In order for the embedded window designer to understand resource files with layouts, their extension has been changed to .AXML instead of the usual .XML in Eclipse. This is pretty annoying, especially if you draw layouts in Eclipse, and then transfer them to Visual Studio in case Eclipse's window designer is more like it.
The built-in window designer seemed to me inconvenient. Slow, often brings down the whole IDE, I still do not understand how it is easy to switch between the XML-type and UI. Here we can definitely say that it was written by strangers for predators. I decided for myself that in Eclipse it is more convenient for me, more familiar, and more useful information is placed on the laptop screen in Eclipse. Maybe someone designer Visual Studio and enjoy it more, the taste and color markers are different.
Activities
C # code for Mono for Android is very similar to Java code.
namespace XamarinDemo.Android { [Activity(Label = "XamarinDemo.Android", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { int count = 1; protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); Button button = FindViewById<Button>(Resource.Id.MyButton); button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); }; } } }
Differences that are immediately visible:
- To register an activity, you do not need to register anything in the manifest. To do this, use the summary [Activity]
- In order to make the activity start, it is necessary for the [Activity] annotation to specify the parameter MainLauncher. This will be equivalent to setting action = android.intent.action.MAIN and category = android.intent.category.LAUNCHER in the manifest.
- The main methods of the life cycle activity (and indeed all methods) have names in PascalCase.
Controls and Event Handling
To get references to controls, as in the Java version, use the
FindViewById()
method. Very pleased with the presence of a generic version of this method, which returns an object of the desired type and allows you to get rid of c-Cast'ov.
Instead of listeners, or rather, in addition to them, delegates are used to connect event handlers. You can also use listeners, but this is, firstly, not the .NET way, and secondly, it requires writing more code, even compared to Java. In addition, several delegates can be connected:
Button button = FindViewById<Button>(Resource.Id.MyButton); button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); }; button.Click += (o, e) => { Toast.MakeText(this, string.Format("{0} clicks!", count++), ToastLength.Long).Show(); };
Not with all event handlers in the form of delegates things are rosy. One of the projects revealed a problem with the
View.ViewTreeObserver.GlobalLayout
event, which did not disable handler delegates when using the - = operator. Yes, in general, did not turn off. I had to use
IOnGlobalLayoutListener
.
Using Java libraries in a .NET project
Mono for Android has the ability to use existing JAR files in a C # application. For these purposes, a special project type is provided: Java Binding Library.
To use the JAR library, you must:
- Create a Java Binding Library project in the solution.
- Put the required JAR file in the Jars folder (AndroidManifest.xml should not be in the JAR library, because the presence of this file can have a number of unpleasant consequences. You can learn more about this here ).
- Specify a build action for a JAR file as EmbeddedJar.
- In the Android application project add the project created by the Java Binding Library to References.
After that, you can use classes from a JAR file. Be aware that package names in C # and in Java source code may differ slightly. For example, the com.example.androiddemolib package from Java code will be renamed to Com.Example.Androiddemolib (that is, it will be converted to PascalCase).
A good guide to using Java libraries can be found
here .
Work with databases
Mono for Android uses the
Mono.Data.Sqlite
namespace
Mono.Data.Sqlite
(used to access SQLite databases) and
System.Data.SqlClient
(for accessing Microsoft SQL Server) to work with databases. The classes of the
System.Data.SqlClient
namespace are available only in the Business edition of the development tools. You can also use wrapper classes over native Java API for Android and third-party development, such as sqlite-net, in which an asynchronous API is available.
Using the available API is quite simple. Everything is very similar to desktop development:
namespace XamarinDemo.Android { [Activity(Label = "XamarinDemo.Android", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { SqliteConnection GetConnection(string path) { SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder(); if (!File.Exists(path)) { FileInfo info = new FileInfo(path); if (!Directory.Exists(info.Directory.FullName)) { Directory.CreateDirectory(info.Directory.FullName); } SqliteConnection.CreateFile(path); } builder.DataSource = path; return new SqliteConnection(builder.ToString()); } protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); try { string path = GetDatabasePath("xamarindemo.sqlite").Path; SqliteConnection connection = GetConnection(path); connection.Open(); SqliteCommand command = connection.CreateCommand(); command.CommandType = CommandType.Text; command.CommandText = "CREATE TABLE IF NOT EXISTS DemoTable(" + "id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL" + ", name VARCHAR(32))"; command.ExecuteNonQuery(); connection.Close(); } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.ToString()); } } } }
IOS development
Controls and Event Handlers
Creating controls in code is no different from analogous work in Objective-C. Event handlers can be hung in the same way as in Android - with the help of delegates. You can also add handlers as in Objective-C through selectors.
namespace XamarinDemo.iOS { public class MyViewController : UIViewController { UIButton button; … public MyViewController() { } public override void ViewDidLoad() { base.ViewDidLoad(); … button = UIButton.FromType(UIButtonType.RoundedRect); … button.AddTarget(this, new Selector("ButtonTouchInside"), UIControlEvent.TouchUpInside); … button.TouchUpInside += (object sender, EventArgs e) => { button.SetTitle(String.Format( "clicked {0} times", numClicks++), UIControlState.Normal); }; … View.AddSubview(button); } [Export("ButtonTouchInside")] void OnButtonTouchInside() { Console.WriteLine("Hello!"); } } }
Using Native Libraries
In Xamarin iOS, as well as for Android, the ability to use native libraries is available. For these purposes, there is a special type of project - the iOS Binding Project, to which you can add static libraries, after which they will be linked together with the main project.
In general, to use a native library in a C # project, you need to do the following:
- Create a static library project in Xcode.
- If you plan to debug a C # project in the simulator, then in the static library project settings you need to add the i386 architecture to the Valid Architectures list.
- Write the necessary logic (let's say we have one exported class):
@interface iOSDemoClass : NSObject - (NSInteger) addData: (NSInteger) value1 andOtherValue: (NSInteger) value2; @end … @implementation iOSDemoClass -(NSInteger) addData: (NSInteger) value1 andOtherValue: (NSInteger) value2 { return value1 + value2; } @end
- Close project in Xcode
- Build a separate static library for the necessary architectures (for example, this way):
xcodebuild -project iOSDemoLib.xcodeproj \ -target iOSDemoLib -sdk iphonesimulator \ -configuration Release clean build xcodebuild -project iOSDemoLib.xcodeproj \ -target iOSDemoLib -sdk iphoneos -arch armv7 \ -configuration Release clean build
- Make a universal library for several platforms from libraries, resulting in the previous step:
lipo -create -output ../iOSDemoLib.Binding/libiOSDemoLib.a ../libiOSDemoLib-i386.a ../libiOSDemoLib-iphone-armv7.a
- Create an iOS Binding Project in a C # solution and add a universal static library there. After that, the file [libname] .linkwith.cs will be automatically created with linking rules that look something like this:
[assembly: LinkWith ("libiOSDemoLib.a", LinkTarget.Simulator | LinkTarget.ArmV7, ForceLoad = true, Frameworks = "Foundation")]
- In the ApiDefinition.cs file (created automatically), specify the rules for binding native classes to .NET types (note that the complex method names must contain colons in places where method parameters must be in Objective-C. That is, Parameters will be two colons):
namespace iOSDemoLib.Binding { [BaseType (typeof(NSObject))] interface iOSDemoClass { [Export("addData:andOtherValue:")] int AddDataAndOtherValue(int value1, int value2); } }
- In the application project in which you plan to use the library, add the iOS Binding project to References.
- Collect, run, be happy.
In general, it is very good and accessible about binding native libraries written
here .
Work with databases
To work with databases in iOS, the same namespaces are used as in Android. The API, respectively, is the same. A small difference can be in the details. For example, to get the path to the SQLite database file on Android, there is a special API call:
string path = GetDatabasePath("xamarindemo.sqlite").Path;
In iOS, you need to use the standard .NET tools:
string path = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Personal), "xamarindemo.sqlite");
Remote debugging of iOS applications from Visual Studio
In Xamarin 2.0, you can debug mobile apps for iOS right from Visual Studio. In order to make this possible, you need to have Macs with XCode, iOS SDK and Xamarin Studio installed on the local network. You do not need to make any additional settings; it’s enough when you open an iOS project in Visual Studio to select the required build server from the list of available ones.
Unfortunately, this approach did not work with a virtual machine running on the same computer as Visual Studio. Although everything works fine with a regular Mac on a local network. I could not find any reasons or explanations for this, I try to communicate with the developers about this.
It is also not very clear how to organize UI testing and application health check from Visual Studio. The simulator runs on a Mac, it seems that VNC is indispensable here.
Cross Platform Class Libraries (Portable Class Libraries)
Xamarin provide the ability to create libraries that can be used (in the form of code or ready-made assemblies) for several platforms at once. Such libraries are called Portable Class Library (PCL).
For the development of such libraries, a special truncated version of the .NET Runtime is used and this is a wonderful tool for the developer, the importance of which is difficult to overestimate, but here, too, everything is not so simple. By default, Visual Studio cannot specify Android and iOS as supported platforms for the PCL project. But this does not mean that Visual Studio immediately becomes useless in this regard.
The problem can be solved by creating XML files in the folder
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile104\SupportedFrameworks
for x64 systems or in the folder
C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile104\SupportedFrameworks
for x86 systems.
Android file
MonoAndroid, Version = v1.6 + .xml <?xml version="1.0" encoding="utf-8"?> <Framework DisplayName="Mono for Android" Identifier="MonoAndroid" Profile="*" MinimumVersion="1.6" MaximumVersion="*" />
iOS
VSMonoTouch file
, Version = v1.0 + .xml <?xml version="1.0" encoding="utf-8"?> <Framework DisplayName="VS MonoTouch" Identifier=".NETFramework" Profile="*" MinimumVersion="1.0" MaximumVersion="1.0" />
After creating these two files and restarting Visual Studio, you can specify Android and iOS as supported platforms for new PCL projects or add these platforms to existing projects via Properties -> Library -> Target Framework.
But this is not all the nuances.
If you want support for Android and iOS in a PCL project, then you will have to abandon support for Xbox 360 and Windows Phone 7 (but you can support Windows Phone 7.5+). For Xbox, you will have to create another project and add files from the existing PCL project as links. Or, as an option, leave all Microsoft platforms (including Xbox 360) in one PCL project, and only iOS and Android in the other.
There is still such a problem that a PCL project added from Visual Studio for Windows will not participate in the solution build if you open it in Xamarin Studio under OS X. The project will be inactive and a message will be displayed
(not built in active configuration) . This problem is solved by removing the project from the solution and adding it again.
New language features and support for C # 5
In the development for Android, it is important to transfer work with files, databases, network, etc., that is, with long-term operations, into a separate stream. C # 5 provides special capabilities for implementing asynchronous operations, namely
async / await . Unfortunately, in the current version of Mono for Android and MonoTouch these features are not available. Because of this, many quite interesting libraries cannot be used in the form that is provided for them. For example, to work with the asynchronous API of the sqlite-net library, you have to do several tricks with your ears. The good news is that these features
should become available in a few months with the transition from Xamarin to C # 5.
Additional components and libraries
Xamarin, in addition, in fact, sales of software development tools, opened a
store selling third-party components, many of which really greatly simplify the life of the developer. The store has both paid and free libraries and themes. The usefulness, for example, of free
Xamarin.Auth ,
Xamarin.Social and
Xamarin.Mobile is difficult to overestimate. It is possible to publish your own components in this store.
Unpleasant moments
Of the problem points, the most noticeable are:
- Terrible brakes when debugging Android applications, when on a fairly powerful machine with a Core i7 and 8 GB RAM when stopped at a breakpoint Visual Studio freezes for 10-15 seconds. This can be repeated at each step of the debugger.
- Absence in the list of devices available for debugging applications, some devices connected to a computer (which, however, are visible through adb devices and on which Eclipse perfectly launches applications)
- Arbitrary debugger crashes and the need to restart the application due to this.
- Lack of articles and training materials (yes, there are some materials on the official site, but they do not cover all popular tasks)
- The presence of bugs that you learn suddenly! after chatting on the forum. Although at the same official forum, the developers quite cheerfully answer questions and generally well done.
- The price for the Business edition is very biting. In addition, a license for a year, and not forever (the situation is somewhat brightened up by the availability of discounts when purchasing a license for several platforms at once).
Pleasant moments
- When you purchase a license for several platforms at once or for several jobs at once, a discount is provided.
- With the release of the new version, in which there will be support for async \ await, development will indeed become easier and it will be possible to use a bunch of useful libraries without changing their code.
findings
The Xamarin toolkit does yes, it really works and if you plan to develop several applications a year or the planned profit from the application being developed is more than $ 2k, then the Xamarin SDK can clearly make your life easier and save you from duplicate work on the code for each platform.
On the other hand, for the Indy-developer, the price of $ 1k for each platform, for example, seems to me excessive because There are many nuances that you need to know and / or feel for yourself before starting development, there are bugs that are not known in which mode they will be fixed and it is not known how much their presence can slow down the development of your particular project.
A little cool ssylochek!
UPD: Updating from
northicewind about the cost of the license:
The license is perpetual, but includes one year of free updates. I wrote to them in support for explanations. Here is their answer
Your license is perpetual. You have to purchase a renewal every year to get new updates.