Calling handler.removeCallbacks from onDeleted met

2019-08-23 23:26发布

问题:

I need to stop the handler when the widget is removed by the user but calling handler.removeCallbacks throws a nullpointerexception from the onDeleted method. I tried other workarounds like creating a method,in a class which implements runnable, to kill the runnable but this throw a nullpointerexception also.
Maybe handler gets null after the call of the onDeleted method so I tried to put it in the onDisabled method but nothing stop.
What am I doing wrong?

Here the code :

public class RAMWidget extends AppWidgetProvider {

private PieGraph pg;
private Context context;
private RemoteViews remoteViews;
private AppWidgetManager appWidgetManager;
private ComponentName widget;
private Handler handler;
private CustomRunnable runnable;

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
    this.context=context;
    this.appWidgetManager=appWidgetManager;
    remoteViews=new RemoteViews(context.getPackageName(),R.layout.widget_ram);
    widget=new ComponentName(context,RAMWidget.class);
    new DrawTask().execute();
    handler=new Handler();
    runnable=new CustomRunnable();
    handler.postDelayed(runnable,3000);
}

private class CustomRunnable implements Runnable
{
    private boolean stop;

    public CustomRunnable()
    {
        stop=false;
    }

    @Override
    public void run()
    {
         new DrawTask().execute();
         Log.i("STOP",stop+"");
         if(!stop)
            handler.postDelayed(this,3000);
         else
            return;
         Log.i("STOP",stop+"");
    }

    void killThread()
    {
        stop=true;
    }
}

private class DrawTask extends AsyncTask<Void,Void, Void>
{
    private PieSlice slice,_slice;
    private long total=0,free=0,rate=0;

    @Override
    protected Void doInBackground(Void... unused)
    {
        RandomAccessFile reader=null;
        try
        {
            reader=new RandomAccessFile("/proc/meminfo","r");
            long[] mems=new long[4];
            for(int i=0;i<4;i++)
            {
                String load = reader.readLine();
                String[] toks = load.split(":");
                mems[i] = Long.parseLong(toks[1].replace("kB","").trim());
            }
            total=mems[0]/1024;
            free=(mems[1]+mems[2]+mems[3])/1024;
            rate=(int)((float)(total-free)/total*100);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        if(reader!=null)
            try
            {
                reader.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        slice=new PieSlice();
        slice.setTitle("Available RAM");
        slice.setColor(Color.parseColor("#99CC00"));
        slice.setValue(total-free);
        _slice=new PieSlice();
        _slice.setTitle("Used RAM");
        _slice.setColor(Color.parseColor("#FFBB33"));
        _slice.setValue(free);
        publishProgress();
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values)
    {
        pg=new PieGraph(context);
        pg.measure(200,200);
        pg.layout(0,0,200,200);
        pg.setDrawingCacheEnabled(true);
        pg.addSlice(slice);
        pg.addSlice(_slice);
        pg.setInnerCircleRatio(150);
        for (PieSlice s : pg.getSlices())
            s.setGoalValue(s.getValue());
        pg.setDuration(1000);
        pg.setInterpolator(new AccelerateDecelerateInterpolator());
        pg.animateToGoalValues();
        pg.setPadding(3);
        remoteViews.setTextViewText(R.id.widget_ram_text, "Total RAM " + total + " MB");
        remoteViews.setTextViewText(R.id.widget_ram_text1,"Avaiable RAM "+(total-free)+" MB");
        remoteViews.setTextViewText(R.id.widget_ram_text2,"Used RAM "+free+" MB");
        Bitmap bitmap=pg.getDrawingCache();
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLACK);
        paint.setTextSize(18);
        paint.setShadowLayer(1f,0f,1f,Color.WHITE);
        Rect bounds=new Rect();
        paint.getTextBounds(rate+" %",0,new String(rate+" %").length(),bounds);
        int x=(bitmap.getWidth()-bounds.width())/2;
        int y=(bitmap.getHeight()+bounds.height())/2;
        canvas.drawText(rate+" %",x,y,paint);
        remoteViews.setImageViewBitmap(R.id.graph_widget,bitmap);
        appWidgetManager.updateAppWidget(widget,remoteViews);
    }
  }

  @Override
  public void onDeleted(Context context, int[] appWidgetIds) {
      runnable.killThread();
      handler.removeCallbacks(runnable);       //both of them don't work
      super.onDeleted(context, appWidgetIds);
  }

  @Override
  public void onDisabled(Context context) {
      runnable.killThread();
      handler.removeCallbacks(runnable);
      super.onDisabled(context);
  }
}

回答1:

The problem is that you can't depend on the same instance of your widget being called by Android each time, and so keeping non-static fields in your widget provider is a problem.

An easy solution would be to use static fields for handler and runnable. It looks like some of the other fields could go away too, for example PieGraph is constructed each time onProgressUpdate is called, so it could be a local. Basically you should avoid all non-static fields in a widget.