我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android ANR的原理是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android ANR的原理是什么

本篇内容介绍了“Android ANR的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    一、ANR说明和原因

    1.1 简介

    ANR全称:Application Not Responding,也就是应用程序无响应。

    1.2 原因

    Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。

    以下四个条件都可以造成ANR发生:

    • InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件

    • BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。

    • Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。

    • ContentProvider Timeout :ContentProvider的publish在10s内没进行完。

    1.3 避免

    尽量避免在主线程(UI线程)中作耗时操作。

    那么耗时操作就放在子线程中。

    二、ANR分析办法

    2.1 ANR重现

    这里使用的是号称Google亲儿子的Google Pixel xl(Android 8.0系统)做的测试,生成一个按钮跳转到ANRTestActivity,在后者的onCreate()中主线程休眠20秒:

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_anr_test);    // 这是Android提供线程休眠函数,与Thread.sleep()最大的区别是    // 该使用该函数不会抛出InterruptedException异常。    SystemClock.sleep(20 * 1000);}

    在进入ANRTestActivity后黑屏一段时间,大概有七八秒,终于弹出了ANR异常。

    Android ANR的原理是什么

    2.2 ANR分析办法一:Log

    刚才产生ANR后,看下Log:

    Android ANR的原理是什么

    可以看到logcat清晰地记录了ANR发生的时间,以及线程的tid和一句话概括原因:WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。

    最后一句The application may be doing too much work on its main thread.告知可能在主线程做了太多的工作。

    2.3 ANR分析办法二:traces.txt

    刚才的log有第二句Wrote stack traces to '/data/anr/traces.txt',说明ANR异常已经输出到traces.txt文件,使用adb命令把这个文件从手机里导出来:

    cd到adb.exe所在的目录,也就是Android SDK的platform-tools目录,例如:

    cd D:\Android\AndroidSdk\platform-tools

    此外,除了Windows的cmd以外,还可以使用AndroidStudio的Terminal来输入adb命令。

    到指定目录后执行以下adb命令导出traces.txt文件:

    adb pull /data/anr/traces.txt

    traces.txt默认会被导出到Android SDK的\platform-tools目录。一般来说traces.txt文件记录的东西会比较多,分析的时候需要有针对性地去找相关记录。

    ----- pid 23346 at 2017-11-07 11:33:57 -----  ----> 进程id和ANR产生时间Cmd line: com.sky.myjavatestBuild fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'ABI: 'arm64'Build type: optimizedZygote loaded classes=4681 post zygote classes=106Intern table: 42675 strong; 137 weakJNI: CheckJNI is on; globals=526 (plus 22 weak)Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so /system/lib64/libjavacrypto.so/system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so/system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9)Heap: 22% free, 1478KB/1896KB; 21881 objects    ----> 内存使用情况..."main" prio=5 tid=1 Sleeping    ----> 原因为Sleeping  | group="main" sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00  | sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0  | state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100  | stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB  | held mutexes=  at java.lang.Thread.sleep(Native method)  - sleeping on <0x053fd2c2> (a java.lang.Object)  at java.lang.Thread.sleep(Thread.java:373)  - locked <0x053fd2c2> (a java.lang.Object)  at java.lang.Thread.sleep(Thread.java:314)  at android.os.SystemClock.sleep(SystemClock.java:122)  at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20) ----> 产生ANR的包名以及具体行数  at android.app.Activity.performCreate(Activity.java:6975)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)  at android.app.ActivityThread.-wrap11(ActivityThread.java:-1)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)  at android.os.Handler.dispatchMessage(Handler.java:105)  at android.os.Looper.loop(Looper.java:164)  at android.app.ActivityThread.main(ActivityThread.java:6541)  at java.lang.reflect.Method.invoke(Native method)  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

    在文件中使用 ctrl + F 查找包名可以快速定位相关代码。

    • 通过上方log可以看出相关问题:

    • 进程id和包名:pid 23346 com.sky.myjavatest

    • 造成ANR的原因:Sleeping

    • 造成ANR的具体行数:ANRTestActivity.java:20类的第20行

    特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。

    2.4 ANR分析办法三:Java线程调用分析

    通过JDK提供的命令可以帮助分析和调试Java应用,命令为:

    jstack {pid}

    其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程,比如

    7266 Test7267 Jps

    2.5 ANR分析办法四:DDMS分析ANR问题

    • 使用DDMS——Update Threads工具

    • 阅读Update Threads的输出

    三、造成ANR的原因及解决办法

    上面例子只是由于简单的主线程耗时操作造成的ANR,造成ANR的原因还有很多:

    主线程阻塞或主线程数据读取

    解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS

    CPU满负荷,I/O阻塞

    解决办法:文件读写或数据库操作放在子线程异步操作。

    内存不足

    解决办法:AndroidManifest.xml文件<applicatiion>中可以设置 android:largeHeap="true",以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。

    各大组件ANR

    各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。

    四、ANR源码分析

    4.1 Service造成的Service Timeout

    Service Timeout是位于"ActivityManager"线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。

    1.1 发送延时消息

    Service进程attach到system_server进程的过程中会调用realStartServiceLocked,紧接着mAm.mHandler.sendMessageAtTime()来发送一个延时消息,延时的时常是定义好的,如前台Service的20秒。ActivityManager线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时会触发。

    AS.realStartServiceLocked

    ActiveServices.java

    private final void realStartServiceLocked(ServiceRecord r,        ProcessRecord app, boolean execInFg) throws RemoteException {    ...    //发送delay消息(SERVICE_TIMEOUT_MSG)    bumpServiceExecutingLocked(r, execInFg, "create");    try {        ...        //最终执行服务的onCreate()方法        app.thread.scheduleCreateService(r, r.serviceInfo,                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),                app.repProcState);    } catch (DeadObjectException e) {        mAm.appDiedLocked(app);        throw e;    } finally {        ...    }}

    AS.bumpServiceExecutingLocked

    private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {    ...     scheduleServiceTimeoutLocked(r.app);}void scheduleServiceTimeoutLocked(ProcessRecord proc) {    if (proc.executingServices.size() == 0 || proc.thread == null) {        return;    }    long now = SystemClock.uptimeMillis();    Message msg = mAm.mHandler.obtainMessage(            ActivityManagerService.SERVICE_TIMEOUT_MSG);    msg.obj = proc;    //当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程    mAm.mHandler.sendMessageAtTime(msg,        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));}

    1.2 进入目标进程的主线程创建Service

    经过Binder等层层调用进入目标进程的主线程 handleCreateService(CreateServiceData data)。

    ActivityThread.java

       private void handleCreateService(CreateServiceData data) {        ...        java.lang.ClassLoader cl = packageInfo.getClassLoader();        Service service = (Service) cl.loadClass(data.info.name).newInstance();        ...        try {            //创建ContextImpl对象            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);            context.setOuterContext(service);            //创建Application对象            Application app = packageInfo.makeApplication(false, mInstrumentation);            service.attach(context, this, data.info.name, data.token, app,                    ActivityManagerNative.getDefault());            //调用服务onCreate()方法             service.onCreate();            //取消AMS.MainHandler的延时消息            ActivityManagerNative.getDefault().serviceDoneExecuting(                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);        } catch (Exception e) {            ...        }    }

    这个方法中会创建目标服务对象,以及回调常用的Service的onCreate()方法,紧接着通过serviceDoneExecuting()回到system_server执行取消AMS.MainHandler的延时消息。

    1.3 回到system_server执行取消AMS.MainHandler的延时消息

    AS.serviceDoneExecutingLocked

    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,            boolean finishing) {    ...    if (r.executeNesting <= 0) {        if (r.app != null) {            r.app.execServicesFg = false;            r.app.executingServices.remove(r);            if (r.app.executingServices.size() == 0) {                //当前服务所在进程中没有正在执行的service                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);        ...    }    ...}

    此方法中Service逻辑处理完成则移除之前延时的消息SERVICE_TIMEOUT_MSG。如果没有执行完毕不调用这个方法,则超时后会发出SERVICE_TIMEOUT_MSG来告知ANR发生。

    4.2 BroadcastReceiver造成的BroadcastQueue Timeout

    BroadcastReceiver Timeout是位于"ActivityManager"线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

    2.1 处理广播函数 processNextBroadcast() 中 broadcastTimeoutLocked(false) 发送延时消息

    广播处理顺序为先处理并行广播,再处理当前有序广播。

    final void processNextBroadcast(boolean fromMsg) {    synchronized(mService) {        ...        // 处理当前有序广播        do {            r = mOrderedBroadcasts.get(0);            //获取所有该广播所有的接收者            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;            if (mService.mProcessesReady && r.dispatchTime > 0) {                long now = SystemClock.uptimeMillis();                if ((numReceivers > 0) &&                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {                    //step 1\. 发送延时消息,这个函数处理了很多事情,比如广播处理超时结束广播                    broadcastTimeoutLocked(false);                    ...                }            }            if (r.receivers == null || r.nextReceiver >= numReceivers                    || r.resultAbort || forceReceive) {                if (r.resultTo != null) {                    //2\. 处理广播消息消息                    performReceiveLocked(r.callerApp, r.resultTo,                        new Intent(r.intent), r.resultCode,                        r.resultData, r.resultExtras, false, false, r.userId);                    r.resultTo = null;                }                //3\. 取消广播超时ANR消息                cancelBroadcastTimeoutLocked();            }        } while (r == null);        ...        // 获取下条有序广播        r.receiverTime = SystemClock.uptimeMillis();        if (!mPendingBroadcastTimeoutMessage) {            long timeoutTime = r.receiverTime + mTimeoutPeriod;            //设置广播超时            setBroadcastTimeoutLocked(timeoutTime);        }        ...    }}

    上文的step 1. broadcastTimeoutLocked(false)函数:记录时间信息并调用函数设置发送延时消息

    final void broadcastTimeoutLocked(boolean fromMsg) {    ...        long now = SystemClock.uptimeMillis();        if (fromMsg) {            if (mService.mDidDexOpt) {                // Delay timeouts until dexopt finishes.                mService.mDidDexOpt = false;                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;                setBroadcastTimeoutLocked(timeoutTime);                return;            }            if (!mService.mProcessesReady) {                return;            }            long timeoutTime = r.receiverTime + mTimeoutPeriod;            if (timeoutTime > now) {                // step 2                setBroadcastTimeoutLocked(timeoutTime);                return;            }        }

    上文的step 2.setBroadcastTimeoutLocked函数: 设置广播超时具体操作,同样是发送延时消息

    final void setBroadcastTimeoutLocked(long timeoutTime) {    if (! mPendingBroadcastTimeoutMessage) {        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);        mHandler.sendMessageAtTime(msg, timeoutTime);        mPendingBroadcastTimeoutMessage = true;    }}

    2.2 setBroadcastTimeoutLocked(long timeoutTime)函数的参数timeoutTime是当前时间加上设定好的超时时间。

    也就是上文的

    long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;

    mTimeoutPeriod 也就是前台队列的10s和后台队列的60s。

    public ActivityManagerService(Context systemContext) {    ...    static final int BROADCAST_FG_TIMEOUT = 10 * 1000;    static final int BROADCAST_BG_TIMEOUT = 60 * 1000;    ...    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,            "foreground", BROADCAST_FG_TIMEOUT, false);    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,            "background", BROADCAST_BG_TIMEOUT, true);    ...}

    2.3 在processNextBroadcast()过程,执行完performReceiveLocked后调用cancelBroadcastTimeoutLocked

    cancelBroadcastTimeoutLocked :处理广播消息函数 processNextBroadcast() 中 performReceiveLocked() 处理广播消息完毕则调用 cancelBroadcastTimeoutLocked() 取消超时消息。

    final void cancelBroadcastTimeoutLocked() {    if (mPendingBroadcastTimeoutMessage) {        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);        mPendingBroadcastTimeoutMessage = false;    }}

    4.3 ContentProvider的ContentProvider Timeout

    ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。

    五、Android ANR的信息收集

    无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法。

    “Android ANR的原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Android ANR的原理是什么

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Android ANR的原理是什么

    本篇内容介绍了“Android ANR的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、ANR说明和原因1.1 简介ANR全称:
    2023-06-21

    SharedPreference引发ANR原理是什么

    这篇文章主要介绍“SharedPreference引发ANR原理是什么”,在日常操作中,相信很多人在SharedPreference引发ANR原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Share
    2023-07-05

    Android ANR的信息收集过程是什么

    本篇内容介绍了“Android ANR的信息收集过程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一. ANR场景无论是四大组件或者进
    2023-06-21

    android leakcanary的原理是什么

    Android LeakCanary是一个用于检测内存泄漏的开源库。它的原理主要包括以下几个步骤:1. 监测对象的引用关系:LeakCanary会监测应用中所有的对象引用关系,包括Activity、Fragment、View等。它会跟踪对象
    2023-09-23

    Android的root原理是什么

    本篇内容主要讲解“Android的root原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Android的root原理是什么”吧!0x00 关于rootlinux和类Unix系统的最初设
    2023-06-27

    android glide原理是什么

    Glide是一种用于加载和显示图片的Android开源库。它使用了一种流畅且高效的方式来加载、缓存和显示图片,具有较低的内存占用和较高的性能。Glide的工作原理如下:1. 请求管理:Glide接收到一个图片加载请求后,将其添加到请求队列中
    2023-09-22

    Android中Lint的原理是什么

    这篇文章将为大家详细讲解有关Android中Lint的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Lint 的工作过程lint 工具的代码扫描工作流:应用源文件:源文件包含组成
    2023-06-14

    Android中Lifecycle的原理是什么

    本文小编为大家详细介绍“Android中Lifecycle的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android中Lifecycle的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
    2023-06-29

    Android中ViewPager的原理是什么

    ViewPager是Android中的一个布局容器控件,主要用于实现页面切换效果。它的原理是通过管理多个Fragment或View的显示与隐藏,实现页面的滑动切换。具体原理如下:1. ViewPager通过PagerAdapter来管理多个
    2023-09-25

    android Handler机制的原理是什么

    Android中的Handler机制是用来实现线程之间的通信的一种机制。它的原理是基于消息队列和消息循环。每个线程都有自己的消息队列,当一个线程需要与其他线程进行通信时,它可以创建一个Handler对象,并将消息发送到其他线程的消息队列中。
    2023-09-20

    android线程池的原理是什么

    Android线程池的原理是通过管理和调度线程来实现并发执行任务的机制。线程池主要由线程池管理器、工作队列和线程池的线程组成。线程池管理器:线程池管理器负责创建、销毁和管理线程池。它根据任务的类型和优先级来决定将任务分配给线程池中的线程执行
    2023-09-23

    深入学习Android ANR 的原理分析及解决办法

    目录一、ANR说明和原因1.1 简介1.2 原因1.3 避免二、ANR分析办法2.1 ANR重现2.2 ANR分析办法一:Log2.3 ANR分析办法二:traces.txt2.4 ANR分析办法三:Java线程调用分析2.5 ANR分析办
    2022-06-07

    Android中的Coroutine协程原理是什么

    这篇文章主要介绍了Android中的Coroutine协程原理是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言协程是一个并发方案。也是一种思想。传统意义上的协程是单线
    2023-06-29

    android虚拟机原理是什么

    Android虚拟机的原理是将Android操作系统安装在主机操作系统上,通过虚拟化技术实现在主机上运行Android应用程序。具体来说,Android虚拟机的实现可以分为两个层次:硬件层次和软件层次。在硬件层次上,Android虚拟机利用
    2023-10-12

    android binder机制原理是什么

    Android Binder机制是Android系统中用于进程间通信(IPC)的核心机制,它基于进程间通信的原理,实现了高效、安全、稳定的进程间通信。Android Binder机制的原理主要包括以下几个方面:1. Binder驱动:Bin
    2023-09-20

    Android中AsyncTask的工作原理是什么

    这篇文章给大家分享的是有关Android中AsyncTask的工作原理是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。概述实际上,AsyncTask内部是封装了Thread和Handler。虽然AsyncTa
    2023-06-15

    Android中的SurfaceFlinger工作原理是什么

    这篇文章将为大家详细讲解有关Android中的SurfaceFlinger工作原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。概念SurfaceFlinger是一个系统服务,如:au
    2023-06-22

    android handler的机制和原理是什么

    Android中的Handler机制是用于在不同线程之间进行消息传递和任务调度的一种机制。它的原理是基于消息队列和Looper。1. 消息队列(MessageQueue):每个线程都有一个消息队列,用于存放待处理的消息。Handler通过向
    2023-08-24

    Android Adapter机制和原理是什么

    Android Adapter机制是一种将数据与视图之间进行绑定的机制,用于在Android应用中将数据呈现给用户。Adapter负责将数据源(如数组、列表、数据库等)转化为视图(如列表项、网格项等),并将其展示在界面上。Android的A
    2023-09-26

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录