Android 读取U盘apk文件信息,拔掉U盘进程被杀

开发环境

Android 9

前提及重点

打印应用程序进程占用的文件资源

public class FdInfo {
    public static class Node {
        Node(int fd, String path) {
            this.fd = fd;
            this.path = path;
        }

        @Override
        public String toString() {
            return fd + "--->" + path;
        }

        public int fd;
        public String path;
    }

    public static List<Node> getFdList() {
        int pid = android.os.Process.myPid();
        String fdPath = "/proc/" + pid + "/fd";
        List<Node> fdList = new ArrayList<>();
        File fdDir = new File(fdPath);
        if (fdDir.exists() && fdDir.canRead()) {
            String[] filePath = fdDir.list();
            if (null != filePath) {
                for (String temp : filePath) {
                    File subFile = new File(fdPath + File.separator + temp);
                    try {
                        fdList.add(new Node(Integer.parseInt(subFile.getName()), subFile.getCanonicalPath()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return fdList;
    }
}

过滤出u盘路径下的文件占用

// uPath为u盘的根路径
public class IoUtils {
    public static void list(String uPath) {
        log("============ list start ============");
        List<FdInfo.Node> nodeList = FdInfo.getFdList();
        if (nodeList.size() > 0) {
            for (FdInfo.Node Node : nodeList) {
                if (Node.path.startsWith(uPath)) {
                    log("文件占用:" + Node);
                }
            }
            nodeList.clear();
        }
        log("============ list end ============");
    }
    
    private static void log(String text) {
        Log.d("apk_manager", text);
    }
}

此代码为本篇核心,问题的解决围绕于它。

问题1

调用PackageManager#getPackageArchiveInfo(String archiveFilePath, int flags)查看完u盘apk文件信息,拔掉u盘,崩溃

    String uPath = "/storage/usb1-1-2-p4";
    String apkPath = uPath + "/android_apps/test0.apk";

    File appsFile = new File(apkPath);
    log("解析apk文件:" + appsFile.getAbsolutePath());
    AppInfo appInfo = new AppInfo();
    appInfo.path = appsFile.getAbsolutePath();
    appInfo.fileName = appsFile.getName();
    log("获取包管理器");
    PackageManager pm = getPackageManager();
    log("获取包信息");
    IoUtils.list(uPath);
    PackageInfo usbPackageInfo = pm.getPackageArchiveInfo(appInfo.path, PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
    IoUtils.list(uPath);

日志(精简过的)

023-05-22 18:13:01.971 14191-14191 apk_manager             pid-14191                            D  usb插入:
2023-05-22 18:13:03.849 14191-14191 apk_manager             pid-14191                            D  usb装载:
2023-05-22 18:13:06.352 14191-14414 apk_manager             pid-14191                            D  解析apk文件:/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:13:06.353 14191-14414 apk_manager             pid-14191                            D  获取包管理器
2023-05-22 18:13:06.353 14191-14414 apk_manager             pid-14191                            D  获取包信息
2023-05-22 18:13:06.356 14191-14414 apk_manager             pid-14191                            D  ============ list start ============
2023-05-22 18:13:06.399 14191-14414 apk_manager             pid-14191                            D  ============ list end ============
2023-05-22 18:13:06.476 14191-14414 apk_manager             pid-14191                            D  ============ list start ============
2023-05-22 18:13:06.489 14191-14414 apk_manager             pid-14191                            D  文件占用:62--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:13:06.489 14191-14414 apk_manager             pid-14191                            D  文件占用:70--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:13:06.489 14191-14414 apk_manager             pid-14191                            D  ============ list end ============
2023-05-22 18:13:10.062 14191-14191 apk_manager             pid-14191                            D  usb拔出:
2023-05-22 18:13:11.619   457-459   vold                    vold                                 W  Found symlink /proc/14191/fd/62 referencing /storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:13:11.619   457-459   vold                    vold                                 W  Found symlink /proc/14191/fd/70 referencing /storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:13:11.620   457-459   vold                    vold                                 W  Sending Interrupt to 14191
2023-05-22 18:13:11.719  1272-1528  InputDispatcher         system_server                        E  channel '90ed54 com.test.u/com.xxx.xxx.AppActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
2023-05-22 18:13:18.803 14434-14434 apk_manager             com.test.u                           D  usb卸载:

这里可以看到,对同一个文件,这个方法产生了两个占用。查看过PackageManager#getPackageArchiveInfo() FrameWork层的代码,发现资源都有通过 libcore.io.IoUtils.closeQuietly(assetLoader); 进行释放,但是释放完都没有解除占用。我用反射模拟模拟了加载的过程,d 模拟的过程中,我萌生了一个想法,我想看看调用完closeQuietly()到底过多久会释放,这个想法造就了这篇文章。我在调用PackageManager#getPackageArchiveInfo()后,执行下面的代码

 new Thread(new Runnable() {
    @Override
     public void run() {
         while (true){
             IoUtils.list(uPath);
             SystemClock.sleep(100);
         }
     }
 }).start();

日志:

2023-05-22 18:52:03.040 17896-18371 apk_manager             com.test.u                           D  解析apk文件:/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:52:03.042 17896-18371 apk_manager             com.test.u                           D  获取包管理器
2023-05-22 18:52:03.042 17896-18371 apk_manager             com.test.u                           D  获取包信息
2023-05-22 18:52:03.044 17896-18371 apk_manager             com.test.u                           D  ============ list start ============
2023-05-22 18:52:03.045 17896-17896 Thread-2                com.test.u                           I  type=1400 audit(0.0:8159): avc: denied { getattr } for path="/sys/kernel/debug/tracing/trace_marker" dev="tracefs" ino=6068 scontext=u:r:system_app:s0 tcontext=u:object_r:debugfs_trace_marker:s0 tclass=file permissive=1
2023-05-22 18:52:03.072 17896-18371 apk_manager             com.test.u                           D  ============ list end ============
2023-05-22 18:52:03.148 17896-18373 apk_manager             com.test.u                           D  ============ list start ============
2023-05-22 18:52:03.173 17896-18373 apk_manager             com.test.u                           D  文件占用:62--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:52:03.173 17896-18373 apk_manager             com.test.u                           D  文件占用:68--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:52:03.173 17896-18373 apk_manager             com.test.u                           D  ============ list end ============

......

2023-05-22 18:52:05.133 17896-18373 apk_manager             com.test.u                           D  ============ list start ============
2023-05-22 18:52:05.143 17896-18373 apk_manager             com.test.u                           D  文件占用:62--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:52:05.144 17896-18373 apk_manager             com.test.u                           D  文件占用:68--->/storage/usb1-1-2-p4/android_apps/test0.apk
2023-05-22 18:52:05.144 17896-18373 apk_manager             com.test.u                           D  ============ list end ============
2023-05-22 18:52:05.244 17896-18373 apk_manager             com.test.u                           D  ============ list start ============
2023-05-22 18:52:05.262 17896-18373 apk_manager             com.test.u                           D  ============ list end ============

上面日志打印太多,做了下精简,我们看到经过大概2秒左右的循环调用,占用被解除了,这个时间有长有短,甚至有调用一次就能释放的,这时候拔u盘就不再崩溃。但是如果你把循环打印的代码注掉,不要这个打印这个过程,即使经过10多秒,拔u盘照样会崩溃。所以我们可以把它做为一个释放占用的方法来使用。使用时需要一些技巧和统筹来替代无限循环和对线程进行管理。
题外话:发现这个方法的时,我的感觉像是哔了dog。

释放的工具类

根据问题1中得到的释放方式,我做了个工具类

public class IoUtils {

    private static class LoopThread extends Thread {
        // 新添加的,需要记录
        static int WHAT_ADD = 0;
        // 非新添加,进行中的,不需要记录
        static int WHAT_LAST = 1;

        public Handler handler;

        private final HashMap<String, List<FdInfo.Node>> pathNoReleaseMap = new HashMap<>();

        private Runnable delayAction;

        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler(Looper.myLooper()) {

                @Override
                public void handleMessage(Message msg) {

                    String uPath = (String) msg.obj;
                    pathNoReleaseMap.put(uPath, new ArrayList<>());

                    if (msg.what == WHAT_ADD && delayAction != null) {
                        // 如果有待执行,将uPath放入后,等待待执行,防止 WHAT_ADD 调用多次,创建多个待执行
                        return;
                    }
                    if (delayAction != null) {
                        // 如果是待执行,将待执行置空,执行一次
                        delayAction = null;
                    }

                    // 只要 pathNoReleaseMap 有值,说明还需要继续释放, 创建新的待执行
                    delayAction = new Runnable() {
                        @Override
                        public void run() {
                            IoUtils.requestNoRelease(pathNoReleaseMap);
                            for (Map.Entry<String, List<FdInfo.Node>> item : pathNoReleaseMap.entrySet()) {
                                String itemPath = item.getKey();
                                List<FdInfo.Node> noReleaseList = item.getValue();

                                if (noReleaseList == null || noReleaseList.size() == 0) {
                                    // 1.释放完成,需要从记录中移除
                                    pathNoReleaseMap.remove(itemPath);
                                }
                            }
                            if (pathNoReleaseMap.keySet().size() > 0) {
                                Message message = handler.obtainMessage(LoopThread.WHAT_LAST);
                                message.obj = uPath;
                                handler.sendMessage(message);
                            }

                        }
                    };
                    handler.postDelayed(delayAction, 100);

                }
            };
            Looper.loop();
        }
    }

    private static final LoopThread loopThread = new LoopThread();

    static {
        loopThread.start();
    }

    /**
     * 释放占用的u盘文件
     *
     * @param uPath u盘路径
     */
    public static void release(String uPath) {
        Handler threadHandler = loopThread.handler;
        if (threadHandler != null) {
            Message message = threadHandler.obtainMessage(LoopThread.WHAT_ADD);
            message.obj = uPath;
            threadHandler.sendMessage(message);
        }
    }

    private static void requestNoRelease(HashMap<String, List<FdInfo.Node>> pathNoReleaseMap) {
        log("============ list start ============");
        List<FdInfo.Node> nodeList = FdInfo.getFdList();
        if (nodeList.size() > 0) {
            for (FdInfo.Node node : nodeList) {
                addMapElement(pathNoReleaseMap, node);
            }
            nodeList.clear();
        }
        log("============ list end ============");
    }

    private static void addMapElement(HashMap<String, List<FdInfo.Node>> pathNoReleaseMap, FdInfo.Node node) {
        for (Map.Entry<String, List<FdInfo.Node>> entry : pathNoReleaseMap.entrySet()) {
            if (isInUPath(entry.getKey(), node.path)) {
                log("文件占用" + node);
                entry.getValue().add(node);
            }
        }
    }

    private static boolean isInUPath(String uPath, String filePath) {
        return !TextUtils.isEmpty(uPath) && !TextUtils.isEmpty(filePath) && filePath.startsWith(uPath);
    }

    private static void log(String text) {
        Log.d("apk_manager", text);
    }

    @SuppressWarnings("deprecation")
    @SuppressLint("UseCompatLoadingForDrawables")
    public static void readInfo(Context context, String apkPath, AppInfo appInfo, ApplicationInfo applicationInfo) {
        AssetManager assetManager = createAssetManager(apkPath);
        Resources resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
        appInfo.name = resources.getString(applicationInfo.labelRes);
        appInfo.icon = resources.getDrawable(applicationInfo.icon);
        if (assetManager != null) {
            assetManager.close();
        }
    }

    //利用反射调用AssetManager的addAssetPath()方法
    private static AssetManager createAssetManager(String apkPath) {
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(
                    assetManager, apkPath);
            return assetManager;
        } catch (Throwable th) {
            th.printStackTrace();
        }
        return null;
    }
}

其中也包含网上翻找到的加载icon的方法,AppInfo是我自定义的保存信息的类

问题2 读取apk Icon ,释放后程序会崩溃

  遇到了新的问题,为了解决问题1,我网上看过好博客,但基本都是解决读取icon后,拔u盘崩溃的,所以这步,我上来就用网上现有的AssetManager读取后close掉的解决方式,方法可用但是有问题,方法已经放在上面的工具类中,发现问题也很偶然,发现后各种找原因,最后还是定位到了原因,并找到了解决方法。
  我在安卓内置存储中放apk包读取试了下,也会崩溃,而调用我们的释放会让这个过程提前。当然并非所有的apk都有这个问题,原因见下。

日志:

2023-05-23 09:32:41.324  5834-5843  libc                    com.test.u                           A  FORTIFY: pthread_mutex_destroy called on a destroyed mutex (0x7bf25f7ac0)
2023-05-23 09:32:41.325  5834-5843  libc                    com.test.u                           A  Fatal signal 6 (SIGABRT), code -6 (SI_TKILL) in tid 5843 (FinalizerDaemon), pid 5834 (com.test.u)
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  Build fingerprint: 'Android/msm8953_64/msm8953_64:9/PKQ1.181105.001/pansiyuan04121703:user/test-keys'
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  Revision: '0'
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  ABI: 'arm64'
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  pid: 5834, tid: 5843, name: FinalizerDaemon  >>> com.test.u <<<
2023-05-23 09:32:41.405  5917-5917  DEBUG                   pid-5917                             A  signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A  Abort message: 'FORTIFY: pthread_mutex_destroy called on a destroyed mutex (0x7bf25f7ac0)'
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A      x0  0000000000000000  x1  00000000000016d3  x2  0000000000000006  x3  0000000000000008
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A      x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000010
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A      x8  0000000000000083  x9  2d8023c7cffd8873  x10 0000000000000000  x11 fffffffc7ffffbdf
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A      x12 0000000000000001  x13 00000000646c17b9  x14 0012897a9f355600  x15 0000d218ca3ea7c7
2023-05-23 09:32:41.406  5917-5917  DEBUG                   pid-5917                             A      x16 0000007c7cc642c0  x17 0000007c7cb959d0  x18 0000000000000000  x19 00000000000016ca
2023-05-23 09:32:41.407  5917-5917  DEBUG                   pid-5917                             A      x20 00000000000016d3  x21 0000007bf2550400  x22 0000007be36451c0  x23 000000007375a7e0
2023-05-23 09:32:41.407  5917-5917  DEBUG                   pid-5917                             A      x24 0000000000000008  x25 0000007be3647588  x26 0000007bf25504a0  x27 0000000000000002
2023-05-23 09:32:41.407  5917-5917  DEBUG                   pid-5917                             A      x28 0000000000000000  x29 0000007be3644d70
2023-05-23 09:32:41.407  5917-5917  DEBUG                   pid-5917                             A      sp  0000007be3644d30  lr  0000007c7cb87004  pc  0000007c7cb8702c
2023-05-23 09:32:41.697  5917-5917  DEBUG                   pid-5917                             A  
                                                                                                    backtrace:
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #00 pc 000000000002202c  /system/lib64/libc.so (abort+116)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #01 pc 00000000000935ec  /system/lib64/libc.so (__fortify_fatal(char const*, ...)+120)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #02 pc 0000000000092c54  /system/lib64/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #03 pc 0000000000093560  /system/lib64/libc.so (pthread_mutex_destroy+148)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #04 pc 00000000000acfd0  /system/lib64/libc++.so (std::__1::mutex::~mutex()+8)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #05 pc 000000000012f23c  /system/lib64/libandroid_runtime.so (android::NativeDestroy(_JNIEnv*, _jclass*, long)+104)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #06 pc 00000000003cf278  /system/framework/arm64/boot-framework.oat (offset 0x3ce000) (android.app.backup.BackupDataInput.dtor [DEDUPED]+152)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #07 pc 000000000055544c  /system/lib64/libart.so (art_quick_invoke_static_stub+604)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #08 pc 00000000000cf6e8  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+232)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #09 pc 000000000027f2b4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #10 pc 00000000002792bc  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #11 pc 0000000000525cc4  /system/lib64/libart.so (MterpInvokeStatic+204)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #12 pc 0000000000547914  /system/lib64/libart.so (ExecuteMterpImpl+14612)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #13 pc 0000000000564042  /system/framework/boot-framework.vdex (android.content.res.AssetManager.decRefsLocked+40)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #14 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #15 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #16 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #17 pc 0000000000525b00  /system/lib64/libart.so (MterpInvokeDirect+296)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #18 pc 0000000000547894  /system/lib64/libart.so (ExecuteMterpImpl+14484)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #19 pc 000000000041c04c  /system/framework/boot-framework.vdex (android.content.res.AssetManager.xmlBlockGone+4)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #20 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #21 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #22 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #23 pc 0000000000527804  /system/lib64/libart.so (MterpInvokeVirtualQuick+584)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #24 pc 000000000054b514  /system/lib64/libart.so (ExecuteMterpImpl+29972)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #25 pc 00000000005665a2  /system/framework/boot-framework.vdex (android.content.res.XmlBlock.decOpenCountLocked+50)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #26 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #27 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #28 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #29 pc 0000000000525b00  /system/lib64/libart.so (MterpInvokeDirect+296)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #30 pc 0000000000547894  /system/lib64/libart.so (ExecuteMterpImpl+14484)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #31 pc 0000000000425734  /system/framework/boot-framework.vdex (android.content.res.XmlBlock.close+16)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #32 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #33 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #34 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #35 pc 00000000005247c0  /system/lib64/libart.so (MterpInvokeVirtual+588)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #36 pc 0000000000547794  /system/lib64/libart.so (ExecuteMterpImpl+14228)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #37 pc 0000000000425754  /system/framework/boot-framework.vdex (android.content.res.XmlBlock.finalize)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #38 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #39 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #40 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.698  5917-5917  DEBUG                   pid-5917                             A      #41 pc 00000000005247c0  /system/lib64/libart.so (MterpInvokeVirtual+588)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #42 pc 0000000000547794  /system/lib64/libart.so (ExecuteMterpImpl+14228)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #43 pc 00000000000aec98  /system/framework/boot-core-libart.vdex (java.lang.Daemons$FinalizerDaemon.doFinalize+22)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #44 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #45 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #46 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #47 pc 0000000000525b00  /system/lib64/libart.so (MterpInvokeDirect+296)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #48 pc 0000000000547894  /system/lib64/libart.so (ExecuteMterpImpl+14484)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #49 pc 00000000000aed80  /system/framework/boot-core-libart.vdex (java.lang.Daemons$FinalizerDaemon.runInternal+164)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #50 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #51 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #52 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #53 pc 00000000005247c0  /system/lib64/libart.so (MterpInvokeVirtual+588)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #54 pc 0000000000547794  /system/lib64/libart.so (ExecuteMterpImpl+14228)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #55 pc 00000000000aeb78  /system/framework/boot-core-libart.vdex (java.lang.Daemons$Daemon.run+20)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #56 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #57 pc 0000000000258ab4  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #58 pc 00000000002792a0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+940)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #59 pc 000000000052573c  /system/lib64/libart.so (MterpInvokeInterface+1392)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #60 pc 0000000000547994  /system/lib64/libart.so (ExecuteMterpImpl+14740)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #61 pc 00000000000ca876  /system/framework/boot-core-oj.vdex (java.lang.Thread.run+12)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #62 pc 0000000000252fc0  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.3783761849+488)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #63 pc 0000000000515054  /system/lib64/libart.so (artQuickToInterpreterBridge+1020)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #64 pc 000000000055e2fc  /system/lib64/libart.so (art_quick_to_interpreter_bridge+92)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #65 pc 0000000000555188  /system/lib64/libart.so (art_quick_invoke_stub+584)
2023-05-23 09:32:41.699  5917-5917  DEBUG                   pid-5917                             A      #66 pc 00000000000cf6c8  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
2023-05-23 09:32:41.700  5917-5917  DEBUG                   pid-5917                             A      #67 pc 000000000045c8f0  /system/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104)
2023-05-23 09:32:41.700  5917-5917  DEBUG                   pid-5917                             A      #68 pc 000000000045d9ac  /system/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue*)+424)
2023-05-23 09:32:41.700  5917-5917  DEBUG                   pid-5917                             A      #69 pc 0000000000488848  /system/lib64/libart.so (art::Thread::CreateCallback(void*)+1120)
2023-05-23 09:32:41.700  5917-5917  DEBUG                   pid-5917                             A      #70 pc 0000000000091f2c  /system/lib64/libc.so (__pthread_start(void*)+36)
2023-05-23 09:32:41.700  5917-5917  DEBUG                   pid-5917                             A      #71 pc 00000000000238e8  /system/lib64/libc.so (__start_thread+68)
2023-05-23 09:32:42.200  1257-1529  InputDispatcher         system_server                        E  channel '7a47b3d com.test.u/com.xxx.xxx.AppActivity (server)' ~ Channel is unrecoverably broken and will be disposed!

发现过程

  我有自己开发程序的源码,也有系统开发都那里拿到了一些上层应用的源码,用来调整界面UI。出问题的是系统开发提供的源码编译出来的apk。同一个编译器,我自己的程序编译的apk,没有问题。系统开发提供的源码编译出的apk,读取完icon后,使用问题1中发现的释放占用的方法,会崩溃。

定位原因

  经过各种折腾后,终于还是定位到了问题产生的原因 ,在源码的资源目录下存在:
res
– mipmap-anydpi-v26
---- ic_launcher.xml

并且在清单文件中配置应用图标时使用的是:

android:icon="@mipmap/ic_launcher"

这个资源xml图片资源文件是产生问题的罪魁祸首,删掉后可正常读取,但你只能对自己编译的apk能这么干,不能限制别人这么用,所以这不是最终解决方法。

解决方法:

  我在漫漫折腾中找到一个非常简单直接的解决方式。你可能想不到,答案近在手边 。
  在发现并定位问题,我产生一个猜测,调用了 assetManager.close(); 后,只是让它可以被释放,但遇到xml图片的时候,内部的流操作并未停止,当系统把文件占用释放时,流操作就出现了问题,基于这个猜测,我寻找着终止流操作的办法,最后在查看 Resources 方法的时候,找到两个看着可能有用的方法:

resources.flushLayoutCache();
resources.finishPreloading();

  抱着试一试的态度,我把它们放在读取图片之后,果然不再出现错误,最后确定是 resources.flushLayoutCache(); 发挥了作用,所以上面的工具类,应在读取读取完图片后加上这行代码;

appInfo.icon = resources.getDrawable(applicationInfo.icon);
resources.flushLayoutCache();

  这样就不会在读取完图片后出现崩溃了。

未整理部分

  3.新的问题,读取多个apk信息后立刻拔掉u盘,因为问题1中的释放方式不是立即释放的,有短暂的延迟时间,通常在几百毫秒,系统赶在释放未完全时,检测文件文件占用情况,这时会导致程序崩溃。
解决思路:之前在解决1时曾在网上看到过,系统在关闭进程时会发送信号,我们的应用程序可通过jni对系统终结信号拦截,我通过将代码植入我的程序,发现拦截后会系统会给大概5,6秒的缓冲时间,这期间如果什么都不做,系统会再次检测,发现还有文件占用,会再改善结束信号,此次信号会直接杀死进程。之前没有发现问题1中的释放方式,所以这个方法就被放置了,但这个5,6秒的缓冲时间对我们释放占用是不是很搭配,于是我便猜测释放完后系统不会再发送终结信号。
测试方法:注释掉代码中调用释放占用的地方,在拦截到信号后,再调用释放,观察系统是否会继续终结进程,如果不终结,并且重复验证都如此,说明我的猜测是正确的。
验证结果: 猜测成立,信号拦截可以做为兜底操作,可以防止上面的情形导致的程序崩溃。
有人就要说了,既然这里可以释放掉,为什么不统一在这里释放:因为进程占用文件若不及时释放,肯定会增加内存和性能上的开销,所以读取到需要信息就后应及时释放,这里的释放只做为兜底的保险。
另外,这个信号只在人拔u盘后,在系统检测时,占用文件没有释放完,系统才会发出,如果系统检测时,已经释放完,系统是不会发信号的,这时拔u盘也不会崩溃,我们也不需要再处理。

  又发现新问题,有的资源在接收到信号后并不能及时释放掉,或者在拔掉又快速插入,也会出现问题

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值