几分钟后,带有处理程序的 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;
}
}
我正在开发应用程序。令牌数显示在每个带有计时器的网格中。我有 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;
}
}