Android中计时的两种方法
2016-07-29 16:08
393 查看
1.Android中计时
趁最近两周不忙,自己支配的时间比较多,正好查漏补缺,这两天看了些Thread的基础知识,正好工作有个需求就是要记时。就把想到的记录一下。在Android中实现计时,有好几种方式,我用过的是单独开启一个Thread和利用Handler。单独开一个线程的话,没有办法直接进行UI更新,想到的依然是借助Handler。感觉还是直接利用Handler比较方便容易一下。效果图,简单还丑。
2.本Demo中的MVP模式
通过最近的学习,对Android中使用MVP有一些了解,但并没在完整的实际项目开发中使用过。本demo中,也是通过网上的资料以及看Google官方的Demo中的MVP模式后加上自己个人的习惯所写。我个人的习惯是:
1. 创建
BasePresenter、BaseView接口
2. 创建一个代理
contract接口,里面写好Model、View、Presenter所要实现的接口
3. 创建Model
4. 创建Presenter
5. View层实现代理
contract接口中的View的接口
2.1BasePresenter和BaseView接口
public interface BasePresenter { void atFirst(); } public interface BaseView<T> { void bindPresenter(T presenter); }
这两个接口都很简单。BasePresenter中这个方法在本demo中并没有用到,是个空的方法,只做了声明并没有实现回调。这个方法可以用来初始化一些资源或者控件。BaseView使用了泛型T,目前对于泛型也只是会一些简单使用,深入的讲解不了。
bindPresenter()这个方法顾名思义,就是将Presenter和View层关联起来。
2.2代理contract接口
这个代理接口的作用就是方便管理m,v,p各层的接口。还有个好处就是,一旦定好了方法,接下来的逻辑就会很清晰了,只需要实现这些方法就可以了。这里并不需要在意接口里我声明了哪些方法。看一下形式就可以。稍微需要注意的就是interface MainView extends BaseView<Presenter>这里的泛型
public interface MainContract { interface MainBiz{ void onStartByHandler(onStartListener onStartListener); void onStartByThread(onStartListener onStartListener); void onStop(onStopListener onStopListener); interface onStartListener{ void start(String time); } interface onStopListener{ void stop(String info,boolean b); } } interface MainView extends BaseView<Presenter>{ void initView(); void onStop(String info,boolean b); } interface Presenter extends BasePresenter{ void startByHandler(); void startByThread(); void stop(); void initView(TextView tv); void onRecycler(); } }
2.3创建桥梁Presenter
我并没有按照前面的顺序来,因为Model层里是逻辑关键,先把简单的层给介绍了。public class MainPresenter implements MainContract.Presenter { private MainContract.MainView view; private MainModel model; private TextView tv; public static MainPresenter newInstance(MainContract.MainView view) { return new MainPresenter(view); } public MainPresenter(MainContract.MainView view) { this.view = view; this.view.bindPresenter(this); model = new MainModel(); } @Override public void initView(TextView tv) { this.tv = tv; } @Override public void startByHandler() { model.onStartByHandler(new MainContract.MainBiz.onStartListener() { @Override public void start(String time) { if (view != null) { view.initView(); if (tv != null) { tv.setText(time); } } } }); } @Override public void startByThread() { model.onStartByThread(new MainContract.MainBiz.onStartListener() { @Override public void start(String time) { if (view != null) { view.initView(); if (tv != null) { tv.setText(time); } } } }); } @Override public void stop() { model.onStop(new MainContract.MainBiz.onStopListener() { @Override public void stop(String info, boolean b) { if (view != null) view.onStop(info, b); } }); } @Override public void onRecycler() { if (model != null) model = null; if (view != null) view = null; } @Override public void atFirst() { } }
这里想说的也就两点,一个是构造方法,一个是onRecycler()方法。
Presnter是Model层和View层的桥梁。构造方法将3者给联系起来。
关于onRecycler()这个方法,我的目的是将创建的Model层、View层和Presenter层的对象置null,这样能被回收,不然这三个对象在内存并不会被回收。这个方法我会在Activity后者Frgment的onDestroy()调用。如果要用到了RecyclerView,可以再加上
recyclerView.setAdapter(null)
然而,onRecycler()只是我的个人想法,我目前还没验证清楚,这个方法到底能不能起到些防止内存泄露的作用。若看博客的哪位对于有好的想法,请留言。
2.5View层实现MainContract.MainView接口
public class MainActivity extends AppCompatActivity implements MainContract.MainView { private MainContract.Presenter presenter; private Unbinder unbinder; @BindView(R.id.tv_time_main_activity) TextView tv; @BindView(R.id.bt_handler_main_activity) Button bt_handler; @BindView(R.id.bt_thread_main_activity) Button bt_thread; @BindView(R.id.bt_stop_main_activity) Button bt_stop; private boolean isHasClicked = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); unbinder = ButterKnife.bind(this); //初始化MainPresenter MainPresenter.newInstance(this); } /** * 点击Handler 按钮 */ @OnClick(R.id.bt_handler_main_activity) void onClickBtHandler() { if (presenter != null && !isHasClicked) { isHasClicked = true; presenter.startByHandler(); } } /** * 点击 Thread 按钮 */ @OnClick(R.id.bt_thread_main_activity) void onClickBtThread() { if (presenter != null && !isHasClicked) { isHasClicked = true; presenter.startByThread(); } } /** * 点击停止按钮 */ @OnClick(R.id.bt_stop_main_activity) void onClickBtStop() { if (presenter != null) { presenter.stop(); } } /** * 点击按钮时 拿到显示时间的TextView */ @Override public void initView() { if (tv != null && presenter != null) presenter.initView(tv); } /** * 结束计时给出提示 * * @param info */ @Override public void onStop(String info, boolean b) { isHasClicked = b; ToastUtils.show(MainActivity.this, info); } @Override public void bindPresenter(MainContract.Presenter presenter) { this.presenter = presenter; } @Override protected void onDestroy() { super.onDestroy(); if (presenter != null) presenter.onRecycler(); if (unbinder != null) unbinder.unbind(); } }
代码很简单,就是接口方法的回调。使用了ButterKnife省了
findViewById(),setListener()
记得在合适的时机初始化MainPresenter就可以。
2.6Model层,实现计时逻辑
public class MainModel implements MainContract.MainBiz { private onStartListener onStartListener; private final int MSG_WHAT_HANDLER = 101; private final int MSG_WHAT_THREAD = 102; private int currentTime; private int type = 0; private final int TYPE_DEFAULT = 0; private final int TYPE_HANDLER = 1; private final int TYPE_THREAD = 2; private final String THREAD_NAME = "thread_time"; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_WHAT_HANDLER && onStartListener != null) { onStartListener.start(TimeUitls.milliSecondToMinute(currentTime)); currentTime += 1000; onStartByHandler(onStartListener); } else if (msg.what == MSG_WHAT_THREAD && onStartListener != null) { onStartListener.start(TimeUitls.milliSecondToMinute(currentTime)); currentTime += 1000; } } }; /** * 使用Handler方式 * @param onStartListener */ @Override public void onStartByHandler(onStartListener onStartListener) { if (this.onStartListener == null) { this.onStartListener = onStartListener; type = TYPE_HANDLER; } long now = SystemClock.uptimeMillis(); long next = now + (1000 - now % 1000); handler.sendEmptyMessageAtTime(MSG_WHAT_HANDLER, next); } /** * 使用单独开启线程 * @param onStartListener */ @Override public void onStartByThread(onStartListener onStartListener) { if (this.onStartListener == null) { this.onStartListener = onStartListener; type = TYPE_THREAD; } ThreadUtils.newThread(THREAD_NAME, new Runnable() { @Override public void run() { while (ThreadUtils.isAlive(THREAD_NAME)) { handler.sendEmptyMessage(MSG_WHAT_THREAD); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } @Override public void onStop(onStopListener onStopListener) { if (type == TYPE_DEFAULT) { onStopListener.stop("计时还没有开始",false); return; } if (type == TYPE_HANDLER) { handler.removeMessages(MSG_WHAT_HANDLER); } else if (type == TYPE_THREAD) { ThreadUtils.killThread(THREAD_NAME); } onStopListener.stop("计时结束",false); currentTime = 0; onStartListener = null; type = TYPE_DEFAULT; } }
在
onStartByHandler()方法中,Handler发送一个Message的方法用的是
handler.sendEmptyMessageAtTime(),并没有使用
handler.sendEmptyMessageDelayed()。这里是看了徐医生大神的博客强迫症的研究——MediaPlayer播放进度条的优化中学习到的一个知识点。
long next = now + (1000 - now % 1000);这短短的一行代码可以有误差补偿的作用,算法这玩意果然好神奇。我也直接在Handler的
handleMessage()方法中,在
onStartByHandler(onStartListener)这句前,直接用
Thread.sleep(500)验证了下,拿另外一个手机打开系统带的计时器,同时看两个手机,感觉两次计时的间隔还是1s,也可能是感觉不出来。但设置900后,就明显感觉到两次计时间隔不是1s了。这里还有待继续了解。但目前直接来用,是没有问题的。我把手机放在那不管,一直50分钟也没有问题。
在
onStartByThread()方法中,就是开启一个子线程,每隔1s利用Handler发一个空消息。结束一个Thread,就是让
run()方法结束就可以了,只需要将
while()循环的条件改为
false就可以。这里我简单实现了一个工具类,可以直接方便的更改循环条件。
3.最后
上篇博客说实现了两种计时后,就尝试自己来写一下多线程下载一个大文件。这正好可以用来学习Thread和I/O的知识。本打算明天就写的,但明天周末打算回家一趟。既然回家了,就不敲代码了。代码
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories