移動開發多平臺代碼共享

作者: luzj  來源: 博客園  發布時間: 2011-06-08 15:29  閱讀: 5025 次  推薦: 4   原文鏈接   [收藏]  

  做移動開發快一年了,有幸接觸了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* 的相互轉化,并調用了接口:

 
char* strWeatherInfo = getWeather([txtLocation.text UTF8String]);
NSString
*weatherInfo = [NSString stringWithUTF8String:strWeatherInfo];

  最后將數據顯示到界面上即可。

  五、Symbian平臺

  我始終對這個平臺沒啥好感,況且這個平臺的開發本身就是使用C、C++,實現代碼共用很容易的,在此我就不浪費筆墨了,有興趣的自己試試。

  六、總結

  這一技術還是很有使用前景的,比如游戲開發者,可用通過代碼共用,用Open GL 實現一個底層的游戲引擎給各個平臺使用。

  由于時間的關系,代碼中對于異常的處理和一些條件的判斷都沒有去做,有興趣的同學自己完善!

  文章中的Demo已經打包,需要的可以自行下載。

  下載地址:http://u.115.com/file/dn69hko2

  在我的獨立博客還有一些好文章,有興趣的可以去看看:http://luzj.me/share-lib-test-mobil.html

4
0
 
 
 

文章列表

arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()