Windows Shell编程-第四章.文件的本质

第四章 文件的本质

         以前,所有文件和目录都有一个确定的属性集:时间,日期,尺寸,以及表示‘只读的’,‘隐藏的,‘存档的’,或‘系统的’状态标志。然而,Windos95(及后来的WindowsNT4.0)出现使这些概念产生了改变,其中最重要的‘文件’变得更加广泛。现在,文件可以是任何Shell部件对象—不一定必须是文件系统的部件。

         文件的精确定义是,任何作为Shell命名空间部件的对象称之为文件对象。注意,在定义中所说的‘命名空间’,它不是C++的关键字。‘Shell 命名空间’所指的是实际组成Shell的所有命名项的集合。它们都被显示在探测器的树观察中。

         并不是所有文件都是文件系统中的一个实体,比如‘打印机’和‘我的计算机’。一个包含子文件对象的文件对象成为文件夹对象,文件和目录是最普通的文件对象。

         所有这些变化作为微软完全面向对象的操作系统实现的第一步,已经融入到Windows9xWindowsNT中。

         一个文件对象可以有多少属性呢?回答是,它是一个集合,它完全包含MS-DOS下一个文件的所有属性以及几个由Windows95Windows NT外壳的图形本质要求的属性。Shell API提供了一个复合函数和相当丰富的功能来探索给定文件对象的特征,它可以是一个普通文件,一个目录,甚或一个系统文件夹,一个象打印机或拨号连接那样的系统对象。这个函数就是SHGetFileInfo()。

         在这一章中,主要目标是研究这个函数的原形。对于特定的文件对象,重点是:

                   怎样获取类型名

                   怎样获取探测器图标的Handle

                   怎样获取可执行文件的目标平台

                   怎样读出属性,以确定在探测器下对文件对象有哪些事情是可以做的,哪些事情是不能做的。

对SHGetFileInfo()函数所获得信息的多样性,你会感到奇怪。曾记得,有一个读者询问我,怎样确定一个给定的.EXE文件是16位的还是32位的(不用映射EXE的头结构)。我的答案是SHGetFileInfo()。几天以后,他回来再次问我,怎样获取驱动器的图标,我再次告诉他,使用SHGetFileInfo()。这最终的结果告诉我们仔细研究SHGetFileInfo()函数是理解Shell文件对象的最好方法。

 

SHGetFileInfo()函数的功能

         同以往一样,我们从函数的原形开始,它在shellapi.h中定义。这个函数有五个变量,定义如下:

DWORD SHGetFileInfo(    LPCTSTR pszPath,

DWORD dwFileAttributes,

SHFILEINFO FAR* psfi,

UINT cbFileInfo,

UINT uFlags);

基本上讲,SHGetFileInfo()函数提供关于文件系统对象的信息。如前面解释的,这个对象可以是文件,文件夹,目录或驱动器根。DWORD的返回是指可能有相当多的返回状态,这与uFlags变量的设置有关。简单地说,使用这个函数,你可以期望:

    确定可执行文件的目标平台(Win32,Win16,MS-DOS)

    获取各种有特色的文件图标(小的,大的,有关联重叠的,选中的,打开的)

    读出其它显示属性,如文件类型(显示在探测器类型列上的简短描述)和显示名(出现在名字列上)

读出任何其它属性,可以是文件特有的,如,是否可以拷贝,移动,删除或重命名,是否它可以形成一个快捷方式,它是否有子文件夹,是否是共享的,是拖拽目标,或有附加的属性页,等等。

 

SHGetFileInfo()函数的工作原理

       为了正确地理解函数具有的功能,使用所有可能的方法强制调用这个函数是十分必要的。首先,让我们查看一下他所要求的变量:

变量名

描述

pszPath

一个包含要取得信息的文件相对或绝对路径的缓冲。它可以处理长或短文件名。

dwFileAttributes

资料上说,这个参数仅用于uFlags中包含SHGFI_USEFILEATTRIBUTES标志的情况。如此,它应该是文件属性的组合:存档,只读,目录,系统等。

Psfi

指向一个接收数据的SHFILEINFO结构的指针。

cbFileInfo

简单地给出上项结构的尺寸。

uFlags

函数的核心变量,通过所有可能的标志,你就能驾驭函数的行为和实际地得到信息。

 

SHFILEINFO结构定义如下:

typedef struct _SHFILEINFO

{

HICON hIcon;

int iIcon;

DWORD dwAttributes;

char szDisplayName[MAX_PATH];

char szTypeName[80];

} SHFILEINFO;

此外,这个结构总是用于返回数据到调用程序,并且从不需要初始化。唯一可以包含信息来影响函数行为的是dwAttributes成员,在后面将进一步给出解释。显然,驾驭SHGetFileInfo()函数各种行为的所有兴趣都集中在对uFlags变量值的设置上。绝大多数情况下,信息经由psfi缓冲返回,但也有些情况,回应可以有效地包含在函数的DWORD返回之中。

 

指定输入文件

         返回文件信息的函数首先要求操作文件的名字,pszPath参数就是用于这个目的。然而,有一些观念是需要澄清的。其中之一是,它可以是路径名(正象所期望的),或是一个PIDL,这在第二章中讨论过了。

         如果想要传递一个PIDL,而不是普通的路径名,你就应该设置SHGFI_PIDL标志到uFlags变量中。反之也是如此:如果设置了SHGFI_PIDL,则pszPath就必须指向一个ITEMIDLIST结构(即一个PIDL)。当然,pszPath也可以是文件夹名或驱动器名,此时,你需要把一个 ‘/’留在路径名的最后。即,你应该指定‘c:/’而不是‘c:’以避免错误地返回了驱动器信息。

 

SHGetFileInfo()中使用通配符

         资料中并没有给出关于在SHGetFileInfo()函数中使用通配符的的任何解释,因此,你可能认为通配符不能识别。然而,我发现,如果传递一个带有通配符的串,然后提供至少一个文件匹配这个模式,函数照样正确工作。下图显示了一个简单程序的输出结果,这个程序将在后面详细讨论:

                 

这个程序让我们选择一个文件名或路径名,然后返回它的信息。它返回一个图标,显示名,类型名和所有其它属性的列表。另,你也可以询问程序以确定可执行文件的类型。Exe文件类型复选框抑制所有其它的选择。‘返回码’标签显示函数的返回码(或它的文字描述),通过选中‘接受任何文件名’复选框,可以强迫函数接受任何东西作为输入文件。

         上面显示,程序使用e:/mssdk/doc/misc/*.txt路径名,你可以看到程序的响应:图标和类型名是正确的(在我的PC上,文本文件的描述是‘Text Document’)。奇怪,尽管指定了通配符,我们还是获得了非空的文件属性和显示名。图标和类型名可以从文件的扩展名中获得,但是,同样的情况对显示名和属性是不行的—显然那些信息是相对于一个特殊文件的。

         为了检测所发生的事情,我们再使用不同的路径调用函数:e:/mssdk/doc/misc/g*.*。象所看到的,在扩展名中和文件名中都有通配符。对话框的结果如下所示:

                  

正如显示所见,我们获得了与前面相同的信息文件,这明显说明,如果传递通配符,SHGetFileInfo()函数取第一个匹配这个串的文件,并对它进行操作。如果没有匹配这个模式的文件,这个函数什么也不做,直接返回0。另一个我们需要检测的情况是传递一个由‘**’结尾的路径名。如图所示,函数返回相关文件夹的信息:

                  

还要继续检测吗?先暂停一会,让我们来仔细地查看一下函数的输出。在‘显示(Display)’字段,你可以看到一个点(),就象在老DOS下目录列表一样。这个结果印证了我前面所说的:SHGetFileInfo()函数操作在名字匹配于这个模式的第一个文件对象上。事实上,如果你试着使用*.*来枚举一个文件夹的内容,作为匹配的串,所获得的第一个项是点(.)。如果还不信,选择测试下面的代码:

WIN32_FIND_DATA wff;

FindFirstFile("*.*", &wff);

Msg(wff.cFileName);

总的来说,即使这个特征没有写进资料,你也可以在函数中使用通配符如果:

         指定一个至少匹配一个文件的模式串

         知道函数停止在第一个找到的文件上

这可能是SHGetFileInfo()函数的内在代码的某个地方保持了一个WIN32_FIND_DATA结构所致。它由底层文件信息所填写,因此,用在这里就一点也不奇怪了。附带地,这个结构还涉及到另一个Shell函数SHGetDataFromIDList(),他也返回文件对象的信息,这将在后面章节中进行表述。

 

文件的显示名

         查看上面的截图,并在你自己的机器上运行这个程序,你就会注意到它返回的显示名稍微有些不同。在我的机器上,显示名是由文件名加扩展名组成,但是在你的机器上可能只看到文件名。这依赖于探测器的‘观察|文件夹选项’对话框的设置,在此,你可以选择‘隐藏已知类型的文件扩展名’。

         这里,‘已知文件类型’是指一个注册的文件类型。我们在第十四章中讨论怎样注册文件类型。现在,知道它就是一个Shell知道怎样处理的文档类型就足够了。如果你双击一个已知类型的文件,偶然地这个资料将由知道怎样处理它的程序打开。要编程取得这个设置,你需要使用SHGetSettings()函数。我们将在下一章讲述。

 

示例程序

         前面,我们已经看到了这个用于测试的示例程序。它是一个基于对话框的应用程序。这次我给它取的工程(project)名为FileInfo。示例的操作部分围绕SHGetFileInfo()函数展开,作为一个通用查询执行器,它查询给定文件或文件夹的状态和属性。下面是程序界面:

  

   

正如所见,用户界面由一个编辑框和相关的按钮组成,使你能选择一个文件。不幸地是,这种风格不能选择目录,如果你想传递文件夹名,就必须手动键入。复选检查框允许选择想要加到调用中的标志,如果选中了EXE类型框,所有其它复选框都被禁止。这是因为SHGetFileInfo()函数要求单独指定可执行类型标志。文件图标绘制在一个静态控件上,属性被解析并转换为描述串。

大多数代码都在OnOK()方法中,当用户单击‘Go’按钮后执行这段代码。要成功地编译这段代码,记住包含 #include "resource.h"语句,并保存对话框控件的IDs,<shlobj.h>中声明了SHGetFileInfo()的原形:

void OnOK(HWND hDlg)

{

TCHAR szFile[MAX_PATH] = {0};

TCHAR szBuf[1024] = {0};

// 取得文件名

GetDlgItemText(hDlg, IDC_FILENAME, szFile, MAX_PATH);

/

// 收集标志

//

DWORD dwFileAttributes = 0;

UINT uFlags = 0;

if(IsDlgButtonChecked(hDlg, IDC_FILEICON))

uFlags |= SHGFI_ICON;

if(IsDlgButtonChecked(hDlg, IDC_DISPLAYNAME))

uFlags |= SHGFI_DISPLAYNAME;

if(IsDlgButtonChecked(hDlg, IDC_TYPENAME))

uFlags |= SHGFI_TYPENAME;

if(IsDlgButtonChecked(hDlg, IDC_OTHER))

uFlags |= SHGFI_ATTRIBUTES;

if(IsDlgButtonChecked(hDlg, IDC_WILDCARD))

uFlags |= SHGFI_USEFILEATTRIBUTES;

if(IsDlgButtonChecked(hDlg, IDC_EXETYPE))

uFlags = SHGFI_EXETYPE;

/

// 调用函数

//

SHFILEINFO sfi;

ZeroMemory(&sfi, sizeof(SHFILEINFO));

DWORD dwRC = SHGetFileInfo(

szFile, dwFileAttributes, &sfi, sizeof(SHFILEINFO), uFlags);

/

// 处理界面显示

//

wsprintf(szBuf, "%d", dwRC);

SetDlgItemText(hDlg, IDC_RETCODE, szBuf);

wsprintf(szBuf, "Icon Index: %d", sfi.iIcon);

SetDlgItemText(hDlg, IDC_ICONINDEX, szBuf);

SetDlgItemText(hDlg, IDC_DISPLAY, sfi.szDisplayName);

SetDlgItemText(hDlg, IDC_TYPE, sfi.szTypeName);

/

// Parse 解析和显示属性

//

DWORD dwAttrib = sfi.dwAttributes;

lstrcpy(szBuf, "");

if(dwAttrib != 0)

{

if(dwAttrib & SFGAO_CANCOPY)

lstrcat(szBuf, "Copy, ");

if(dwAttrib & SFGAO_CANMOVE)

lstrcat(szBuf, "Move, ");

if(dwAttrib & SFGAO_CANDELETE)

lstrcat(szBuf, "Delete, ");

if(dwAttrib & SFGAO_CANRENAME)

lstrcat(szBuf, "Rename, ");

if(dwAttrib & SFGAO_CANLINK)

lstrcat(szBuf, "Link, ");

if(dwAttrib & SFGAO_HASPROPSHEET)

lstrcat(szBuf, "PropSheet, ");

if(dwAttrib & SFGAO_GHOSTED)

lstrcat(szBuf, "Ghosted, ");

if(dwAttrib & SFGAO_SHARE)

lstrcat(szBuf, "Shared, ");

if(dwAttrib & SFGAO_HASSUBFOLDER)

lstrcat(szBuf, "SubFolders, ");

if(dwAttrib & SFGAO_REMOVABLE)

lstrcat(szBuf, "On removable media, ");

if(dwAttrib & SFGAO_FOLDER)

lstrcat(szBuf, "Folder, ");

lstrcat(szBuf, "and more!");

}

SetDlgItemText(hDlg, IDC_ATTRIB, szBuf);

/

// 显示图标

//

HICON hIcon = sfi.hIcon;

SendDlgItemMessage(hDlg, IDI_ICON, STM_SETICON,

reinterpret_cast<WPARAM>(hIcon), 0);

}

这段代码足以产生你所看到的函数行为,但是我们还是要逐步加入更多的功能。If 开始是两个最主要块。它做了些什么并不是马上就清楚的。在这章剩余的部分,我们将主要讨论这两个代码段。这一段唯一要实现的是处理浏览(…)按钮。这需要在APP_DlgProc()函数和OnBrowse()函数中添加分枝语句开关,代码如下:

void OnBrowse(HWND hDlg)

{

TCHAR szFile[MAX_PATH] = {0};

OPENFILENAME ofn;

ZeroMemory(&ofn, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.lpstrFilter = "All files/0*.*/0";

ofn.nMaxFile = MAX_PATH;

ofn.lpstrFile = szFile;

if(GetOpenFileName(&ofn))

SetDlgItemText(hDlg, IDC_FILENAME, ofn.lpstrFile);

}

为了使用通用对话框(此处是Open对话框),需要连接comdlg32.lib库和添加#include <commdlg.h>语句。下面图象显示了这个程序的典型输出,它被要求提供关于Favorites文件夹的图标,类型,显示名和属性:

           

注意,此时我们是通过物理名而不是PIDL引用文件夹的。在我的PC上,目录是C:/WINDOWS/Favorites

,这并没有关系,即使我所寻找的文件在网络驱动器上—如果这样,详细资料也会传送到SHGetFileInfo()

 

函数的标志

         显然uFlags是SHGetFileInfo()函数的绝对中心。它可以用下列值的几乎任何组合构成,有一些组合在上面的代码中已经看到了:

 

代码

描述

SHGFI_ICON

0x0100

将文件的HICON类型的图标Handle存储到结构SHFILEINFO的hIcon成员中。

SHGFI_DISPLAYNAME

0x0200

将指向文件显示名串的指针存储到结构SHFILEINFO的szDisplayName成员中。

SHGFI_TYPENAME

0x0400

将指向文件类型串的指针存储到结构SHFILEINFO的szTypeName成员中。

SHGFI_ATTRIBUTES

0x0800

将DWORD类型的给定文件所有要恢的属性值存储到SHFILEINFO结构的dwAttributes成员中。

SHGFI_ICONLOCATION

0x1000

将指向包含了Shell正在用于指定对象的图标的文件名串的指针存储到SHFILEINFO结构的szDisplayName中。因此,这个标志不能和SHGFI_DISPLAYNAME标志一起使用。奇怪的是,它似乎仅在指定文件夹时才能工作,指定文件名则总是返回空。

SHGFI_EXETYPE

0x2000

引起函数返回一个表示可执行文件二进制格式和它的目标平台的值。

SHGFI_SYSICONINDEX

0x4000

引起函数返回一个包含图标的系统图像列表Handle。图标的索引存储在SHFILEINFO 结构的iIcon字段中。

 

通过使用这个测试程序,我们发现一个有趣的现象。似乎在SHGFI_ICON和SHGFI_ATTRIBUTES之间存在一种关系:前者暗含了后者,即,当指定SHGFI_ICON时,SHFILEINFO结构的dwAttributes成员总是被填写,无论是否指定了SHGFI_ATTRIBUTES。

    所有上面的标志都告诉函数为程序员执行某种特有的任务。还有其它一些标志,但是它们不是主要的,其中有一些可以修饰上表标志指定的操作:

 

代码

描述

SHGFI_LARGEICON

0x00000

引起函数返回文件的大图标。

SHGFI_SMALLICON

0x00001

引起函数返回文件的小图标。

SHGFI_OPENICON

0x00002

对于文件夹,这个设置引起函数返回它打开时显示的图标。

SHGFI_SHELLICONSIZE

0x00004

引起函数返回具有一定尺寸的图标,这个图标尺寸在显示控制板的‘外观’标签中设置。

SHGFI_SELECTED

0x10000

返回的图标是一个文件选中时显示的图标(与高亮颜色混合的)。

SHGFI_LINKOVERLAY

0x08000

返回的图标是一个文件作为快捷方式显示的图标。

 

这个表中的标志影响到SHGFI_ICON,并且仅在与它相关的时候起作用。就象你可以看到的那样,它能够使你获得非常有个性的图标。

         另一个修饰前述标志的是SHGFI_ATTR_SPECIFIED标志,它作用于SHGFI_ATTRIBUTES标志。也就是说SHFILEINFO结构的dwAttributes字段已经由调用者想要SHGetFileInfo()函数返回的属性初始化了。换句话说,如果dwAttributes设置了特殊的标志,比如SFGAO_SHARE,则在操作的文件上函数必须检查这个标志(仅仅是这个标志)。默认情况下,dwAttributes 包含0xFFFFFFFF,就是说需要检查所有属性。关于文件的属性,在后面我们将进一步说明。

         对于完整的标志列表,到现在为止正好还有两个没有讲到:SHGFI_PIDL和SHGFI_USEFILEATTRIBUTES。在下一节中我们集中讨论这两个标志,以及修饰SHGFI_ICON的标志。

如果有一种方法返回数据,则你可以同时指定几个标志。也就是说,你可以一起请求图标,显示名和类型名,但是不能同时有大图标和小图标,因为它们通过同一个缓冲区返回。

 

获取给定文件的类型信息

    如果想要知道系统用于给定文档种类的图标和类型名,你就必须借助于通配符。反之,你可以采用SHGetFileInfo()函数的特性,这是有很好的资料说明的。

    由在uFlags参数中设置SHGFI_USEFILEATTRIBUTES标志,可以使函数认为在pszPath参数中传递的文件是存在的,此时,它可以获得扩展名,并且搜索注册表来得到图标和类型名信息。这确实是有趣的特征,它允许你查询给定文件族类的图标,仅需要简单地指定*.ext就可以了。

    当然,如果使用了SHGFI_USEFILEATTRIBUTES标志,就不能再有其他标志如SHGFI_EXETYPE,SHGFI_ATTRIBUTES或SHGFI_PIDL标志了,因为它们都是特指文件存在的标志。

    其实,在上述过程中,最不可理解的就是这个标志的名称,为什么使用SHGFI_USEFILEATTRIBUTES名呢?这个名字和资料似乎暗示了它与SHGetFileInfo()函数的dwFileAttributes变量之间有某种联系:函数的行为就象pszPath指定名的文件存在一样,并且属性被设置到dwFileAttributes中。然而,这里文件属性的作用似乎被弱化了。不管wFileAttributes的值如何,上面所写的程序总能很好地运行。

    为了查看这个标志的活动,在上面应用中,选中‘接受任何文件名’复选框,并输入*.htm到‘文件名’编辑框。下面是一个辅助函数可以独立的获得任何文件类型的类型名和图标:

HICON GetFileTypeIcon(LPCTSTR szFileType, LPTSTR szTypeName)

{

SHFILEINFO sfi;

ZeroMemory(&sfi, sizeof(SHFILEINFO));

SHGetFileInfo(szFileType, 0, &sfi, sizeof(SHFILEINFO),

SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_TYPENAME);

lstrcpy(szTypeName, sfi.szTypeName);

return sfi.hIcon;

}

 

Shell图标尺寸

         SHGFI_SHELLICONSIZE标志迫使函数返回具有‘Shell图标尺寸’值指定尺寸的大图标,‘Shell图标尺寸’在下面的注册表路径上:

              

HKEY_CURRENT_USER/Control Panel/desktop/WindowMetrics

这些值由控制板显示小程序通过选择外观标签设置。他影响到整个桌面和文件夹内的大图标尺寸:

                   

         如果这个键不存在,或没有指定SHGFI_SHELLICONSIZE标志,则由SHGetFileInfo()函数接受到的图标尺寸遵循默认窗口设置,为32x32像素尺寸。每当你改变‘Shell图标尺寸’键时,探测器都刷新其内部的图标缓存,它仅是由SHGFI_SYSICONINDEX返回的系统图标列表。要获得所返回的实际图标尺寸,应该使用ImageList_GetIconSize()函数。

 

使用PIDL

         SHGFI_PIDL标志简单地通知系统所传递的项如果代表了文件名,则实际是一个PIDL,因此需要特殊处理。例如,下面代码说明怎样获得‘我的计算机’文件夹的图标:

LPITEMIDLIST pidl;

SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl);

DWORD dwRC = SHGetFileInfo(reinterpret_cast<LPCTSTR>(pidl),

dwFileAttributes, &sfi, sizeof(SHFILEINFO), uFlags | SHGFI_PIDL);

‘我的计算机’是一个特殊文件夹,它并不映射到计算机磁盘上的物理目录。相反它是一个由命名空间扩展编码支持的虚拟文件夹。因为这样的文件夹并没有匹配的路径名,因此我们需要用其他的方法在SHGetFileInfo()函数中标识它,显然是PIDL。

    从版本4.71之后,Shell API定义了SHGetSpecialFolderLocation()函数,它使用一个符号标识特殊文件夹,并且返回对应的PIDL。对‘我的计算机’,其符号是CSIDL_DRIVES。在早期的Shell版本中取得特殊文件夹的PIDL也是可能的,但是操作很复杂。可是,如果我们想要获得不是文件系统的对象的PIDL,或不是特殊文件夹的PIDL,这实际上确实是一种必须由我们自己处理的代码。这样的代码在第五章中给出,在那里我们将写出遍历任何文件夹内容的客户例程。

    如果使用上面那三行代码调用SHGetFileInfo(),输出的结果应该是:

               

 

获取文件属性

         你可以从给定的文件对象中返回一个很长的属性列表,很多都在OnOK()实现中的第二个 if 块中出现。这些使用SHGetFileInfo()函数所能获得的属性与使用IShellFolder接口的GetAttributesOf()方法返回的属性是一样的。换句话说,SHGetFileInfo()函数,在这种情况下,封装了IShellFolder接口。你所能读到的属性全部都定义在shlobj.h头文件中。这里就是那些最有可能涉及到的属性的列表:

 

属性

描述

SFGAO_CANCOPY

文件对象可以通过拖拽或剪裁板进行拷贝

SFGAO_CANDELETE

可以通过Shell删除文件对象

SFGAO_CANLINK

文件对象可以建立快捷方式

SFGAO_CANMOVE

文件对象可以通过拖拽或剪裁板移动

SFGAO_CANRENAME

文件对象可以通过Shell重命名

SFGAO_HASPROPSHEET

文件对象至少有一张树形表单

SFGAO_GHOSTED

问津对象使用精灵图标显示(一般是隐藏文件)

SFGAO_LINK

这个文件对象是一个快捷方式

SFGAO_READONLY

这个文件对象是只读的

SFGAO_SHARE

指定的文件夹是共享的

SFGAO_HASSUBFOLDER

指定的文件夹至少有一个自文件夹

SFGAO_COMPRESSED

文件对象驻留于压缩驱动器上

SFGAO_FILESYSTEM

文件对象是文件系统得部件,不是虚拟文件夹。也就是说它是一个存在的物理对象(驱动器,目录或文件)

SFGAO_FOLDER

指定的对象是一个文件夹

SFGAO_REMOVABLE

文件对象驻留于可移动介质上(典型地软盘)

 

给出这个表之后,很容易写出函数来测试这些条件。例如,怎样才能知道一个给定的目录是否是共享的。

确定这个问题的最简单的方法是检查SHGetFileInfo()函数返回结构SHFILEINFO的dwAttributes字段相对SHGAO_SHARE位的内容:

BOOL IsDirectoryShared(LPCTSTR szDirName)

{

SHFILEINFO sfi;

ZeroMemory(&sfi, sizeof(SHFILEINFO));

SHGetFileInfo(szDirName, 0, &sfi, sizeof(SHFILEINFO), SHGFI_ATTRIBUTES);

return(sfi.dwAttributes & SFGAO_SHARE);

}

对于共享的给定文件夹,可能要求函数返回具有‘手捧’样式的图标: ,此后,还要求SHGetFileInfo()返回文件夹被‘选中’的图标,这些要求都不是牵强的。不幸地是这个函数并不支持这些特性,没有解决问题的办法了吗!下面说明解决这个问题需要做的工作。

 

建立‘手捧’文件夹图标

         ‘手捧’图标是shell32.dll的第29个图标(0为第一个时,是第28个):

                            

  

无需使用设备关联,XORAND屏蔽等操作,我们可以充分利用Windows95 通用控件:图像列表的功能。

         图像列表表示一个图像(图标和Bitmap)的集合,它以一种非常特殊而有效的方法驻留在内存中:图像以单独的Bitmap形式并排存储,一个Bitmap包含了所有组成图像的要素。可以把它看作图像带或一盘电影胶片。图像列表的基本约束是所有图像都有相同的尺寸,因而,允许系统通过索引快速而容易地访问图像。图像列表一般应用于有大量小图片需要管理的场合,而且,很多Windows95WindowsNT通用控件(列表观察和树观察等)要求通过图像列表提供图标。

         从编程的角度讲,图像列表是不可视控件,有它自己的消息和风格集,且有一个特殊的Handle(HIMAGELIST)。图像列表有一个非常丰富的编程接口,以及管理列表 (抽取,添加,拷贝)操作的函数,用以支持拖拽和绘制。更多关于图像列表的信息,可以参考Platform SDK | User Interface

Services | Shell and Common Controls | Image Lists.

         关于图像列表我们感兴趣的是它内建对图标与小Bitmap图像组合和重叠操作的支持。探测器本身使用图像列表来显示一定类型文件对象的复合图像,例如,快捷方式和共享文件夹:首先取得基本图标,然后,在必要时,以下述方式处理它:

         混合以高亮色(选中状态)

         混合以灰色(精灵状态或隐藏文件状态)

         与另外的图标重叠,如连接或‘手捧’

如果你想要做的全部就是简单地产生输出,则ImageList_SetOverlayImage()函数就是做这个操作的函数。它与ImageList_Draw()函数一道工作,组合给定设备关联的两个图标。下面是例子:

HICON hiFolder;

HICON hiHand;

// 装入图标

// hiFolder = ...;

// hiHand = ...;

// 取得要绘制的设备DC

HDC hdc = GetDC(GetFocus());

// 建立图像列表

HIMAGELIST himl = ImageList_Create(32, 32, ILC_MASK, 1, 0);

// 添加图标

ImageList_AddIcon(himl, hiFolder); // Icon index of 0

ImageList_AddIcon(himl, hiHand); // Icon index of 1

// 图标1(手捧图标)是要重叠的,设置为一号屏蔽

ImageList_SetOverlayImage(himl, 1, 1);

// 图标0(文件夹)必须由一号屏蔽重叠

ImageList_Draw(himl, 0, hdc, 0, 0, INDEXTOOVERLAYMASK(1));

// 释放图标

DestroyIcon(hiFolder);

DestroyIcon(hiHand);

// 清理和退出

ReleaseDC(GetFocus(), hdc);

ImageList_Destroy(himl);

这个源码段采用了几个图像列表的API函数。特别是ImageList_Create(),给出具有指定尺寸(32x32)图像的新列表标识。最终操作完成后,使用ImageList_Destroy()函数销毁它。与名字的意义一样ImageList_AddIcon()函数添加图标到指定的图像列表。这些函数以及其他函数在VC++在线资料中均有说明。

         在上面的代码中是调用ImageList_SetOverlayImage()函数实现图像列表中第二个图标(索引1,‘手捧’图标)作为重叠修饰#1,与索引为0的图标(即文件夹图标)在hdc指定的关联设备上重叠。注意,图标与修饰图标的索引是不同的—前者为0,后者为1。

         尽管这段代码可以很好地运行并且也能完成所要求的操作,然而,如果能返回新图标的标识到调用者就更好了,因此,我们需要寻找象ImageList_Draw()样的函数,建立一个可以由HICON handle 标识的图标,然后返回这个handle给调用者。事实上,我们并不需要做太多的工作,ImageList_Merge()函数正好有能满足我们需要的功能。

HIMAGELIST ImageList_Merge(HIMAGELIST himl1,

int i1,

HIMAGELIST himl2,

int i2,

int dx,

int dy);

这个函数从两个图像列表中分别取得两个图像(可以是同一个列表),用第二个落在第一个上的绘制方式合并图像。新图像存储在新的图像列表中,它是由在提交的两个图像上使用OR操作获得结果图像。资料上并没有完全清楚地说明dx,dy 参数的作用,但是有一些试验解释说它们表示重叠图像绘制在第一个图像上的相对位置。这个偏移从左上角像素开始计算,然而在绝大多数情况下,它们应该设置为0

         下面是函数GetSharedFolderIcon()的源码,它携带一个HICON,和返回一个新图标Handle,这是一个出示图像与‘手捧’图像重叠的图标。

HICON GetSharedFolderIcon(HICON hiFolder)

{

HICON hiShared;

HICON hiHand;

// 取得‘手捧’图标

ExtractIconEx("shell32.dll", 28, &hiHand, NULL, 1);

// 建立一个用于合并文件夹图标与手捧图标的图像列表

HIMAGELIST himl = ImageList_Create(32, 32, ILC_MASK, 1, 0);

// 添加图标到列表

ImageList_AddIcon(himl, hiFolder);

ImageList_AddIcon(himl, hiHand);

// 合并图标到新的图像列表

HIMAGELIST himlNew = ImageList_Merge(himl, 0, himl, 1, 0, 0);

// 抽取新图像列表中的图标

hiShared = ImageList_ExtractIcon(0, himlNew, 0);

// 释放‘手捧’图标,并不释放‘文件夹’图标

// 因为它是从调用者传递来的.

DestroyIcon(hiHand);

// 清理图像列表,并退出

ImageList_Destroy(himl);

ImageList_Destroy(himlNew);

return hiShared;

}

这个函数接收一个调用者要求建立共享标志的图标,所以,必须做的第一件事就是取得‘手捧’图标,它存储在shell32.dll中的第28个索引处。使用ExtractIconEx()函数抽取图标,这不是惟一可用的方法(ExtractIcon()也能做的很好),但是它有更多灵活的选择:可以同时抽取多种尺寸的多个图标。

ExtractIconEx("shell32.dll", 28, &hiHand, NULL, 1);

这一行指令仅仅从shell32.dll中装入第29个大尺寸图标(记住,索引从0开始)。我们既不想要小图标也不想要多个图标。关于ExtractIconEx()函数更多的细节参见VC++ 在线帮助资料

         对这两个图标,我们建立一个图像列表,并添加这两个要组合的图标到列表中。然后合并图标,存储结果到另一个新列表中,这个列表仅有一个图标—ImageList_Merge()函数的语法要求你通过图像列表和索引对来标识图标。调用了ImageList_Merge()之后,得到了一个新的,可以从其中抽取合成图标的图像列表标识。现在来修改OnOK()代码,如果SHGetFileInfo()函数操作的对象是一个共享对象,则这个新函数就被调用。

/

// 显示图标

//

HICON hIcon;

if(dwAttrib & SFGAO_SHARE)

hIcon = GetSharedFolderIcon(sfi.hIcon);

else

hIcon = sfi.hIcon;

SendDlgItemMessage(hDlg, IDI_ICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);

下面显示了函数执行的结果:

                    

 

可执行文件的二进制格式

         SHGetFileInfo()函数另一个特征是它能够返回可执行文件的二进制格式。通过设置正确的标志,你就可以知道一个给定.exe是32位还是16位模块,即使它仅需要最小的Windows平台。需要这个功能的典型情况是:

在编写一个系统范围的例程,分析窗口进程,或扫描文件时,你可能想要说明建立进程或窗口的程序是16位的还是32位的。

         编程检测是否你的客户已经把工具从16位版本升级到32位版本

         编写底层工具检测系统和文件

         实现进程内通讯,这也需要可执行程序的类型知识。

如果是这些情况,惟一的方法就是了解Windows(或许还包括DOS)的可执行文件的二进制格式,并且手动查看二进制代码以查找它们的标识。幸运地是SHGetFileInfo()函数从必须读二进制码的工作中拯救了我们。为了确定给定程序设计在那个Windows平台时代,你只需要指定SHGFI_EXETYPE标志就可以了。注意,这个标志不能与任何其它标志进行组合,否则不能正常执行。

         返回可执行文件的格式信息有几种情况,你必须分析函数的返回码来推断结果。SHGetFileInfo()返回DWORD值,此时低字表示可执行文件的签名,下面表中给出解释:

 

文件签名

Hex

意义

PE

0x4550

Win32可执行格式,由微软所有32位操作系统采用。

NE

0x454E

Windows 3.x新的可执行格式,典型地16位窗口程序

MZ

0x5A4D

DOS 可执行格式,如果查询.com  bat也返回这个值。

 

对应的Hex码实际是文件签名列的字符码。例如 0x50 对应 P 0x45 对应 E 等。

         高位字的两个字节包含了运行要求的最小操作系统版本号,如果你的目标只是要知道是否给定模块是16位的还是32位的,这个信息就不是确实必须的了,但是你会发现,对于老的Windows32程序,它是0x030A,而对于所有其它32位平台,它是0x0400。惟一的例外是程序指定的目标平台是WindowsNT3.5。此时这个值小于0x0400,即使它是32位程序—在这种情况下,这个值是0x0350。另一种可能是高位字为零,这说明,你查看了一个Win32控制台应用程序。

         所以,当SHGetFileInfo()给出你想要知道的关于文件的所有信息时,可以看到它的编程接口还有相当大的改进余地。例如,给定一个文件名,检测它是否为32位还是16位或是DOS程序的过程还相当复杂。调用这个函数仅仅做了一半的工作,你必须检查结果来确定需要做些什么。

         下面通过实现涉及‘Exe类型’框被选中的代码来结束这个讨论。这需要在FileInfo.cpp的前头定义三个不同类型文件的常量,然后用上述行为测试SHGetFileInfo()函数的返回值。然后用测试结果修改这个应用的输出:

// 常量

const int PE_SIGN = 0x4550;

const int NE_SIGN = 0x454E;

const int MZ_SIGN = 0x5A4D;

        

/

// 涉及到界面UI

//

if(uFlags == SHGFI_EXETYPE)

{

if(dwRC == 0)

lstrcpy(szBuf, "Not an executable file.");

else

lstrcpy(szBuf, "");

if(LOWORD(dwRC) == PE_SIGN)

{

lstrcat(szBuf, "32-bit");

if(HIWORD(dwRC))

lstrcat(szBuf, " Windows executable");

else

lstrcat(szBuf, " Console executable");

}

else if(LOWORD(dwRC) == NE_SIGN)

lstrcat(szBuf, "16-bit executable");

else if(LOWORD(dwRC) == MZ_SIGN)

lstrcat(szBuf, "DOS executable");

}

else

wsprintf(szBuf, "%d", dwRC);

下面图像说明查询Explorer.exe所发生的情况:

                   

奇怪的是,SHGetFileInfo()函数并不认为DLL 或VxD 是可执行文件,也不返回二进制格式。因此没有办法知道DLL的二进制格式。这就是说上述解释工作仅仅是针对独立可执行文件(具有.EXE扩展)的。事实上这个函数对屏幕保护文件,无论有否.scr扩展,都失败。这可能是一个Bug。

 

SHGetFileInfo()函数的返回值

         如果函数返回0,则某个地方发生了错误。在大多数情况下,是因为传递了不合理的文件名或PIDL,或指定了矛盾的标志组合。与前两个相比,后面一个更有可能。

         除非指定的标志告诉它做指定的操作,如果每一个操作都顺利完成,这个函数返回1。一个例外是,当SHGFI_EXETYPE标志设置的时候,就象前一节所讨论的。另一个使返回码包含更多意义的情况是SHGFI_SYSICONINDEX标志被设置。此时,函数返回一个系统图像列表Handle,它包含了指定文件或文件夹的图标。

         有趣的是SHGetFileInfo()函数甚至可以用于成功地返回关联与CD-ROM的图标。对于其它驱动器而言这个几乎总是标准的图标的结果是由探测器依据autorun.inf文件的内容显示的。有一个函数能够正确地返回这个图标,对于编程而言是一个极大的帮助,无论你是否需要这个图标。

 

小结

         SHGetFileInfo()并没有任何明显的Bug,但是他遭受到资料残缺不全的危害。如果你花费一点时间来研究资料里有什么和测试所有可能的标志组合,你可能偶然地会发现你所需要的东西,关键点在于—好的资料必须明显地说明函数能做什么和不能做什么。至少有三个问题需要SHGetFileInfo()函数来回答,但是发现这些问题远不是容易解决的。

    为了修补这些漏洞,我们在这一章讨论了:

                   怎样取得相关于给定文件或文件夹的各种图标

                   怎样知道可执行文件(EXE)的二进制格式

                   怎样确定给定文件或文件夹可能具有的系统属性

                   怎样使用图像列表合并两个图标,不用XOR修饰与设备关联操作


引用:http://blog.csdn.net/chchzh/article/details/2246694


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
新手编程导论 ———— A Programming Introduction For Beginners By Minlearn @ http://www.actyou.com.cn/ 设计才是真正的编程! 对类型的设计才是设计! 面向对象并非一切? 无论你以为上述观点是惊天大秘或不过尔尔,你都需要这本书! -------------------------------------------------------------------------------------------------------------- Todo: 整合过长的目录 完善前二十页 -------------------------------------------------------------------------------------------------------------- 目 录 第一部分 9 前 言 9 By Chenyi 9 By Minlearn 10 导 读 14 任何语言都是有门槛的 14 什么是语言级和语言外要学习的(数据结构与代码结构) 15 什么是语言级要学习的 17 编程学习方法 18 计算机学生专业课程本质讲解 18 用C++开发要学到什么程度 20 本书目录安排 21 第二部分 基础:导论 25 第1章 系统 25 1.1 何谓PC 25 1.2 图灵机与冯氏架构 26 1.3计算机能干什么 27 1.4 内存地址 28 1.5 分段和分页以及保护模式 30 1.7 操作系统 31 1.6 并发与协程 33 1.6 CPU与异常 33 1.7 所谓堆栈 34 1.8 真正的保护模式 36 1.9 异常与流转 38 1.10 最小,最完美的系统 39 1.11 操作系统与语言的关系 41 1.12 虚拟机与语言 41 1.13 虚拟机与语言 42 1.14 调试器与汇编器 43 1.15 平台之GUI 45 1.16 界面的本质应该是命令行功能支持下的配置描述文件 45 1.17 命令行下编程实践 46 第2章 语言 47 2.1 真正的计算模型 47 2.2 开发模型与语言模型 49 2.3 正规表达式与有限自动机 53 2.4 联系编译原理学语言 56 2.6 如何理解运行时 59 2.7 运行时环境 60 2.7 运行时 60 6.3 语言的类型系统 60 2.8 编译与解释 62 2.9 运行期与编译期 62 2.9 编译与运行期分开的本质与抽象 63 2.10 脚本语言 63 2.11 灵活性与安全性 65 2.12 二进制指令与循环 66 2.13 所谓函数 67 2.14 所谓流程 68 2.15 为什么需要数据类型和数据结构 68 2.16 数据类型和数据结构是二种不一样的东西 69 2.17 为什么需要变量这些东东 69 2.18 面向类型化的设计和面向无类型泛化的设计-OO不是银弹 70 第3章 语言之争 71 3.1 学编程之初,语言之争 71 3.2 语言与应用与人(1) 72 3.2 语言与应用与人(2) 73 3.3 C与Ruby 74 3.4 你为什么需要Ruby 75 3.5 C++还是Ruby 76 3.6 C++与Java 76 3.7 .NET与JVM 77 3.8 你为什么需要Ruby 78 3.9 语言功能上的差别 79 3.10 C与C++之争 80 3.11 我为什么选择C而不是C++及其它语言 81 3.12 类VB,DELPHI类RAD语言分析 82 第4章 语言最小内核(C) 83 4.1 C与C++是二种不同的语言 83 4.2 C的数组,指针与字符串 84 4.3 C的输入与输出流 84 4.4 C的类型系统与表达式 85 4.5 二进制指令看循环 85 4.6 所谓指针:当指针用于设计居多时 86 4.7 指针成就的C语言 86 4.8 指针是语言的一种抽象机制 88 4.9 学C千万不能走入的一个误区(其实JAVA比C难) 88 4.10 C抽象惯用法 90 4.11 C的抽象范式之OOP 91 4.12 C的观点:底层不需要直接抽象 93 4.13 指针:间接操作者 94 4.14 真正的typedef 95 4.15 真正的指针类型 95 4.16 真正的函数指针 97 4.17 真正的句柄 97 4.18 真正的循环 98 4.19 真正的static 98 4.20 真正的数组索引 99 4.21 类型和屏看原理 100 4.22 位操作与多维数组指针与元素 101 4.23 变量与VOID 102 第5章 抽象 102 5.1 人与软件 103 5.2 软件活动的特点 103 5.2 抽象与接口 104 5.3 过度抽象 105 5.3 OO为什么不是银弹 - 过度抽象与抽象偏差 106 5.4 真正的设计与编码 107 5.5 真正的构件库 109 5.6 大逻辑与小逻辑 112 5.7 什么是范式 112 第6章 抽象之数据结构 113 6.1 所谓数据结构 113 6.2 算法+数据结构的本质 115 6.4 算法不是设计 115 6.5 函数增长与算法复杂性分析 115 6.6 数据结构初步引象(1) 116 6.7 数据结构初步引象(2) 117 6.8 数据结构初步引象(3) 118 6.9 数据结构初步引象(4) 119 6.10 ordered与sorted 119 6.11 数据结构与抽象 119 6.12 真正的逻辑数据结构只有二种 120 6.12 树与图初步引象 121 6.13 树初步引象 122 6.14 B减树 123 6.15 图初步引象 124 6.16 树的平衡与旋转 125 6.17 完全与满 129 6.18 多路234树与红黑树的导出 129 6.19 快速排序思想 130 6.20 数据结构之数组 131 6.21 数据结构的抽象名字 132 6.22 真正的ADT 133 6.23 Vector的观点 135 6.24 真正的数据结构 136 6.25 堆栈与队列 138 6.26 真正的递归 140 6.27 树与单链表,图 145 6.28 树 146 6.29 真正的散列表 148 6.30 算法设计方法 148 第7章 抽象之高级语法机制 149 7.1 真正的OO解 149 7.2真正的对象 151 7.3真正的继承 152 7.4真正的OO 152 7.5真正的OOP 154 7.6 真正的构造函数 155 7.7 真正的私有,保护和公有 156 7.8 真正的重载与复写 156 7.9 C++的元编程 156 7.10 模板的意义在于编译前端的设计 157 7.11 C++的元编程和泛型编程 159 7.12 元编程和函数式编程 159 7.13 C++的模板编译技术本质 161 7.14 OO的缺点 161 7.15 模板的继承 162 7.16 模板的偏特化 162 7.17 真正的策略 162 7.18 为设计产生代码 164 7.19 真正的metaprogramming 165 7.20 元编程技术 166 第8章 抽象之设计和领域逻辑 167 8.1 大设计 167 8.1 什么是设计 167 8.2 编程能力,代码控制能力,复用与接口,轮子发明与使用 170 8.3 OO,模板,设计模式与设计 171 8.4 设计能力和程序员能力模型 172 8.4 自上而下设计和自下而上设计 173 8.5 大中型软件和复用与逻辑达成 177 8.6 通用设计与专门设计 178 8.7 具象与抽象 178 8.7 架构与应用 179 8.8 应用与设计 179 8.9 与软件有关的哲学 联系 180 8.10 与软工有关的哲学 唯物主义 180 8.11 真正的设计模式 182 8.12 设计模式与数据结构 182 8.12 设计模式之基础 183 8.12 真正的开闭原则 183 8.13 真正的通米特原则 184 8.14 真正的好莱钨原则 185 8.15 真正的策略模式 185 8.16 真正的观察者模式 185 8.17 真正的装饰模式 186 8.18 真正的单例模式 186 8.19 真正的迭代器模式 186 8.20 真正的工厂模式 187 8.21 真正的门面模式 187 8.22 真正的命令模式 188 8.23 真正的模板方法模式 188 8.24 真正的适配器模式 188 8.25 业务与逻辑分开 189 8.26 架构不是功能的要求,但却是工程的要求 189 8.27 你需不需要一个库 190 8.28 可复用与可移殖的区别 190 8.28 再谈可复用 193 8.29 真正的可复用 193 8.30 你能理解XP编程吗 194 8.31 构件与接口,软工 195 8.32 设计方法论 196 8.33 真正的interface 198 8.34 真正的对接口进行编程 200 8.35 实践方法之极限编程 200 8.36 设计模式复用与框架复用 201 第三部分 进阶: C,C++代码阅读与控制 201 第9章 语法与初级标准库 202 9.1 C++的基于过程设计 203 9.2 C++的基于对象设计: 模板与设计 203 9.3 面向对象设计 204 9.4 泛型开发与初级StdC库 204 第10章 数据逻辑与STL库 204 10.1 仿函数 204 10.2 iterater 204 10.3 adapter 205 第11章 高级代码逻辑与LOKI库 205 11.1 typelist 205 11.2 traits 206 11.2 policy 206 第四部分 一个例子:游戏引擎和实现 206 第12章 设计(需求分析) 207 12.1 第一天:接到一个案子 207 12.2 第二天:需求分析 208 第13章 设计(领域分析与抽象) 210 13.1 原语设计 210 13.2 了解Yake 216 13.3 GVWL1.0开发 222 13.4 范型设计与实作 223 第14章 编码 224 14.1 原语设计 224 14.2 实现《梦想与财富》 224 第15章 重构 225 15.1 增加Jxta 225 第五部分 225 选读 225 字符与字符串 226 为什么我说Java是脚本语言 226 宽松语法,无语法语言 227 Linux与3D 228 伪码语言 229 最强大的语言原来是预处理 230 语言宿主 231 shell编程和交互式语句编程 232 Debug,编译期断言 232 图形原理之位图,图象和字体 233 为Windows说些好话 233 Minlearn Ruby (5) 网络原理与P2P 234 Minlearn Ruby(4) 字符串与WEB 234 加密与解密 235 Minlearn(3)载体逻辑 236 Minlearn(2) 平台与并发 237 Minlearn(1)平台之持久 237 平台之多媒体编程 237 Minlearn Ruby 238 思想,维度,细节 240 理想 241 XML 242 面向更好复用的封装机制 243 SOA 244 浮点标准与实现 244 Desktop,web,internet,云计算不过WEB的集中化这种说法的偷换概念 246 编程设计与经济 246 晕计算 247 在形式主义与直觉主义之间:数学与后现代思想的根源 248 Scheme 程序语言介绍之一 248 与软工有关的哲学 辩证 251 富网页技术 251 形式维度 252 开源与开放 253 Core learning and min learing编程 253 与软工有关的哲学 联系 254 本地化和语言编码 254 学习与维度 255 跟软件有关的哲学.唯物主义 255 关于逻辑的逻辑 256 合理性 257 语言,道理和感情 258 可恶OO 259 互联网与企业开发 259 会学习的人首先要学历史 260 离散数学与代数系统 262 线代与矩阵 262 计算机与编程 263 真正的二进制 265 真正的文件 266 真正的数据 267 真正的Unicode 267 真正的Windows消息 269 真正的XML 270 真正的GUI 271 设备环境 271 真正的MFC 273 真正的虚拟机 275 真正的.NET 276 真正的脚本 278 真正的并发性 279 真正的反工程 280 真正的DSL 282 真正的多范型设计 284 真正的调试 285 真正的浮点数 286 真正的布尔 288 真正的整型数 289 真正的引用 290 真正的RTTI 290 真正的句柄 292 真正的循环 293 真正的STL 293 真正的容器 295 真正的智能指针 295 真正的数组索引 296 数据库 297 真正的可持久化 298 真正的类库 299 真正的COM 300 真正的DCOM 301 真正的Sun策略 302 真正的J2EE 303 真正的EJB 304 附录:一些领域逻辑,通用OO设计策略,框架设计 305 附录:参考文献 305 附录:一些建议 305
----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance Alliance Alliance Alliance 什么是开放手机联盟? 开放手机联盟, Open Handset Alliance :是美国 Google 公司与 2007 年 11 月 5 日宣布组建的一个全球性的联 盟组织。这一联盟将会支持 Google 发布的 Android 手机操作系统或者应用软件,共同开发名为 Android 的 开 放源代码的移动系统。开放手机联盟包括手机制造商、手机芯片厂商和移动运营商几类。目前,联盟成员 数 量已经达到了 43 家。 移动手机联盟创始成员: Aplix 、 Ascender 、 Audience 、 Broadcom 、中国移动、 eBay 、 Esmertec 、谷歌、宏达电、英特尔、 KDDI 、 Living Image 、 LG 、 Marvell 、摩托罗拉、 NMS 、 NTT DoCoMo 、 Nuance 、 Nvidia 、 PacketVideo 、高通、三星 、 SiRF 、 SkyPop 、 Sonic Network 、 Sprint Nextel 、 Synaptics 、 TAT 、意大利电信、西班牙电信、德州仪器、 T-M obile 和 Wind River 。 Mobile Mobile Mobile Mobile Operators Operators Operators Operators 移动运营商类 China Mobile Communications Corporation 中国移动通信 KDDI CORPORATION 日本 KDDI 电信 NTT DoCoMo, Inc. 日本多科莫电信 SOFTBANK MOBILE Corp. 日本软银移动 Sprint Nextel( 美国 ) T-Mobile( 德国 ) Telecom Italia( 意大利 ) Telef ó nica( 西班牙 ) Vodafone 沃达丰电信 China Unicom 中国联通 Semiconductor Semiconductor Semiconductor Semiconductor Companies Companies Companies Companies 半导体制造公司 AKM Semiconductor Inc Audience ARM Atheros Communications Broadcom Corporation( 博通 ) Ericsson ( 爱立信公司 ) Intel Corporation ( 英特尔公司 ) Marvell Semiconductor, Inc. ( 收购了 intel 手机芯片部门的公司 )----------------------------------- Android 编程基础 3 NVIDIA Corporation ( 英伟达公司 ) Qualcomm Inc.( 高通公司 ) SiRF Technology Holdings, Inc.( 知名 GPS 芯片制造商 ) Synaptics, Inc. Texas Instruments Incorporated ( 德州仪器 ) Handset Handset Handset Handset Manufacturers Manufacturers Manufacturers Manufacturers 电话制造商 ASUSTeK Computer Inc. 华硕 Garmin International, Inc. HTC Corporation ( 多普达的母公司 ) 宏达电子 Huawei Technologies 华为科技 LG Electronics, Inc. 乐金电子 Motorola, Inc. 摩托罗拉 Samsung Electronics 三星电子 Sony Ericsson 索尼爱立信 Toshiba Corporation 东芝公司 lenovo 联想移动 联盟成员: Software Software Software Software Companies Companies Companies Companies 软件提供公司 Ascender Corp. eBay Inc. Esmertec Google Inc. LivingImage LTD. Nuance Communications, Inc. OMRON SOFTWARE Co, Ltd. 日本欧姆龙软件 有限公司 PacketVideo (PV) SkyPop SONiVOX ASUSTeK Computer Inc. 华硕 AKM Semiconductor AKM 半导体公司 ARM 公司 Atheros Communications Toshiba Corporation 东芝公司 lenovo 联想移动 软银移动 日本无线运营商软银 瑞典计算机咨询公司 Teleca AB Garmin International, Inc. 高明 HTC Corporation ( 多普达的母公司 ) 宏达电子 Huawei Technologies 华为科技 LG Electronics, Inc. 乐金电子 Motorola, Inc. 摩托罗拉 Samsung Electronics 三星电子 Sony Ericsson 索尼爱立信 沃达丰 Teleca Borqs 播思通讯 联盟目的 将会支持 Google 可能发布的手机操作系统或者应用软件,共同开发名为 Android 的开放源代码的移动 系 统。 谷歌早在 2002 年就进入了移动领域,可是由于目前的手机操作系统企业和手机企业相对封闭,提高了 行业的进入门槛,移动互联网的发展远没有拥有统一标准的传统互联网发展迅速,此次推出的开源手机操 作 系统平台就是出于这个目的。 也有分析认为,谷歌并不想做一个简单的手机终端制造商或者软件平台开发商,而意在一统传统互联网和 移 动互联网。----------------------------------- Android 编程基础 4 Android Android Android Android 手机新概念 操作系统的选择 -------- 定制和长尾 � 重构 � MVC 和 Web APP 架构 Android Android Android Android 开发背景 � 计算技术、无线接入技术的发展,使嵌入式系统逐渐有能力对桌面系统常规业务进行支持。 � 谷歌长期以来奉行的移动发展战略:通过与全球各地的手机制造商和移动运营商结成合作伙伴,开发 既 有用又有吸引力的移动服务,并推广这些产品。 Android 进一步推进了 " 随时随地为每个人提供信息 " 这一企 业 目标的实现。 � Open Handset Alliance 汇集了多家业界巨头。运营商如: China Mobile 、 NTT DoCoMo 、 Vodafone 、 T-M obile 等;设备制造商如 ASUS 、 HTC 、 Huawei 、 LG 、 Motorola 、 Samsung 、 Sony Ericsson 、 Toshiba 等;芯片厂商 如 ARM 、 Broadcom 、 Intel 、 Marvell 、 NVIDIA 、 Qualcomm 等。软件厂商如 Ascender 、 eBay 、 Esmertec 、 Li vingImage 等。 � Android 更像一款桌面环境为 Java 的 Linux 操作系统。有助于 Google 实现其 " 随时随地为每个人提供信 息 " 的企业战略。 HTC HTC HTC HTC Dream/G1 Dream/G1 Dream/G1 Dream/G1 具体配置 硬件 3.17 英寸 HVGA (480 x 320) ; 1150mAh 电池 ;高通 528Mhz 7201 处理器 ; 64MB RAM 、 128MB ROM ; 1GB MicroSD 卡 ; QWERTY 全键盘; 310 万像素摄像头。 流媒体 支持视频格式: H.264 、流媒体、 3GPP 、 MPEG4 和 Codec 3GP ;支持音频格式: MP3 、 AAC 、 AAC+ 、 W MA 、 MPEG4 、 WAV 、 MIDI 、 REAL 、 AUDIO 和 OGG ;支持墙纸格式: JPG 、 BMP 、 PNG 和 GIF ;铃声 (MP3 、 AAC 、 AAC+ 和 WMA) 。 接入技术 蓝牙 (class 1) ;四频 (850 , 900 , 1800 , 1900) ;支持 3G , 802.11b 和 802.11g 。----------------------------------- Android 编程基础 5 互联网 支持 HTTP 、 WAP Push 和 xHTML ;支持 POP 、 IMAP 、 SMTP ,以及 AOL 和 GMAIL 电子邮件服务;支持 AIM 、 MSN 、雅虎通和 GTALK ;与谷歌日历同步;与 Android Market 联机;支持谷歌 “ 街景 ” 服务;包装盒内附 数据工具包。 更多信息 https://sites.google.com/a/android.com/opensource/release-features Android Android Android Android 盈利模式 Android 的 App Market 模式,软件开发者获得 7 成收入, 3 成用于系统维护。难点在于位置营销。 设备商通过卖设备、内置特色应用来获得盈利。也可以兼职专业软件开发者进行赢利。 Google 自身通过基于统一平台为用户提供信息来盈利。 Android Android Android Android 的优势 � 源代码完全开放,便于开发人员更清楚的把握实现细节,便于提高开发人员的技术水平,有利于开发 出 更具差异性的应用。 � 采用了对有限内存、电池和 CPU 优化过的虚拟机 Dalvik , Android 的运行速度比想象的要快很多。 � 运营商(中国移动等)的大力支持,产业链条的热捧。 � 良好的盈利模式( 3/7 开),产业链条的各方:运营商、制造商、独立软件生产商都可以获得不错的利 益 。 将移动终端的评价标准从硬件向软件转变,极大的激发了软件开发者的热情。 � Android 的源代码遵循 Apache V2 软件许可,而不是通常的 GPL v2 许可。有利于商业开发。 � 具有强大的 Linux 社区的支持。 Android Android Android Android 的不足 � 由于采用了 Java 作为应用开发语言,目前可用的传统第三方应用还很少,但由于 Android 是一款完全 开 源的移动计算平台,相信第三方应用会很快的丰富起来。 � Google 提供了一套 Java 核心包 (J2SE 5,J2SE 6) 的有限子集,尚不承诺遵守 Java 任何 Java 规范 , 可能会造 成J ava 阵营的进一步分裂。 � 现有应用完善度不太够,需要的开发工作量较大。----------------------------------- Android 编程基础 6 � 基于 QEMU 开发的模拟器调试手段不十分丰富,只支持通话、SMS等,速度慢。 � 暂不具备 Push Mail 和 Office(DataViz 、 QuickOffice 计划近期推出 ) 功能,目前主要面向的是普通消费 者 用户,对商业用户支持尚弱。 Android Android Android Android 带来的影响 ANDROID 的推出后可能影响的产业包括移动电信业,软件开发业,手机制造业,在以消费者为核心的状 态 。 对消费者的影响 � 高档手机选择面增加。 � A ndroid 在设计初期就考虑了与现其有业务的融合,改变以往从计算机为主改成从手机使用为导向。新 生应用如:G oogle 地图及其衍生应用、 GMail 、 GTalk 等。 � GPS 卫星导航功能,手机照相, MP3 ,蓝芽等均被列为 Android 所提供支持的基本选项。 � Android 的平台基本上是免费的,虽然有部份原生链接库会要求费用,但大部份是免权利金; Android 的 程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算软件平台,组建了 google 主导的拥有众多产业界巨头的产业联盟,有利于 高效开发、降低成本。 � 由于是源代码开放的产品,对非主导厂商而言,可以避开与主导厂商在核心技术上面的差距,开发出 更 具竞争力和差异化的产品。 对运营商的影响 � 丰富的数据业务,将导致数据流量的显著增加 。 � 手机来源增加,价格更为低廉。 对软件开发者的影响 � 因为 Android 移动软件平台抱持开放互通的观念,势必吸引不少自由软件的拥护者。 � 开发方向有三个重点 :----------------------------------- Android 编程基础 7 � 应用软件的开发 � 特殊功能的原生链接库 � 专属应用程序框架 � 由于 Android 的A pp Market 性质,可能催生出专门的应用软件开发商。 Android Android Android Android 应用现状 � 设备商: lenovo 、琦基、戴尔、三星、摩托罗拉、华为、英特尔、 Kogan 、索爱、华硕、多普达、爱可 视 、 Archos 等。 � 制造商: HTC 、 Telstra 等。 � 手机设计公司:播思、德信无线等。 � 运营商:中国移动、 Sprint 、 T-Mobile 、 Teleca AB 等。 � 芯片商: Qualcomm 、 Marvell 、 TI 、 Boardcom 等。----------------------------------- Android 编程基础 8 Android Android Android Android 开发入门 System System System System Requirements Requirements Requirements Requirements The sections below describe the system and software requirements for developing Android applications using the Android SDK tools included in Android 1.1 SDK, Release 1. Supported Supported Supported Supported Operating Operating Operating Operating Systems Systems Systems Systems • Windows XP (32-bit) or Vista (32- or 64-bit) • Mac OS X 10.4.8 or later (x86 only) • Linux (tested on Linux Ubuntu Dapper Drake) Supported Supported Supported Supported Development Development Development Development Environments Environments Environments Environments Eclipse IDE o Eclipse 3.3 (Europa), 3.4 (Ganymede) � Eclipse JDT plugin (included in most Eclipse IDE packages) � WST (optional, but needed for the Android Editors feature; included in most Eclipse IDE packages ) o JDK 5 or JDK 6 (JRE alone is not sufficient) o Android Development Tools plugin (optional) o Not Not Not Not compatible with Gnu Compiler for Java (gcj) Other development environments or IDEs o JDK 5 or JDK 6 (JRE alone is not sufficient) o Apache Ant 1.6.5 or later for Linux and Mac, 1.7 or later for Windows o Not Not Not Not compatible with Gnu Compiler for Java (gcj) Note: Note: Note: Note: If JDK is already installed on your development computer, please take a moment to make sure that it meets the version requirements listed above. In particular, note that some Linux distributions may include JDK 1.4 or Gnu Compiler for Java, both of which are not supported for Android development----------------------------------- Android 编程基础 9 什么是 Android? Android? Android? Android? Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 • 应用程序框架 支持组件的重用与替换 • Dalvik Dalvik Dalvik Dalvik 虚拟机 专为移动设备优化 • 集成的浏览器 基于开源的 WebKit 引擎 • 优化的图形库 包括定制的 2D 图形库, 3D 图形库基于 OpenGL ES 1.0 (硬件加速可选) • SQLite SQLite SQLite SQLite 用作结构化的数据存储 • 多媒体支持 包括常见的音频、视频和静态图像格式 ( 如 MPEG4, H.264, MP3, AAC, AMR, JPG, PNG , GIF ) • GSM GSM GSM GSM 电话技术 (依赖于硬件) • 蓝牙 Bluetooth, Bluetooth, Bluetooth, Bluetooth, EDGE, EDGE, EDGE, EDGE, 3G, 3G, 3G, 3G, 和 WiFi WiFi WiFi WiFi (依赖于硬件) • 照相机, GPS GPS GPS GPS ,指南针,和加速度计( accelerometer accelerometer accelerometer accelerometer ) (依赖于硬件) • 丰富的开发环境 包括设备模拟器,调试工具,内存及性能分析图表,和 Eclipse 集成开发环境插件 应用程序 Android 会同一系列核心应用程序包一起发布,该应用程序包包括 email 客户端, SMS 短消息程序,日历, 地图,浏览器,联系人管理程序等。所有的应用程序都是使用 JAVA 语言编写的。 应用程序框架 开发人员也可以完全访问核心应用程序所使用的 API 框架。该应用程序的架构设计简化了组件的重用;任 何 一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循 框 架的安全性限制)。同样,该应用程序重用机制也使用户可以方便的替换程序组件。 隐藏在每个应用后面的是一系列的服务和系统 , 其中包括; • 丰富而又可扩展的视图( Views ),可以用来构建应用程序, 它包括列表( lists ),网格( grids ),文 本框( text boxes ),按钮( buttons ), 甚至可嵌入的 web 浏览器。 • 内容提供器( Content Providers )使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或 者共享它们自己的数据 • 资源管理器( Resource Manager )提供 非代码资源的访问,如本地字符串,图形,和布局文件( la yout files )。 • 通知管理器 ( Notification Manager ) 使得应用程序可以在状态栏中显示自定义的提示信息。 • 活动管理器( Activity Manager ) 用来管理应用程序生命周期并提供常用的导航回退功能。----------------------------------- Android 编程基础 10 程序库 Android 包含一些 C/C++ 库,这些库能被 Android 系统中不同的组件使用。它们通过 Android 应用程序框架 为开发者提供服务。以下是一些核心库: • 系统 C C C C 库 - 一个从 BSD 继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linu x 的设备定制的。 • 媒体库 - 基于 PacketVideo OpenCORE ;该库支持多种常用的音频、视频格式回放和录制,同时支 持 静态图像文件。编码格式包括 MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。 • Surface Surface Surface Surface Manager Manager Manager Manager - 对显示子系统的管理,并且为多个应用程序提 供了 2D 和 3D 图层的无缝融合。 • LibWebCore LibWebCore LibWebCore LibWebCore - 一个最新的 web 浏览器引擎用,支持 Android 浏览器和一个可嵌入的 web 视图。 • SGL SGL SGL SGL - 底层的 2D 图形引擎 • 3D 3D 3D 3D libraries libraries libraries libraries - 基于 OpenGL ES 1.0 APIs 实现;该库可以使用硬件 3D 加速(如果可用)或者使用高 度优化的 3D 软加速。 • FreeType FreeType FreeType FreeType - 位图( bitmap )和矢量( vector )字体显示。 • SQLite SQLite SQLite SQLite - 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。 Android Android Android Android 运行库 Android 包括了一个核心库,该核心库提供了 JAVA 编程语言核心库的大多数功能。 每一个 Android 应用程序都在它自己的进程中运行,都拥有一个独立的 Dalvik 虚拟 机实例。 Dalvik 被设计 成一个设备可以同时高效地运行多个虚拟系统。 Dalvik 虚拟机执行( .dex )的 Dalvik 可执行文件,该格式 文 件针对小内存使用做了 优化。同时虚拟机是基于寄存器的,所有的类都经由 JAVA 编译器编译,然后通过 SDK 中 的 "dx" 工具转化成 .dex 格式由虚拟机执行。 Dalvik 虚拟机依赖于 linux 内核的一些功能,比如线程机制和底层内存管理机制。 Linux Linux Linux Linux 内核 Android 的核心系统服务依赖于 Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模 型 。 Linux 内核也同时作为硬件和软件栈之间的抽象层。----------------------------------- Android 编程基础 11 Android Android Android Android 的系统架构 系统构架 Android Android Android Android 内核 � Linux 内核版本 2.6 � 位于硬件和软件堆之间的抽象层 � 核心服务:安全机制、内存管理、进程管理、网络、硬件驱动。 Android 依赖 Linux 内核 2.6 提供核心服务,比如安全、内存管理、进程管理、网络、硬件驱动。在这里, L inux 内核扮演的是硬件层和系统其它层次之间的一个抽象层的概念。这个操作系统并非类 GNU/Linux 的,因为 其 系统库,系统初始化和编程接口都和标准的 Linux 系统是有所不同的。----------------------------------- Android 编程基础 12 从 Google 目前 release 的 Linux 系统来看,其没有虚拟内存文件系统,系统所用的是 yaffs2 文件系统,具体 的映像也都位于 SDK 安装目录下。通过 emulator -console 命令,我们可以在 host 中断下得到一个简单的可 以 控制 Android 的 shell ,这 个 系 统 包 含 了 一 个 Toolbox ,提 供 一 些 基 本 的 命 令 工 具 , 集 中 在 /sbin,/system/sbin,/system/bin 中,但是很简陋,命令种类也很少。 目前 Android 的程序安装模式是靠 Eclipse 自动进行的,通过对底层的分析可知,大致步骤就是在 /data/app 和 data/data 下存放 android 底层和普通内核没有什么大的区别,我们可以将其作为一个 Linux 来进行开发和 hacking 。 Lib Lib Lib Lib 和运行环境 lib � C/C++ 库:被各种 Android 组件使用 � 通过应用程序框架开发者可以使用其功能 � 包括: � 媒体库: MPEG4 H.264 MP3 JPG PNG ..... � WebKit/LibWebCore : Web 浏览引擎 � SQLite 关系数据库引擎 � 2D , 3D 图形库、引擎 丰富的类库支持: 2D 和 3D 图像库 OpenGL ES 、数据库 SQLite 、对象数据库 db4o 类库、媒体库、基于 Lin ux 底层系统 C 库等等,让应用开发更简单多样。 Google 使用 Apache 的 Harmony 类库, Harmony 某些方面速 度 快于 Sun 的 VM 。 Runtime 在 Dalvik Java VM 上, Dalvik 采用简练、高效的 byte code 格式运行,它能够在 低 资耗和没有应用相互干扰的情况下并行执行多个应用。 运行时环境 � 核心库提供的 Java 功能 � Dalvik 虚拟机依赖于 Linux 内核,例如线程或底层内存管理 � 设备可以运行多个 Dalvik 虚拟机,每一个 Android 应用程序在它自己的 Dalvik VM 实例中运行 � VM 执行优化的 Dalvik 可执行文件 (.dex) � Dx- 工具把编译过的 Java 文件转换为 dex 文件----------------------------------- Android 编程基础 13 应用和框架 � 核心应用,例如联系人,电子邮件,电话,浏览器,日历,地图, ... � 充分访问所有核心应用框架 API � 简化组件的重用 � 用 Java 编写应用程序----------------------------------- Android 编程基础 14 支持的功能 + Application framework: 可重用的和可替换的组件部分,在这个层面上,所有的软件都是平等的。 + Dalvik virtul machine: 一个基于 Linux 的虚拟机。 + Integrated browser: 一个基于开源的 WebKit 引擎的浏览器,在应用程序层。 + Optimized graphics: 包含一个自定义的 2D 图形库和基于 OpenGL ES 1.0 标准的 3D 实现。 + SQLite: 数据库 + Media support: 通用的音频,视频和对各种图片格式的支持 (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GI F) + GSM Telephony: GSM 移动网络 , 硬件支持。 + Bluetooth, EDGE, 3G, and WiFi: 都依赖于硬件支持。 + Camera, GPS, compass, and accelerometer: 都依赖于硬件支持。 + Rich development environment: 包含一套完整的开发工具集,方便跟踪调试,内存检测和性能测试,而且 提供了 Eclipse 的插件。最底层的是一个 Linux Kernel ,加载了几个移动设备必要的系统驱动(这么说来 Android 基 础系统是要以 GPL 发布了?不知道 34 家厂商的硬件开发商们是怎么样想的);上面是类库和 Runtime ,绿 色 的类库部分可以看到大名鼎鼎的 SQLite ,这个软件甚至声称自己属于公共领域(比 MIT License 还要强 @ @ ) , 字体 FreeType 是 BSD-style License 的,图形库 OpenGL ES 只需通过产品测试,无偿使用于产品。再向上看 是应用层的东西了,这里可以做的事情就非常多了 ,各个社区,各个厂家都可以参与进来。难怪 Android 的 sdk 可以 Apache License 发布了 , 对企业和开发人员友好啊。 那么 Google 自己的东西在哪里呢?没错,就是 右 边那个 runtime ,最吸引技术人员的就是这个 runtime (注意,这个才是 Android 的核心)。 Google 为它准备 了 一个虚拟机,叫做 Dalvik 。这个让人摸不着头脑的东西的到底是什么?从开发平台上我们清清楚楚地得到 了 答案: Java----------------------------------- Android 编程基础 15 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 7 7 7 7 个 Linux Linux Linux Linux 手机平台 � Maemo � Android � LIMO � OpenMOKO � GPE^2 � ALP � QTopia Phone Edition Maemo Maemo Maemo Maemo 架构----------------------------------- Android 编程基础 3 Android Android Android Android 架构----------------------------------- Android 编程基础 4 LIMO LIMO LIMO LIMO 架构----------------------------------- Android 编程基础 5 OpneMOKO OpneMOKO OpneMOKO OpneMOKO 架构----------------------------------- Android 编程基础 6 GPE^2 GPE^2 GPE^2 GPE^2 架构----------------------------------- Android 编程基础 7 ALP ALP ALP ALP 架构----------------------------------- Android 编程基础 8 QTopia QTopia QTopia QTopia Phone Phone Phone Phone Edition Edition Edition Edition 架构----------------------------------- Android 编程基础 9 进程间的通信 Linux 手机平台进程间通信 � Maemo 采用 D-BUS � Android 采用 OpenBinder � LiMO 采用 D-BUS � OpenMoko 采用 D-BUS � GPE Phone Edition 采用 D-BUS � ALC 采用 OpenBinder � Qtopia Phone Edition 采用 D-BUS 进程间通信种类 � D-BUS � Openbinder � CORBA/Corbit � IVY � GNET D-BUS----------------------------------- Android 编程基础 10 Android Android Android Android 学习方法 ① 了解什么是 Androi ② 建立开发环境 ③ 阅读 SDK 文档 ④ 背景知识 � Java � 面向对象 � 设计模式 � J2ME 、 Brew 、 Symbian 建立 Android Android Android Android 开发环境 ① 下载 JDK 5 or JDK 6 (JRE alone is not sufficient) -> 安装 -> 设置环境变量 JAVA_HOME CLASSPATH path ② 下载 Eclipse 3.3 (Europa), 3.4 (Ganymede) IDE for JAVA-> 解压 ③ 下载 Android SDK 解压 -> path 里加入 SDK 包中的 tools 目录全路径 ④ 下载 ADT 0.8.0 解压 ⑤ 打开 Eclipse 安装 ADT 插件----------------------------------- Android 编程基础 11 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 开发环境搭建 ADV ADV ADV ADV 的创建 ADT0.9.1 版本 ① 在 Eclipse 中创建----------------------------------- Android 编程基础 3 ② 在命令行中创建 打开 CMD 命令行,进入到 Android SDK tools 目录 使用 android 命令列出 target 值 使用 android create avd 命令来创建 AVD cd E:\Mobile DEV\Android_SDK1.5\tools android list targets 行为: "create avd": 创建一个新的 Android 虚拟设备。 选项: -t --target 新的 AVD 的 Target ID( 必须 ) -c --sdcard 指向一个共享的 SD 存储卡的路径或是为新的 AVD 定制的新 SD 存储卡的容量大小 -p --path 新 AVD 将被创建的位置路径 -n --name 新 AVD 的名称 ( 必须 ) -f --force 强制创建 ( 覆盖已存在的 AVD) -s --skin 新 AVD 的皮肤----------------------------------- Android 编程基础 4 例子 : 将建一个名叫 GPhone 的 AVD , Target ID=2 、 SD 存储卡容量 52M 、路径 C:\AVD\ 、皮肤 SUSE-HVGA- P 查看自己新创建的 ADV : list avd 命令 ADT0.9.0 版本 只能在命令行中创建 开启命令行进入 Android SDK tools 目录 列出 Target ID 创建一个新的 AVD 查看新创建的 AVD 运行指定的 AVD 运行新创建的 AVD:GPhone android create avd -n GPhone -t 2 -c 52M -p C:\AVD\ -s SUSE-HVGA-P android list avd cd E:\Mobile DEV\Android_SDK1.5\tools andriod list target android create avd -n GPhone -t 2 -c 52M -p C:\AVD\ -s SUSE-HVGA-P android list avd emulator -avd GPhone----------------------------------- Android 编程基础 5 Windows Windows Windows Windows 平台: Eclipse IDE 版本 ------------JDK+Eclipse+Android SDK+ADT 1. 必须软件 2. 安装过程 ① 安装 JAVA JDK SE 1.6 � 设置环境变量 � JAVA_HOME � JAVA_JRE_HOME � JRE_HOME � Android_SDK_HOME � CLASSPATH � Path ① JAVA JDK SE 1.6 jdk-6u13-windows-i586-p.exe ② Eclipse 3.4.2 eclipse-java-ganymede-SR2-win32.zip ③ Google Android SDK android-sdk-windows-1.5_r1.zip ④ ADT-0.9.0 ADT-0.9.0.zip JAVA_HOME=C:\Program Files\Java\jdk1.6.0_13 JAVA_JRE_HOME=C:\Program Files\Java\jdk1.6.0_13\jre JRE_HOME=C:\Program Files\Java\jre6 Android_SDK_HOME =C:\Mobile Phone DEV\Android SDK CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt. jar;%JRE_HOME%\lib;%JRE_HOME%\lib\rt.jar;%JAVA_JRE_HOME%\lib;%JAVA_JRE_HOME% \lib\rt.jar Path= %Android_SDK_HOME%\tools ;%JAVA_HOME%\bin;%JRE_HOME%\bin;%JAVA_JRE _HOME%\bin; 要使用命令行工具必须配置----------------------------------- Android 编程基础 6 ② 解压 Eclipse 3.4.2 ③ 解压 Google Android SDK ④ Eclipse 下安装 ADT 0.9.0 ⑤ 设置 Google Android SDK 路径 解压 eclipse-java-ganymede-SR2-win32.zip 到 C:\Eclipse For Android\ 解压 android-sdk-windows-1.5_r1.zip 到 C:\Mobile Phone DEV\Android SDK 复制 ADT-0.9.0.zip 到 C:\ 打开 C:\Eclipse For Android\eclipse.exe 设置工作路径为 C:\WorkSpace Help->SoftWare Update->find and install ->Search for new features to install ->Next->New Archived site-> 选中 C:\ ADT-0.9.0.zip->OK->Finish->ADT-0.9.0.zip 选勾 ->Next->Accept->Next->Finish- >Install All->Restart “ YES ” Window->preferences-> 选中 Android->SDK Location 中选择 Google Android SDK 的安装路 径 C:\Mobile Phone DEV\Android SDK->OK----------------------------------- Android 编程基础 7 3. HelloWorld 程序实例 ① 新建一个 Android Project � Project name 设置工程名 Hello Google Android � Package name 设置包名 zyf.android.test.hello � Activity name 设置活动名 Hello � Application name 设置应用程序名 Hello � Build Target 设置 AVD API 的版本 3 Android1.5----------------------------------- Android 编程基础 8----------------------------------- Android 编程基础 9 ② 修改 Hello.java 文件 内容如下: ③ 运行 as Android package package package package zyf.android.test.hello; import import import import android.app.Activity; import import import import android.os.Bundle; import import import import android.widget.TextView; public public public public class class class class Hello extends extends extends extends Activity { /** Called when the activity is first created. */ @Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); // setContentView (R.layout.main); TextView tv = new new new new TextView( this this this this ); tv.setText( " 这是一个测试 Android 的 helloWorld" ); setContentView(tv); } }----------------------------------- Android 编程基础 10 ④ 代码分析: 在 Android 中,用户界面控件被封装成了各种 Class 叫做 Views 。一个 View 是一个可以显示的控件对 象,比如 RadioButton , Animation , TextLable 等。其中的一个简单的控件是 TextView: 传入 TextView 构造函数的参数是一个 Context 对象,通过这个对象可以使用系统提供的功能接口,比 如加载资源,访问数据库和共享数据等等。 Activity 类从 Context 类继承而来,所以 Activity 本身 是 一个 Context ( Java 中的继承概念)。 TextView 对象构建以后就可以设置要显示的数据了。 tv.setText(" 这是一个测试 Android 的 helloWorld"); 最后是连接 TextView 到屏幕 , 类似这样 : setContentView() 方法可以控制具体哪一个控件和系统的 UI 联系起来(我的理 解是设置为主显示 View )。如果没有设置,屏幕中将会显示空白。 ⑤ 结果 TextView tv = new new new new TextView( this this this this ); setContentView(tv);----------------------------------- Android 编程基础 11----------------------------------- Android 编程基础 12 Apache Ant IDE 版本 ------------JDK+Android SDK +Ant 1. 必须软件 2. 安装过程 ① 安装 JAVA JDK SE 1.6 � 设置环境变量 � JAVA_HOME � JAVA_JRE_HOME � JRE_HOME � Android_SDK_HOME � ANT_HOME � CLASSPATH � Path ① JAVA JDK SE 1.6 jdk-6u13-windows-i586-p.exe ② Google Android SDK android-sdk-windows-1.5_r1.zip ③ Apache Ant apache-ant-1.7.1-bin.zip JAVA_HOME=C:\Program Files\Java\jdk1.6.0_13 JAVA_JRE_HOME=C:\Program Files\Java\jdk1.6.0_13\jre JRE_HOME=C:\Program Files\Java\jre6 Android_SDK_HOME =C:\Mobile Phone DEV\Android SDK ANT_HOME=C:\Mobile Phone DEV\Apache Ant\apache-ant-1.7.1 CLASSPATH=.;%ANT_HOME%\lib;%ANT_HOME%\lib\ant.jar;%JAVA_HOME%\lib;%JAV A_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar;%JRE_HOME%\lib;%JRE_HOME%\lib\r t.jar;%JAVA_JRE_HOME%\lib;%JAVA_JRE_HOME%\lib\rt.jar Path=%ANT_HOME%\bin;%Android_SDK_HOME%\tools;%JAVA_HOME%\bin;%JRE_HO ME%\bin;%JAVA_JRE_HOME%\bin;----------------------------------- Android 编程基础 13 ② 解压 Google Android SDK ③ 解压 apache-ant-1.7.1.zip 3. HelloWorld 程序实例 结果 解压 android-sdk-windows-1.5_r1.zip 到 C:\Mobile Phone DEV\Android SDK 解压 Apache Ant apache-ant-1.7.1.zip 到 C:\Mobile Phone DEV\Apache Ant\apache-ant-1.7.1 ① 开始 -> 运行 ->cmd ② cd C:\Mobile Phone DEV\WorkSpace ③ 使用命令行工具来创建一个新工程 ④ cd Hello ⑤ ant debug ⑥ cd bin ⑦ emulator -avd Android_SDK1.5 ⑧ adb install ./hello-debug.apk ⑨ 在模拟器中运行 hello 程序 android create project -k zyf.hello -n HelloAndroid -t 2 -a AntActivity -p ./Hello----------------------------------- Android 编程基础 14 Linux Linux Linux Linux 平台: JDK+Eclipse+Android SDK+ADT JDK+Android SDK +Ant----------------------------------- Android 编程基础 15 应用解析 Activity Activity Activity Activity : : : : 活动是最基本的 Android 应用程序组件,应用程序中,一个活动通常就是一个单独的屏幕。每一个活动 都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并 对 事件做出响应。大多数的应用是由多个屏幕显示组成。例如 : 一个文本信息的应用也许有一个显示发送消息 的 联系人列表屏幕,第二个屏幕用来写文本消息和选择收件人,再来一个屏幕查看消息历史或者消息设置操 作 等。这里每一个这样的屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕并且完成新的活动。在 某 些情况下当前的屏幕也许需要向上一个屏 幕活动提供返回值 -- 比如让用户从手机中挑选一张照片返回通讯录 做为电话拨入者的头像。 当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历史堆栈中。用户可以返回到历史堆栈中的 前 一个屏幕。当屏幕不再使用时,还可以从历史堆栈中删除。默认情况下, Android 将会保留从主屏幕到每一 个应用的运行屏幕。 简单理解 Activity 代表一个用户所能看到的屏幕, Activity 主要是处理一个应用的整体性工作,例如, 监 听系统事件 ( 按键事件、触摸屏事件等 ) 、为用户显示指定的 View ,启动其他 Activity 等。所有应用的 Activit y 都继承于 android.app.Activity 类,该类是 Android 提供的基层类,其他的 Activity 继承该父类后,通过 Over ride 父类的方法来实现各种功能,这种设计在其他领域也较为常见。 Intent Intent Intent Intent : : : : 调用 Android 专有类 Intent 进行架构屏幕之间的切换。 Intent 是描述应用想要做什么。 Intent 数据结构两 个最重要的部分是动作和动作对应的数据。典型的动作类型有 :MAIN (活动的门户)、 VIEW 、 PICK 、 EDIT 等。而动作对应的数据则以 URI 的形式进行表示。例如 : 要查看某个人的联系方式,你需要创建一个动作类 型为 VIEW 的 Intent ,以及一个表示这个人的 URI 。 Android 使用了 Intent 这个特殊类,实现在屏幕与屏幕之间移动。 Intent 类用于描述一个应用将会做什 么 事。在 Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有: MAIN ( a ctivity 的门户)、 VIEW 、 PICK 、 EDIT 等。而动作对应的数据则以 URI 的形式进行表示。例如:要查看一个人的 联 系方式,你需要创建一个动作类型为 VIEW 的 intent ,以及一个表示这个人的 URI 。 与之有关系的一个类叫 IntentFilter 。相对于 intent 是一个有效的做某事的请求,一个 intentfilter 则用于 描 述一个 activity (或者 IntentReceiver )能够操作哪些 intent 。一个 activity 如果要显示一个人的联系方式时, 需 要声明一个 IntentFilter ,这个 IntentFilter 要知道怎么去处理 VIEW 动作和表示一个人的 URI 。 IntentFilter 需 要在 AndroidManifest.xml 中定义。 通过解析各种 intent ,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时, activity 将会调用 startActivity(IntentmyIntent) 方法。然后,系统会在所有安装的应用程序中定义的 IntentFilter 中查找,找到最 匹配 myIntent 的 Intent 对应的 activity 。新的 activity 接收到 myIntent 的通知后,开始运行。当 startActivity 方 法被调用将触发解析 myIntent 的动作,这个机制提供了两个关键好处:----------------------------------- Android 编程基础 16 A 、 Activities 能够重复利用从其它组件中以 Intent 的形式产生的一个请求; B 、 Activities 可以在任何时候被一个具有相同 IntentFilter 的新的 Activity 取代。 IntentReceiver: IntentReceiver: IntentReceiver: IntentReceiver: 当你希望你的应用能够对一个外部的事件 ( 如当电话呼入时,或者数据网络可用时,或者到了晚上时 ) 做出响 应,你可以使用一个 IntentReceiver 。虽然 IntentReceiver 在感兴趣的事件发生时,会使用 NotificationManage r 通知用户,但它并不能生成一个 UI 。 IntentReceiver 在 AndroidManifest.xml 中注册,但也可以在代码中使用 Context.registerReceiver() 进行注册。当一个 intentreceiver 被触发时,你的应用不必对请求调用 inten treceiver , 系统会在需要的时候启动你的应用。各种应用还可以通过使用 Context.broadcastIntent() 将它们自己的 intentreceiver 广播给其它应用程序。 Service Service Service Service : : : : 一个 Service 是一段长生命周期的,没有用户界面的程序。比较好的一个例子就是一个正在从播放列表中 播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个 activity ,让使用者可以选择歌曲并播 放 歌曲。然而,音乐重放这个功能并没有对应的 activity ,因为使用者当然会认为在导航到其它屏幕时音乐应 该 还在播放的。在这个例子中,媒体播放器这个 activity 会使用 Context.startService() 来启动一个 service ,从而 可以在后台保持音乐的播放。同时,系统也将保持这个 service 一直执行,直到这个 service 运行结束。另外 , 我们还可以通过使用 Context.bindService() 方法,连接到一个 service 上(如果这个 service 还没有运行将启动 它)。当连接到一个 service 之后,我们还可以 service 提供的接口与它进行通讯。拿媒体播放器这个例子来 说 , 我们还可以进行暂停、重播等操作。 Content Content Content Content Provider Provider Provider Provider : : : : Android 应用程序能够将它们的数据保存到文件、 SQLite 数据库中,甚至是任何有效的设备中。当你想 将你的应用数据与其它的应用共享时,内容提供器就可以发挥作用了。因为内容提供器类实现了一组标准 的 方法,从而能够让其它的应用保存或读取此内容提供器处理的各种数据类型。 数据是应用的核心。在 Android 中,默认使用鼎鼎大名的 SQLite 作为系统 DB 。但是在 Android 中,使用方 法有点小小的不一样。在 Android 中每一个应用都运行在各自的进程中,当你的应用需要访问其他应用的数 据时,也就需要数据在不同的虚拟机之间传递,这样的情况操作起来可能有些困难 ( 正常情况下,你不能读 取 其他的应用的 db 文件 ) , ContentProvider 正是用来解决在不同的应用包之间共享数据的工具。 � 所有被一个 Android 应用程序创建的偏好设置,文件和数据库都是私有的。 � 为了和其他应用程序共享数据,应用程序不得不创建一个 Content Provider � 要回索其他应用程序的数据,它自己的 Content Provider 必须被调用 � Android 本地 Content Provider 包括: � CallLog :地址和接收到的电话信息 � Contact.People.Phones :存储电话号码 � Setting.System :系统设置和偏好设置 � 等等----------------------------------- Android 编程基础 17 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 虚拟机 Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik 冲击 随着 Google 的 AndroidSDK 的发布,关于它的 API 以及在移动电话领域所带来的预 期影响这些方面的讨论不胜枚举。不过,其中的一个话题在 Java 社区是一石激起千层浪, 这就是 Android 平台的基础 —— Dalvik 虚拟机。 Dalvik Dalvik Dalvik Dalvik 和标准 Java Java Java Java 虚拟机 (JVM) (JVM) (JVM) (JVM) 首要差别 Dalvik 基于寄存器,而 JVM 基于栈。,基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花 费 的时间更短。 Dalvik Dalvik Dalvik Dalvik 和 Java Java Java Java 运行环境的区别 Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik 应用作为一个独立 的 Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭 . Dalvik Dalvik Dalvik Dalvik 形势 Dalvik 的诞生也导致人们开始忧虑 Java 平台的第一次大规模的分道扬镳或许已经是进行时了 —— 有人已经 把 Davlik 和微软的 JVM 以及 Sun 对微软的诉讼联系起来,等着看 Google 身上是否也会发生类似事情;另外 一 些人则指出, Google 并没有宣称 Dalvik 是一个 Java 实现,而微软却是这样做的。 Sun 也对可能带来的阵营 分裂表达了忧虑情绪,并提出和 Google 合作来保证 Dalvik 和 JVM 之间的兼容性 —— Google 对此的解释是, Dalvik 是对解决目前 JavaME 平台上分裂的一次尝试,也是为了提供一个拥 有较少限制许可证的平台。甚至 还有人怀疑这是否是 Sun 和 Google 两大阵营对 Java 之未来的一次大规模较量。----------------------------------- Android 编程基础 3 Android Android Android Android 中各种 JAVA JAVA JAVA JAVA 包的功能描述 在 Android 的应用程序开发中,通常使用的是 JAVA 语言,除了需要熟悉 JAVA 语 言的基础知识之外,还需要了解 Android 提供的扩展的 JAVA 功能。 在一般的 JAVA 应用中,如果需用引用基础类库,通常需要使用如下的方式: import javax.swing.*; 以上代码表示了引用 JAVA 的 GUI 组件 Swing,javax.swing 即 JAVA 中的一个包。 android 提供一些扩展的 JAVA 类库,类库分为若干个包,每个包中包含若干个类。 重要包的描述: android.app :提供高层的程序模型、提供基本的运行环境 android.content :包含各种的对设备上的数据进行访问和发布的类 android.database :通过内容提供者浏览和操作数据库 android.graphics :底层的图形库,包含画布,颜色过滤,点,矩形,可以将他们直接绘制到屏幕上 . android.location :定位和相关服务的类 android.media :提供一些类管理多种音频、视频的媒体接口 android.net :提供帮助网络访问的类,超过通常的 java.net.* 接口 android.os :提供了系统服务、消息传输、 IPC 机制 android.opengl :提供 OpenGL 的工具 android.provider :提供类访问 Android 的内容提供者 android.telephony :提供与拨打电话相关的 API 交互 android.view :提供基础的用户界面接口框架 android.util :涉及工具性的方法,例如时间日期的操作 android.webkit :默认浏览器操作接口 android.widget :包含各种 UI 元素(大部分是可见的)在应用程序的屏幕中使用----------------------------------- Android 编程基础 4 Android Android Android Android 的相关文件类型 Java Java Java Java 文件 ----- ----- ----- ----- 应用程序源文件 android 本身相当一部分都是用 java 编写而成 ( 基本上架构图里头蓝色的部份都是用 Java 开发的 ) , android 的 应用必须使用 java 来开发。 Class Class Class Class 文件 ------Java ------Java ------Java ------Java 编译后的目标文件 不像 J2se , java 编译成 class 就可以直接运行, android 平台上 class 文件不能直接在 android 上运行。由于 G oogle 使用了自己的 Dalvik 来运行应用,所以这里的 class 也肯定不能在 AndroidDalvik 的 java 环境中运行, androi d 的 class 文件实际上只是编译过程中的中间目标文件,需要链接成 dex 文件后才能在 dalvik 上运行。 Dex Dex Dex Dex 文件 -----Android -----Android -----Android -----Android 平台上的可执行文件 Android 虚拟机 Dalvik 支持的字节码文件格式 Google 在新发布的 Android 平台上使用了自己的 Dalvik 虚拟 机 来定义,这种虚拟机执行的并非 Java 字节码,而是另一种字节码: dex 格式的字节码。在编译 Java 代码之 后 , 通过 Android 平台上的工具可以将 Java 字节码转换成 Dex 字节码。虽然 Google 称 Dalvik 是为了移动设备定 做的,但是业界很多人认为这是为了规避向 sun 申请 Javalicense 。这个 DalvikVM 针对手机程式 /CPU 做过 最 佳化,可以同时执行许多 VM 而不会占用太多 Res ource 。 Apk Apk Apk Apk 文件 -------Android -------Android -------Android -------Android 上的安装文件 Apk 是 Android 安装包的扩展名,一个 Android 安装包包含了与某个 Android 应用程序相关的所有文件。 apk 文件将 AndroidManifest.xml 文件、应用程序代码 (.dex 文件 ) 、资源文件和其他文件打成一个压缩包。一个工 程只能打进一个 .apk 文件。----------------------------------- Android 编程基础 5 Android Android Android Android 的应用程序结构分析: HelloActivity 本例以一个简单的 HelloActivity 程序为例,简单介绍 Android 应用程序的源代码结构。事实 上, Android 应用程序虽然不是很复杂,但是通常涉及了 JAVA 程序 ,XML 文件, Makefile 多方面的内容。 HelloActivity 虽然简单,但是麻雀虽小,五脏俱全,是学习 Android 应用程 序的最好示例。 第一部分: HelloActivity HelloActivity HelloActivity HelloActivity 的源代码 HelloActivity 工程的源代码在 Android 目录的 development/samples/HelloActivity/ 中,代码的 结构如下所示: 其中 tests 是一个独立的项目,可以暂时不考虑。其他部分看作一个 Android 的一应用程序 的工程。这个工程主要的组成部分如下所示: AndroidManifest.xml :工程的描述文件,在运行时有用处 Android.mk :整个工程的 Makefile development/samples/HelloActivity/ |-- Android.mk |-- AndroidManifest.xml |-- res | |-- layout | | `-- hello_activity.xml | `-- values | `-- strings.xml |-- src | `-- com | `-- example | `-- android | `-- helloactivity | `-- HelloActivity.java `-- tests |-- Android.mk |-- AndroidManifest.xml `-- src `-- com `-- android `-- helloactivity `-- HelloActivityTest.java----------------------------------- Android 编程基础 6 res :放置资源文件的目录 src/com/example/android/helloactivity/HelloActivity.java :这是 JAVA 类文件,这个文件的路径 表示在 Andorid 的 JAVA 包的结构中的位置, 这个包的使用方式为 com.example.android.helloactivity 。 第二部分: 编译的中间结果 这个 HelloActivity 工程经过编译后将生成 out/target/common/obj/APPS/He lloActivity_intermediates/ 目录, 这个目录中的内容都是 HelloActivity 工程相关的, 更具体地说都与 development/samples/HelloActivity/ 中的 Android.mk 文件相关。 classes.dex 是一个最重要的文件,它是给 Android 的 JAVA 虚拟机 Dalvik 运行的字节码文 件。 classes.jar 是一个 JAR 文件, JAR 的含义为 Java ARchive ,也就是 Java 归档,是一种与平台 无关的文件格式,可将多个文件合成一个文件。解压缩之后的目录结构: (JAVA 标准编译得 到的类 ) out/target/common/obj/APPS/He lloActivity_intermediates/ |-- classes.dex (字节码) |-- classes.jar ( JAR 文件 ) |-- public_resources.xml (根据 resources 结构生成的 xml ) `-- src |-- R.stamp `-- com `-- example `-- android `-- helloactivity `-- R.java ( resources 生成的文件)----------------------------------- Android 编程基础 7 各个以 class 为扩展名的文件,事实上是 JAVA 程序经过编译后的各个类的字节码。 第三部分: 目标 apk apk apk apk 文件 目标 apk 文件是 Android 的 JAVA 虚拟机 Dalvik 安装和运行的文件,事实上这个 apk 文件将 由编译的中间结果和原始文件生成。 apk 文件本质是一个 zip 包。这个 APK 包解压缩后的 目录结构如下所示: 值得注意的是,这里的 xml 文件经过了处理,和原始的文件不太一样,不能按照文本文件 的方式阅读。 classes |-- META-INF | `-- MANIFEST.MF `-- com `-- example `-- android `-- helloactivity |-- HelloActivity.class |-- R$attr.class |-- R$id.class |-- R$layout.class |-- R$string.class `-- R.class out/target/product/generic/obj/APPS/HelloActivity_intermediates/package.apk_FILES/ |-- AndroidManifest.xml |-- META-INF | |-- CERT.RSA | |-- CERT.SF | `-- MANIFEST.MF |-- classes.dex |-- res | `-- layout | `-- hello_activity.xml `-- resources.arsc----------------------------------- Android 编程基础 8 第四部分: 源代码的各个文件 Android.mk 是整个工程的 “ Makefile ” ,其内容如下所示: � LOCAL_PATH:= $(call my-dir) � include $(CLEAR_VARS) � LOCAL_MODULE_TAGS := samples � # Only compile source java files in this apk. � LOCAL_SRC_FILES := $(call all-java-files-under, src) � LOCAL_PACKAGE_NAME := HelloActivity � LOCAL_SDK_VERSION := current � include $(BUILD_PACKAGE) � # Use the following include to make our test apk. � include $(call all-makefiles-under,$(LOCAL_PATH)) 这个文件在各个 Android 的工程中都是类似的,其中 LOCAL_PACKAGE_NAME 表示了这 个包的名字。 LOCAL_MODULE_TAGS 表示了模块的标,在这里使用的是 samples ,正式的应用程序( packages 目录中的应用)中多使用 eng development 。 AndroidManifest.xml 是这个 HelloActivity 工程的描述文件,其内容如下所示: 其中 package 用于说明这个包的名称, android:labeapplication 中的内容是表示这个应用程序 在界面上显示的标题, activity 中的 android:name 表示这个 Android 的活动的名称。 ----------------------------------- Android 编程基础 9 文件 src/com/example/android/helloactivity/HelloActivity.java 是程序主要文件,由 JAVA 语言 写成 com.example.android.helloactivity 表示的是这个包的名称 , 在文件的头部引入了两个包 android.app.Activity 是一个 Android 活动( Activity )包,每一个 Android 活动都需要继承 Activity 类。 包 android.os.Bundle 用于映射字符串的值。 onCreate() 是一个重载的函数,在这个函数中实现应用程序创建的所执行的过程。其中 setContentView() 设置当前的视图( View )。 设置的方法是使用一个文件,这个文件因此决定了视图中包含的内容。这里使用的是 R.layout.hello_activity ,表示从 res/layout/ 目录中使用 hello_activity.xml 文件。 res/layout/hello_activity.xml 文件的内容如下所示: 其中定义了一个可编辑的文本( EditText ),下面的各项其实是它的各种属性, android:text 表示这个文本 的 内 容 ,string/hello_activity_text_text 表 示 找 到 相 应 的 文 件 , 也 就 是 res/value/string.xml 文 件 中 的 hello_activity_text_text 文本。 res/value/string.xml 的内容如下所示: hello_activity_text_text 文本被 res/layout/hello_activity.xml 文件引用,正是应用程序运行时在 屏幕显示的文本。 package package package package com.example.android.helloactivity; import import import import android.app.Activity; import import import import android.os.Bundle; public public public public class class class class HelloActivity extends extends extends extends Activity { public public public public HelloActivity() { } @ Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); setContentView(R.layout.hello_activity); } } He llo , World! ----------------------------------- Android 编程基础 10 Android Android Android Android ADB ADB ADB ADB 工具使用 adb(Android Debug Bridge) 是 Android 提供的一个通用调试工具,借助这个工具,我妈可以管理设备或手机 模 拟器的状态。 adb adb adb adb 功能操作: � 快速更新设备或手机模拟器中的代码,如应用或 Android 系统升级 � 在设备上运行 shell 命令 � 管理设备或手机模拟器上预定端口 � 在设备或手机模拟器上复制、粘贴文件 adb adb adb adb 常用操作: 安装应用到模拟器 Android 没有提供一个卸载应用的命令,只能手动删除: 进入设备或模拟器的 Shell 通过以上命令,可以进入设备或模拟器的 shell 环境中,在这个 Linux Shell 中,你可以执行各种 Linux 的命 令 , 另外如果只想执行一条 shell 命令,可以采用以下方式: 如: 会打印出内核的调试信息 发布端口 可以设置任意的端口号,做为主机向模拟器或设备的请求端口。如 : adb install app.apk adb shell cd data/app rm app.apk adb shell adb shell [command] adb shell dmesg adb forward tcp:5555 tcp:8000----------------------------------- Android 编程基础 11 复制文件 可向一个设备或从一个设备中复制文件 � 复制一个文件或目录到设备或模拟器上: 如: � 从设备或模拟器上复制一个文件或目录 如: 搜索 / 等待模拟器、设备实例 取得当前运行的模拟器、设备的实例列表及每个实例的状态 | 等待正在运行的设备 查看 Bug 报告 记录无线通讯日志 无线通讯记录日志非常多,在运行时没必要记录,可以通过命令设置记录 获取设备 ID 和序列号 访问数据库 SQLite3 adb push adb push test.txt /tmp/test.txt adb pull adb pull /android/lib/libwebcore.os adb devices adb wait-for-device adb bugreport adb shell logcat -b radio adb get-product adb get-serialno adb shell sqlite3----------------------------------- Android 编程基础 12 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 模拟器 模拟器参数 参数格式 option 选项 emulator [option] [-qemu args] -sysdir 为模拟器在 目录中搜索系统硬盘镜像 -system 为模拟器从 文件中读取初始化系统镜像 -datadir 设置用户数据写入的目录 -kernel 为模拟器设置使用指定的模拟器内核 -ramdisk 设置内存 RAM 镜像文件 ( 默认为 /ramdisk.img) -image 废弃,使用 -system 替代 -init-data 设置初始化数据镜像 ( 默认为 /userdata.img) -initdata 和 "-init-data " 使用方法一致 -data 设置数据镜像 ( 默认为 /userdata-qemu.img) -partition-size system/data 分区容量大小 (MB) -cache 设置模拟器缓存分区镜像 ( 默认为 零时文件 ) -no-cache 禁用缓存分区 -nocache 与 "-no-cache" 使用方法相同 -sdcard 指定模拟器 SDCard 镜像文件 ( 默认为 /sdcard.img) -wipe-data 清除并重置用户数据镜像 ( 从 initdata 拷贝 ) -avd 指定模拟器使用 Android 虚拟设备 -skindir 设置模拟器皮肤 在 目录中搜索皮肤 ( 默认为 /skins 目录 ) -skin 选择使用给定的皮肤 -no-skin 不适用任何模拟器皮肤 -noskin 使用方法与 "-no-skin" 相同 -memory 物理 RAM 内存大小 (MB) -netspeed 设置最大网络下载、上传速度 -netdelay 网络时延模拟 -netfast 禁用网络形态 -tarce 代码配置可用 -show-kernel 显示内核信息 -shell 在当前终端中使用根 Shell 命令 -no-jni Dalvik 运行时禁用 JNI 检测 -nojni 使用方法与 "-no-jni" 相同 -logcat 输出给定 tag 的 Logcat 信息----------------------------------- Android 编程基础 3 -no-audio 禁用音频支持 -noaudio 与 "-no-audio" 用法相同 -audio 使用指定的音频 backend -audio-in 使用指定的输入音频 backend -audoi-out 使用指定的输出音频 backend -raw-keys 禁用 Unicode 键盘翻转图 -radio 重定向无线模式接口到个性化设备 -port 设置控制台使用的 TCP 端口 -ports , 设置控制台使用的 TCP 端口和 ADB 调试桥使用的 TCP 端口 -onion 在屏幕上层使用覆盖 PNG 图片 -onion-alpha 指定上层皮肤半透明度 -onion-rotation 0|1|2|3 指定上层皮肤旋转 -scale 调节模拟器窗口尺寸 ( 三种: 1.0-3.0 、 dpi 、 auto) -dpi-device 设置设备的 resolution (dpi 单位 ) ( 默认 165) -http-proxy 通过一个 HTTP 或 HTTPS 代理来创建 TCP 连接 -timezone 使用给定的时区,而不是主机默认的 -dns-server 在模拟系统上使用给定的 DNS 服务 -cpu-delay 调节 CUP 模拟 -no-boot-anim 禁用动画来快速启动 -no-window 禁用图形化窗口显示 -version 显示模拟器版本号 -report-console 向远程 socket 报告控制台端口 -gps 重定向 GPS 导航到个性化设备 -keyset 指定按键设置文件名 -shell-serial 根 shell 的个性化设备 -old-system 支持旧版本 (pre 1.4) 系统镜像 -tcpdump 把网络数据包捕获到文件中 -bootchart bootcharting 可用 -qemu args.... 向 qemu 传递参数 -qemu -h 显示 qemu 帮助 -verbose 和 "-debug-init" 相同 -debug 可用、禁用调试信息 -debug- 使指定的调试信息可用 -debug-no- 禁用指定的调试信息 -help 打印出该帮助文档 -help- 打印出指定 option 的帮助文档 -help-disk-images 关于硬盘镜像帮助 -help-keys 支持按钮捆绑 ( 手机快捷键 ) -help-debug-tags 显示出 -debug 命令中的 tag 可选值 -help-char-devices 个性化设备说明 -help-environment 环境变量 -help-keyset-file 指定按键绑定设置文件 -help-virtula-device 虚拟设备管理----------------------------------- Android 编程基础 4 -help-sdk-images 当使用 SDK 时关于硬盘镜像的信息 -help-build-images 当构建 Android 时,关于硬盘镜像的信息 -help-all 打印出所有帮助----------------------------------- Android 编程基础 5 进程: 在 Android 中,进程完全是应用程序的实现细节,不是用户一般想象的那样。 它们的用途很简单: � 通过把不信任或是不稳定的代码放到其他进程中来提高稳定性或是安全性 � 通过在相同的进程中运行多个 .apk 代码来减少消耗 � 通过把重量级代码放入一个分开的进程中来帮助系统管理资源。该分开的进程可以被应用程序的其他 部 分单独地杀死 � 如果两个没有共享相同的用户 ID 的 .apk 试图在相同的进程中运行,这将不被允许,并且系统会为每一 个 apk 程序创建不同的进程会 线程 � Android 让一个应用程序在单独的线程中,指导它创建自己的线程 � 应用程序组件( Activity 、 service 、 broadcast receiver )所有都在理想的主线程中实例化 � 没有一个组件应该执行长时间或是阻塞操作 ( 例如网络呼叫或是计算循环 ) 当被系统调用时,这将中断所 有在该进程的其他组件 � 你可以创建一个新的线程来执行长期操作----------------------------------- Android 编程基础 6 Android Android Android Android 释放手机资源,进程释放优先级 当系统资源消耗, Android 将会杀死一些进程来释放资源。 进程优先级顺序: ① 前台进程: 包含一个前台 Activity 、包含一个正在运行的广播接收器、正在运行的服务(当前用户所需的 Activity 、 正在屏幕顶层运行的 Activity ) ② 可视进程: 包含一个可视化的 Activity ( Activity 可视的,但是不是在前台的( onPause ))、例如显示在一个前台对 话框之后的以前的 Activity ) ③ 服务进程: 包含一个被开启的服务 ( 处理服务,不是直接可视,例如媒体播放器,网络上传、下载 ) ④ 后台进程: 包含一个不可视的 Activity( 带有一个当前不可视的 Activity 、可以在任意时刻杀死该进程来回收内存 ) ⑤ 空进程 没有持有任何应用程序组件----------------------------------- Android 编程基础 7 Android Android Android Android 应用开发 1 1 1 1 分析 Hello Hello Hello Hello Android Android Android Android 打开 Hello Android 工程 Main.xml src 文件夹 HelloAndroid.java R.java Android Library Assets 文件夹 源文件 主程序文件 资源文件 Java 库 静态文件 打包 res 文件夹 drawable 文件夹 layout 文件夹 values 文件夹 程序图标 (ico.png) 布局 UI (main.xml) 程序用到的 String 、颜色 **(string.xml) AndroidMainfest.xml 描述应用程序、构成、组件、权限 bin 文件夹 classes.dex HelloAndroid.apk 自定义的包文件夹 编译的 java 二进制 码 Android 安装包 (APK 包 ) 存放编译后的字节码文件 整体布局 表示线性布局 xmlns:android = "http://schemas.android.com/apk/res/android" 名字空间 android:orientation = "vertical" 控件布局 垂直往下布局 android:layout_width = "fill_parent" android:layout_height = "fill_parent" 上层控件填充满 图形空间 派生于 View ----------------------------------- Android 编程基础 8 R.java 通过 res 文件夹下的 xml 文件定义自动生成的, main.xml ico.png string.xml 是配套的关联,进行修改后 R.java 自动重新生成 AndroidManifest.xml 有关版本,程序信息, java 包,程序图标,程序记录信息等。 Manifest.xml 文件轮廓 ----------------------------------- Android 编程基础 9 添加编辑框与按钮 package package package package zyf.Study.AndroidSturdyByMyself; import import import import android.app.Activity; import import import import android.os.Bundle; import import import import android.view.View; import import import import android.view.View.OnClickListener; import import import import android.widget.Button; import import import import android.widget.EditText; import import import import android.widget.TextView; public public public public class class class class AndroidSturdyByMyself extends extends extends extends Activity { private private private private EditText getNameEditText ; private private private private Button button_Login ; private private private private TextView show_Login_TextView ; /** Called when the activity is first created. */ @Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); setContentView(R.layout. main ); getNameEditText =(EditText)findViewById(R.id. widget29_getName_EditText ); button_Login =(Button)findViewById(R.id. widget30_Login_Button ); show_Login_TextView =(TextView)findViewById(R.id. widget31_showLogin_TextView ); button_Login .setOnClickListener( new new new new OnClickListener(){ @Override public public public public void void void void onClick(View v) { // TODO TODO TODO TODO Auto-generated method stub show_Login_TextView .setText( getNameEditText .getText()+ " 欢迎您进入 " ); } }); } }----------------------------------- Android 编程基础 10 使用 Intent Intent Intent Intent 启动另一个 Activity Activity Activity Activity 在多个 Activity Activity Activity Activity 之间切换时候,注意每个 Activity Activity Activity Activity 都应在 AndroidManifest.xml AndroidManifest.xml AndroidManifest.xml AndroidManifest.xml 中有所声 明定义(如下) 在不同 Task Task Task Task 中启动 Activity Activity Activity Activity Intent.FLAG_ACTIVITY_NEW_TASK Intent showNextPage_Intent= new new new new Intent(); showNextPage_Intent.setClass(UsingBundel. this this th

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值