美文网首页
android性能优化——fps检测和Java、native异常

android性能优化——fps检测和Java、native异常

作者: Peakmain | 来源:发表于2021-05-13 11:43 被阅读0次

    fps帧率监控

    项目地址:https://github.com/Peakmain/BasicUI/tree/kotlin/ui/src/main/java/com/peakmain/ui/utils/fps
    如果大家觉得有点帮助,还请抬抬你的贵手,给我点个小星星。

    如果我们跟Choreographer的postFrameCallback源码我们会发现最后会走到CallbackRecord 的run方法

        private static final class CallbackRecord {
            public CallbackRecord next;
            public long dueTime;
            public Object action; // Runnable or FrameCallback
            public Object token;
    
            public void run(long frameTimeNanos) {
                if (token == FRAME_CALLBACK_TOKEN) {
                    ((FrameCallback)action).doFrame(frameTimeNanos);
                } else {
                    ((Runnable)action).run();
                }
            }
        }
    

    也就是说自定义的FrameCallback会在下一个frame被渲染的时候回调,因此我们可以通过这个原理实现应用的帧率监听

    internal class FrameMonitor : Choreographer.FrameCallback {
        private val mChoreographer = Choreographer.getInstance()
        private var mFrameStartTime: Long = 0
    
        //1s绘制了多少帧
        private var mFrameCount = 0
        private var mCallbackLists = arrayListOf<FpsMonitorUtils.FpsCallback>()
        private var isPrintMessage = false
        override fun doFrame(frameTimeNanos: Long) {
            val currentTimeMills = TimeUnit.NANOSECONDS.toMillis(frameTimeNanos)
            if (mFrameStartTime > 0) {
                val time = currentTimeMills - mFrameStartTime
                mFrameCount++
                if (time > 1000) {
                    val fps = mFrameCount * 1000 / time.toDouble()
                    val topActivity = ActivityUtils.mInstance.getTopActivity(true)
                    if (isPrintMessage) {
                        LogUtils.d("当前Activity是:$topActivity,它的fps是:$fps")
                    }
                    mCallbackLists.forEach {
                        it.onFrame(fps)
                    }
                    mFrameCount = 0
                    mFrameStartTime = currentTimeMills
                }
            } else {
                mFrameStartTime = currentTimeMills
            }
            start()
        }
    
        fun start() {
            mChoreographer.postFrameCallback(this)
        }
    
        fun stop() {
            mFrameStartTime = 0
            mChoreographer.removeFrameCallback(this)
        }
    
        fun addCallback(callback: FpsMonitorUtils.FpsCallback) {
            mCallbackLists.add(callback)
        }
    
        fun reset() {
            mFrameStartTime = 0
            mChoreographer.removeFrameCallback(this)
            mFrameCount = 0
            mCallbackLists.clear()
        }
    
        fun printMessage(print: Boolean) {
            this.isPrintMessage = print
        }
    }
    

    之后我们只需要调用FrameMonitor的start即可开启帧率检测。我这里对其进行了封装大家可以查看FpsMonitorUtils

    动画.gif

    java、native异常捕获

    https://github.com/Peakmain/BasicUI/tree/kotlin/ui/src/main/java/com/peakmain/ui/utils/crash

    • java捕获异常
      java捕获异常其实很简单,只需要继承 Thread.UncaughtExceptionHandler,然后重写uncaughtException方法
       var CRASH_DIR = "crash_dir"
        fun init(crashDir: String) {
            Thread.setDefaultUncaughtExceptionHandler(CaughtExceptionHandler())
            this.CRASH_DIR = crashDir
        }
    
        class CaughtExceptionHandler : Thread.UncaughtExceptionHandler {
            private val context = BasicUIUtils.application!!
            private val formatter = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.CHINA)
            private val LAUNCH_TIME = formatter.format(Date())
            private val mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
            override fun uncaughtException(thread: Thread?, e: Throwable?) {
                if (!handleException(e) && mDefaultExceptionHandler != null) {
                    //默认系统处理
                    mDefaultExceptionHandler.uncaughtException(thread, e)
                }
                //重启app
                restartApp()
            }
    
            private fun restartApp() {
                val intent: Intent? =
                        context.packageManager?.getLaunchIntentForPackage(context.packageName)
                intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                context.startActivity(intent)
    
                Process.killProcess(Process.myPid())
                exitProcess(10)
            }
    
            //是否有异常
            private fun handleException(e: Throwable?): Boolean {
                if (e == null) return false
                val logInfo = createLogInfo(e)
                if (BuildConfig.DEBUG) {
                    LogUtils.e(logInfo)
                }
                val file = saveCrashInfoToFile(logInfo)
                if(CrashUtils.mListener!=null){
                    CrashUtils.mListener!!.onFileUploadListener(file)
                }
                return true
            }
    
            private fun saveCrashInfoToFile(logInfo: String): File {
                val crashDir = File(CRASH_DIR)
                if (!crashDir.exists()) {
                    crashDir.mkdirs()
                }
                val file = File(crashDir, formatter.format(Date()) + "-crash.txt")
                file.createNewFile()
                LogUtils.e(file.absolutePath)
                val fos = FileOutputStream(file)
                try {
                    fos.write(logInfo.toByteArray())
                    fos.flush()
                } catch (e: Exception) {
                    e.printStackTrace()
                } finally {
                    fos.close()
                }
                return file
            }
    
            private fun createLogInfo(e: Throwable): String {
                val sb = StringBuilder()
                sb.append("brand=${Build.BRAND}\n")
                sb.append("rom=${Build.MODEL}\n")
                sb.append("os=${Build.VERSION.RELEASE}\n")
                sb.append("sdk=${Build.VERSION.SDK_INT}\n")
                sb.append("launch_time=${LAUNCH_TIME}\n")//启动app的时间
                sb.append("crash_time=${formatter.format(Date())}\n")//crash发生时间
                sb.append("forground=${ActivityUtils.mInstance.isFront}\n")//应用处于前后台
                sb.append("thread=${Thread.currentThread().name}\n")//异常线程名
                sb.append("cpu_arch=${Build.CPU_ABI}\n")
    
                //app 信息
                val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
                sb.append("versionCode=${packageInfo.versionCode}\n")//版本号
                sb.append("versionName=${packageInfo.versionName}\n")
                sb.append("packageName=${packageInfo.packageName}\n")
                sb.append("requestedPermission=${Arrays.toString(packageInfo.requestedPermissions)}\n")
    
                val memoryInfo = ActivityManager.MemoryInfo()
                val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
                activityManager.getMemoryInfo(memoryInfo)
                sb.append("availMemory=${Formatter.formatFileSize(context, memoryInfo.availMem)}\n")//可用内存
                sb.append("totalMemory=${Formatter.formatFileSize(context, memoryInfo.totalMem)}\n")//设备总内存
    
                val file = Environment.getExternalStorageDirectory()
                //手机内部可用空间
                val statFs = StatFs(file.path)
                val availableSize = statFs.availableBlocks * statFs.blockSize
                sb.append(
                        "availStorage=${Formatter.formatFileSize(
                                context,
                                availableSize.toLong()
                        )}\n"
                )
                val write: Writer = StringWriter()
                val printWriter = PrintWriter(write)
                e.printStackTrace(printWriter)
                var cause = e.cause
                while (cause != null) {
                    cause.printStackTrace(printWriter)
                    cause = cause.cause
                }
                printWriter.close()
                sb.append(write.toString())
                return sb.toString()
            }
    
        }
    
    • native异常捕获
      native的异常捕获实际调用的是breadkpad的方法,这里我已经编译成aar,大家可以直接去下载放到自己项目就可以了aar目录,使用也很简单
    NativeCrashHandler.init(nativeCrashDir.absolutePath)
    

    Java和native的异常我封装成了一个CrashUtils工具类

    object CrashUtils {
        private const val CRASH_DIR_JAVA = "javaCrash"
        private const val CRASH_DIR_NATIVE = "nativeCrash"
        fun init() {
            val javaCrashDir = getJavaCrashDir()
            val nativeCrashDir = getNativeCrashDir()
            CrashHelper.init(javaCrashDir.absolutePath)
            NativeCrashHandler.init(nativeCrashDir.absolutePath)
        }
    
        private fun getNativeCrashDir(): File {
            val file = File(BasicUIUtils.application!!.cacheDir, CRASH_DIR_NATIVE)
            if (!file.exists()) {
                file.mkdirs()
            }
            return file
        }
    
        private fun getJavaCrashDir(): File {
            val file = File(BasicUIUtils.application!!.cacheDir, CRASH_DIR_JAVA)
            if (!file.exists()) {
                file.mkdirs()
            }
            return file
        }
    
         var mListener: OnFileUploadListener? = null
    
        interface OnFileUploadListener {
            fun onFileUploadListener(file: File)
        }
       //文件上传
        fun setOnFileUploadListener(listener: OnFileUploadListener?) {
            this.mListener = listener
        }
    }
    

    只需要CrashUtils.init()初始化一下就可以了

    我的开源库生成给的目录在data/user/com.peakmain.basicui/cache中


    image.png

    相关文章

      网友评论

          本文标题:android性能优化——fps检测和Java、native异常

          本文链接:https://www.haomeiwen.com/subject/peetjltx.html