昆明凡科建站多少钱南昌做网站的公司哪里好
news/
2025/9/24 4:04:57/
文章来源:
昆明凡科建站多少钱,南昌做网站的公司哪里好,网页制作软件分为两类,网销工作内容简述概述 对于刚入门的同学来说#xff0c;往往都会对Handler比较迷茫#xff0c;到底Handler是个什么样的东西。当然#xff0c;可能对于一些有工作经验的工程师来说#xff0c;他们也不一定能很准确地描述#xff0c;我们来看下API的介绍。 Handler是用来结合线程的消息队列…概述 对于刚入门的同学来说往往都会对Handler比较迷茫到底Handler是个什么样的东西。当然可能对于一些有工作经验的工程师来说他们也不一定能很准确地描述我们来看下API的介绍。 Handler是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每一个Handler实例之后会关联一个线程和该线程的消息队列。当你创建一个Handler的时候从这时开始它就会自动关联到所在的线程/消息队列然后它就会陆续把Message/Runnalbe分发到消息队列并在它们出队的时候处理掉。 从官方文档中我们不难找出其中的关键词就是“线程”。我们都知道一个涉及到网络操作耗时操作等的Android应用都离不开多线程操作然而如果这时我们允许并发更新UI那么最终导致控件的状态都是不可确定的。所以我们可以通过对控件进行加锁在不需要用时解锁这是一个解决方案之一但最后很容易造成线程阻塞效率会非常差。所以谷歌采用了只允许在主线程更新UI所以作为线程通信桥梁的Handler也就应运而生了。 Looper、MessageQueue、Message、Handler的关系 讲到Handler肯定离不开Looper、MessageQueue、Message这三者和Handler之间的关系下面简略地带过详细自己可以查阅相关资料或者查看源码这样更方便大家深入学习。 Looper 每一个线程只有一个Looper每个线程在初始化Looper之后然后Looper会维护好该线程的消息队列用来存放Handler发送的Message并处理消息队列出队的Message。它的特点是它跟它的线程是绑定的处理消息也是在Looper所在的线程去处理所以当我们在主线程创建Handler时它就会跟主线程唯一的Looper绑定从而我们使用Handler在子线程发消息时最终也是在主线程处理达到了异步的效果。 那么就会有人问为什么我们使用Handler的时候从来都不需要创建Looper呢这是因为在主线程中ActivityThread默认会把Looper初始化好prepare以后当前线程就会变成一个Looper线程。 MessageQueue MessageQueue是一个消息队列用来存放Handler发送的消息。每个线程最多只有一个MessageQueue。MessageQueue通常都是由Looper来管理而主线程创建时会创建一个默认的Looper对象而Looper对象的创建将自动创建一个MessageQueue。其他非主线程不会自动创建Looper。 Message 消息对象就是MessageQueue里面存放的对象一个MessageQueu可以包括多个Message。当我们需要发送一个Message时我们一般不建议使用new Message()的形式来创建更推荐使用Message.obtain()来获取Message实例因为在Message类里面定义了一个消息池当消息池里存在未使用的消息时便返回如果没有未使用的消息则通过new的方式创建返回所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。 四者关系总体如下如有不对的地方谢谢指出 Handler的主要用途 推送未来某个时间点将要执行的Message或者Runnable到消息队列。在子线程把需要在另一个线程执行的操作加入到消息队列中去。废话不多说通过举例来说明Handler的两个主要用途。 1. 推送未来某个时间点将要执行的Message或者Runnable到消息队列 实例通过Handler配合Message或者Runnable实现倒计时 首先看一下效果图 方法一通过Handler Message的方式实现倒计时。代码如下public class MainActivity extends AppCompatActivity {private ActivityMainBinding mBinding;private Handler mHandler ;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding DataBindingUtil.setContentView(this, R.layout.activity_main);//设置监听事件mBinding.clickBtn.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {//通过Handler Message的方式实现倒计时for (int i 1; i 10; i) {Message message Message.obtain(mHandler);message.what 10 - i;mHandler.sendMessageDelayed(message, 1000 * i); //通过延迟发送消息每隔一秒发送一条消息}}});mHandler new Handler() {Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);mBinding.time.setText(msg.what ); //在handleMessage中处理消息队列中的消息}};}
} 其实代码不用怎么解释都比较通俗易懂但是这里用到了DataBiding可能没用过的同学看起来有点奇怪但其实反而简略了代码有一定基础的同学看起来都不会有太大压力所以不做太多解释。通过这个小程序作者希望大家可以了解到Handler的一个作用就是在主线程中可以通过Handler来处理一些有顺序的操作让它们在固定的时间点被执行。 方法二通过Handler Runnable的方式实现倒计时。代码如下public class MainActivity extends AppCompatActivity {private ActivityMainBinding mBinding;private Handler mHandler new Handler();Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding DataBindingUtil.setContentView(this, R.layout.activity_main);//设置监听事件mBinding.clickBtn.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {for (int i 1; i 10; i) {final int fadedSecond i;//每延迟一秒发送一个Runnable对象mHandler.postDelayed(new Runnable() {Overridepublic void run() {mBinding.time.setText((10 - fadedSecond) );}}, 1000 * i);}}});}
} 方法二也是通过代码让大家加深Handler处理有序事件的用途之所以分开Runnable和Message两种方法来实现是因为很多人都搞不清楚为什么Handler可以推送Runnable和Message两种对象。其实无论Handler将Runnable还是Message加入MessageQueue最终都只是将Message加入到MessageQueue。只要大家看一下源码就可以知道Handler的post Runnable对象这个方法只是对post Message进行了一层封装所以最终我们都是通过Handler推送了一个Message罢了至于为什么会分开两种方法下文会给大家详说究竟。下面再来看看Handler的第二个主要用途。 2. 在子线程把需要在另一个线程执行的操作加入到消息队列中去 实例通过Handler Message来实现子线程加载图片在UI线程显示图片 效果图如下 代码如下(布局代码也不放出来了)
public class ThreadActivity extends AppCompatActivity implements View.OnClickListener {private ActivityThreadBinding mBinding null;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding DataBindingUtil.setContentView(this, R.layout.activity_thread);// 设置点击事件mBinding.clickBtn.setOnClickListener(this);mBinding.resetBtn.setOnClickListener(this);}Overridepublic void onClick(View v) {switch (v.getId()) {// 响应load按钮case R.id.clickBtn:// 开启一个线程new Thread(new Runnable() {Overridepublic void run() {// 在Runnable中进行网络读取操作返回bitmapfinal Bitmap bitmap loadPicFromInternet();// 在子线程中实例化Handler同样是可以的只要在构造函数的参数中传入主线程的Looper即可Handler handler new Handler(Looper.getMainLooper());// 通过Handler的post Runnable到UI线程的MessageQueue中去即可handler.post(new Runnable() {Overridepublic void run() {// 在MessageQueue出队该Runnable时进行的操作mBinding.photo.setImageBitmap(bitmap);}});}}).start();break;case R.id.resetBtn:mBinding.photo.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.default_pic));break;}}/**** HttpUrlConnection加载图片不多说* return*/public Bitmap loadPicFromInternet() {Bitmap bitmap null;int respondCode 0;InputStream is null;try {URL url new URL(https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u1421494343,3838991329fm23gp0.jpg);HttpURLConnection connection (HttpURLConnection) url.openConnection();connection.setRequestMethod(GET);connection.setConnectTimeout(10 * 1000);connection.setReadTimeout(5 * 1000);connection.connect();respondCode connection.getResponseCode();if (respondCode 200) {is connection.getInputStream();bitmap BitmapFactory.decodeStream(is);}} catch (MalformedURLException e) {e.printStackTrace();Toast.makeText(getApplicationContext(), 访问失败, Toast.LENGTH_SHORT).show();} catch (IOException e) {e.printStackTrace();} finally {if (is ! null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}return bitmap;}
} Handler推送Message和Runnable的区别 在上文我们通过用Handler推送Message和Runnable实现相同的倒计时效果这里我们就说一下Post(Runnable)和SendMessage(Message)的区别。 首先我们看看post方法和sendMessage方法的源码 public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);} public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);} 可见两个方法都是通过调用sendMessageDelayed方法实现的所以可以知道它们的底层逻辑是一致的。 但是post方法的底层调用sendMessageDelayed的时候却是通过getPostMessage(r)来将Runnable对象来转为Message我们点进方getPostMessage()法可以看到 private static Message getPostMessage(Runnable r) {Message m Message.obtain();m.callback r;return m;} 其实最终runnable最终也是转化为一个Message而这个Message只有一个被赋值的成员变量就是Runnable的回调函数也就是说这个Message在进入MessageQueue之后它只是一个“动作”即我们Runnbale的run方法里面的操作。 要知道我们的Message类可是有很多参数的所以你可以理解为它是一个非常丰富的JavaBean可以看看它的成员变量 public int what;public int arg1;public int arg2;public Object obj;...那么讲到这里大家也应该有所理解为什么Google工程师为什么会封装这两种方法我总结如为为了更方便开发者根据不同需要进行调用。当我们需要传输很多数据时我们可以使用sendMessage来实现因为通过给Message的不同成员变量赋值可以封装成数据非常丰富的对象从而进行传输当我们只需要进行一个动作时直接使用Runnable在run方法中实现动作内容即可。当然我们也可以通过Message.obtain(Handler h, Runnable callback)来传入callback接口但这样看起来就没有post(Ruannable callback)那么直观。 API API是我们学习最好的文档所以我也简要跟大家学习一下其实大家认真看我上面的介绍加上自己亲手实践Handler的API大家都可以随便翻阅了。 构造函数 Handler()Handler(Handler.Callback callback)传入一个实现的Handler.Callback接口接口只需要实现handleMessage方法。Handler(Looper looper)将Handler关联到任意一个线程的Looper在实现子线程之间通信可以用到。Handler(Looper looper, Handler.Callback callback)主要方法 void dispatchMessage (Message msg)一般情况下不会使用因为它的底层实现其实是作为处理系统消息的一个方法如果真要用效果和sendMessage(Message m)效果一样。 public void dispatchMessage(Message msg) {if (msg.callback ! null) {// 如果有Runnbale则直接执行它的run方法handleCallback(msg);} else {//如果有实现自己的callback接口if (mCallback ! null) {//执行callback的handleMessage方法if (mCallback.handleMessage(msg)) {return;}}//否则执行自身的handleMessage方法handleMessage(msg);}}private static void handleCallback(Message message) {message.callback.run();} void dump (Printer pw, String prefix)主要Debug时使用的一个方法dump函数只是使用了Printer对象进行了打印打印出Handler以及Looper和Queue中的一些信息源码如下 public final void dump(Printer pw, String prefix) {pw.println(prefix this SystemClock.uptimeMillis());// 如果Looper为空输出Looper没有初始化if (mLooper null) {pw.println(prefix looper uninitialized);} else {// 否则调用Looper的dump方法Looper的dump方法也是mLooper.dump(pw, prefix );}} 通过测试用例大家会了解得更清晰 //测试代码Printer pw new LogPrinter(Log.ERROR, MyTag);mHandler.dump(pw, prefix); 结果 Looper getLooper ()拿到Handler相关联的Looper String getMessageName (Message message)获取Message的名字默认名字为message.what的值。 void handleMessage (Message msg)处理消息。 boolean hasMessages (int what)判断是否有Message的what值为参数what。 boolean hasMessages (int what, Object object)判断是否有Message的what值为参数whatobj值为参数object。 Message obtainMessage (int what, Object obj)从消息池中拿到一个消息并赋值what和obj其他重载函数同理。 boolean post (Runnable r)将Runnable对象加入MessageQueue。 boolean post (Runnable r)将Runnbale加入到消息队列的队首。但是官方不推荐这么做因为很容易打乱队列顺序。 boolean postAtTime (Runnable r, Object token, long uptimeMillis)在某个时间点执行Runnable r。 boolean postDelayed (Runnable r, long delayMillis)当前时间延迟delayMillis个毫秒后执行Runnable r。 void removeCallbacks (Runnable r, Object token)移除MessageQueue中的所有Runnable对象。 void removeCallbacksAndMessages (Object token)移除MessageQueue中的所有Runnable和Message对象。 void removeMessages (int what)移除所有what值得Message对象。 boolean sendEmptyMessage (int what)直接拿到一个空的消息并赋值what然后发送到MessageQueue。 boolean sendMessageDelayed (Message msg, long delayMillis)在延迟delayMillis毫秒之后发送一个Message到MessageQueue。 Handler引发的内存泄漏 在上面的例子中为了展示方便我都没有考虑内存泄漏的情况但是在实际开发中如果不考虑代码的安全性的话尤其当一个项目到达了一定的规模之后那么对于代码的维护和系统的调试都是非常困难的。而Handler的内存泄漏在Android中也是一个非常经典的案例。 详细可以参考How to Leak a Context: Handlers Inner Classes 或参考翻译文Android中Handler引起的内存泄露 通常我们都会在一个Activity内部定义一个Handler的内部类 public class MainActivity extends AppCompatActivity {private Handler mHandler new Handler(){Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){...}}};Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding DataBindingUtil.setContentView(this, R.layout.activity_main);mHandler.postDelayed(new Runnable() {Overridepublic void run() {...}}, 1000000);}
} 1外部类Activity中定义了一个非静态内部类Handler非静态内部类默认持有对外部类的引用。如果外部Activity突然关闭了但是MessageQueue中的消息还没处理完那么Handler就会一直持有对外部Activty的引用垃圾回收器无法回收Activity从而导致内存泄漏。 (2) 如上代码在postDelayed中我们在参数中传入一个非静态内部类Runnable这同样会造成内存泄漏假如此时关闭了Activity那么垃圾回收器在接下来的1000000ms内都无法回收Activity造成内存泄漏。 解决方案 (1) 将非静态内部类Handler和Runnable转为静态内部类因为非静态内部类(匿名内部类)都会默认持有对外部类的强引用。 (2) 改成静态内部类后对外部类的引用设为弱引用因为在垃圾回收时会自动将弱引用的对象回收。 避免内存泄漏的例子 public class HandlerActivity extends AppCompatActivity {private final MyHandler mHandler new MyHandler(this);private static final Runnable mRunnable new Runnable() {Overridepublic void run() {// 操作}};Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_fourth);mHandler.postDelayed(mRunnable, 1000*10);finish(); }private static class MyHandler extends Handler {WeakReferenceHandlerActivity mWeakActivity;public MyHandler(HandlerActivity activity) {this.mWeakActivity new WeakReferenceHandlerActivity(activity);}Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);final HandlerActivity mActivity mWeakActivity.get();if (mActivity ! null) {// 处理消息}}}} HandlerThread 思考一下假如我们需要同时下载A和B下载A需要6s下载B需要5s在它们下载完成后Toast信息出来即可此时HandlerThread便是一种解决方式之一。那么HandlerThread到底是什么 HandlerThread就是一种线程。HandlerThread和普通的Thread之间的区别就是HandlerThread在创建的时候会提供自己该线程的Looper对象。所以如果大家了解清楚了我前面所讲的Looper、Message、Handler、MessageQueue的关系的话这里就很清楚HandlerThread是什么东西了。大家都知道我们在Actvity创建时系统会自动帮我们初始化好主线程的Looper然后这个Looper就会管理主线程的消息队列。但是在我们创建子线程时系统并不会帮我们创建子线程的Looper需要我们自己手动创建如下 new Thread(){Overridepublic void run() {super.run();Looper.prepare();Handler mHandler new Handler(Looper.myLooper());Looper.loop();}}.start(); 所以HandlerThread就在内部帮我们封装了Looper的创建过程从源码可以看到HandlerThread集成于Thread然后覆写run方法进行Looper的创建从而通过getLooper方法暴露出该线程的Looper对象 public class HandlerThread extends Thread {int mPriority;int mTid -1;Looper mLooper;...Overridepublic void run() {mTid Process.myTid();Looper.prepare();synchronized (this) {mLooper Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid -1;}public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() mLooper null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}...
} 所以通过HandlerThread我们可以轻松创建一个包含了Looper的子线程 final HandlerThread mHandlerThread new HandlerThread(HandlerThread);mHandlerThread.start();Handler mHandler new Handler(mHandlerThread.getLooper()); 使用HandlerThread同时下载A和B的Demo 代码 public class HandlerThreadActivity extends AppCompatActivity {private TextView tv_A, tv_B;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler_thread);tv_A (TextView) findViewById(R.id.txt_dlA);tv_B (TextView) findViewById(R.id.txt_dlB);final Handler mainHandler new Handler();final HandlerThread downloadAThread new HandlerThread(downloadAThread);downloadAThread.start();Handler downloadAHandler new Handler(downloadAThread.getLooper());// 通过postDelayed模拟耗时操作downloadAHandler.postDelayed(new Runnable() {Overridepublic void run() {Toast.makeText(getApplicationContext(), 下载A完成, Toast.LENGTH_SHORT).show();mainHandler.post(new Runnable() {Overridepublic void run() {tv_A.setText(A任务已经下载完成);}});}}, 1000 * 5);final HandlerThread downloadBThread new HandlerThread(downloadBThread);downloadBThread.start();Handler downloadBHandler new Handler(downloadBThread.getLooper());// 通过postDelayed模拟耗时操作downloadBHandler.postDelayed(new Runnable() {Overridepublic void run() {Toast.makeText(getApplicationContext(), 下载B完成, Toast.LENGTH_SHORT).show();mainHandler.post(new Runnable() {Overridepublic void run() {tv_B.setText(B任务已经下载完成);}});}}, 1000 * 7);}
} 总结 由于Android的UI更新只能在主线程所以Handler是Android中一套非常重要的更新UI线程机制虽然在很多框架的帮助下我们可以减少了很多Handler的代码编写但实际上很多框架的底层实现都是通过Handler来更新UI的所以可见掌握好Handler对我们来说是多么重要所以这也是很多面试官在面试中的高频考点之一。虽然Handler对开发者来说是一个非常方便的存在但是我们也不能否认它也是存在缺点的如处理不当Handler所造成的的内存泄漏对开发者来说也是一个非常头疼的难题。 转载于:https://www.cnblogs.com/ryanleee/p/8204450.html
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/914793.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!