APK扩展文件及使用 (APK Expansion Files)

一、APK扩展文件基本知识

Android Market (Google Play Store)中每个APK文件的最大限制是50MB。如果您的程序中包含大量的数据文件,以前您只能把这些数据文件放到自己的服务器上,当用户启动程序的时候让用户去下载。现在这些数据文件可以直接上传到Android Market了。在新的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(opaque binary blobs)文件,并且会根据如下文件命名规则来重命名扩展文件: [main|patch].<expansion-version>.<package-name>.obb

main or patch       指定文件是main扩展文件还是patch扩展文件,每个APK只能有一个main扩展文件和一个patch扩展文件。

<expansion-version>        和第一次上传该扩展文件的APK文件的android:versionCode一致。后续版本的APK可以重用前面上传的扩展文件。 您程序的Java包名

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

三、扩展文件的保存位置

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

<shared-storage> 代表共享文件的目录路径,通过函数getExternalStorageDirectory()获取; <package-name> APK的Java包名。        对于每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件。当更新程序的时候,如果有新的扩展文件则新文件会覆盖旧的扩展文件。

如果您需要解压缩扩展文件来使用,请注意不要删除该.obb文件,并且也不要把文件解压缩到该目录。您应该把解压缩后的文件保存到getExternalFilesDir()返回的目录下面。如果有可能的话,最好使用程序能直接读取的文件格式而不用再次解压缩文件了。Android开发团队提供了一个项目( APK Expansion Zip Library)可以直接读取ZIP文件中的内容而不用解压缩该文件. 需要注意的是:保存在系统共享存储区的文件,用户和其他APP也可以访问。

四、下载扩展文件的流程

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

开发者检查清单:

您可以通过下面的清单来检查是否需要使用扩展文件

  • 1. 您的程序是否真的需要超过50MB的大小限制。在移动设备上空间是非常宝贵的,您应该尽可能减少App的大小。如果您仅仅是为了提供支持多种显示设备的图片资源的话,可以考虑使用发布多个APK的方式来减少每个APK的大小。
  • 2.判断哪些数据需要打包为扩展文件发布。
  • 3.在程序中添加访问共享存储区中扩展文件的代码
  • 4.在程序的启动Activity中添加检测扩展文件是否存在,以及下载扩展文件的代码

五、扩展文件的规则和限制

  • 1.每个扩展文件最大为2GB
  • 2.用户必需要从Android Market获取您的程序才能自动从Market中下载扩展文件
  • 3.当在您的程序中下载扩展文件的时候,Market每次都会为每个文件生成一个唯一的下载URL,该URL会在短期内失效。
  • 4.当你上传一个新的APK的时候,可以选择使用以前上传的扩展文件
  • 5.如果您使用多个APK文件来适配不同的设备,并且也希望使用多个扩展文件。为了获取一个唯一的versionCode和不同的Market filter, 您需要分别为每个设备上传不同的APK文件。
  • 6. 不能通过更新扩展文件来发布一个新的版本。
  • 7. 不要在obb/文件夹中保存其他数据
  • 8.不要删除或者重命名.obb文件

六、APK扩展文件使用实例

要在App中使用扩展文件,需要两个附加的Android库项目:       1)Google Market Licensing package       2)Google Market APK Expansion Library package

可以通过Android SDK Manager来下载,也可以直接通过如下链接下载: https://dl-ssl.google.com/android/repository/market_licensing-r02.zip https://dl-ssl.google.com/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. 声明需要的权限


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<manifest...>

    <!-- Required to access Android Market Licensing -->

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

 

    <!-- Required to download files from Android Market -->

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

 

    <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->

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

 

    <!-- Required to poll the state of the network connection and respond to changes -->

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

 

    <!-- Required to check whether Wi-Fi is enabled -->

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

 

    <!-- Required to read and write the expansion files on shared storage -->

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

    ...

</manifest>

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

准备工作完成后,下面来具体看看如何使用扩展文件。

2. 实现下载服务(Implementing the downloader service)

为了实现在后台下载文件,下载库项目提供了一个Service实现,名称为DownloaderService。您应该继承自这个文件来实现您的下载服务。为了简化下载服务的开发,该DownloaderService还实现了如下功能:

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

您仅仅需要创建一个继承自DownloaderService的类,并且实现如下三个函数即可: getPublicKey():您Market账号的 Base64 编码 RSA 公共密钥,可以通过如下网址获取: https://market.android.com/publish/Home#ProfileEditorPlace:

getSALT(): 许可策略用来生成混淆器(Obfuscator)的一组随机bytes。

getAlarmReceiverClassName(): 返回您程序中用来重启下载进程的BroadcastReceiver类名称。当某些情况下,下载服务被意外终止的时候通过该BroadcastReceiver类来重新下载。比如 进程管理的程序终止了下载服务。

下面是一个DownloaderService类的实现代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class SampleDownloaderService extendsDownloaderService {

    // You must use the public key belonging to your publisher account

    publicstaticfinalString BASE64_PUBLIC_KEY ="YourAndroidMarketLVLKey";

    // You should also modify this salt

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

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

    };

 

    @Override

    publicString getPublicKey() {

        returnBASE64_PUBLIC_KEY;

    }

 

    @Override

    publicbyte[] getSALT() {

        returnSALT;

    }

 

    @Override

    publicString getAlarmReceiverClassName() {

        returnSampleAlarmReceiver.class.getName();

    }

}

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

3. 实现 alarm receiver

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

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


1

2

3

4

5

6

7

8

9

10

11

public class SampleAlarmReceiver extendsBroadcastReceiver {

    @Override

    publicvoidonReceive(Context context, Intent intent) {

        try{

            DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,

                    SampleDownloaderService.class);

        }catch(NameNotFoundException e) {

            e.printStackTrace();

        }

    }

}

注意这个类的名字就是前面getAlarmReceiverClassName()函数返回的名称。然后在manifest文件中声明该receiver即可。 同样很简单吧!

4. 开始下载扩展文件

程序的主Activity(通过Launcher图标启动的Activity)应该负责检查扩展文件是否存在、如果不存在就启动下载服务。 使用Downloader Library来下载需要遵守如下步骤:

1)检查文件是否已经下载了 Downloader Library中的Helper类中包含了一些函数来简化这个步骤: getExtendedAPKFileName(Context, c, boolean mainFile, int versionCode) doesFileExist(Context c, String fileName, long fileSize) 例如在示例项目中,在Activity的onCreate()函数中通过如下函数来检查文件是否存在:


1

2

3

4

5

6

7

8

boolean expansionFilesDelivered() {

    for(XAPKFile xf : xAPKS) {

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

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

            returnfalse;

    }

    returntrue;

}

这里的XAPKFile对象保存了已知扩展文件的版本号和大小以及是否为main扩展文件。如果该函数返回false则启动下载服务。

2)通过 DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, ClassserviceClass)该函数来开始下载。 该函数的参数如下: context: Your application’s Context. 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, Downloader Library 开始启动下载,您应该更新程序界面来显示下载进度;如果返回值是  NO_DOWNLOAD_REQUIRED,表明该文件已经下载好了,您的程序可以正常启动了。

例如:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

@Override

public void onCreate(Bundle savedInstanceState) {

    // Check if expansion files are available before going any further

    if(!expansionFilesDelivered()) {

        // Build an Intent to start this activity from the Notification

        Intent notifierIntent =newIntent(this, MainActivity.getClass());

        notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |

                                Intent.FLAG_ACTIVITY_CLEAR_TOP);

        ...

        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,

                notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);

 

        // Start the download service (if required)

        intstartResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,

                        pendingIntent, SampleDownloaderService.class);

        // If download has started, initialize this activity to show download progress

        if(startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {

            // This is where you do set up to display the download progress (next step)

            ...

            return;

        }// If the download wasn‘t necessary, fall through to start the app

    }

    startApp();// Expansion files are available, start the app

}

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

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

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

例如:


1

2

3

4

5

6

7

8

9

10

11

12

// Start the download service (if required)

int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,

                pendingIntent, SampleDownloaderService.class);

// If download has started, initialize activity to show progress

if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {

    // Instantiate a member instance of IStub

    mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,

            SampleDownloaderService.class);

    // Inflate layout that shows download progress

    setContentView(R.layout.downloader_ui);

    return;

}

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

例如:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@Override

protectedvoidonResume() {

    if(null!= mDownloaderClientStub) {

        mDownloaderClientStub.connect(this);

    }

    super.onResume();

}

 

@Override

protectedvoidonStop() {

    if(null!= mDownloaderClientStub) {

        mDownloaderClientStub.disconnect(this);

    }

    super.onStop();

}

调用connect()用来绑定Activity和DownloaderService 。

5. 处理下载进度

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

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

推荐的实现方式:


1

2

3

4

5

6

7

8

private IDownloaderService mRemoteService;

...

 

@Override

public void onServiceConnected(Messenger m) {

    mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);

    mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());

}

onDownloadStateChanged(int newState)     当下载状态发生变化的时候调用该函数,例如 开始下载或者下载完成。

参数newState的值是IDownloaderClient接口中定义的一些常量之一(以 STATE_ 开头的); 可以通过函数 Helpers.getDownloaderStringResourceIDFromState()来获取一个状态的文本描述,这样用户更容易理解。例如 STATE_PAUSED_ROAMING 对应的文本描述是: “Download paused because you are roaming/当前在漫游状态,下载停止”

onDownloadProgress(DownloadProgressInfo progress) 该函数的参数DownloadProgressInfo包含了下载进度的各种信息,例如 预计完成时间、当前下载速度、完成的百分比等。可以根据该信息来更新下载界面。

另外还有一些有用的函数: requestPauseDownload() 暂停下载 requestContinueDownload() 恢复下载 setDownloadFlags(int flags) 设置下载的网络标示。当前只支持一个标示:FLAGS_DOWNLOAD_OVER_CELLULAR。 通过移动网络下载扩展文件。默认情况下该标示没有启用,所以默认情况下只通过WIFI下载。

6. 读取扩展文件

首先要获取扩展文件的路径,可以通过如下代码完成该操作:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// The shared path to all app expansion files

private final static String EXP_PATH ="/Android/obb/";

 

static String[] getAPKExpansionFiles(Context ctx, intmainVersion,intpatchVersion) {

    String packageName = ctx.getPackageName();

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

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

        // Build the full path to the app‘s expansion files

        File root = Environment.getExternalStorageDirectory();

        File expPath =newFile(root.toString() + EXP_PATH + packageName);

 

        // Check that expansion file path exists

        if(expPath.exists()) {

            if( mainVersion >0) {

                String strMainPath = expPath + File.separator +"main."+

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

                File main =newFile(strMainPath);

                if( main.isFile() ) {

                        ret.add(strMainPath);

                }

            }

            if( patchVersion >0) {

                String strPatchPath = expPath + File.separator +"patch."+

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

                File main =newFile(strPatchPath);

                if( main.isFile() ) {

                        ret.add(strPatchPath);

                }

            }

        }

    }

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

    ret.toArray(retArray);

    returnretArray;

}

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

7. 使用 APK Expansion Zip Library

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

APK Expansion Zip Library项目包含如下类和函数: APKExpansionSupport 提供一些函数来访问扩展文件名称和ZIP文件。

getAPKExpansionFiles() 返回扩展文件的文件路径

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

ZipResourceFile 用来处理ZIP文件的类 getInputStream(String assetPath) 读取ZIP文件中的具体文件,assetPath应该是相对于ZIP文件的路径信息

getAssetFileDescriptor(String assetPath) 获取ZIP文件中具体文件的 AssetFileDescriptor 信息。

APEZProvider 大多数的程序都不会用到这个类。具体情况请参考其文档。

使用APK 扩展ZIP库从ZIP文件中读取文件参考代码如下:

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

上面的代码可以读取main扩展文件或patch扩展文件(通过读取两个文件的合并文件来实现)中的任何文件

如果要读取指定的扩展文件,其方法如下:

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

8. 测试扩展文件

在发布之前要测试两个东东,下载文件和读取文件。

测试读取文件 在发布您的程序之前应该先测试下您的程序能否读取扩展文件,测试很简单,只要把扩展文件放到共享存储区的特殊位置,然后启动程序即可。 1)创建文件目录: 如果程序的包名为org.goodev,就创建如下的目录:Android/obb/org.goodev/ 2)把扩展文件添加到该目录 如果程序的包名为org.goodev,则主扩展文件名如下:main.03.org.goodev.obb。 版本号可以为大于零的任意值。

3)现在可以启动程序来测试读取扩展文件的功能了。

测试下载文件 由于在某些情况下需要在程序第一次使用的时候手工下载扩展文件。所以需要测试来确保您的程序可以成功的获取下载URL、下载文件并且保存到设备中。

您可以把程序上传到Market,同时上传扩展文件,然后不要发布程序。这样扩展文件已经可以从Market下载了。 当你测试完成后再发布您的程序。

9. 更新程序

使用扩展文件的一大好处就是每次更新App 用户不用重新下载几十上百兆的数据文件了。Android Market让你可以为每个APK提供两个扩展文件,这样可以避免每次更新App都重新下载主扩展文件数据,减少下载时间。

为了方便大家研究如下使用扩展文件,可以到这里下载示例项目代码: http://code.google.com/p/goodev-demo-code/downloads/list 文件名:Market_Downloader_Sample.zip 里面包含了所需要的各种库项目。 在Eclipse中导入即可使用。

APK扩展文件及使用 (APK Expansion Files)

时间: 2024-10-12 20:37:17

APK扩展文件及使用 (APK Expansion Files)的相关文章

APK扩展文件及使用

转自:http://blog.csdn.net/myarrow/article/details/7760579 一.APK扩展文件基本知识 Android Market (Google Play Store)中每个APK文件的最大限制是50MB.如果您的程序中包含大量的数据文件,以前您只能把这些数据文件放到自己的服务器上,当用户启动程序的时候让用户去下载.现在这些数据文件可以直接上传到Android Market了.在新的Market控制台上传App的时候,可以添加扩展文件了. 下面就来看看什么

APK扩展文件介绍、功能及用法

APK扩展文件介绍 Android Market (Google Play Store)中每一个APK文件的最大限制是50MB.假设您的程序中包括大量的数据文件,曾经您仅仅能把这些数据文件放到自己的server上,当用户启动程序的时候让用户去下载. 如今这些数据文件能够直接上传到Android Market了.在新的Market控制台上传App的时候.能够加入扩展文件了. 怎样使用扩展文件: 每一个APK能够有2个扩展文件,每一个文件最大限制是2GB. 为了降低用户的带宽消耗,最好使用压缩格式文

APK扩展文件介绍、功能及使用方法

APK扩展文件介绍 Android Market (Google Play Store)中每个APK文件的最大限制是50MB.如果您的程序中包含大量的数据文件,以前您只能把这些数据文件放到自己的服务器上,当用户启动程序的时候让用户去下载.现在这些数据文件可以直接上传到Android Market了.在新的Market控制台上传App的时候,可以添加扩展文件了. 如何使用扩展文件: 每个APK可以有2个扩展文件,每个文件最大限制是2GB.为了减少用户的带宽消耗,最好使用压缩格式文件吧. 这两扩展文

关于android 如何安装 assets文件下的apk

在自己的app中安装assets目录下的apk文件 public class MainActivity extends Activity { Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; //Toa

关于android 怎样安装 assets文件下的apk

在自己的app中安装assets文件夹下的apk文件 public class MainActivity extends Activity { Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; //To

通过上传的APK文件,解析APK文件内容,获取应用权限包名等

通过上传的APK文件,解析APK文件内容,获取应用权限包名等 1工具导入: 下载aapt.exe反编译执行程序,放入Java工程资源文件夹下(具体路径自己可定义) 2编写ApkUtil类,获取apk文件信息 public class ApkUtil { public static final String VERSION_CODE = "versionCode"; public static final String VERSION_NAME = "versionName&q

APK可视化修改工具 APK改之理(APK IDE)

标 题: [下载]APK可视化修改工具:APK改之理(APK IDE)作 者: 青椒时 间: 2013-04-10,11:03:12链 接: http://bbs.pediy.com/showthread.php?t=168001 小米人APK改之理(Apk IDE)是一款可视化的用于修改安卓Apk程序文件的工具,集成了ApkTool.Dex2jar.JD-GUI等Apk修改工具,集Apk反编译.Apk打包.Apk签名,支持语法高亮的代码编辑器,基于文件内容的关键字(支持单行代码或多行代码段)搜

我的Android进阶之旅------&gt;解决Android Studio编译后安装apk报错:The APK file does not exist on disk

1.错误描述 今天用Android Studio编译应用后安装APK的时候,报错了,错误如下所示: The APK file build\outputs\apk\OYP_2.3.4_I2Base_6476_official_debug.apk does not exist on disk. Error while Installing APK 如下图所示 2.解决方法 1.尝试了Build -> Clean Project再编译,还是无效 2.尝试重启Android Studio无效 然后我去打

【转】如何下载和离线安装Chrome的CRX扩展文件包

5月某天,Google将扩展工具库网址中的Extensions,全部跳转到Webstore,页面统一了. 这样做的后果是:原本“右键--另存”就能下载到CRX包,现在则不能了. 任何人都必须点击那个傻拉八叽的INSTALL按钮去使用喜欢的扩展…… 点击INSTALL之后,会发现窗口底部的下载器还是会弹出,只不过很快消失,这时打开Chrome内置下载器去寻找CRX,已经晚了,Chrome自动删除了CRX. Google真是霸气,苹果在线商店也没你们这么狠啊! 我觉得,把常用扩展随身携带,这其实不是