游天堂X游聚社区
标题:
移动开发多平台代码共享
[打印本页]
作者:
响良牙
时间:
2011-8-18 19:40
标题:
移动开发多平台代码共享
做移动开发快一年了,有幸接触了WM、Symbian、Android、iPhone等多个平台的开发。往往一个软件需要实现多个平台的版本,对于不同平台进行重新编码是一件很费劲的事情,其实我们可以通过代码共用技术,实现对一些代码的重用。
这一技术的应用,可用带来不少的好处:
1. 代码重用:节约开发和维护的时间
2. 核心代码的保护:将核心代码编译成库,只将库而不是源代码提供给上层开发人员使用。
3. 。。。。。。
本文将实现一个支持这几个平台的天气信息查询软件,软件采用C语言去实现调用WebService接口获取天气信息的功能,并将其编译成各种平台能够调用的库,而UI则采用各个平台各自的语言去实现,最终实现底层代码的共用。
先来看看最终的效果图:
[attach]63841[/attach]
[attach]63842[/attach]
[attach]63843[/attach]
一、底层代码的实现
我们要调用到WebService接口,需要使用网络
而对于不同的平台socket的使用上有细微差异,我通过条件编译的方式,实现对不同平台的兼容。
下面是实现代码:
[pre]
//
对于VC的DLL,需要导出函数,而其他的则不需要
头文件Common.h
#ifdef _MSC_VER
#define DLLFLAG _declspec(dllexport)
#
else
#define DLLFLAG
""
#endif
//
通过WebService接口获取天气信息
DLLFLAG
char
*
getWeather(
const
char
*
cityName);
源文件Common.c
#include
<
string.h
>
#include
<
Common.h
>
#include
<
stdlib.h
>
//
根据系统加载不同的网络库
#ifdef _MSC_VER
#include
<
winsock2.h
>
#pragma comment(lib,
"
winsock.lib
"
)
#
else
#include
<
sys
/
socket.h
>
#include
<
netinet
/
in.h
>
#include
<
arpa
/
inet.h
>
#include
<
netdb.h
>
#endif
#define BUFSIZ
4096
DLLFLAG
char
*
getWeather(
const
char
*
pCityName)
{
//
对于VC需要初始化socket版本
#ifdef _MSC_VER
WSADATA wsaData;
int
iResult
=
WSAStartup( MAKEWORD(
2
,
2
),
&
wsaData );
#endif
int
sockfd
=
0
;
struct sockaddr_in addr;
char
text[BUFSIZ]
=
""
;
char
header[BUFSIZ]
=
""
;
char
*
content
=
(
char
*
)malloc(BUFSIZ);
sockfd
=
socket(AF_INET, SOCK_STREAM,
0
);
addr.sin_family
=
AF_INET;
//
不同平台填充地址字段的方式不同
#ifdef _MSC_VER
addr.sin_addr.S_un.S_addr
=
inet_addr(
"
222.73.218.218
"
);
#
else
inet_aton(
"
222.73.218.218
"
,
&
addr.sin_addr);
#endif
addr.sin_port
=
htons(
80
);
memset(header,
0
, sizeof(header));
strcat(header,
"
GET /Service.asmx/getWeatherbyCityName?theCityName=
"
);
strcat(header, pCityName);
strcat(header,
"
&theDayFlag=Today HTTP/1.1rn
"
);
strcat(header,
"
Host:
www.ayandy.comrnrn
"
);
connect(sockfd,(struct sockaddr
*
)
&
addr,sizeof(struct sockaddr_in));
send(sockfd, header, strlen(header),
0
);
memset(text,
0
, BUFSIZ);
memset(content,
0
, BUFSIZ);
recv(sockfd, text, BUFSIZ,
0
);
strcat(content, text);
//
while ( recv(sockfd, text, BUFSIZ, 0) > 0)
//
{
//
strcat(content, text);
//
memset(text, 0, BUFSIZ);
//
}
//
不同平台关闭socket的方法不同
#ifdef _MSC_VER
closesocket(sockfd);
WSACleanup();
#
else
shutdown(sockfd, SHUT_RDWR);
#endif
return
content;
}
[/pre]
二、Windows Mobile 平台
对于WM平台,为了与底层库相区别,我们就不用VC去实现UI,而使用C#来实现UI。
1. 编译dll
Visual C++ – 智能设备 – 由于没有好的dll模板,我们就建一个“MFC智能设备DLL”,
[attach]63844[/attach]
取名ShareLib,然后在生成的项目中删除掉没用的文件(比如预编译啥的),添加我们的底层代码文件Common.h和 Common.c,最终文件结构如图:
[attach]63845[/attach]
然后编译,最终会生成ShareLib.dll文件。
2. 实现主界面
新建一个C#的智能设备项目,取名ShareLibTest。
[attach]63846[/attach]
在Form1.cs文件中,添加对于dll的引用:
[pre]
[DllImport(
@"
ShareLib.dll
"
,CharSet
=
CharSet.Unicode) ]
public
static
extern
IntPtr getWeather(
string
cityName);
[/pre]
然后进行界面的设计,最终界面如图:
[attach]63847[/attach]
然后给按钮设置点击事件:
[pre]
try
{
byte
[] paraByte
=
Encoding.UTF8.GetBytes(txtLocation.Text);
IntPtr p
=
getWeather(Encoding.Unicode.GetString(paraByte,
0
, paraByte.Length));
string
newWeather
=
Marshal.PtrToStringUni(p);
byte
[] strByte
=
Encoding.Unicode.GetBytes(newWeather);
newWeather
=
Encoding.UTF8.GetString(strByte,
0
, strByte.Length);
int
a
=
newWeather.IndexOf(
"
/>
"
);
int
b
=
newWeather.IndexOf(
"
http://
"
);
int
aLength
=
"
/>
"
.Length;
newWeather
=
newWeather.Substring(a
+
aLength, b
-
a
-
aLength);
newWeather
=
newWeather.Replace(
""
,
""
).Replace(
""
,
""
);
txtWeatherInfo.Text
=
newWeather;
}
catch
(System.Exception )
{
}
[/pre]
代码中关键的一句就是使用Marshal完成从c的char* 转换为C#的string,还有,需要注意字符的编码,否则可能会导致获取的数据乱码。
最终,运行程序就能看到效果了。
三、Android平台
1. 编译so
对于Android平台,我们用Java实现UI。
在java中,要调用C/C++,需要使用jni技术。
我们先写一个java类JniTest.java。
代码如下:
[pre]
public
class
JniTest {
public
native String getWeather(String cityNmae);
}
[/pre]
然后打开命令提示符:
[pre]
//
输入:
javac JniTest.java
//
然后输入:
javah
-
jni com.luzj.ShareLibTest.JniTest
[/pre]
最终就会产生com_luzj_ShareLibTest_JniTest.h 文件,复制一份,将后者改为.c。
打开com_luzj_ShareLibTest_JniTest.c文件,通过#include ”Common.h” 引入底层代码。
然后去实现那个getWeather函数,在这个函数中主要完成对java的String和C的char*的相互转换工作,代码如下:
[pre]
const
char
*
name
=
(
*
env)
->
GetStringUTFChars(env, cityName,
0
);
const
char
*
cWeatherInfo
=
getWeather( name );
jstring weatherInfo
=
(
*
env)
->
NewStringUTF(env, cWeatherInfo);
(
*
env)
->
ReleaseStringUTFChars(env,cityName,name);
return
weatherInfo;
[/pre]
然后编写一个make文件Android.mk,代码如下:
[pre]
LOCAL_PATH:
=
http:
//
www.cnblogs.com/lib
include $(CLEAR_VARS)
LOCAL_MODULE :
=
JniTest
LOCAL_SRC_FILES :
=
com_luzj_ShareLibTest_JniTest.c Common.c
include $(BUILD_SHARED_LIBRARY)
[/pre]
最后,使用NDK-build一下,就会生成Android可用的动态链接库.so文件了。
2. 实现主界面
没啥好说的,对于开发过Android的人都能做到。
然后在Activity中通过代码加载动态链接库:
[pre]
//
加载c库
static
{
System.loadLibrary(
"
JniTest
"
);
}
[/pre]
最后在要获取天气数据的地方调用JniTest类的中的getWeather方法即可。
四、iPhone平台
在iPhone平台,对于UI,使用Obj-C来实现。
在iPhone平台上,我们可以将公用代码编译成静态库然后给程序调用。
1. 编译静态库
首先,通过“Cocoa Touch Static Library”创建一个静态库的工程:
[attach]63848[/attach]
将我们的底层库添加上去,编译一下,就会生成一个以“.a”为后缀的静态库了。
2. 实现主界面
拉个按钮到界面上去,给它添加事件。
主要代码就下面两行,完成了NSString 与 char* 的相互转化,并调用了接口:
[pre]
char
*
strWeatherInfo
=
getWeather([txtLocation.text UTF8String]);
NSString
*
weatherInfo
=
[NSString stringWithUTF8String:strWeatherInfo];
[/pre]
最后将数据显示到界面上即可。
五、Symbian平台
我始终对这个平台没啥好感,况且这个平台的开发本身就是使用C、C++,实现代码共用很容易的,在此我就不浪费笔墨了,有兴趣的自己试试。
六、总结
这一技术还是很有使用前景的,比如游戏开发者,可用通过代码共用,用Open GL 实现一个底层的游戏引擎给各个平台使用。
由于时间的关系,代码中对于异常的处理和一些条件的判断都没有去做,有兴趣的同学自己完善!
文章中的Demo已经打包,需要的可以自行下载。
下载地址:
http://u.115.com/file/dn69hko2
作者:
怪蜀黍
时间:
2011-9-11 11:14
还有下载地址,楼主真是厚道啊,可惜我不会用,但是看了这么多好文,还是要支持下
欢迎光临 游天堂X游聚社区 (https://bbs.gotvg.com/)
Powered by Discuz! X3.1