首页 文章 精选 留言 我的

精选列表

搜索[性能改进],共10000篇文章
优秀的个人博客,低调大师

Android 性能优化 - APK 瘦身

如何查看 apk 的组成 如果要优化 apk 的大小,我们首先需要知道我们编译出来的 apk 都包含哪些东西,然后针对占用大的做裁剪,或者删除不需要的东西,从而达到瘦身的目的。 查看 apk 的内容占用情况很简单,打开 AS ,把 apk 拖到 AS 里面就可以查看 apk 包含的内容了。 可以看到占大头的是 res 代码等,所以瘦身可以从这几个方面来考虑。 如何减少 res 资源大小 删除冗余的资源 一般随着项目的迭代,部分图片等资源不再使用了,但是可能仍然被编译到了 apk 里面,所以可以删除这部分不再使用的资源,可以使用 lint 工具来搜索项目中不再使用的图片等资源。 重复资源的优化 除了有冗余资源,还有些是文件名不一样,但是内容一样的图片,可以通过比较 md5 值来判断是不是一样的资源,然后编辑 resources.arsc 来重定向。 图片压缩 未压缩的图片文件占用空间较大,可以考虑压缩未压缩过的图片来瘦身。常用的工具是 tinypng 网站。 同时也可以借助 TinyPngPlugin 等插件或者其他开源工具来帮助压缩图片。 资源混淆 通过将资源路径 res/drawable/wechat 变为 r/d/a 的方式来减少 apk 的大小,当 apk 有较多资源项的时候,效果比较明显,这是一款微信开源的工具,详细地址是:AndResGuard 指定语言 如果没有特殊的需求的话,可以只编译中文,因为其他的语言用不上,如果用不上的语言编译了,会在 resource 的表里面占用大量的空间,故 android { defaultConfig { ... // 仅支持 中文 resConfigs "zh" } } 如何减少 so 库资源大小 自己编译的 so release 包的 so 中移除调试符号。可以使用 Android NDK 中提供的 arm-eabi-strip 工具从原生库中移除不必要的调试符号。 如果是 cmake 来编译的话,可以再编辑脚本添加如下代码 set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") 别人编译的 so 联系作者修改,一般很难联系到。 动态下发 so 可以通过服务器下发 so , 下载完后再进入应用,但是体验不好,但是是一个思路。 只编译指定平台的 so 一般我们都是给 arm 平台的机器开发,如果没有特殊情况,我们一般只需要考虑 arm 平台的。具体的方法是 app 下的 build.gradle 添加如下代码 android { defaultConfig { ndk { abiFilter "armeabi" } } } 各个平台的差别如下: 平台 说明 armeabi-v7a arm 第 7 代及以上的处理器,2011 年后的设备基本都是 arm64-v8a arm 第 8 代 64 位处理器设备 armeabi arm 第 5、6 代处理器,早期的机器都是这个平台 x86 x86 32 位平台,平板和模拟器用的多 x86_64 x86 64 位平台 如何减少代码资源大小 一个功能尽量用一个库 比如加载图片库,不要 glide 和 fresco 混用,因为功能是类似的,只是使用的方法不一样,用了多个库来做类似的事情,代码肯定就变多了。 混淆 混淆的话,减少了生成的 class 大小,这样积少成多,也可以从一定层度减少 apk 的大小。 R 文件内联 通过把 R 文件里面的资源内联到代码中,从而减少 R 文件的大小。 可以使用 shrink-r-plugin 工具来做 R 文件的内联 参考文档 Android App包瘦身优化实践 联系我 Github: https://github.com/XanderWang Mail: 420640763@qq.com Blog: https://xander_wang.gitee.io/android-note/

优秀的个人博客,低调大师

Android MEM性能数据获取

查看内存使用情况使用adb dumpsys 命令 adb shell dumpsys meminfo 其中,package_name 也可以换成程序的pid,pid可以通过 adb shell top | grep app_name 来查找,下图是滴滴主端的内存使用情况 应用级内存 didi@bogon  ~  adb shell dumpsys meminfo com.sdu.didi.psnger Applications Memory Usage (in Kilobytes): Uptime: 180975 Realtime: 180975 ** MEMINFO in pid 7914 [com.sdu.didi.psnger] ** Pss Private Private SwapPss Heap Heap Heap Total Dirty Clean Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ ------ Native Heap 32081 31972 92 294 59904 41985 17918 Dalvik Heap 26859 26764 0 318 27257 16354 10903 Dalvik Other 6354 6348 0 0 Stack 1424 1424 0 0 Ashmem 1558 1544 0 0 Gfx dev 4942 1636 8 0 Other dev 27 0 24 0 .so mmap 13066 384 8904 199 .apk mmap 19677 80 16676 0 .ttf mmap 11 0 0 0 .dex mmap 48628 24 37604 0 .oat mmap 12470 0 3128 0 .art mmap 2754 1872 44 15 Other mmap 2227 8 1256 0 EGL mtrack 41100 41100 0 0 GL mtrack 6984 6984 0 0 Unknown 5939 5924 12 99 TOTAL 227026 126064 67748 925 87161 58339 28821 App Summary Pss(KB) ------ Java Heap: 28680 Native Heap: 31972 Code: 66800 Stack: 1424 Graphics: 49728 Private Other: 15208 System: 33214 TOTAL: 227026 TOTAL SWAP PSS: 925 Objects Views: 121 ViewRootImpl: 1 AppContexts: 3 Activities: 1 Assets: 4 AssetManagers: 3 Local Binders: 75 Proxy Binders: 35 Parcel memory: 37 Parcel count: 131 Death Recipients: 1 OpenSSL Sockets: 7 SQL MEMORY_USED: 945 PAGECACHE_OVERFLOW: 263 MALLOC_SIZE: 62 DATABASES pgsz dbsz Lookaside(b) cache Dbname 4 88 135 16/40/7 /storage/emulated/0/Android/data/com.sdu.didi.psnger/files/im/im_database_282680000258050.db 4 28 45 171/107/4 /data/user/0/com.sdu.didi.psnger/databases/dns_record.db 4 32 84 10/24/7 /data/user/0/com.sdu.didi.psnger/databases/ad 4 20 37 46/22/5 /data/user/0/com.sdu.didi.psnger/databases/location_info.db 4 24 41 5/19/2 /data/user/0/com.sdu.didi.psnger/databases/download_file.db 4 100 149 58/44/25 /data/user/0/com.sdu.didi.psnger/databases/DIDI_DATABASE 4 20 19 0/23/2 /data/user/0/com.sdu.didi.psnger/databases/audio_record_2 4 12 0/0/0 (attached) temp 4 20 56 3/15/3 /data/user/0/com.sdu.didi.psnger/databases/audio_record_2 (1) 重点关注如下几个字段: (1) 私有(Clean and Dirty)内存 进程独占的内存,也就是应用进程销毁时系统可以直接回收的内存容量。 通常来说,“private dirty”内存是其最重要的部分,因为只被自己的进程使用。它只在内存中存储,因此不能做分页存储到外存(Android不支持swap)。 所有分配的Dalvik堆和本地堆都是“private dirty”内存;Dalvik堆和本地堆中和Zygote进程共享的部分是共享dirty内存。 (2) Total 的 PSS 信息 实际使用内存,这是另一种应用内存使用的计算方式,这个值就是我们应用真正占据的内存大小。 PSS会把跨进程的共享页也计算在内。任何独占的内存页直接计算它的PSS值,而和其它进程共享的页则按照共享的比例计算PSS值。例如,在两个进程间共享的页,计算进每个进程PPS的值是它的一半大小。 PSS计算方式的一个好处是:把所有进程的PSS值加起来就可以确定所有进程总共占用的内存。这意味着用PSS来计算进程的实际内存使用、进程间对比内存使用和总共剩余内存大小是很好的方式。 通常来说,只需关心Pss Total列和Private Dirty列就可以了。在一些情况下,Private Clean列和Heap Alloc列也会提供很有用的信息。 代码示例 /** * 获取进程内存Private Dirty数据 * * @param context * @param pid * 进程ID * @return nativePrivateDirty、dalvikPrivateDirty、 TotalPrivateDirty */ public static long[] getPrivDirty(Context context, int pid) { ActivityManager mAm = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); int[] pids = new int[1]; pids[0] = pid; MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids); MemoryInfo pidMemoryInfo = memoryInfoArray[0]; long[] value = new long[3]; // Natvie Dalvik Total value[0] = pidMemoryInfo.nativePrivateDirty; value[1] = pidMemoryInfo.dalvikPrivateDirty; value[2] = pidMemoryInfo.getTotalPrivateDirty(); return value; } /** * 获取进程内存PSS数据 * * @param context * @param pid * @return nativePss、dalvikPss、TotalPss */ public static long[] getPSS(Context context, int pid) { long[] value = new long[3]; // Natvie Dalvik Total if (pid >= 0) { int[] pids = new int[1]; pids[0] = pid; ActivityManager mAm = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids); MemoryInfo pidMemoryInfo = memoryInfoArray[0]; value[0] = pidMemoryInfo.nativePss; value[1] = pidMemoryInfo.dalvikPss; value[2] = pidMemoryInfo.getTotalPss(); } else { value[0] = 0; value[1] = 0; value[2] = 0; } return value; } 获取手机总内存和可用内存信息 “/proc/meminfo”文件记录了android手机的一些内存信息,通过读取文件”/proc/meminfo”的信息能够获取手机Memory的总量。 # cat /proc/meminfo cat /proc/meminfo MemTotal: 94096 kB 所有可用RAM大小。 MemFree: 1684 kB LowFree与HighFree的总和,被系统留着未使用的内存。 Buffers: 16 kB 用来给文件做缓冲大小 Cached: 27160 kB 被高速缓冲存储器(cache memory)用的内存的大小(等于diskcache minus SwapCache)。 SwapCached: 0 kB 被高速缓冲存储器(cache memory)用的交换空间的大小。已经被交换出来的内存,仍然被存放在swapfile中,用来在需要的时候很快的被替换而不需要再次打开I/O端口。 Active: 35392 kB 在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非非常必要,否则不会被移作他用。 Inactive: 44180 kB 在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,可能被用于其他途径。 Active(anon): 26540 kB Inactive(anon): 28244 kB Active(file): 8852 kB Inactive(file): 15936 kB Unevictable: 280 kB Mlocked: 0 kB SwapTotal: 0 kB 交换空间的总大小。 SwapFree: 0 kB 未被使用交换空间的大小。 Dirty: 0 kB 等待被写回到磁盘的内存大小。 Writeback: 0 kB 正在被写回到磁盘的内存大小。 AnonPages: 52688 kB 未映射页的内存大小。 Mapped: 17960 kB 设备和文件等映射的大小。 Slab: 3816 kB 内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗。 SReclaimable: 936 kB 可收回Slab的大小。 SUnreclaim: 2880 kB 不可收回Slab的大小(SUnreclaim+SReclaimable=Slab)。 PageTables: 5260 kB 管理内存分页页面的索引表的大小。 NFS_Unstable: 0 kB 不稳定页表的大小。 Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 47048 kB Committed_AS: 1483784 kB VmallocTotal: 876544 kB VmallocUsed: 15456 kB VmallocChunk: 829444 kB 要获取android手机总内存大小,只需读取”/proc/meminfo”文件的第1行,并进行简单的字符串处理即可。 代码示例 /** * 获取空闲内存和总内存拼接字符串 * * @return 总内存 */ public static String getFreeAndTotalMem() { long[] memInfo = getMemInfo(); return Long.toString(memInfo[1] + memInfo[2] + memInfo[3]) + "M/" + Long.toString(memInfo[0]) + "M"; } /** * 获取内存信息:total、free、buffers、cached,单位MB * * @return 内存信息 */ public static long[] getMemInfo() { long memInfo[] = new long[4]; try { Class<?> procClazz = Class.forName("android.os.Process"); Class<?> paramTypes[] = new Class[] { String.class, String[].class, long[].class }; Method readProclines = procClazz.getMethod("readProcLines", paramTypes); Object args[] = new Object[3]; final String[] memInfoFields = new String[] { "MemTotal:", "MemFree:", "Buffers:", "Cached:" }; long[] memInfoSizes = new long[memInfoFields.length]; memInfoSizes[0] = 30; memInfoSizes[1] = -30; args[0] = new String("/proc/meminfo"); args[1] = memInfoFields; args[2] = memInfoSizes; if (null != readProclines) { readProclines.invoke(null, args); for (int i = 0; i < memInfoSizes.length; i++) { memInfo[i] = memInfoSizes[i] / 1024; } } } catch (Exception e) { e.printStackTrace(); } return memInfo; } 总结 1)Pss/SharedDirty/Private Dirty三列是读取了/proc/process-id/smaps文件获取的,可以通过adb shell cat /proc/process-id/smaps来查看(需要root)。这是个普通的linux文件,描述了进程的虚拟内存区域的具体信息。 2)Native HeapSize/Alloc/Free三列是使用C函数mallinfo得到的。 3)Dalvik HeapSize/Alloc/Free并非该cpp文件产生,而是android的Debug类生成。 meminfo结果详细分析 Pss对应的TOTAL值:内存所实际占用的值。 Dalvik Heap Size:从RuntimetotalMemory()获得,DalvikHeap总共的内存大小。 Dalvik HeapAlloc:RuntimetotalMemory()-freeMemory() ,Dalvik Heap分配的内存大小。 Dalvik Heap Free:从RuntimefreeMemory()获得,DalvikHeap剩余的内存大小。 Dalvik Heap Size 约等于Dalvik HeapAlloc+ Dalvik HeapFree。 Cursor:/dev/ashmem/Cursor Cursor消耗的内存(KB)。 Ashmem:/dev/ashmem,匿名共享内存用来提供共享内存通过分配一个多个进程可以共享的带名称的内存块。 Other dev:/dev/,内部driver占用的在 “Otherdev”。 .so mmap:C 库代码占用的内存。 .jar mmap:Java 文件代码占用的内存。 .apk mmap:apk代码占用的内存。 .ttf mmap:ttf 文件代码占用的内存。 .dex mmap:Dex 文件代码占用的内存。 Other mmap:其他文件占用的内存。 私有(Clean and Dirty)内存: 进程独占的内存。也就是应用进程销毁时系统可以直接回收的内存容量。通常来说,“private dirty”内存是其最重要的部分,因为只被自己的进程使用。它只在内存中存储,因此不能做分页存储到外存(Android不支持swap)。所有分配的Dalvik堆和本地堆都是“private dirty”内存;Dalvik堆和本地堆中和Zygote进程共享的部分是共享dirty内存。 实际使用内存 (PSS): 这是另一种应用内存使用的计算方式,把跨进程的共享页也计算在内。任何独占的内存页直接计算它的PSS值,而和其它进程共享的页则按照共享的比例计算PSS值。例如,在两个进程间共享的页,计算进每个进程PPS的值是它的一半大小。PSS计算方式的一个好处是:把所有进程的PSS值加起来就可以确定所有进程总共占用的内存。这意味着用PSS来计算进程的实际内存使用、进程间对比内存使用和总共剩余内存大小是很好的方式。 通常来说,只需关心Pss Total列和Private Dirty列就可以了。在一些情况下,Private Clean列和Heap Alloc列也会提供很有用的信息。下面是一些应该查看的内存分配类型(行中列出的类型): Dalvik Heap: 应用中Dalvik分配使用的内存。Pss Total包含所有的Zygote分配(如上面PSS定义所描述的,共享跨进程的加权)。Private Dirty是应用堆独占的内存大小,包含了独自分配的部分和应用进程从Zygote复制分裂时被修改的Zygote分配的内存页。注意:新平台版本有Dalvik Other这一项。Dalvik Heap中的Pss Total和Private Dirty不包括Dalvik的开销,例如即时编译(JIT)和垃圾回收(GC),然而老版本都包含在Dalvik的开销里面。 Heap Alloc: 是应用中Dalvik堆和本地堆已经分配使用的大小。它的值比Pss Total和Private Dirty大,因为进程是从Zygote中复制分裂出来的,包含了进程共享的分配部分。 .so mmap和.dex mmap: mmap映射的.so(本地) 和.dex(Dalvik)代码使用的内存。Pss Total 包含了跨应用共享的平台代码;Private Clean是应用独享的代码。通常来说,实际映射的内存大小要大一点——这里显示的内存大小是执行了当前操作后应用使用的内存大小。然而,.so mmap 的private dirty比较大,这是由于在加载到最终地址时已经为本地代码分配好了内存空间。 Unknown: 无法归类到其它项的内存页。目前,这主要包含大部分的本地分配,就是那些在工具收集数据时由于地址空间布局随机化(Address Space Layout Randomization ,ASLR)不能被计算在内的部分。和Dalvik堆一样, Unknown中的Pss Total把和Zygote共享的部分计算在内,Unknown中的Private Dirty只计算应用独自使用的内存。 TOTAL: 进程总使用的实际使用内存(PSS),是上面所有PSS项的总和。它表明了进程总的内存使用量,可以直接用来和其它进程或总的可以内存进行比较。Private Dirty和Private Clean是进程独自占用的总内存,不会和其它进程共享。当进程销毁时,它们(特别是Private Dirty)占用的内存会重新释放回系统。Dirty内存是已经被修改的内存页,因此必须常驻内存(因为没有swap);Clean内存是已经映射持久文件使用的内存页(例如正在被执行的代码),因此一段时间不使用的话就可以置换出去。 ViewRootImpl: 进程中活动的根视图的数量。每个根视图与一个窗口关联,因此可以帮助确定涉及对话框和窗口的内存泄露。 AppContexts和Activities: 当前驻留在进程中的Context和Activity对象的数量。可以很快的确认常见的由于静态引用而不能被垃圾回收的泄露的 Activity对象。这些对象通常有很多其它相关联的分配,因此这是追查大的内存泄露的很好办法。 注意:View 和 Drawable 对象也持有所在Activity的引用,因此,持有View 或 Drawable 对象也可能会导致应用Activity泄露。

优秀的个人博客,低调大师

Android CPU性能数据获取

总体CPU 获取CPU信息思路 Android系统是基于Linux内核的,所以系统文件的结构和Linux下一样,系统总体CPU使用信息放在/proc/stat文件下,/proc/cpuinfo文件存放CPU的其它信息,包括CPU名称,直接读取即可。 通过proc获取CPU信息: Linux CPU 九元组参数解析(单位:jiffies): (jiffies是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数,在linux中,一个节拍大致可理解为操作系统进程调度的最小时间片,不同linux内核可能值有不同,通常在1ms到10ms之间) user 从系统启动开始累计到当前时刻,处于用户态的运行时间,不包含 nice值为负进程。 nice 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间 system 从系统启动开始累计到当前时刻,处于核心态的运行时间 idle 从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间 iowait 从系统启动开始累计到当前时刻,IO等待时间(since 2.5.41) irq 从系统启动开始累计到当前时刻,硬中断时间(since 2.6.0-test4) softirq 从系统启动开始累计到当前时刻,软中断时间(since 2.6.0-test4) 可以每1s获取一次CPU信息,分析整机CPU占用率。总的cpu时间totalCpuTime = user + nice + system + idle + iowait + irq + softirq + stealstolen +guest 计算方法 1、 采样两个足够短的时间间隔的Cpu快照,分别记作t1,t2,其中t1、t2的结构均为: (user、nice、system、idle、iowait、irq、softirq、stealstolen、guest)的9元组; 2、 计算总的Cpu时间片totalCpuTime a) 把第一次的所有cpu使用情况求和,得到s1; b) 把第二次的所有cpu使用情况求和,得到s2; c) s2 - s1得到这个时间间隔内的所有时间片,即totalCpuTime = s2 - s1 ; 3、计算空闲时间idle idle对应第四列的数据,用第二次的idle - 第一次的idle即可 idle = idle2 - idle1 4、计算cpu使用率 CPU总使用率(%) = 100*((totalCputime2- totalCputime1)-(idle2-idle1))/(totalCputime2-totalCputime1) 示例代码 public static long getTotalCpuTime() { // 获取系统总CPU使用时间 String[] cpuInfos = null; BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("/proc/stat")), 1000); String load = reader.readLine(); cpuInfos = load.split(" "); } catch (IOException ex) { ex.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } long totalCpu = Long.parseLong(cpuInfos[2]) + Long.parseLong(cpuInfos[3]) + Long.parseLong(cpuInfos[4]) + Long.parseLong(cpuInfos[6]) + Long.parseLong(cpuInfos[5]) + Long.parseLong(cpuInfos[7]) + Long.parseLong(cpuInfos[8]); return totalCpu; } 应用级CPU 单个应用CPU监控 Emmagee是将选中应用的PID传入,读取/proc/PID/stat文件信息及可获取该PID对应程序的CPU信息。 计算方法 1、首先获取应用的进程id: adb shell ps | grep com.package | awk '{print $2}' > tmp 2、根据进程id,通过proc获取CPU信息 while read line; do adb shell cat /proc/$line/stat | awk '{print $14,$15,$16,$17}' >> appcpu0; done < tmp 说明:以下只解释对我们计算Cpu使用率有用相关参数(14-17列) 参数解释 pid 进程号 utime 该任务在用户态运行的时间,单位为jiffies stime 该任务在核心态运行的时间,单位为jiffies cutime 所有已死线程在用户态运行的时间,单位为jiffies cstime 所有已死在核心态运行的时间,单位为jiffies 结论:进程的总Cpu时间processCpuTime = utime + stime + cutime + cstime,该值包括其所有线程的cpu时间。 之后可以每1s获取一次CPU信息,分析获得app的CPU占用率等信息 单个程序的CPU使用率(%) = 100*(processCpuTime2-processCpuTime1)/(totalCpuTime2-totalCpuTime1) 示例代码 public static long getAppCpuTime(int pid) { // 获取应用占用的CPU时间 String[] cpuInfos = null; BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("/proc/" + pid + "/stat")), 1000); String load = reader.readLine(); cpuInfos = load.split(" "); } catch (IOException ex) { ex.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } long appCpuTime = Long.parseLong(cpuInfos[13]) + Long.parseLong(cpuInfos[14]) + Long.parseLong(cpuInfos[15]) + Long.parseLong(cpuInfos[16]); return appCpuTime; } }

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册