APK扩展文件及使用_第1页
APK扩展文件及使用_第2页
APK扩展文件及使用_第3页
APK扩展文件及使用_第4页
APK扩展文件及使用_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

APK扩展文件基本知识一、APK扩展文件基本知识

AndroidMarket(GooglePlayStore)中每个APK文件的最大限制是50MB。如果您的程序中包含大量的数据文件,以前您只能把这些数据文件放到自己的服务器上,当用户启动程序的时候让用户去下载。现在这些数据文件可以直接上传到AndroidMarket了。在新的Market控制台上传App的时候,可以添加扩展文件了。

下面就来看看什么时候该使用扩展文件,该如何使用?

每个APK可以有2个扩展文件,每个文件最大限制是2GB。为了减少用户的带宽消耗,最好使用压缩格式文件吧。这两扩展文件具有不同的用途:

第一个被称为main(主)扩展文件,该扩展文件保护您程序中需要用到的附加数据;

第二个被称为patch扩展(修补)文件,该文件是可选的,并且应该只包含一些不同版本的补丁数据。

当然您可以按照您需要的方式来使用这两个扩展文件,不过Android官方还是推荐把这两个文件的功能分开。main扩展文件包含核心数据,并且尽量不随程序版本的升级去修改;而patch扩展文件可以随程序版本的升级做修改。为了帮助大家理解具体的含义,我们使用一个地图App来解释下:

比如Google地图程序需要包含一个离线地图数据包,这样可以方便用户离线查看地图,在程序发布的时候,可以把现有的离线数据包作为main扩展文件上传到Market。然后过了半年Google地图更新了,新添加了一些刚刚修好的高速公路、新建立的商场等信息,可以把这些新增的信息作为patch扩展文件使用。这样Google地图1.0版本对应一个main扩展文件;而Google地图1.1版本对应一个main扩展文件和一个1.1版本的patch扩展文件;Google地图1.2版本对应一个main扩展文件和一个1.2版本的patch扩展文件。这里面的main扩展文件是同一个文件而patch扩文件是随版本变化的。

这样的好处就是当程序升级的时候,用户不用重新下载main扩展文件了,只需要下载少量的新增文件即可,节省用户流量。二、扩展文件的命名格式

扩展文件可以使用任何文件格式(ZIP,PDF,MP4,等)。不管任何文件格式Android都认为他们是obb(opaquebinaryblobs)文件,并且会根据如下文件命名规则来重命名扩展文件:

[main|patch].<expansion-version>.<package-name>.obbmainorpatch

指定文件是main扩展文件还是patch扩展文件,每个APK只能有一个main扩展文件和一个patch扩展文件。<expansion-version>

和第一次上传该扩展文件的APK文件的android:versionCode一致。后续版本的APK可以重用前面上传的扩展文件。

您程序的Java包名<package-name>

例如程序的版本为5,程序的包名为org.goodev.expansion.downloader。则上传的main扩展文件会被重命名为:

.goodev.expansion.downloader.obb三、扩展文件的保存位置

当AndroidMarket下载程序的扩展文件的时候会保存到系统的共享存储区。为了确保程序正常运行,您不能删除、移动或者重命名扩展文件。在某些设备上Market无法自动下载该扩展文件,那么您应该在程序启动的时候去下载该文件并且保存到同样的位置。

扩展文件保存位置如下:

<shared-storage>/Android/obb/<package-name>/<shared-storage>

代表共享文件的目录路径,通过函数getExternalStorageDirectory()获取;

<package-name>

APK的Java包名。

对于每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件。当更新程序的时候,如果有新的扩展文件则新文件会覆盖旧的扩展文件。

如果您需要解压缩扩展文件来使用,请注意不要删除该.obb文件,并且也不要把文件解压缩到该目录。您应该把解压缩后的文件保存到getExternalFilesDir()返回的目录下面。如果有可能的话,最好使用程序能直接读取的文件格式而不用再次解压缩文件了。Android开发团队提供了一个项目(APKExpansionZipLibrary)可以直接读取ZIP文件中的内容而不用解压缩该文件.

需要注意的是:保存在系统共享存储区的文件,用户和其他APP也可以访问。四、下载扩展文件的流程

在大多数情况下,Market会在下载APK的同时去下载扩展文件。然而,在某些情况下Market无法下载扩展文件或者用户删除了以前下载的扩展文件,您的程序需要处理这种异常情况。当您的程序启动的时候,可以检测文件是否存在并且可以从Market上下载。

开发者检查清单:

您可以通过下面的清单来检查是否需要使用扩展文件1.您的程序是否真的需要超过50MB的大小限制。在移动设备上空间是非常宝贵的,您应该尽可能减少App的大小。如果您仅仅是为了提供支持多种显示设备的图片资源的话,可以考虑使用发布多个APK的方式来减少每个APK的大小。2.判断哪些数据需要打包为扩展文件发布。3.在程序中添加访问共享存储区中扩展文件的代码4.在程序的启动Activity中添加检测扩展文件是否存在,以及下载扩展文件的代码五、扩展文件的规则和限制1.每个扩展文件最大为2GB2.用户必需要从AndroidMarket获取您的程序才能自动从Market中下载扩展文件3.当在您的程序中下载扩展文件的时候,Market每次都会为每个文件生成一个唯一的下载URL,该URL会在短期内失效。4.当你上传一个新的APK的时候,可以选择使用以前上传的扩展文件5.如果您使用多个APK文件来适配不同的设备,并且也希望使用多个扩展文件。为了获取一个唯一的versionCode和不同的Marketfilter,您需要分别为每个设备上传不同的APK文件。6.不能通过更新扩展文件来发布一个新的版本。7.不要在obb/文件夹中保存其他数据8.不要删除或者重命名.obb文件

六、APK扩展文件使用实例

要在App中使用扩展文件,需要两个附加的Android库项目:

1)GoogleMarketLicensingpackage

2)GoogleMarketAPKExpansionLibrarypackage

可以通过AndroidSDKManager来下载,也可以直接通过如下链接下载:

/android/repository/market_licensing-r02.zip

/android/repository/market_apk_expansion-r01.zip

下载完成后使用market_licensing-r02.zip文件中的目录google_market_licensing\library来创建一个库项目;然后使用market_apk_expansion-r01.zip中的google_market_apk_expansion\downloader_library来创建另外一个库项目。同时为了简化对ZIP格式扩展文件的处理,在market_apk_expansion-r01.zip文件中还包含了一个对ZIP文件处理的库项目:google_market_apk_expansion\zip_file。如果您使用的扩展文件格式是ZIP,那么也可以创建这个库项目。

1.声明需要的权限

1234567891011121314151617181920<manifest...>

<!--RequiredtoaccessAndroidMarketLicensing-->

<uses-permissionandroid:name="com.android.vending.CHECK_LICENSE"/>

<!--RequiredtodownloadfilesfromAndroidMarket-->

<uses-permissionandroid:name="android.permission.INTERNET"/>

<!--RequiredtokeepCPUalivewhiledownloadingfiles(NOTtokeepscreenawake)-->

<uses-permissionandroid:name="android.permission.WAKE_LOCK"/>

<!--Requiredtopollthestateofthenetworkconnectionandrespondtochanges-->

<uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>

<!--RequiredtocheckwhetherWi-Fiisenabled-->

<uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/>

<!--Requiredtoreadandwritetheexpansionfilesonsharedstorage-->

<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

...</manifest>

注意:默认情况下,下载库项目需要的APIlevel为4而APK扩展ZIP库项目需要APIlevel为5.

准备工作完成后,下面来具体看看如何使用扩展文件。2.实现下载服务(Implementingthedownloaderservice)

为了实现在后台下载文件,下载库项目提供了一个Service实现,名称为DownloaderService。您应该继承自这个文件来实现您的下载服务。为了简化下载服务的开发,该DownloaderService还实现了如下功能:注册一个BroadcastReceiver来监听设备的网络连接状态的改变。如果网络连接断开就暂停下载;如果网络连接恢复就继续下载。安排一个RTC_WAKEUP通知,当下载服务被终结的时候可以通过该通知来启动下载服务生成一个通知(Notification)来显示下载的进度以及下载错误等状态允许您的程序手工的暂停和恢复下载检测共享存储区挂载了并且可用,在下载文件之前检测文件是否已经存在、存储空间是否足够。如果出现问题就通知用户。

您仅仅需要创建一个继承自DownloaderService的类,并且实现如下三个函数即可:

getPublicKey():您Market账号的Base64编码RSA公共密钥,可以通过如下网址获取:

/publish/Home#ProfileEditorPlace:getSALT():许可策略用来生成混淆器(Obfuscator)的一组随机bytes。getAlarmReceiverClassName():返回您程序中用来重启下载进程的BroadcastReceiver类名称。当某些情况下,下载服务被意外终止的时候通过该BroadcastReceiver类来重新下载。比如进程管理的程序终止了下载服务。下面是一个DownloaderService类的实现代码:

1234567891011121314151617181920212223publicclassSampleDownloaderServiceextendsDownloaderService{

//Youmustusethepublickeybelongingtoyourpublisheraccount

publicstaticfinalStringBASE64_PUBLIC_KEY="YourAndroidMarketLVLKey";

//Youshouldalsomodifythissalt

publicstaticfinalbyte[]SALT=newbyte[]{1,42,-12,-1,54,98,

-100,-12,43,2,-8,-4,9,5,-106,-107,-33,45,-1,84

};

@Override

publicStringgetPublicKey(){

returnBASE64_PUBLIC_KEY;

}

@Override

publicbyte[]getSALT(){

returnSALT;

}

@Override

publicStringgetAlarmReceiverClassName(){

returnSampleAlarmReceiver.class.getName();

}}

然后在manifest文件中声明该Service即可。非常简单吧!3.实现alarmreceiver

为了检测下载进程和重启下载服务,DownloaderService会安排一个RTC_WAKEUPAlarm来发送一个Intent到程序的BroadcastReceiver。你必需定义这个BroadcastReceiver来调用DownloaderLibrary提供的函数,通过该函数来检测下载状态和在必要的情况下重启下载服务。

实现这个类也是非常简单的,一般来说只要覆写onReceive()函数并且调用DownloaderClientMarshaller.startDownloadServiceIfRequired()函数即可。如下所示:

1234567891011publicclassSampleAlarmReceiverextendsBroadcastReceiver{

@Override

publicvoidonReceive(Contextcontext,Intentintent){

try{

DownloaderClientMarshaller.startDownloadServiceIfRequired(context,intent,

SampleDownloaderService.class);

}catch(NameNotFoundExceptione){

e.printStackTrace();

}

}}

注意这个类的名字就是前面getAlarmReceiverClassName()函数返回的名称。然后在manifest文件中声明该receiver即可。同样很简单吧!4.开始下载扩展文件程序的主Activity(通过Launcher图标启动的Activity)应该负责检查扩展文件是否存在、如果不存在就启动下载服务。

使用DownloaderLibrary来下载需要遵守如下步骤:1)检查文件是否已经下载了

DownloaderLibrary中的Helper类中包含了一些函数来简化这个步骤:

getExtendedAPKFileName(Context,c,booleanmainFile,intversionCode)

doesFileExist(Contextc,StringfileName,longfileSize)

例如在示例项目中,在Activity的onCreate()函数中通过如下函数来检查文件是否存在:

12345678booleanexpansionFilesDelivered(){

for(XAPKFilexf:xAPKS){

StringfileName=Helpers.getExpansionAPKFileName(this,xf.mIsBase,xf.mFileVersion);

if(!Helpers.doesFileExist(this,fileName,xf.mFileSize,false))

returnfalse;

}

returntrue;}这里的XAPKFile对象保存了已知扩展文件的版本号和大小以及是否为main扩展文件。如果该函数返回false则启动下载服务。2)通过DownloaderClientMarshaller.startDownloadServiceIfRequired(Contextc,PendingIntentnotificationClient,ClassserviceClass)该函数来开始下载。

该函数的参数如下:

context:Yourapplication’sContext.

notificationClient:用来启动主Activity的PendingIntent。用在DownloaderService创建的用来显示下载进度的通知中。当用户选择该通知,系统调用该PendingIntent来打开显示下载进度的Activity(一般而言就是启动下载的Activity)。

serviceClass:程序中继承自DownloaderService的类。在必要的情况下会启动该服务来开始下载。这个函数返回一个整数来表示是否有必要下载文件。有如下几个值:NO_DOWNLOAD_REQUIRED:表示文件已经存在或者当前正在下载。LVL_CHECK_REQUIRED:表示需要授权验证来获取下载扩展文件的URL。DOWNLOAD_REQUIRED:表示扩展文件的URL已经获取到了,但是还没开始下载。LVL_CHECK_REQUIRED和DOWNLOAD_REQUIRED在本质上是一样的,一般而言您无需关注这个状态。在您的主Activity中调用startDownloadServiceIfRequired(),你只需要看看返回值是否为NO_DOWNLOAD_REQUIRED即可。如果返回值不是NO_DOWNLOAD_REQUIRED,DownloaderLibrary开始启动下载,您应该更新程序界面来显示下载进度;如果返回值是NO_DOWNLOAD_REQUIRED,表明该文件已经下载好了,您的程序可以正常启动了。例如:

123456789101112131415161718192021222324@OverridepublicvoidonCreate(BundlesavedInstanceState){

//Checkifexpansionfilesareavailablebeforegoinganyfurther

if(!expansionFilesDelivered()){

//BuildanIntenttostartthisactivityfromtheNotification

IntentnotifierIntent=newIntent(this,MainActivity.getClass());

notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|

Intent.FLAG_ACTIVITY_CLEAR_TOP);

...

PendingIntentpendingIntent=PendingIntent.getActivity(this,0,

notifierIntent,PendingIntent.FLAG_UPDATE_CURRENT);

//Startthedownloadservice(ifrequired)

intstartResult=DownloaderClientMarshaller.startDownloadServiceIfRequired(this,

pendingIntent,SampleDownloaderService.class);

//Ifdownloadhasstarted,initializethisactivitytoshowdownloadprogress

if(startResult!=DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED){

//Thisiswhereyoudosetuptodisplaythedownloadprogress(nextstep)

...

return;

}//Ifthedownloadwasn'tnecessary,fallthroughtostarttheapp

}

startApp();//Expansionfilesareavailable,starttheapp}3)当startDownloadServiceIfRequired()函数的返回值不是NO_DOWNLOAD_REQUIRED的时候,调用DownloaderClientMarshaller.CreateStub(IDownloaderClientclient,ClassdownloaderService)函数来创建一个IStub实例。这个IStub实例提供了Activity和下载服务之前的绑定功能,这样您的Activity就可以收到下载事件了。

CreateStub()函数需要一个实现了IDownloaderClient接口的类和DownloaderService的实现类作为参数。一般而言只要让Activity实现IDownloaderClient接口即可。

Android开发团队推荐在Activity的onCreate()函数中创建IStub对象(在startDownloadServiceIfRequired()函数之后创建)。例如:

123456789101112//Startthedownloadservice(ifrequired)intstartResult=DownloaderClientMarshaller.startDownloadServiceIfRequired(this,

pendingIntent,SampleDownloaderService.class);//Ifdownloadhasstarted,initializeactivitytoshowprogressif(startResult!=DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED){

//InstantiateamemberinstanceofIStub

mDownloaderClientStub=DownloaderClientMarshaller.CreateStub(this,

SampleDownloaderService.class);

//Inflatelayoutthatshowsdownloadprogress

setContentView(R.layout.downloader_ui);

return;}

当onCreate()函数返回以后,Activity会执行onResume()函数,在该函数中调用IStub的connect()函数。同样在onStop()函数中调用IStub的disconnect()函数。例如:

123456789101112131415@OverrideprotectedvoidonResume(){

if(null!=mDownloaderClientStub){

mDownloaderClientStub.connect(this);

}

super.onResume();}

@OverrideprotectedvoidonStop(){

if(null!=mDownloaderClientStub){

mDownloaderClientStub.disconnect(this);

}

super.onStop();}

调用connect()用来绑定Activity和DownloaderService。5.处理下载进度

要接收下载进度信息,需要实现IDownloaderClient接口。该接口有如下函数:onServiceConnected(Messengerm)

在初始化完IStub后,会回调该函数。该函数的参数是用来访问您的DownloaderService的,通过DownloaderServiceMarshaller.CreateProxy()函数来创建这个IDownloaderService对象,然后可以用这个对象来控制下载服务,比如暂停、继续下载等。推荐的实现方式:

12345678privateIDownloaderServicemRemoteService;...

@OverridepublicvoidonServiceConnected(Messengerm){

mRemoteService=DownloaderServiceMarshaller.CreateProxy(m);

mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());}

onDownloadStateChanged(intnewState)

当下载状态发生变化的时候调用该函数,例如开始下载或者下载完成。参数newState的值是IDownloaderClient接口中定义的一些常量之一(以STATE_开头的);

可以通过函数Helpers.getDownloaderStringResourceIDFromState()来获取一个状态的文本描述,这样用户更容易理解。例如STATE_PAUSED_ROAMING对应的文本描述是:“Downloadpausedbecauseyouareroaming/当前在漫游状态,下载停止”onDownloadProgress(DownloadProgressInfoprogress)

该函数的参数DownloadProgressInfo包含了下载进度的各种信息,例如预计完成时间、当前下载速度、完成的百分比等。可以根据该信息来更新下载界面。另外还有一些有用的函数:

requestPauseDownload()

暂停下载

requestContinueDownload()

恢复下载

setDownloadFlags(intflags)

设置下载的网络标示。当前只支持一个标示:FLAGS_DOWNLOAD_OVER_CELLULAR。通过移动网络下载扩展文件。默认情况下该标示没有启用,所以默认情况下只通过WIFI下载。6.读取扩展文件首先要获取扩展文件的路径,可以通过如下代码完成该操作:

1234567891011121314151617181920212223242526272829303132333435//ThesharedpathtoallappexpansionfilesprivatefinalstaticStringEXP_PATH="/Android/obb/";

staticString[]getAPKExpansionFiles(Contextctx,intmainVersion,intpatchVersion){

StringpackageName=ctx.getPackageName();

Vector<String>ret=newVector<String>();

if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){

//Buildthefullpathtotheapp'sexpansionfiles

Fileroot=Environment.getExternalStorageDirectory();

FileexpPath=newFile(root.toString()+EXP_PATH+packageName);

//Checkthatexpansionfilepathexists

if(expPath.exists()){

if(mainVersion>0){

StringstrMainPath=expPath+File.separator+"main."+

mainVersion+"."+packageName+".obb";

Filemain=newFile(strMainPath);

if(main.isFile()){

ret.add(strMainPath);

}

}

if(patchVersion>0){

StringstrPatchPath=expPath+File.separator+"patch."+

mainVersion+"."+packageName+".obb";

Filemain=newFile(strPatchPath);

if(main.isFile()){

ret.add(strPatchPath);

}

}

}

}

String[]retArray=newString[ret.size()];

ret.toArray(retArray);

returnretArray;}您可以在开始下载的时候,把扩展文件的版本号保存到SharedPreferences中,然后在这里使用。7.使用APKExpansionZipLibrary

APKExpansionZipLibrary项目包含了对ZIP文件的处理,您可以通过该项目提供的函数来直接读取ZIP文件内容而不用解压缩扩展文件,它把ZIP扩展文件当一个虚拟文件系统来使用。

APKExpansionZipLibrary项目包含如下类和函数:

APKExpansionSupport

提供一些函数来访问扩展文件名称和ZIP文件。getAPKExpansionFiles()

返回扩展文件的文件路径getAPKExpansionZipFile(Contextctx,intmainVersion,intpatchVersion)

返回一个包含main扩展文件和patch扩展文件的ZipResourceFile。如果您同时提供了mainVersion和patchVersion,则该函数返回main和patch扩展文件的所有内容,如果patch中的内容和main中的有重复,则使用patch的内容覆盖main中的内容。ZipResourceFile

用来处理ZIP文件的类

getInputStream(StringassetPath)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论