您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
通过示例将Object-C与MonoTouch进行对比
 
作者:Merlin 来源:MONO开发者联盟 发布于 2015-03-06
   次浏览      
 

本章将介绍MonoTouch如何将iOS SDK抽象化,从而可以使用C#的原生类进行开发,还介绍如何使用outlet,并将CocoaTouch Delegate模型和C#的事件模型进行比较,讲解如何在C#中使用这两种模式。本章还将介绍Objective-C的内存管理与MonoTouch的垃圾回收机制之间的异同。

下面通过示例来说明上面讲到的一些概念。前面提到的UIActionSheet将会在示例中使用。通过Objective-C与C#的对比,将有助于清楚地了解如何使用MonoTouch开发应用程序。

注意 通常,使用MonoTouch开发应用程序不需要Xcode或Objective-C,这里这样做的目的是作为基础知识辅助说明MonoTouch的设计。如果有兴趣想了解更多的相关技术,推荐阅读Stephen G. Kochan写的《Programming in Objective-C 2.0》。

示例将会如图2-2所示,允许改变应用程序的背景图片。

图2-2 应用程序运行时改变它的背景图片

2.2.1 从Xcode开始编写应用程序

首先使用Xcode创建示例应用程序。在开发时,可以借鉴之前使用MonoDevelop开发应用程序时的步骤,并对比它们之间的不同。打开Xcode并创建一个名为LMT2-1的基于窗口的应用程序。在IB中双击MainWindow.xib文件打开它。如图2-3所示,在IB中将Library窗口中的UIImageView拖到该窗口。UIImageView将用来显示背景图片,因为要在代码中改变背景图片,所以需要为它创建一个outlet。

图2-3 在IB中添加UIImageView

IB中要连接的outlet在Objective-C的头文件中定义,稍后要在Xcode中进行添加。不过,在添加之前,先要在IB中完成界面设计,将一个按钮拖放到图片视图的顶部。

注意 按钮通常与UIBarButtonItem一起放在UIToolbar内。

按钮的作用是打开UIActionSheet,然后在UIActionSheet中切换图片视图中的图片。要将响应按钮触碰动作的处理代码与按钮连接起来,需要在头文件定义一个操作。在Xcode定义好这些之后,返回IB中进行连接。现在,回到Xcode。

在Xcode中,选择AppDelegate的头文件(LMT2_1AppDelegate.h),然后在文件里添加代码清单2-1中的代码。

代码清单2-1 Objective-C头文件中的Outlet和Action

#import <UIKit/UIKit.h> 

@interface LMT2_1AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UIImageView *imageView;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UIImageView *imageView;

-(IBAction) changePicture: (id) sender;

@end

代码中为UIImageView增加了一个称为imageView的实例变量,并使用@property指令声明它是IBOutlet,这样在xib文件中,IB就可以使用这个实例变量来连接图片视图。

注意 IBOutlet只是一个空的宏,不需要为它编写代码,它的主要作用是与IB集成。同样,IBAction也不会有返回值,它只是作为IB的一个提示。

还需要在头文件中定义动作,以便在IB中连接按钮的触碰事件。此外,在括号中定义的可选参数(nonatomic和retain),会让编译器根据内存管理器和线程来生成实际的属性。可以看到,这里确实没有属性,它只是围绕getter和setter方法的“糖衣语法”(syntactic sugar)。完成Xcode的代码后,转到实现文件(LMT2_1AppDelegate.m)添加属性和动作方法的代码。

另一半的Objective-C属性需要使用@synthesize指令,在实现文件添加该指令后,编译器会在头文件中生成属性的定义。稍后在使用MonoTouch实现此过程的时候,会发现MonoTouch更简单、更便捷。现在开始添加属性和方法。

在LMT2_1AppDelegate.m文件中,在Xcode模板已有的窗口语句下添加imageView的Synthesize语句。此外,添加changePicture:方法,并在方法内添加如代码清单2-2中所列出的记录字符串的存根实现。要记住,对编译器来说,IBAction是没有返回值的。在介绍UIActionSheet的时候,还要回到这里的实现操作。现在,保存Xcode中的文件后,切换回IB中的MainWindow.xib。

代码清单2-2 LMT2_AppDelegate.m

#import "LMT2_1AppDelegate.h"  

@implementation

@synthesize window;
@synthesize imageView;


- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch

[window makeKeyAndVisible];

return YES;
}

-(IBAction) changePicture: (id) sender{

NSLog(@"placeholder for UIActionSheet code");
}

- (void)dealloc {
[window release];
[imageView release];
[super dealloc];
}

在IB中,现在要将outlet连接到imageView,这样才可以在AppDelegate类中通过代码改变背景图片。还要设置Target-Action,这样才能在触碰按钮的时候调用changePicture:方法。现在开始完成这些步骤,要注意区分Xcode和Objective-C与MonoTouch之间的不同。

首先,将AppDelegate的imageView的outlet连接到xib中的UIImageView。如图2-4所示,在MainWindow.xib和Connections Inspector中选择AppDelegate,然后将imageView outlet拖到窗口的UIImageView中。

图2-4 将imageView的outlet连接到UIImageView

接着,需要连接的是AppDelegate代码中创建的changePicture:方法。这是前面讲过的Target-Action模式的一个例子。当在MonoTouch中实现这些功能时,会看到在C#中是如何使用动作及如何将代码连接到C#风格的事件的。要将Objective-C的处理代码连接到按钮上的事件,先在MainWindow.xib中选择该按钮,然后在Connections Inspector中将TouchUpInside事件拖到AppDelegate。当松开鼠标按钮,IB会弹出一个包含所有使用IBAction声明的方法菜单。此时这个菜单中只有一个changePicture:方法。选择changePicture,注意在Connections Inspector中,按钮的TouchUpInside事件与目标类AppDelegate中的changePicture:动作建立了连接。保存后,在Xcode中运行应用程序。

应用程序运行后,从Xcode运行调试器控制台(Run→Console)。在触碰应用程序的按钮后,会在控制台看到如图2-5所示的日志信息。

图2-5 调试器控制台显示的changePicture:方法中的日志代码

现在已经通过Objective-C的Target-Action模式连接了事件,下一步要做的是使用MonoDevelop实现同样的功能。

2.2.2 在MonoTouch中实现相同的功能

现在,留意一下MonoTouch是如何使用C#的方式实现事件响应的Target-Action模式的。打开MonoDevelop并创建一个名为LMT2-2的基于iPhone窗口的新工程。在IB中打开MainWindow.xib,添加一个UIImageView和一个按钮,修改按钮上的文本为“Change Image”。现在要为图片视图添加一个outlet以便代码可以访问到它,因此在此立即创建这个outlet。因为是由AppDelegate的代码访问这个outlet,所以要在AppDelegate中添加这个outlet。在Library窗口和IB的标签页内再次选择AppDelegate,在Outlets区域内添加一个名为imageView的outlet,修改它的类型为UIImageView。

注意 MonoTouch中的UIKit版本和基础架构分别包含在命名空间MonoTouch.UIKit和MonoTouch.Foundation中。

在Objective-C中,是在AppDelegate的头文件中添加outlet的,而在MonoTouch中,一旦xib文件中的对象(如UIImageView实例)和outlet之间建立连接,就会通过在局部类中生成的属性将outlet连接到代码。属性用ConnectAttribute进行声明,它的作用与Objective-C中的IBOutlet一样,即与IB集成。

现在,在MainWindow.xib窗口中选择AppDelegate,如之前做过的一样,将imageView的outlet连接到在窗口中的UIImageView。如果在IB中保存文件后,切换到MonoDevelop中查看MainWindow.xib.designer.cs文件,就会看到局部类和刚才提及的属性。现在,连接按钮。

第1章演示了如何使用C#风格的事件来连接UIButton事件。现在可以试着使用与刚才在Objective-C中使用的Target-Aciton模式来达到相同的效果。要记住,目标是AppDelegate,要在AppDelegate中创建动作方法来响应按钮的TouchUpInside事件。利用MonoDevelop与IB集成,可以达到相同的效果。在IB的Library窗口中,选择AppDelegate的Actions标签(在Outlets标签页的右边第一个),用“changePicture:”选择器添加一个动作。然后,选择窗口中的按钮,将Connections Inspector中的Touch Up Inside事件拖到MainWindow.xib中的AppDelegate上进行连接。松开鼠标,弹出一个之前在Xcode和IB中出现过的changePicture:选择器菜单。选择changePicture:选择器,并在IB中保存所有修改。如果回头看看在MainWindow.xib.designer.cs文件中的局部类,就会看到新代码已添加,ExportAttribute声明的作用是将IB中添加的动作处理代码挂接到changePicture选择器。现在添加实现代码(类似占位符的代码,后面将会替换为UIActionSheet)。在Main.cs文件中,添加以下代码到AppDelegate类:

《这里有点要特别说明的地方》

运行应用程序并触碰按钮,将会看到写在MonoDevelop中的Application Output标签页中的文本信息(如图2-6所示)。

图2-6 MonoDevelop中Application Output标签页显示的按钮事件的运行结果

提示 使用C#事件可以避免不必要的动作。就如第1章所看到的,这样做的好处是,可以使用C#风格的事件来实现,从而避免使用动作方法来实现。只需要为按钮创建一个outlet就可通过程序访问注册的事件。MonoTouch支持创建C#事件委托的显式回调方法,以及匿名方法和lambda表达式。

正如所看到的,MonoTouch可以使用与Objective-C相同的模式实现事件处理。此外,MonoTouch提供了更灵活的C#风格的事件以供选择。

现在要做的是为UIActionSheet编码以改变图片。还是先通过Xcode实现,然后再通过MonoTouch实现,以比较两者在实现Objective-C委托模式之间的不同。不过,在实现之前,要先花点时间了解MonoTouch是如何实现AppDelegate和如何处理它的。

2.2.3 AppDelegate实现的比较

在本书前几章的简单示例中,还不能对应用程序进行结构化设计,这将在第3章进行讲述。然而,在开发应用程序中总会用到AppDelegate。在目前的示例中,所有的应用程序代码都是在它内部添加的,实际上它的作用是通过其他类来处理应用程序逻辑。AppDelegate是一个在应用程序的生命周期内用来响应UIApplication各类操作并进行处理的类,这些处理包括应用程序终止、应用程序启动完成、接收系统发出的内存警告等。在Objective-C中,AppDelegate定义为一个名为UIApplicationDelegate的协议。记住Objective-C协议基本类似于C#接口,除了一些方法指定为可选的外。如果打开Xcode中的LMT2_1AppDelegate.h文件,会看到以下代码:

《这里有点要特别说明的地方》

这是Objective-C在相关LMT2_1AppDelegate.m文件中实现LMT2_1AppDelegate类定义的方式,它派生于NSObject(CocoaTouch中其他类的基类,类似于.NET中的Object类)并遵循UIApplicationDelegate协议。这意味着实现将包含协议的所有请求方法以及可选方法。这是在LMT2_AppDelegate.m文件中实现的方法application:didFinishLaunchingWithOptions:,应用程序完成启动后会调用该方法。与MonoTouch进行比较,就会发现AppDelegate是作为一个类实现的。如果作为接口实现,那会工作不了,因为它不能给可选方法附加协议类型,因而它必须实现为一个如代码清单2-3所示的包含各种虚函数的类。这就是在MonoTouch中看到的模式,将Objective-C协议转换为派生于基类的类,并通过重写虚函数插入实现代码。

代码清单2-3 MonoTouch AppDelegate类

[Register("UIApplicationDelegate")]  
[Model()]
public class UIApplicationDelegate : NSObject

{
// Constructors
public UIApplicationDelegate();
public UIApplicationDelegate(NSCoder coder);
public UIApplicationDelegate(NSObjectFlag t);
public UIApplicationDelegate(IntPtr handle);

// Methods
public virtual void FinishedLaunching(UIApplication application);

public virtual bool FinishedLaunching(UIApplication application,
NSDictionary launchOptions);

public virtual void OnActivated(UIApplication application);

public virtual void OnResignActivation(
UIApplication application);

public virtual void HandleOpenURL(UIApplication application,
NSUrl url);

public virtual void ReceiveMemoryWarning(
UIApplication application);

public virtual void WillTerminate(UIApplication application);

public virtual void ApplicationSignificantTimeChange(
UIApplication application);

public virtual void WillChangeStatusBarOrientation(
UIApplication application,
UIInterfaceOrientation newStatusBarOrientation,
double duration);

public virtual void DidChangeStatusBarOrientation(
UIApplication application,
UIInterfaceOrientation oldStatusBarOrientation);

public virtual void WillChangeStatusBarFrame(
UIApplication application, RectangleF newStatusBarFrame);

public virtual void ChangedStatusBarFrame(
UIApplication application, RectangleF oldStatusBarFrame);

public virtual void RegisteredForRemoteNotifications(
UIApplication application, NSData deviceToken);

public virtual void FailedToRegisterForRemoteNotifications(
UIApplication application, NSError error);

public virtual void ReceivedRemoteNotification(
UIApplication application, NSDictionary userInfo);

...
}

注意 所有有ExportAttribute定义的方法,在重写虚函数时,不需要知道实际的选择器或提供属性。

正如前面所看到的,在MonoTouch的实现中,MonoDevelop和IB之间相结合产生的所有代码都结束在一个局部类定义中,这为实现应用程序代码提供了很大的自由度,无须使用工具接口。继承的层次结构可由开发人员在局部类定义中自行设置。在AppDelegate的情况下,MonoDevelop代码模板从UIApplicationDelegate派生出AppDelegate,在Main.cs文件中,代码如下:

public partial class AppDelegate : UIApplicationDelegate

模板还重写了FinishedLaunching方法,这里可以实现之前在Objective-C实现的(就像前面在Xcode应用程序中看到的)application:didFinishLaunchin-gWithOptions:。

注意 在http://tirania.org/tmp/rosetta.html中,可以找到一个非常有用的名为“MonoTouch Rosetta Stone”的文档,它列出了MonoTouch中与Objective-C选择器对应的C#实现。

AppDelegate是CocoaTouch中使用的Delegation模式的一个示例,MonoTouch完全支持。现在要做的是在应用程序中使用UIActionSheet,这是iOS SDK中另一种Delegation例子。

2.2.4 通过Xcode实现UIActionSheet

现在回到Xcode(听起来像电影)创建UIActionSheet并设置不同按钮以便改变imageView中的图片。目前,示例一直是在设计窗口和AppDelegate中完成的,不算复杂。如前所述,这不是太规范的设计,具体会在第3章中讲述。现在需要一个视图来作为UI的容器,一直以来,为了简单起见都直接跳过了,没有讨论。这个视图就是早已存在的imageView,将用来显示ActionSheet。在LMT2_1AppDelegate.m文件中,添加如代码清单2-4所示的代码到changPicture:方法来创建ActionSheet。

代码清单2-4 创建UIActionSheet的代码

-(IBAction) changePicture: (id) sender{  

UIActionSheet *changeImageSheet = [[UIActionSheet alloc]
initWithTitle:@"Change Image"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:NULL
otherButtonTitles:@"Image 1", @"Image 2", NULL];

[changeImageSheet showInView:imageView];
[changeImageSheet release];
}

代码将创建并显示一个ActionSheet,它内部除了包含"Image 1"和"Image 2"两个按钮外,还包含一个"Cancel"(取消)按钮。使用otherButtonTitles定义的按钮的索引分别是0和1,而取消按钮的索引是2。按钮的单击操作将使用索引进行区分,这样的处理方式是Objective-C Delegation(委托)的又一个例子。注意代码中创建ActionSheet时设置的Delegation(委托)属性为self,说明它将引用当前对象,与C#中的this一样。也就是说,传递给ActionSheet的对象就是LMT2_1AppDelegate实例,因为当前类就是这个。因此,LMT2_1AppDelegate需要遵循UIActionSheetDelegate协议。现在设置UIActionSheetDelegate协议,打开对应的头文件并在尖括号内UIApplicationDelegate协议定义的后面加入协议。现在要实现UIActionSheetDelegate协议的带有选择器的actionSheet: clickedButtonAtIndex:方法以便接收UIActionSheet的按钮单击后的回调处理。要完成这个,在@end:前的头文件添加以下代码:

-(void)actionSheet:(UIActionSheet *)actionSheet  
clickedButtonAtIndex:(NSInteger)buttonIndex;

当UIActionSheet的按钮单击后,实现方法将执行并返回单击按钮的索引。处理代码可根据索引改变图片。

在LMT2_1AppDelegate.m文件中添加代码清单2-5所示的代码完成实现。

代码清单2-5 actionSheet:clickedButtonAtIndex:方法的实现代码

- (void)actionSheet:(UIActionSheet *)actionSheet  
clickedButtonAtIndex:(NSInteger)buttonIndex{
switch (buttonIndex) {
case 0:
imageView.image = [UIImage imageNamed: @"image1.jpg"];
break;
case 1:
imageView.image = [UIImage imageNamed: @"image2.jpg"];
break;
case 2:
NSLog(@"cancel");
break;
default:
break;
}
}

代码清单2-5引用了image1.jpg和iamge2.jpg这两个图片。

注意 本书的示例图片和代码可以到作者在Github的空间https://github.com/mikebluestein下载。

如图2-7所示,将图片从Finder拖放到Group & files下的LMT2-1工程的根目录下,Xcode自动将它们打包到应用程序发布包。松开鼠标后,选择Copy Items into Destination Group抯 Folder以便将它们复制到与Xcode工程相同的位置。

图2-7 添加到XCode的Group && Files下的图片文件

在Xcode中生成并运行应用程序,然后单击按钮打开ActionSheet并从中选择一个按钮来改变图片。当选择ActionSheet的按钮时,ActionSheet的委托被调用,从而处理按钮单击的代码也会调用。可以看到,Objective-C中的委托设计模式非常类似于其他语言的回调接口,譬如C#。不过,由于可选方法需要支持协议,所以MonoTouch通过类来实现。如以上看到的,在Objective-C中,AppDelegate的单一实现在示例中要遵循名为UIApplicationDelegate和UIActionSheetDelegate的协议。在MonoTouch中,要处理这个可能会不知所措,无从下手,因而需要使用类来实现,但不允许多重继承。现在回到MonoDevelop并完成实现。

2.2.5 在MonoTouch中实现UIActionSheet

在MonoTouch中,会在构造函数中映射代码清单2-4中Objective-C的initWithTitle: delegate:cancelButtonTitle:destructiveButtonTitle:otherB-uttonTitles:方法。此外,在C#中显示UIActionSheet仍然可以调用ShowInView方法。不过,这时候委托指针指向的不是当前实例,因为它已经是UIApplicationDelegate的子类,而是从UIActionSheetDelegate派生的嵌套类,这样就可以保证委托实现封装在类(AppDelegate)内,以便创建的类实例可以进行委托处理(UIActionSheet)。其实,也不必非得这样嵌入委托类,还可以在这个类的外部创建它。不过,像嵌入委托这样的处理方式在MonoTouch中是经常用到的,而且它表现得很好。根据以上说明,就可实现如代码清单2-6所示的changePicture方法。

代码清单2-6 在MonoTouch中显示UIActionSheet

UIActionSheet _changePictureSheet;  
...

partial void changePicture (MonoTouch.UIKit.UIButton sender){

_changePictureSheet = new UIActionSheet(
"Change Picture", new ChangePictureActionSheetDelegate(this),
"Cancel", null, "Image 1", "Image 2");

_changePictureSheet.ShowInView(imageView);
}

ChangePictureActionSheetDelegate就是刚才讲到的嵌套类。在Objective-C中,使用actionSheet:clickedButtonAtIndex:方法来处理UIActionSheet按钮的单击操作。在MonoTouch中,前面已经提过,要通过重写虚函数来实现。实现方法的定义代码如下:

public virtual void Clicked (UIActionSheet actionSheet, int buttonIndex)

与Objective-C示例一样,实现将使用单击按钮的索引来切换图片视图的图片。在当前版本的MonoTouch中要注意,Cancel(取消)按钮的索引是最后一个索引,而不是Objective-C中的第一个索引。如果需要,也可以通过UIActionSheet的CancelButtonIndex属性来修改Cancel(取消)按钮的索引值。为了简单起见,这里将使用默认值,ChangePictureActionSheetDelegate类的实现代码如代码清单2-7所示。

注意 下一版本的MonoTouch会固定Cancel(取消)按钮的位置。

代码清单2-7 在ChangePictureActionSheetDelegate类实现单击操作

class ChangePictureActionSheetDelegate : UIActionSheetDelegate  
{
AppDelegate _appDel;

public ChangePictureActionSheetDelegate (AppDelegate appDel)
{
_appDel = appDel;
}

public override void Clicked (UIActionSheet actionSheet,
int buttonIndex)
{
switch (buttonIndex) {
case 1:
appDel.imageView.Image = UIImage.FromFile("image1.jpg");
break;
case 2:
_appDel.imageView.Image = UIImage.FromFile("image2.jpg");
break;
}
}
}

现在重复在Xcode中的操作,将图片复制到工程目录下。在Finder中将图片拖到MonoDevelop解决方案树下,然后单击如图2-8所示的对话框中的Copy按钮。在解决方案树中的图片上右击(如果使用的单按钮鼠标,请按下Ctrl键再单击),然后设置为Build Action to Content,这样就可以在MonoDevelop生成应用程序时把图片打包到应用程序。

图2-8 MonoDevelop的复制文件对话框

注意 如果在MonoDevelop中创建文件夹,等同于在磁盘上创建文件夹,这与在Xcode中在磁盘根目录下默认为虚拟目录不同。

最后完成如代码清单2-8所示的Main.cs文件。如果现在生成并运行应用程序,切换图片,那么效果与在Objective-C中看到的一样。

代码清单2-8 Main.cs的最后版本

using System;  
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace LMT22
{
public class Application
{
static void Main (string[] args)
{
UIApplication.Main (args);
}
}

// The name AppDelegate is referenced in the MainWindow.xib file.
public partial class AppDelegate : UIApplicationDelegate
{
UIActionSheet _changePictureSheet;

// This method is invoked when the application has
// loaded its UI and its ready to run
public override bool FinishedLaunching (UIApplication app,
NSDictionary options)
{
window.MakeKeyAndVisible ();

return true;
}

class ChangePictureActionSheetDelegate : UIActionSheetDelegate
{
AppDelegate _appDel;

public ChangePictureActionSheetDelegate (
AppDelegate appDel)
{
_appDel = appDel;
}

public override void Clicked (UIActionSheet actionSheet,
int buttonIndex)
{
switch (buttonIndex) {
case 1:
_appDel.imageView.Image =
UIImage.FromFile ("image1.jpg");
break;
case 2:
_appDel.imageView.Image =
UIImage.FromFile ("image2.jpg");
break;
}
}

}

partial void changePicture (MonoTouch.UIKit.UIButton sender)
{

_changePictureSheet = new UIActionSheet (
"Change Picture",
new ChangePictureActionSheetDelegate (this), "Cancel",
null, "Image 1", "Image 2");

_changePictureSheet.ShowInView (imageView);
}

// This method is required in iPhoneOS 3.0
public override void OnActivated (UIApplication application)
{
}
}
}

现在,已经了解了如何在MonoTouch中使用C#开发应用程序,并与在Xcode中使用Objective-C开发iPhone应用程序的核心设计模式做了比较。下面要了解的是MonoTouch如何创建这些并让它们工作。

   
次浏览       
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...