做移动开发快一年了,有幸接触了WM、Symbian、Android、iPhone等多个平台的开发。往往一个软件需要实现多个平台的版本,对于不同平台进行重新编码是一件很费劲的事情,其实我们可以通过代码共用技术,实现对一些代码的重用。
这一技术的应用,可以带来不少的好处:
1.代码重用:节约开发和维护的时间
2.核心代码的保护:将核心代码编译成库,只将库而不是源代码提供给上层开发人员使用。
3.。。。。。。
本文将实现一个支持这几个平台的天气信息查询软件,软件采用C语言去实现调用WebService接口获取天气信息的功能,并将其编译成各种平台能够调用的库,而UI则采用各个平台各自的语言去实现,最终实现底层代码的共用。
先来看看最终的效果图:



一、底层代码的实现
我们要调用到WebService接口,需要使用网络
而对于不同的平台socket的使用上有细微差异,我通过条件编译的方式,实现对不同平台的兼容。
下面是实现代码:
//对于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;
}
二、Windows Mobile 平台
对于WM平台,为了与底层库相区别,我们就不用VC去实现UI,而使用C#来实现UI。
1.编译dll
Visual C++ – 智能设备 – 由于没有好的dll模板,我们就建一个“MFC智能设备DLL”,

取名ShareLib,然后在生成的项目中删除掉没用的文件(比如预编译啥的),添加我们的底层代码文件Common.h和 Common.c,最终文件结构如图:

然后编译,最终会生成ShareLib.dll文件
2.实现主界面
新建一个C#的智能设备项目,取名ShareLibTest

在Form1.cs文件中,添加对于dll的引用
[DllImport(@"ShareLib.dll",CharSet=CharSet.Unicode) ] public static extern IntPtr getWeather(string cityName);
然后进行界面的设计,最终界面如图:

然后给按钮设置点击事件:
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 )
{
}
代码中关键的一句就是使用Marshal完成从c的char* 转换为C#的string,还有,需要注意字符的编码,否则可能会导致获取的数据乱码。
最终,运行程序就能看到效果了。
三、Android平台
1.编译so
对于Android平台,我们用Java实现UI。
在java中,要调用C/C++,需要使用jni技术
我们先写一个java类JniTest.java
代码如下:
public class JniTest {
public native String getWeather(String cityNmae);
}
然后打开命令提示符:
//输入: javac JniTest.java //然后输入: javah -jni com.luzj.ShareLibTest.JniTest
最终就会产生com_luzj_ShareLibTest_JniTest.h 文件,复制一份,将后者改为.c
打开com_luzj_ShareLibTest_JniTest.c文件,通过#include ”Common.h” 引入底层代码
然后去实现那个getWeather函数,在这个函数中主要完成对java的String和C的char*的相互转换工作,代码如下:
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;
然后编写一个make文件Android.mk,代码如下:
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)
最后,使用NDK-build一下,就会生成Android可用的动态链接库.so文件了
2.实现主界面
没啥好说的,对于开发过Android的人都能做到
然后在Activity中通过代码加载动态链接库
//加载c库
static {
System.loadLibrary("JniTest");
}
最后在要获取天气数据的地方调用JniTest类的中的getWeather方法即可
四、iPhone平台
在iPhone平台,对于UI,使用Obj-C来实现。
在iPhone平台上,我们可以将公用代码编译成静态库然后给程序调用。
1.编译静态库
首先,通过“Cocoa Touch Static Library”创建一个静态库的工程

将我们的底层库添加上去,编译一下,就会生成一个以“.a”为后缀的静态库了
2.实现主界面
拉个按钮到界面上去,给它添加事件
主要代码就下面两行,完成了NSString 与 char* 的相互转化,并调用了接口
最后将数据显示到界面上即可
五、Symbian平台
我始终对这个平台没啥好感,况且这个平台的开发本身就是使用C、C++,实现代码共用很容易的,在此我就不浪费笔墨了,有兴趣的自己试试。
六、总结
这一技术还是很有使用前景的,比如游戏开发者,可用通过代码共用,用Open GL 实现一个底层的游戏引擎给各个平台使用。
由于时间的关系,代码中对于异常的处理和一些条件的判断都没有去做,有兴趣的同学自己完善!
文章中的Demo已经打包,需要的可以自行下载。
下载地址:http://u.115.com/file/dn69hko2
在我的独立博客还有一些好文章,有兴趣的可以去看看:http://luzj.me/share-lib-test-mobil.html
作者:Luzj (luzjcn[at]gmail.com)
出处:http://luzj.me/share-lib-test-mobil.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
下面的六个程序片段主要完成这些事情:
文章来源于我的博客:http://luzj.me/
1. 输出Hello, World
2. 混乱C语言的源代码
下面的所有程序都可以在GCC下编译通过,只有最后一个需要动用C++的编译器g++才能
编程通过。
hello1.c
#define _________ }
#define ________ putchar
#define _______ main
#define _(a) ________(a);
#define ______ _______(){
#define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)
#define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)
#define ____ _(0x72)_(0x6C)_(0x64)_(0x21)
#define _____ __ ___ ____ _________
#include <stdio.h>
_____
hello2.c
#include <stdio.h>
main(){
int x=0,y[14],*z=&y;*(z++)=0x48;*(z++)=y[x++]+0x1D;
*(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;
*(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;
*(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;
*(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;
x=*(--z);while(y[x]!=NULL)putchar(y[x++]);
}
hello3.c
#include <stdio.h>
#define __(a) goto a;
#define ___(a) putchar(a);
#define _(a,b) ___(a) __(b);
main()
{ _:__(t)a:_('r',g)b:_('$',p)
c:_('l',f)d:_(' ',s)e:_('a',s)
f:_('o',q)g:_('l',h)h:_('d',n)
i:_('e',w)j:_('e',x)k:_('\n',z)
l:_('H',l)m:_('X',i)n:_('!',k)
o:_('z',q)p:_('q',b)q:_(',',d)
r:_('i',l)s:_('w',v)t:_('H',j)
u:_('a',a)v:_('o',a)w:_(')',k)
x:_('l',c)y:_('\t',g)z:___(0x0)}
hello4.c
int n[]={0x48,
0x65,0x6C,0x6C,
0x6F,0x2C,0x20,
0x77,0x6F,0x72,
0x6C,0x64,0x21,
0x0A,0x00},*m=n;
main(n){putchar
(*m)!='\0'?main
(m++):exit(n++);}
hello5.c
main(){int i,n[]={(((1<<1)<<(1<<1)<<(1<<
1)<<(1<<(1>>1)))+((1<<1)<<(1<<1))), (((1
<<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(
1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))+ (1
<<(1>>1))),(((1<<1)<<(1<<1)<<(1<<1)<< (1
<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))- ((1
<<1)<<(1<<(1>>1)))),(((1<<1)<<(1<<1)<<(1
<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1
)))-((1<<1)<<(1<<(1>>1)))),(((1<<1)<< (1
<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(
1<<(1>>1)))-(1<<(1>>1))),(((1<<1)<<(1<<1
)<<(1<<1))+((1<<1)<<(1<<1)<<(1<<(1>>1)))
-((1<<1)<<(1<<(1>>1)))),((1<<1)<< (1<<1)
<<(1<<1)),(((1<<1)<<(1<<1)<<(1<<1)<<(1<<
1))-((1<<1)<<(1<<1))-(1<<(1>>1))),(((1<<
1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<< (1
<<1)<<(1<<(1>>1)))-(1<<(1>>1))), (((1<<1
)<<(1<<1)<<(1<<1)<<(1<<1))- ((1<<1)<< (1
<<1)<<(1<<(1>>1)))+(1<<1)), (((1<<1)<< (
1<<1)<<(1<<1)<< (1<<1))-((1<<1)<< (1<<1)
<<(1<<(1>>1)))-((1<<1) <<(1<< (1>>1)))),
(((1<<1)<< (1<<1)<<(1<<1)<< (1<<1))- ((1
<<1)<<(1<<1)<<(1<<1))+((1<<1)<< (1<<(1>>
1)))), (((1<<1)<<(1<<1) <<(1<<1))+(1<<(1
>>1))),(((1<<1)<<(1<<1))+((1<<1)<< (1<<(
1>>1))) + (1<< (1>>1)))}; for(i=(1>>1);i
<(((1<<1) <<(1<<1))+((1 <<1)<< (1<<(1>>1
))) + (1<<1)); i++) printf("%c",n); }
hello6.cpp
下面的程序只能由C++的编译器编译(比如:g++)
#include <stdio.h>
#define _(_) putchar(_);
int main(void){int i = 0;_(
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++i)_(++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++i)_(++++++++++++++
i)_(--++i)_(++++++i)_(------
----------------------------
----------------------------
----------------------------
----------------------------
----------------i)_(--------
----------------i)_(++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++++
++++++++++++++++++++++++++i)
_(----------------i)_(++++++
i)_(------------i)_(--------
--------i)_(----------------
----------------------------
----------------------------
----------------------------
----------------------------
------i)_(------------------
----------------------------
i)return i;}
问题: 目前在Qt开发环境中新建的项目名无法使用中文的.而创建的程序在程"应用程序"文件夹中建立的快捷方式是以项目名来显示的,这样就导致无法使用中文名了?
解决办法:被编译过的项目,在源码的文件夹下会产生*.loc文件(*为你的项目名),打开,修改为以下内容:
CHARACTER_SET UTF8
#ifdef LANGUAGE_SC
#define STRING_r_short_caption "中文名"
#define STRING_r_caption "中文名"
#else
#define STRING_r_short_caption "EnglishName"
#define STRING_r_caption "EnglishName"
#endif
然后保存,要用UTF-8编码来保存.
再在此文件上右键,加上只读属性即可,编译出来的程序在中文的系统上就会显示中文标题了.
iPhone开发笔记(一)
---iPhone开发环境的安装
文章转载自我的博客:http://luzj.me/iphone-setup-and-helloworld.html
今天讲讲iPhone开发环境的安装,目前在Windows上没有一个完善的iPhone开发平台,我们只能借助虚拟机安装Mac系统后在其上进行iPhone的开发,Mac的安装方法网络上介绍文章不少,以下安装过程不少图片来源于网络。
用的系统是 Mac os 10.6.3 ,开发环境是从Apple官网下载的xcode_3.2.4_and_ios_sdk_4.1。
一、安装Mac
首先打开VMWare Workstation 7.1。使用File->New->Virtual Machine创建一个虚拟机,在选择操作系统时选择Other->FreeBSD 64-bit。

创建好虚拟机之后,需要你在刚建立的虚拟机目录下找到一个扩展名为.vmx的文件,用记事本打开,找到 guestOS = "freebsd-64"一行,将引号里的freebsd-64改为darwin10,改完是guestOS = "darwin10",保存修改后的文件。
做了这一步,在这个虚拟机的Options->General选项下就可以看到操作系统版本显示为:MAC OS X Server 10.6,如下图所示。接下来我们就要开始安装了。安装的第一步是用Darwin.iso镜像进行引导。引导完成后插入MAC OS X的DVD光盘。
此时,我们便进入了MAC OS X的安装程序准备阶段:

在屏幕上方的菜单中找到“实用工具”->“磁盘工具”,如图 所示:

对你的虚拟硬盘执行“抹掉”操作,如图所示:

操作完成后关闭“磁盘工具”窗口就可以进行安装了,根据你的电脑配置不同,大概进行几十分钟的安装,你就可以用上苹果操作系统了。

看到下图的界面,表示你的系统已经安装完成,需要重新启动。重新启动前先要去掉加载的苹果镜像,换上Darwin引导镜像。

重启之后,顺利的话你就会看到欢迎界面,进行简单的设置就能进入系统了。

打开光驱,安装VMware Tool

安装好后重启,给虚拟机设置共享盘

设置后就能在Mac下访问主机的文件了
二、安装iPhone SDK
在Mac下去主机共享的文件夹中找到下载下来的xcode_3.2.4_and_ios_sdk_4.1.dmg文件,双击,系统会加载它

桌面会出现一个新的虚拟光驱,打开它,双击里面的mpkg文件,即可进入安装界面

安装过程简单就不抓图了,安装好后打开安装环境。

点击Create a new Xcode project,选择建立Window-based Application,点击Choose,输入项目名就进入了开发环境

在开发环境左边 Rescources 目录中找到 MainWindow.xib,双击,打开窗体编辑器,添加一个label,在上面写上几个字,保存,退出即可。

点击Build and Run 即可启动模拟器了。

此文为学习总结,若有不足之处望指出,谢谢!

