这个Handler类应该是静态的,否则可能会发生泄漏(com.test.test3.ui.MainActivity.1)

4

我是一名新手Android开发者,尝试开发一个系统,但当我完成代码后,处理程序显示此警告:

下面是我编辑后的代码,在事件ontouch中的处理程序显示“handler不能解析”的警告。我尝试用//忽略处理程序,但运行应用程序时结果会强制关闭。

public class MainActivity extends Activity {



protected static final int STOP = 100;
ImageView iv;
private ProgressBar pb;
LinearLayout ll;
private AnimationDrawable anim;
ScrollView sv;
private SQLiteDatabase db;
private boolean flagscanning = false;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ll = new LinearLayout(this);
    new HandlerClass(this);

            db = SQLiteDatabase.openDatabase(Environment.getExternalStorageDirectory()+"/antivirus.sqlite", null, SQLiteDatabase.OPEN_READONLY);  
            iv = (ImageView) this.findViewById(R.id.imageView1);
                    //扫描病毒进度条
            pb = (ProgressBar) this.findViewById(R.id.progressBar1);
            ll = (LinearLayout) this.findViewById(R.id.ll);
                    //设置ImageView背景资源为动画文件
            iv.setBackgroundResource(R.drawable.bg);
                    //sv用来显示病毒的扫描结果
            sv = (ScrollView) this.findViewById(R.id.scrollView1);
            anim = (AnimationDrawable) iv.getBackground();
}

private static class HandlerClass extends Handler{
    private final WeakReference<MainActivity> mTarget;
    public HandlerClass(MainActivity context){
        mTarget = new WeakReference<MainActivity>((MainActivity) context);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity target = mTarget.get();
         if(msg.what==STOP){
             target.ll.removeAllViews();
             //anim.stop();

             }
         String str = (String) msg.obj; 
         TextView tv = new TextView(target);
         tv.setText(str);
         target.ll.setOrientation(LinearLayout.VERTICAL);
         target.ll.addView(tv);
         //sv.scrollBy(0, 20);

        System.out.println(str);

    }
};


@Override
public boolean onTouchEvent(MotionEvent event) {
    //如果程序正在杀毒过程中,拒绝再次启动杀毒线程
    if(flagscanning){
        return false;
    }

    //如果用户触摸屏幕,则开启杀毒线程  
    if (event.getAction() == MotionEvent.ACTION_UP) {
        flagscanning= true;
        anim.start();
        new Thread() {
            public void run() {
                // 获取每一个应用程序的签名,签名须与数据库的签名想比较
                List<PackageInfo> infos = getPackageManager()
                        .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_SIGNATURES);
                //设置进度条的扫描范围
                pb.setMax(infos.size());
                int total = 0;
                int virustotal = 0;//设置初始病毒数为0
                for (PackageInfo info : infos) {
                    total++;
                    try {
                        sleep(20);//只为便于观察扫描效果和进度,无实质作用
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message msg = Message.obtain();
                    msg.obj = "正在扫描" + info.packageName;
                    _handler.sendMessage(msg);_
                    Signature[] signs = info.signatures;
                    String str = signs[0].toCharsString();

                    String md5 = MD5Encoder.encode(str);
                    //将应用程序签名与数据库中保存的签名进行比较,如果相一致,则使病毒数加1,并通过handler在界面显示病毒包名
                    Cursor cursor = db.rawQuery("select desc from datable where md5=?",new String[] { md5 });
                    if (cursor.moveToFirst()) {
                        String desc = cursor.getString(0);
                        msg = Message.obtain();
                        msg.obj = info.packageName + ": " + desc;
                        _handler.sendMessage(msg);_
                        virustotal++;
                    }
                    cursor.close();
                    pb.setProgress(total);

                }
                Message msg = Message.obtain();
                msg.what = STOP;
                msg.obj = "扫描完毕 ,共发现" + virustotal + "个病毒";
                _handler.sendMessage(msg);_
                flagscanning = false;
                pb.setProgress(0);
            };
        }.start();
    }
    return super.onTouchEvent(event);
}

@Override
protected void onDestroy() {
    if (db.isOpen())
        db.close();
    super.onDestroy();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}
}

将你的处理程序类设为静态。https://dev59.com/7XA75IYBdhLWcg3wy8Mi - Raghunandan
HanderClass hc 声明为类成员。然后在 onCreate 中用 hc = new HandlerClass(this); 替换 new HandlerClass(this);。而不是使用 handler.sendMessage,请使用 hc.sendMessage - Raghunandan
2个回答

10

让你的处理程序成为静态类。

这是一个Lint警告。您可以禁用该警告,但它是有用的信息。

以下是Lint Check列表

http://tools.android.com/tips/lint-checks

引用来源于 @

http://android-developers.blogspot.in/2009/01/avoiding-memory-leaks.html

如果您无法控制非静态内部类的生命周期,请勿在活动中使用非静态内部类,而是使用静态内部类并在其中弱引用活动。

解决此问题的方法是使用具有对外部类的弱引用的静态内部类,例如在ViewRoot及其W内部类中所做的那样。

还要查看此Android开发人员小组上的讨论。请检查Romain Guy提出的解决方案

https://groups.google.com/forum/#!topic/android-developers/1aPZXZG6kWk

来自上述链接中Romain Guy解决方案的示例

 class OuterClass { 
 class InnerClass { 
  private final WeakReference<OuterClass> mTarget; 

   InnerClass(OuterClass target) { 
    mTarget = new WeakReference<OuterClass>(target); 
  } 

  void doSomething() { 
  OuterClass target = mTarget.get(); 
  if (target != null) target.do(); 
   }

编辑:

例子:

public class MainActivity extends Activity {

      LinearLayout ll;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ll = new LinearLayout(this);
        new HandlerClass(this);
    }
       private static class HandlerClass extends Handler{
           private final WeakReference<MainActivity> mTarget; 
        public HandlerClass(MainActivity context)
        {
             mTarget = new WeakReference<MainActivity>((MainActivity) context);

        }

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                MainActivity target = mTarget.get(); 
                if (target != null) 
                 if(msg.what==1){
                     target.ll.removeAllViews();
                    // anim.stop();

                     }
                 String str = (String) msg.obj;
                 TextView tv = new TextView(target);
                 tv.setText(str);
                 target.ll.setOrientation(LinearLayout.VERTICAL);
                 target.ll.addView(tv);
                 //sv.scrollBy(0, 20);

                System.out.println(str);

            }

        };
}

如果上述内容有误或存在问题,请纠正我。

你也可以查看Alex Lockwood的博客。

http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html


@chukokwann 尝试按照编辑后的帖子进行操作。还要检查答案中发布的最后一个链接。 - Raghunandan
嗯,如何在评论中添加代码?我尝试使用“code”但无法像您那样发布代码。 - chu kokwann
抱歉,我第一次在这里发布问题,从未考虑过这个。对于任何不便,我感到抱歉。我已经编辑了我的问题中的代码。 - chu kokwann
在类中声明HanderClass hc为一个类成员。然后在onCreate中,将new HandlerClass(this);替换为hc = new HandlerClass(this);。并且不要再使用handler.sendMessage,而是使用hc.sendMessage - Raghunandan
非常感谢,它终于起作用了。为这个问题苦苦挣扎了相当长的时间。真的非常感谢。 - chu kokwann
显示剩余5条评论

1
当您定义一个像这样的匿名内部类时,该类本身会为每个MainActivity实例重新定义。显然,Android SDK将其标记为有可能泄漏这些类定义的潜力。最简单的解决方案是将其变为静态内部类,并在构造函数中引用MainActivity
public class MainActivity extends Activity {
    //Fields and methods of MainActivity...
    private static final class MainHandler extends Handler {
        private final MainActivity caller;
        private MainHandler(final MainActivity caller) { this.caller = caller; }
        @Override public void handleMessage(Message msg) { //Your existing logic }
    }
}

在我编辑代码后,出现了“非法修饰符,仅允许在MainHandler的抽象或最终类中使用”和“无法对handlerMessage中的非静态字段进行静态引用”的错误。我尝试将其更改为“private static LinearLayout ll;”,但仍然无法解决问题。下面是编辑后的代码: - chu kokwann
公共类MainActivity扩展自Activity { 私有Handler处理程序 = new Handler(){ private static final class MainHandler extends Handler { private final MainActivity caller; private MainHandler(final MainActivity caller) { this.caller = caller; } @Override
public void handleMessage(Message msg) { }
ll.setOrientation(LinearLayout.VERTICAL); } } };
- chu kokwann
@Raghunandan 我尝试了Tom G建议的方法,但是它弹出一个非法修饰符,因为本地类mainhandler只允许抽象或最终的修饰符。而且,在handleMessage中声明和使用的值必须更改为静态值。 - chu kokwann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接