几分钟后,带有处理程序的 Gridview 中的多个 Runnable 减慢计时器

Multiple Runnable in Gridview with Handler Slow down timer after some minutes

我正在开发应用程序。令牌数显示在每个带有计时器的网格中。我有 5 个状态 1.not 已启动,2.started、3.pause、4.stop、5.cancel 任何时候我都可以通过 broadcastreceiver 启动、暂停和停止计时器,我用来刷新通过 notifydatasetchange() 网格化。一旦我开始,暂停和停止计时器有时(15 分钟)运行 计时器秒跳过 3 到 5 秒后工作正常。任何人都可以指导我,无论我是否只做对了,或者任何错误都可以帮助我。提前致谢 适配器下方附加的片段,class 和布局

public class CountdownAdapter extends ArrayAdapter<Orders> {

private LayoutInflater lf;
public Context ctx;
private List<Runnable> updateTimerThread;


public CountdownAdapter(Context context, List<Orders> objects) {
    super(context, 0, objects);
    ctx = context;
    lf = LayoutInflater.from(context);
    updateTimerThread = new ArrayList<Runnable>();
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    if (convertView == null) {
        holder = new ViewHolder();
        convertView = lf.inflate(R.layout.list_item3, parent, false);

        holder.status = (TextView) convertView.findViewById(R.id.status);
        holder.tvProduct = (TextView) convertView.findViewById(R.id.tvProduct);
        holder.tvRunningTime = (TextView) convertView.findViewById(R.id.tvTimeRemaining);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    final ViewHolder finalHolder = holder;
    Runnable singlTimerThread = new Runnable() {

      public void run() {
        getItem(position).setTimeInMilliseconds(SystemClock.uptimeMillis() - getItem(position).getStartTime());
        getItem(position).setUpdatedTime(getItem(position).getTimeSwapBuff() + getItem(position).getTimeInMilliseconds());

        int secs = (int) (getItem(position).getUpdatedTime() / 1000);
        int mins = secs / 60;
        secs = secs % 60;

        finalHolder.tvRunningTime.setText("" + mins + ":" + String.format("%02d", secs));// + ":"+ String.format("%03d", milliseconds));

        getItem(position).setRate((long) (mins * getItem(position).getIntervaltime()));

        MeterActivity.mHandler.postDelayed(this, 1000);
      }
    };

    holder.tvProduct.setText("Token : " + getItem(position).getTableno());
    updateTimerThread.add(singlTimerThread);

    //0 means not started
    if (getItem(position).getStatus() == 0) {
        holder.status.setText("Status : Not Started");
    }
    //1 means intialized
    else if (getItem(position).getStatus() == 1) {
        holder.status.setText("Status : Ready to Start");
    }
    //2 means start
    else if (getItem(position).getStatus() == 2) {
        getItem(position).setStartTime(SystemClock.uptimeMillis() - getItem(position).getUpdatedTime());
        MeterActivity.mHandler.postDelayed(updateTimerThread.get(position + 1), 1000);
        holder.status.setText("Status : Started");
    }
    //3 means pause
    else if (getItem(position).getStatus() == 3) {
        holder.status.setText("Status : Paused");
        MeterActivity.mHandler.removeCallbacks(updateTimerThread.get(position + 1));
    }
    //4 means stop
    else if (getItem(position).getStatus() == 4) {
        MeterActivity.mHandler.removeCallbacks(updateTimerThread.get(position + 1));
        holder.status.setText("Status : Stopped");
    }
    return convertView;
}


class ViewHolder {
    TextView tvProduct, status;
    TextView tvRunningTime;
}}  

`

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:background="#565555"
    android:layout_height="wrap_content">

<TextView
    android:id="@+id/tvProduct"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="2dp"
    android:paddingRight="10dp"
    android:gravity="right|center"
    android:background="@color/gray"
    android:textColor="#203020"
    android:text="Token"
    android:visibility="visible"
    android:layout_gravity="right"
    android:textStyle="bold"
    android:textSize="15sp" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="left|center"
    android:textStyle="bold|italic"
    android:text="Status"
    android:id="@+id/status"
    android:padding="5dp"
    android:textColor="@color/white"
    android:textSize="13sp" /><FrameLayout
    android:layout_width="match_parent"
    android:layout_height="85dp">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/cardbackground"
        android:elevation="4dp"
        android:orientation="vertical"
        android:padding="10dp">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:background="@color/cardbackground"
        android:id="@+id/itemlayout"
        android:layout_height="wrap_content">

        <View
            android:layout_width="match_parent"
            android:layout_height="2dp"/>

        <TextView
            android:id="@+id/tvTimeRemaining"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="visible"
            android:textSize="40sp"
            android:layout_margin="5dp"
            android:textStyle="bold|italic"
            android:textColor="@color/redglow"
            android:gravity="center"
            android:text="00:00:00 " />

    </LinearLayout>
   </RelativeLayout>
</LinearLayout>

`

Attached Screenshot

我在下面也提到了 Whosebug link

看来 Runnables 在 updateTimerThread 列表中不断累积,这会使 ui 的响应速度变慢。在代码中看不到适配器项的 status 发生更改的位置或方式,但它似乎为 0 的时间比 4 长,因此更多回调被添加到列表和处理程序的消息队列将被删除。

检查日志是否是问题所在,并至少在 ViewHolder 中移动 Runnables 创建。理想情况下,您应该只有一个 runnable,您将在适配器的构造函数中对其进行初始化,并且 runnable 将保留 views/view 持有者的列表以进行更新。

这是一个代码示例:

public class MeterActivity extends Activity {

//...
private CountDownAdapter adapter;
private Timer timer;
private boolean inScroll = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //...

    adapter = new CountDownAdapter(this, new ArrayList<Orders>());
}

@Override
protected void onStart() {
    super.onStart();

    //...

    list.setOnScrollListener(new AbsListView.OnScrollListener() {
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

        }

        public void onScrollStateChanged(AbsListView view, int scrollState) {
             inScroll = scrollState != SCROLL_STATE_IDLE;
        }
    });
    //create a timer to execute tasks in a background thread
    timer = new Timer();
    //schedule a task to start in and repeat every 1000 ms
    CountdownTask task = new CountdownTask();
    adapter.setTask(task);
    timer.schedule(task, 1000, 1000);
}

@Override
protected void onStop() {
    super.onStop();

    //...

    //stop timer while the activity is in background
    timer.cancel();
    timer = null;
}

class CountdownTask extends TimerTask {

    public Map<Integer, CountDownAdapter.ViewHolder> itemHolders = new ConcurrentHashMap<>();

    @Override
    public void run() {
        Orders item;
        if (inScroll) {
            return;
        }
        //loop through all started items and update their runningTime
        List<CountDownAdapter.ViewHolder> holders = new ArrayList<>();
        List<String> texts = new ArrayList<>();
        for (Integer position : itemHolders.keySet()) {
            item = adapter.getItem(position);
            item.setTimeInMilliseconds(SystemClock.uptimeMillis() - item.getStartTime());
            item.setUpdatedTime(item.getTimeSwapBuff() + item.getTimeInMilliseconds());

            int secs = (int) (item.getUpdatedTime() / 1000);
            int mins = secs / 60;
            secs = secs % 60;

            item.setRate((long) (mins * item.getIntervaltime()));
            holders.add(itemHolders.get(position);
            texts.add("" + mins + ":" + String.format("%02d", secs)); // + ":"+ String.format("%03d", milliseconds

        }
        runOnUiThread(new UpdateRunnable(itemHolders.get(position), texts));
    }

    class UpdateRunnable implements Runnable {
        CountDownAdapter.ViewHolder[] holders;
        String[] texts;

        public UpdateRunnable(CountDownAdapter.ViewHolder[] holder, String[] text) {
            this.holders = holders;
            this.texts = texts;
        }

        @Override
        public void run() {
            for (int i = 0; i < holders.length; i++) {
                holders[i].tvRunningTime.setText(texts[i]);
            }
        }
    };
}
}

`

public class CountDownAdapter extends ArrayAdapter<Orders> {

private MeterActivity.CountdownTask task;

public CountDownAdapter(Context context, List<Orders> objects) {
    super(context, 0, objects);
}

public void setTask(MeterActivity.CountdownTask task) {
    this.task = task;
}

@Override
public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    ViewHolder holder = null;
    if (convertView == null) {
        holder = new ViewHolder();
        convertView = View.inflate(getContext(), R.layout.list_item3, parent, false);
        holder.status = (TextView) convertView.findViewById(R.id.status);
        holder.tvProduct = (TextView) convertView.findViewById(R.id.tvProduct);
        holder.tvRunningTime = (TextView) convertView.findViewById(R.id.tvTimeRemaining);
        convertView.setTag(holder);
    } else {
        //remove the holder from the TimerTask's map to disable updates
        // when the view & holder are recycled; it will be updated only if the status is 2
        task.itemHolders.remove(position);
        holder = (ViewHolder) convertView.getTag();
    }

    holder.tvProduct.setText("Token : " + getItem(position).getTableno());
    //0 means not started
    if (getItem(position).getStatus() == 0) {
        holder.status.setText("Status : Not Started");
    }
    //1 means intialized
    else if (getItem(position).getStatus() == 1) {
        holder.status.setText("Status : Ready to Start");
    }
    //2 means start
    else if (getItem(position).getStatus() == 2) {
        getItem(position).setStartTime(SystemClock.uptimeMillis() - getItem(position).getUpdatedTime());

        //add the holder to the TimerTask's map to enable updates if the item is started
        task.itemHolders.put(position, holder);            
        holder.status.setText("Status : Started");
    }
    //3 means pause
    else if (getItem(position).getStatus() == 3) {
        holder.status.setText("Status : Paused");
    //MeterActivity.mHandler.removeCallbacks(updateTimerThread.get(position + 1));
    }
    //4 means stop
    else if (getItem(position).getStatus() == 4) {

    //MeterActivity.mHandler.removeCallbacks(updateTimerThread.get(position + 1));
        holder.status.setText("Status : Stopped");
    }
    return convertView;
}

class ViewHolder {
    public TextView tvProduct, status;
    public TextView tvRunningTime;
}

}