Archive for September, 2015

作者:AngryFox 分类: Uncategorized September 27th, 2015 暂无评论

朋友圈的技术特色主要有分布式多机房的设计,以及由多机房架构带来的异地数据一致性问题的产生,微信采用的策略是因果一致性,主要目的是让同一个图片下不同机房回复的用户评论保证因果次序。

红包
多主sharding,水平扩展机器。每抢到一个红包,就cas更新剩余金额和红包个数。数据库会累加已经领取的个数与金额,插入一条领取记录。入账则是后台异步操作。最后会有一个take all操作。另外还有一个对账来保障。
推荐是为了解决用户与item之间的关系,将用户感兴趣的item推荐给他/她。那么,一个item被推荐出来会经过候选、排序、策略、展示、反馈到评估再改变候选等等形成一个完整的回路。

cookie session,就是将session中的数据加密之后放在客户的cookie里,然后下发到客户端,这样也能做到与服务端完全无状态。第一个坑是怎么保证加密的密钥不泄露.第二个坑是重放攻击,如何避免 别人通过保存 cookie 去不停地尝试的验证码,当然也还有其他一些攻击手段。

作者:AngryFox 分类: Uncategorized September 24th, 2015 暂无评论

系统代码层面的优化。找出代码热点;重构代码逻辑;优化业务逻辑减少和合并远程调用;
架构优化。优化系统的架构,横向和纵向系统扩展;数据层面的合理cache;用户请求的拆分和合并等都会影响到系统的性能;
链路优化。举个例子用户在手机端访问详情会涉及到手机上的APP,到运营商的基站(CDN的部署),到服务端的接入网关,再到后端的业务系统(分为安全验证、访问控制、会话管理、以及各个业务系统的交互),最终的优化的目标是提升整个用户的访问体验,所以用户的一次访问的各个环节都要综合考虑给出最佳优化方案。

为何避免Java数据的序列化;
为何避免Java数据的编码;
大流量Web系统为何不要有Java做cache。

一是从消息系统自身的开发者角度介绍构建消息系统需要解决的核心问题(可用性、吞吐量、传输延迟、有序消息、消息存储、过滤、重发等)、Hermes的整体架构设计及存储演变(brokerless->master-slave brokers->clustered brokers,MongoDB->MySQL+File)以及与常见消息系统的比较等。二是从消息系统使用者的角度分享如何使用好消息系统、有哪些典型的应用场景以及在携程的应用推广情况等。

证券交易系统,面临的都是低延迟,高并发,事务性的操作。

我们应用Event Sourcing、Leader Election模式,解决交易中间件的高可用问题(多活,零延时切换)。
运用LMAX Disruptor解决服务内的高并发,低延迟消息传递。
基于PGM组播实现的交易总线解决服务任意扩展的问题。
利用CQRS(Command Query Responsibility Segmentation)Pattern实现事务性操作与查询操作分离,从而提高事务性操作并发性能。
基于Google Dapper论文实现消息Tracing,监控并定位性能瓶颈。
应用各种Cloud Design Pattern(Cache Aside,Circuit Breaker,Compute Resource Consolidation)等解决分布式系统的健壮性问题,以及CPU/IO资源的有效调度。

管理中最重要的,一是公司本身业务在蒸蒸日上,一是选对人。

Imgix的核心架构由多个服务层组成,包括原图抓取层、原图缓存层、图像处理层、负载平衡及分配层、以及内容传输层。每个服务层都有遍布全局的配置、日志记录、监视及管理服务。
Imgix的数据中心用46U机柜搭建了Mac Pro图像处理节点群。
可靠性:稳定,而且在出现各种常见问题(网络划分、集群拓扑变化、流量峰值等)时都能提供可以预见的性能;
高吞吐量:在高峰时段可以支持每秒数以百万计的消息传递;
低延迟:始终如一的低延迟;
工作负载隔离:在一个以日志为中心的分布式系统中,工作负载可以分为如下三类:写日志尾、读日志尾和“追赶读(catch-up read)”。各类负载互不影响;
可扩展性:在地理、节点数量、每秒请求数、数据规模、租户数量等各个维度可扩展;
可操作性:在扩展时易于操作,比如可以很容易地增加或移除节点;
简单:接口简单,便于开发人员使用。
(一)使用类似Kafka的发布/订阅系统;(二)使用类似Paxos或Raft的一致性算法构建新的服务或库;(三)使用一种像Apache BookKeeper那样的底层日志服务。
DistributedLog具有如下特性:

面向流的抽象:DistributedLog只暴露了一些简单的实体,在大多数情况下,用户都只需考虑将数据附加到一个已命名的流对象上;
命名和元数据:DistributedLog提供了一种永久命名和元数据方案,用户可以将已命名的日志或主题视为Ledgers集合的替代;
日志归属方案:BookKeeper没有规定一种专门的日志所有者方案,因此,DistributedLog实现了一种基于ZooKeeper的方案;
数据管理策略:DistributedLog允许用户针对不同的场景调整数据保留策略,并提供了一种将日志分段以便在存储节点间平衡数据分布的控制机制;
可调节的读写通道:在写入路径上,DistributedLog提供了一种高度可调节的批处理机制;在读取路径上,它提供了一种可调节的预读机制;
高效“扇入(fan-in)”和“扇出(fan-out)”的服务层:DistributedLog是一个专门针对多租户数据中心环境优化过的服务层,可以通过聚合来自多个数据源的写入来支持不关注日志归属的应用程序。在有成千上万的读者消费同一数据流的情况下,该服务可以通过缓存日志数据优化读取路径;
跨地理位置复制日志:可以保证日志跨多个数据中心可用;

作者:AngryFox 分类: Uncategorized September 20th, 2015 暂无评论

Replace short connections with persistent ones (using a connection pool), to reduce creation of buffers and objects during communication.
Use Objects and Memory pools appropriately, to reduce the load on the GC.
Use a Task Pool, a mechanism with a group of long-lived goroutines consuming global task or message queues sent by connection goroutines, to replace short-lived goroutines.
Monitor and control goroutine numbers in the program. The lack of control can cause unbearable burden on the GC, imposed by surges in goroutines due to uninhibited acceptance of external requests, as RPC invocations sent to inner servers may block goroutines recently created.
Remember to add read and write deadlines to connections when under a mobile network; otherwise, it may lead to goroutine blockage. Apply it properly and with caution when under a LAN network, otherwise your RPC communication efficiency will be hurt.
Use Pipeline (under Full Duplex feature of TCP) to enhance the communication efficiency of RPC framework.

TabViewCtronller, 知道怎么用Storyboard建tabview
TableViewController, IOS最常用的数据列表显示
SearchBar, SearchDisplayController, 配合TabView来搜索数据
Core data 数据存储, 超级好用, 还有NSFetchedResultsController, 配合使用实时显示数据, 相当于View与Model绑定, (不用React-native了)
WebView. Leanote IOS两款编辑器其实都是WebView, 所以webView很重要
AFNetworking

小步快跑,快速求证用户猜想。
灰度发布,在黑与白之间,平滑过渡的一种发布方式。
大系统小做:分进程,尽量保证进程的功能单一。
边运营边重构,细分实施,持续服务。
干干净净:建立信息,勇于承担,边打扫边生活。
工具齐备:自动化部署、监控,大数据分析。
追求小规模团队,成功的产品多由小团队开始尝试,简单的产品不断创新。缩短上线时间,不断迭代。
在条件不允许的情况下要降级处理,进行「有损手术」,并非最重要的功能弱化,在压力过高的时候做逻辑性处理,减轻后端服务器压力。

有自然语言分析、NLP这样的一些技术,我们还有语音识别,有视频或者图片里面对于场景、对于动作的捕捉与识别等,但是这些都还非常早期。这些技术如果能够往前走一步,就会带来巨大的想象空间。用户每一次沟通,每一次交互过程,都沉淀了大量的信息,但限于我们的分析能力还很原始,所以今天几乎所有的非结构化数据都还没有二次分析。

作者:AngryFox 分类: Uncategorized September 18th, 2015 暂无评论

遇到问题描述:
运行android程序控制台输出
[2013-10-13 16:45:50 - ] The connection to adb is down, and a severe error has occured.
[2013-10-1316:45:50 - ] You must restart adb and Eclipse.
[2013-10-1316:45:50 - ] Please ensure that adb is correctly located at ‘<--此处显示adb.exe的全路径-->‘ and can be executed.

问题分析1:
关闭eclipse,打开windows任务管理器,查看进程中是否有adb.exe或adb.exe *32。
解决方案1:
如果有则关闭该进程,重新启动eclipse即可;
问题分析2:
windows任务管理器中就找不到adb.exe或adb.exe *32的进程,可以尝试手动启动adb.exe ;如果手动启动后还找不到adb.exe进程,则 只有一种可能就是adb.exe进程的端口被占用,导致adb.exe未能正常启动所致(adb默认端口为5037)。
解决方案2:
1、查看端口使用情况:
netstat -aon|findstr “5037″
TCP 127.0.0.1:5037 0.0.0.0:0 LISTENING 5004
2、发现5004占用5037端口,查看5037的task:
tasklist|findstr “5037″
wandoujia_daemon.exe 5004 Console 1 4,276 K
打开windows管理器结束wandoujia_daemon.exe该进程,启动eclipse,如果adb.exe未能启动,手动启动即可;

作者:AngryFox 分类: Uncategorized September 13th, 2015 暂无评论

Uber消息系统:
check message(消息检查),而且我们还有很多的就是client state可以通过server push到client。以前整个stack都是基于HTTP和JSON,很多service都是Node.js写的。想法主要是,server和server之间,可能是不同team做的,但documentation还是需要一个strong type的一个IDL,就是你可以定义这个service只能管理这几个function API。每个API有input和response,要有它的full documentation。Thrift这方面做得非常简单。我们很多service这在往这上面迁移。因为client可能会通过一个HTTP Proxy连到server,time-out可能不是client自己去控制,而是中间一个Proxy,整个架构是用HAProxy将各个service连起来,TCP上有fd limit,如果collection point做得不好,QPS/RPS(request pre second)上去之后,server port就会受到限制。
Golang
Go 1.5用新的垃圾收集器代替了原来的STW(stop-the-world) GC,解决了延迟问题。当负载较重时,每50ms,新的GC的活动时间可以控制在10ms以下
状态协调(State coordination):Go 1.5 GC有个主要瓶颈是从Go 1.4继承来的,源自其集中式的GC协调器(coordinator),这是一个单独的goroutine,它会将工作进一步分派给worker goroutine去完成。一种解决方案是用去中心化的状态机代替集中式协调器。这样修改有个额外的好处,使得重新设计标记完成屏障(mark completion barrier)成为可能,因为它已经变得非常凌乱,而且性能很差。
信用系统(Credit system):Go 1.5在两个不同的地方使用了一个信用系统:一个是确保清扫(sweeping)在一个GC周期和下一次触发堆操作(the next heap trigger)之间完成,一个是确保扫描(scanning)在触发堆操作(an heap trigger)和随后实现堆处理目标之间完成。改进信用系统的一种建议方法是,使其操作总是在black阶段进行,以避免未完成的分配操作进入下一个GC周期。
标记结束(Mark termination):根据Clements和Hudson的介绍,在Go 1.5中,标记结束阶段是停顿时间的大头。这里的目标是尝试并确保大部分应用可以在10ms停顿的阈值下运行,这也是Go 1.5在很多情况下已经实现了的。希望所做修改的复杂度较低或中等,比如把finalizer扫描从标记结束阶段移到并发扫描,这样对于每1GB大小的堆,应该可以节省1ms,以及去掉一个成本很高的计数循环,对于较大的堆,这个循环占去了标记阶段的另外一半。
sweeper和scavenger:某些程序会在sweeper上消耗大量时间,在这上面投入些精力,应该有性能改进。一个非常激进的方案是完全去掉sweeper。还有一个不那么激进的方案,可以在GC阶段最后,尽早释放较大的对象,并且在所有的系统上支持scavenger,不管物理页面是多大。

作者:AngryFox 分类: Uncategorized September 12th, 2015 暂无评论
 从内核之上,从文件系统的init开始,
       init.c位置:system/core/init/init.c。
       在init.c的main函数里面完成以下步骤:
       1、创建设备节点。
       2、初始化log系统。
       3、解析init.rc文件,解析函数在同一目录的parser.c里面实现。
       4、初始化属性服务器,在同一目录下的property_service.c里面实现。
       。。。。
       最后、进入loop等待事件到来。
       init.rc的解析过程
       init.rc是一个初始化脚本,路径(不确定):device/renesas/emev/init.rc
       在init.c的main函数里面,调用parser.c的parse_config_file("/init.rc");执行解析过程。
       先读取文件内容到data里面,再调用parse_config(fn, data);进行解析。
       init.rc包含Android初始化语言的四大类声明:行为类(Actions)、命令类(Commands)、服务类(Services)、选项类(Options),解析完会形成两个列表service_list 和action_list。
       其中有一个很重要的服务就是zygote,在init.rc里面的片段:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
       这是脚本中service的格式:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
       zygote对应的可执行文件为app_process,android2.2中它的源代码位置:framework/base/cmds/app_process。
       app_main.cpp的main函数,它先调用AndroidRuntime::addVmArguments将它的参数“-Xzygote /system/bin”传给AndroidRuntime作为启动JavaVM用。接着如果定位余下的参数,如果有"--zygote",执行下面代码,从参数可以知道,这时候要求启动start system server,并且将com.android.internal.os.ZygoteInit装载至虚拟机。
ZygoteInit.java的main方法)。
        if (0 == strcmp("--zygote", arg)) {
            bool startSystemServer = (i < argc) ?
                    strcmp(argv[i], "--start-system-server") == 0 : false;
            setArgv0(argv0, "zygote");
            set_process_name("zygote");
            runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer);
        }
       否则不启动system server:
set_process_name(argv0);      

runtime.mClassName = arg;      

// Remainder of args get passed to startup class main()
runtime.mArgC = argc-i;
runtime.mArgV = argv+i;      

LOGV("App process is starting with pid=%d, class=%s.\n",
     getpid(), runtime.getClassName());
runtime.start();
       runtime是AppRuntime对象,继承自AndroidRuntime,在AndroitRuntime.app里的start()函数如下,默认装载的是RuntimeInit进程,不启动system server。
void AndroidRuntime::start()
{
    start("com.android.internal.os.RuntimeInit",
        false /* Don't start the system server */);
}
       在AndroidRuntime.cpp的start方法内,先启动JAVA虚拟机,在定位执行className类的main方法(如果才存在的话)。
void AndroidRuntime::start(const char* className, const bool startSystemServer)
{
    LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");      

    /* start the virtual machine */
    if (startVm(&mJavaVM, &env) != 0)
        goto bail;      

    startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            LOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
}
       先看怎么启动虚拟机的,在startVm方法内,先将一些系统参数选项,包括虚拟机相关的属性,保存到一个JavaVMOption的实例内,比如虚拟机的堆大小,是否check jni,JNI版本信息,最后将这些参数传给JNI_CreateJavaVM,创建JAVA虚拟机实例。
//JNI_CreateJavaVM在jni.h中有声明,代码位置:dalvik/libnativehelper/include/nativehelper/,在Jni.c中有定义
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
       在JNI_CreateJavaVM中,先检查JNI的版本,为JavaVM,JNIEnv开辟空间,保存通用接口,最后解析传进来的参数,最后调用dvmStartup启动虚拟机(有些传给JNI_CreateJavaVM的参数,也直接传给了dvmStartup)。
       这里的JNIEnv实际是JNIEnvExt强制转换过来的,JNIEnvExt是JNIEnv结构体的扩展,JNIEnExt前端与JNIEnv完全对齐,都是JNI函数指针。相当于JNIEnExt对JNIEnv进行了赋值。
pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
       在dvmStartup在Init.c定义,先调用setCommandLineDefaults进行一些默认的设置,比如虚拟机的默认heap大小。
static void setCommandLineDefaults()
{
    gDvm.heapSizeStart = 2 * 1024 * 1024;   // Spec says 16MB; too big for us.
    gDvm.heapSizeMax = 16 * 1024 * 1024;    // Spec says 75% physical mem
    gDvm.stackSize = kDefaultStackSize;
}
       然后调用dvmProcessOptions处理传进来的参数,比如是否执行zygote,最后,根据gDvm.zygote的值,是否申请一个新的堆,这个值在有参数-Xzygote置1。
if (gDvm.zygote) {
    if (!dvmInitZygote())
        goto fail;
} else {
    if (!dvmInitAfterZygote())
        goto fail;
}
       到这里,虚拟机算是启动成功了。
       回到AndroidRuntime.cpp的start方法,现在我们要启动ZygoteInit进程,找到ZygoteInit的main方法,调用env->CallStaticVoidMethod(startClass, startMeth, strArray);去启动它。
       CallStaticVoidMethod在Jni.c里面有定义,但不是直接定义的,因此用CallStaticVoidMethod作为函数名直接查定义是查不到的,是这样定义的:
       CALL_STATIC(void, Void, , , false);
       再看CALL_STATIC的宏定义:
#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
        jmethodID methodID, ...)                                            \
    {                                                                       \
        UNUSED_PARAMETER(jclazz);                                           \
        JNI_ENTER();                                                        \
        JValue result;                                                      \
        va_list args;                                                       \
        va_start(args, methodID);                                           \
        dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
        va_end(args);                                                       \
        if (_isref && !dvmCheckException(_self))                            \
            result.l = addLocalReference(env, result.l);                    \
        JNI_EXIT();                                                         \
        return _retok;                                                      \
    }                                                                       \
    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
        jmethodID methodID, va_list args)                                   \
    {                                                                       \
        UNUSED_PARAMETER(jclazz);                                           \
        JNI_ENTER();                                                        \
        JValue result;                                                      \
        dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
        if (_isref && !dvmCheckException(_self))                            \
            result.l = addLocalReference(env, result.l);                    \
        JNI_EXIT();                                                         \
        return _retok;                                                      \
    }                                                                       \
    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
        jmethodID methodID, jvalue* args)                                   \
    {                                                                       \
        UNUSED_PARAMETER(jclazz);                                           \
        JNI_ENTER();                                                        \
        JValue result;                                                      \
        dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
        if (_isref && !dvmCheckException(_self))                            \
            result.l = addLocalReference(env, result.l);                    \
        JNI_EXIT();                                                         \
        return _retok;                                                      \
    }
       因此CallStaticVoidMethod调用的是dvmCallMethodV方法,至于怎么样再调用到ZygoteInit的main方法,有待研究,现在直接看ZygoteInit的main方法,它的参数是这样定义的:
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
startSystemServerStr = env->NewStringUTF(startSystemServer ?
                                             "true" : "false");
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
申请一个String变量,给把类名作为第0个参数,第二个参数是"ture"或"false",用来决定是否启动system service,在ZygoteInit的main方法会读取这个参数。
if (argv[1].equals("true")) {
    startSystemServer();
} else if (!argv[1].equals("false")) {
    throw new RuntimeException(argv[0] + USAGE_STRING);
}
它读取到第一个参数是"true",所以调用startSystemServer启动system server,在startSystemServer中,调用Zygote的forkSystemServer,fork出来的子进程,就是system server了,调用事的参数如下:
String args[] = {
    "--setuid=1000",
    "--setgid=1000",
    "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
    "--capabilities=130104352,130104352",
    "--runtime-init",
    "--nice-name=system_server",
    "com.android.server.SystemServer",
};
这些参数解析到一个ZygoteConnection.Arguments对象中。forkSystemServer方法实际上是JNI方法,在vm/native/dalvik_system_Zygote.c中有定义。
static void Dalvik_dalvik_system_Zygote_forkSystemServer(
        const u4* args, JValue* pResult)
{
    pid_t pid;
    pid = forkAndSpecializeCommon(args);
    if (pid > 0) {
        int status;      

        LOGI("System server process %d has been created", pid);
        gDvm.systemServerPid = pid;      

        if (waitpid(pid, &status, WNOHANG) == pid) {
            LOGE("System server process %d has died. Restarting Zygote!", pid);
            kill(getpid(), SIGKILL);
        }
    }
    RETURN_INT(pid);
}
       具体的fork操作在forkAndSpecializeCommon里面进行,父进程检查子进程是否died。
       在forkAndSpecializeCommon,先判断是否zygote模式,是否剩余有足够的heap空间,最后才执行fork()系统调用。
       fork之后,父进程不做操作,子进程调用dvmInitAfterZygote去为自己申请堆空间。
       子进程返回到startSystemServer方法。
/* For child process */
if (pid == 0) {
    handleSystemServerProcess(parsedArgs);
}
       调用handleSystemServerProcess方法,在里面先设置uid的权限,再调用RuntimeInit.zygoteInit。

/*
 * Pass the remaining arguments to SystemServer.
 * "--nice-name=system_server com.android.server.SystemServer"
 */
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
/* should never reach here */
       在zygoteInit里面调用invokeStaticMain,invokeStaticMain先将com.android.server.Systemserver类的main方法取出,作为Method对象,然后和参数一起传给ZygoteInit.MethodAndArgsCaller,MethodAndArgsCaller继承自Runner,最终MethodAndArgsCaller调用Method.invode函数启动System Server。
作者:AngryFox 分类: Uncategorized September 4th, 2015 暂无评论

Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型.
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String.
第三,如果只有一个参数成员,最好把参数名称设为”value”,后加小括号.

移动网络环境下的主要坑点
1、长连接的维护
Android平台,维护client到server的长连接
2、心跳包GGSN
维护移动网的GGSN路由
3、消息回执处理(ack)
移动网络有可能丢包、发送、接受需要加入回执机制
4、语音图片的收发优化
大文件分拆为多个数据包,每个包10k字节,如果发生失败,是重发一个包,而不是每次重发整个文件

作者:AngryFox 分类: Uncategorized September 2nd, 2015 暂无评论

Android排错:has leaked window com.android.internal.policy.impl.PhoneWindow$ that was originally added here
异常场景:
经常在应用中需要处理一些耗时的工作,诸如读取大文件、访问网络资源等。为了避免因程序假死而带来的糟糕用户体验,通常我们可以通过线程+Handler或者Android提供的AsyncTask来解决该问题,并一般以ProgressDialog等提示性控件来告知用户当前的程序进度。而标题中描述的异常则会常常出现在这样的场景中,并且往往掩盖了导致异常的真正的罪魁祸首。

问题原因:
从异常描述中,大致的意思是存在窗口句柄泄露,即未能及时销毁某个PhoneWindow。而这往往误导了我们,把过多的精力放在查找所谓的内存泄露上了。其实存在这么一种情况,即因我们在非主线程中的某些操作不当而产生了一个严重的异常,从而强制当前Activity被关闭。而在关闭的同时,却没能及时的调用dismiss来解除对ProgressDialog等的引用,从而系统抛出了标题中的错误,而掩盖了真正导致这个错误的异常信息。

本解决方法并不能真正的解决问题,但是在一定程度上可以将真正导致错误的异常信息显露出来。即重写Activity的onDestroy方法,在方法中调用dismiss来解除对ProgressDialog等的引用。
if (progressDialog != null && progressDialog.isShowing())
{
progressDialog.dismiss();
}