天哪!我们的代码中居然存在这么多的隐患,就连谷歌的安卓类库也是如此!

我们的代码中居然存在这么多的隐患,就连谷歌的安卓类库也是如此!
大家可以看看自己的程序和谷歌的类库,到处都有类似于if ( getActivity() !=null ) ,if ( mHandler != null ) ,if (Thread.currentThread() != mUiThread)等等的判断。。。。。
判断的目的无需解释,大家都明白,判断之后就应该是各种后续操作了。
这逻辑看起来没任何问题,干净清晰。但隐患恰恰就埋下了。
大家都知道,安卓是多线程的,比如,在判断的时候,getActivity() !=null , mHandler != null 是成立的,但恰巧刚判断完后,Activity被用户退出了,那么getActivity()和 mHandler就都是null了,这时,你的后续操作也就不可避免的出现空指针而崩溃!
我知道,可能会有人说,这种概率是非常非常低的。是的,我承认,概率是非常低,所以我们平常就很难发现这个问题。
但如果这种有隐患的代码到处都是,网上的各种例子也堂而皇之的这么写,那么这种代码会越来越多,肯定有一天,某个程序突然崩溃了,而我们却莫名其妙,看程序似乎没错,再次跟踪调试呢,这错误又不容易再现,于是就迷惑了。

咱们一般的码农犯这种错误倒可以理解,为什么谷歌的大神们也犯如此低级的错误呢?难道没有办法避免这种隐患吗?

程序运行过程中这些值都在内存中,你退出了,你的值还在内存中,你觉得这个会是空的么?java 虚拟机了解一下?

淡定,如果谷歌码农不犯错,那还要补丁做什么。
一般情况下,多线程(无阻塞)退出时是线程先退出,然后系统回收资源,所以你这种情况并不容易出现。
况且针对你这个例子,只要Activity是最后退出的就可以了,也就是说如果主线程控制子线程先退出,主线程最后退出时释放Activity就可以。
当然最坏的情况,偶然崩溃确实可能造成用户体验不佳,但并不是易于利用的安全漏洞,几率也很低。

Myc_CSDN 兄弟:下面这段代码, if (getActivity() != null){执行完后,恰巧Activity被用户退出了(虽然概率很低),getActivity().runOnUiThread会不会报空指针呢?

                if (getActivity() != null){
                getActivity().runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        myTextView.setText("更新");//UI更新
                    }

                });
         }       

mosespaul 老师,就算我说的Activity那种情况可以用主线程控制子线程先退出,主线程最后退出时释放Activity就可以。
那么,下面是谷歌runOnUiThread的源码。里面那句if (Thread.currentThread() != mUiThread) 是判断当前线程是不是UI线程是吧,如果恰巧(当然概率很低),
判断的时候Thread.currentThread()是UI线程,判断完后要执行下面语句的时候,当前线程不是UI了,那么会不会导致一些问题呢。
我所担心的就是这种到处隐藏的隐患。

public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}

mosespaul 老师,我再举个例子,您给分析一下。
为了避免Handler内存泄露,现在网上的普遍写法都是自己定义个静态的Handler。但是handleMessage需要用Activity里面的控件怎么办呢,于是来个弱引用,但是使用弱引用时候又担心Activity已经被销毁了,于是,保险起见,先判断是不是空(如下)。
但同理,判断的时候,Activity还没销毁,刚判断完,Activity就被销毁了。那么下面的UI更新处理能不崩溃吗。
我就是觉得这种代码如果到处都是的话,隐藏了许多隐患。平常玩的app还好,如果是网银的app,或是股票交易app呢。崩一次就够要命的。

private static class MyHandler extends Handler {

private WeakReference<Context> reference;   //

public MyHandler(Context context) {
    reference = new WeakReference<>(context);//这里传入activity的上下文
}
@Override
public void handleMessage(Message msg) {
    MainActivity activity = (MainActivity) reference.get();
    if(activity != null){
        activity.mTextView.setText("");
    }
}

}

mosespaul老师,请问给代码块加Syncronized能解决这问题吗?
我的意思是让判断是否为空和下面的实际处理作为一个整体事务进行,就是说在这个期间内,不让别的线程过来打断。这样就能保证整体的一致性了。
我们以前给银行做项目时,比如一个客户端要访问数据库中的一个表,为了防止其他客户端也并行的访问这个表,就要在操作前给这个表加个锁。
其实我们也知道,有时候访问一个表的处理时间基本上也是0.1ms都用不了,这期间别的用户同时过来访问的概率其实理论上是可以忽略不计的
(估计几十年也一次都不可能发生的),如果这个表是不常用的表,那估计一千年也不会发生一次。
但是,为了严谨,我们肯定是要做这个加锁操作的,如果项目组一起check源代码的时候,发现谁的程序有该加锁而不加的时候,那肯定是要改程序的。
我的意思是说,如果能有类似于加锁互斥这种方法来解决的话,不管是谷歌的类库还是我们的程序中,都应该加个锁。毕竟我们程序员的工作就是个严谨的工作。当然,如果,没有类似的解决方案,那就是只能是忽略这种小概率事件了(但心理总觉得不太舒服。。。。)。

Syncronized不行哎,其它线程不访问这个代码块照样可以释放资源。Lock肯定可以。
要是这样严谨的话(不能容忍极限情况下的地址为空访问错误的话),建议少用或者不用WeakReference了,至少不因为怕内存泄漏用WeakReference。
WeakReference的一个特点是它何时被回收是不可确定的, 因为这是由GC运行的不确定性所确定的. 所以, 一般用weak reference引用的对象是有价值被cache, 而且很容易被重新被构建, 且很消耗内存的对象.