最近在优化unity游戏项目(android)内存占用,游戏打开时会先从包里的zip文件中解压一部分资源到本地,原本是使用的www进行解压,解压的包体有400M,解压完毕后发现unity竟然不会释放这部分内存给系统,而是继续持有.

查询了很多相关资料后发现这是C#的设计理念,应用如果可以成功申请到400M的内存进行解压,那这400M内存不会还给系统,而是被系统标记为空闲,后续如果要申请其它内存,会复用之前申请的这400M内存空间,减少数据吞吐提升运行效率,可是经实测发现,后续申请的内存并没有复用这400M,而是另外开辟了别的内存空间,这就无法接受了.

在试了很多办法无果后,决定抛弃C#,拥抱原生java吧,随即用java重新写了一个解压逻辑,没想到解压速度比c#还快很多,再辅以线程的帮助,解压时的loading动画也顺畅了很多,一举两得:

下面给出代码,一个是从apk包里解压文件,一个是从obb包里解压文件(如果采用obb分包,读取文件的路径略有不同)

public void unzipFromApk(Context context, String filePath) {
        new Thread(new Runnable(){

            @Override
            public void run(){
                String destination = getExternalFilesDir("StreamingAssets").getAbsolutePath();
                //String destination2 = getExternalFilesDir("").getAbsolutePath() + "/StreamingAssets/";

                Log.d("Unzip", "destination = " + destination);
                //Log.d("Unzip", "destination2 = " + destination2);

                try {
                    //FileInputStream inputStream = new FileInputStream(filePath);
                    ZipInputStream zipStream = new ZipInputStream(context.getAssets().open(filePath));
                    ZipEntry zEntry = null;
                    String szName = "";

                    while ((zEntry = zipStream.getNextEntry()) != null) {
                        Log.d("Unzip", "Unzipping " + zEntry.getName() + " at "
                                + destination);
                        szName = zEntry.getName();
                        if (zEntry.isDirectory()) {
                            szName = szName.substring(0, szName.length() - 1);
                            String folderName = destination + File.separator + szName;
                            File folder = new File(folderName);
                            folder.mkdirs();
                            zipedFileNum++;
                        } else {
                            String fileName = destination + File.separator + szName;
                            File file = new File(fileName);
                            if (!file.exists()) {
                                //Log.e(TAG, "Create the file:" + outPathString + File.separator + szName);
                                file.getParentFile().mkdirs();
                                file.createNewFile();
                            }

                            FileOutputStream fout = new FileOutputStream(fileName);
                            BufferedOutputStream bufout = new BufferedOutputStream(fout);
                            byte[] buffer = new byte[1024];
                            int read = 0;
                            while ((read = zipStream.read(buffer)) != -1) {
                                bufout.write(buffer, 0, read);
                            }

                            zipedFileNum++;

                            zipStream.closeEntry();
                            bufout.close();
                            fout.close();
                        }
                    }
                    zipStream.close();
                    Log.d("Unzip", "Unzipping complete. path :  " + destination);
                } catch (Exception e) {
                    Log.d("Unzip", "Unzipping failed");
                    e.printStackTrace();
                }
            }

        }).start();
    }

    public void unzipFromObbFile(Context context, String filePath) {
        new Thread(new Runnable(){

            @Override
            public void run(){
                String destination = getExternalFilesDir("StreamingAssets").getAbsolutePath();

                try {
                    //获取Obb扩展文件路径
                    String path = getObbDir().getPath() + "/main." +getVersionCode(MainActivity.this) + "." + getAppPackageName(MainActivity.this) + ".obb";
                    //得到obb文件实例
                    ZipResourceFile expansionFile = new ZipResourceFile(path);
                    //读取
                    InputStream inputStream = expansionFile.getInputStream("assets/" + filePath);

                    if (inputStream == null)
                    {
                        Log.d("Unzip", "zip file not found in assets");
                        inputStream = expansionFile.getInputStream(filePath);
                    }
                    else
                    {
                        Log.d("Unzip", "zip file found in assets!");
                    }

                    //FileInputStream inputStream = new FileInputStream(filePath);
                    ZipInputStream zipStream = new ZipInputStream(inputStream);
                    ZipEntry zEntry = null;
                    String szName = "";

                    while ((zEntry = zipStream.getNextEntry()) != null) {
                        Log.d("Unzip", "Unzipping " + zEntry.getName() + " at "
                                + destination);
                        szName = zEntry.getName();
                        if (zEntry.isDirectory()) {
                            szName = szName.substring(0, szName.length() - 1);
                            String folderName = destination + File.separator + szName;
                            File folder = new File(folderName);
                            folder.mkdirs();
                            zipedFileNum++;
                        } else {
                            String fileName = destination + File.separator + szName;
                            File file = new File(fileName);
                            if (!file.exists()) {
                                //Log.e(TAG, "Create the file:" + outPathString + File.separator + szName);
                                file.getParentFile().mkdirs();
                                file.createNewFile();
                            }

                            FileOutputStream fout = new FileOutputStream(fileName);
                            BufferedOutputStream bufout = new BufferedOutputStream(fout);
                            byte[] buffer = new byte[1024];
                            int read = 0;
                            while ((read = zipStream.read(buffer)) != -1) {
                                bufout.write(buffer, 0, read);
                            }

                            zipedFileNum++;

                            zipStream.closeEntry();
                            bufout.close();
                            fout.close();
                        }
                    }
                    zipStream.close();
                    inputStream.close();
                    Log.d("Unzip", "Unzipping complete. path :  " + destination);
                } catch (Exception e) {
                    Log.d("Unzip", "Unzipping failed");
                    e.printStackTrace();
                }
            }

        }).start();
    }

下面是我在搜寻解决方案时查到的一些相关资料,也是unity申请内存后不释放的类似问题:
https://forum.unity.com/threads/big-byte-array-will-cause-memory-leak.403496/

https://answers.unity.com/questions/225460/big-arrays-are-never-garbage-collected-why.html?_ga=2.57270627.890478631.1572264787-375802978.1561963564

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐