handleandroid
㈠ android 线程可以有几个handle
android.os.Handler Handler在android里负责发送和处理消息。它的主要用途有: 1)按计划发送消息或执行某个Runnanble(使用POST方法); 2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程) 默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理)。在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以sendMessage。Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。 android中Handle类的用法 当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉。Handler就是把这些功能放到一个单独的线程里执行,与Activity互不影响。 当用户点击一个按钮时如果执行的是一个常耗时操作的话,处理不好会导致系统假死,用户体验很差,而Android则更进一步,如果任意一个Acitivity没有响应5秒钟以上就会被强制关闭,因此我们需要另外起动一个线程来处理长耗时操作,而主线程则不受其影响,在耗时操作完结发送消息给主线程,主线程再做相应处理。那么线程之间的消息传递和异步处理用的就是Handler。 加群讨论研究 QQ群:56839077
㈡ android中handle和线程的关系是什么
作者:李板溪
链接:http://www.hu.com/question/24766848/answer/53037579
来源:知乎
着作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
问题背景,假设你要下载一张美女图显示出来。 使用这个问题就可以说明主要的问题了。
好了 上代码,下载美女图片,然后显示在 ImageView 中。 代码如下:
public class MainActivity extends AppCompatActivity {
public static final String beautyUrl = "http://ww3.sinaimg.cn/large/.jpg";
ImageView mBeautyImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBeautyImageView = (ImageView)findViewById(R.id.beauty);
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}
@Nullable
public Bitmap downloadImage(String urlString){
try {
final URL url = new URL(urlString);
try(InputStream is = url.openStream()){
return BitmapFactory.decodeStream(is);
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
然后这样的一段看似没有问题的代码,在 Android 3 以上是会直接报错的。 主要错误原因在
Caused by: android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
为了保证用户体验, Android 在 3.0 之后,就不允许在 主线程(MainThread)即 UI线程 中执行网络请求了。 那怎么办呢?
好吧,我们暂不考试 Android 提供的一系统组件及工具类, 用纯 Java 的方式来解决这个问题。
在新的线程中下载显示图片
在新创建的线程中下载显示图片如下:
new Thread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}
}).start();
看起来来错的样子,跑起来看看。 啊,又报错了。
android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views.
说只能在创建View 层级结构的线程中修改它。 创建它的线程就是主线程。 在别的线程不能修改。 那怎么办?
那现在我们遇到的问题是: 下载不能在主线程下载。 更新ImageView 一定要在 主线程中进行。 在这样的限制下,我们自然而然想去一个解决办法: 就是在新创建的线程中下载。 下载完成在主线程中更新 ImageView。
但是,怎么在下载完成之后,将图片传递给主线程呢?这就是我们问题的关键了。
线程间通信
我们的通信要求,当下载线程中下载完成时,通知主线程下载已经完成,请在主线程中设置图片。 纯 Java 的实现上面的线程间通信的办法我暂没有找到,于是我想到使用 FutureTask 来实现在主线程中等待图片下载完成,然后再设置。
FutureTask<Bitmap> bitmapFutureTask = new FutureTask<>(new Callable<Bitmap>() {
@Override
public Bitmap call() throws Exception {
return downloadImage(beautyUrl);
}
});
new Thread(bitmapFutureTask).start();
try {
Bitmap bitmap = bitmapFutureTask.get();
mBeautyImageView.setImageBitmap(bitmap);
} catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
不过这虽然骗过了 Android 系统,但是虽然系统阻塞的现象没有解决。 例如我在 get() 方法前后设置了输出语句:
Log.i(TAG,"Waiting Bitmap");
Bitmap bitmap = bitmapFutureTask.get();
Log.i(TAG,"Finished download Bitmap");
设置了下载时间至少 5 秒钟之后,输出如下:
06-27 23:30:18.058 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Waiting Bitmap 06-27 23:30:23.393 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Finished download Bitmap
让主线程什么事做不做,就在那傻等了半天。然后由于 onCreate没有返回。用户也就还没有看到界面。 导致说应用半天启动不了。。卡死了。。
查阅了一些资料,没有不用 Android 的框架层的东西而实现在其他进程执行指定代码的。
而 Android 中线程间通信就得用到 android.os.Handler 类了。 每一个 Handler 都与一个线程相关联。 而 Handler 的主要功能之一便是: 在另一个线程上安插一个需要执行的任务。
这样我的问题就通过一个 Handler 来解决了。
于是 onCreate 中的相关代码变成如下了:
final Handler mainThreadHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
看起来很酷的样子嘛,一层套一层的 Runnable. mainThreadHandler 因为是在 主线程中创建的, 而 Handler创建时,绑定到当前线程。 所以 mainThreadHandler 绑定到主线程中了。
当然 Android 为了方便你在向主线程中安排进操作,在 Activity类中提供了 runOnUiThread 方法。 于是上面的代码简化为:
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
你不用自己创建一个 Handler了。
而 runOnUiThread 的具体实现,也跟我们做得差不多。UI线程即主线程。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
Handler 与 Loop 简介
上面说到 每一个 Handler 都与一个线程相绑定。 实际上是,通过 Handler 与 Loop 绑定,而每一个 Loop 都与一个线程想绑定的。 比如 Handler 中两个主要构造函数大概如下:
public Handler(...){
mLooper = Looper.myLooper();
// ...
}
public Handler(Looper looper,...){
mLooper = looper;
// ...
}
public static Looper myLooper() {
return sThreadLocal.get();
}
然后 Looper 的构造函数如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
绑定了当前的线程和生成了一个消息队列。
值得提起的一点是, Looper 类保持了对 应用的主线程的 Looper 对象的静态应用。
private static Looper sMainLooper; // guarded by Looper.class
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
这样就可以方便你在其他线程中,使用一个绑定到主线程的 Handler,从而方便向主线程安插任务。 例如一般的图片处理库即是如此。这样你只要指定一个 图片的 url,及要更新的ImageView 即可。 如 Picasso 库可以用如下代码的读取并更新图片。 Picasso.with(context).load(url).into(imageView);
然后 Handler 可以做的事情还有很多。 因为它后面有 Looper 有 MessageQueue。可以深入了解下。
谈一下线程池与 AsyncTask 类
Android 早期便有这个便利的类来让我们方便的处理 工作线程及主线程的交互及通信。 但是现在先思考一下,我们上面的代码可能遇到的问题。 比如,我们现在要显示一个图片列表。 一百多张图片。 如果每下载一张就开一个线程的话,那一百多个线程,那系统资源估计支持不住。特别是低端的手机。
正确的做法是使用一个线程池。
final ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
executor.shutdown();
}
});
由于我们是在在一个局部方法中使用了一个线程池。所以处理完了之后应该将线程停止掉。 而我们上面只有一个线程,所以直接在下载完成之后,调用 executor停掉线程池。 那如果执行了多个图片的下载请求。需要怎么做呢? 那就要等他们都完成时,再停止掉线程池。 不过这样用一次就停一次还是挺浪费资源的。不过我们可以自己保持一个应用级的线程池。 不过这就麻烦不少。
然后 Android 早已经帮我们想法了这一点了。 我们直接使用 AsyncTask 类即可。
于是我们下载图片并显示图片的代码如下:
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mBeautyImageView.setImageBitmap(bitmap);
}
}.execute(beautyUrl);
相比之前的代码简洁不少。
看一下 AsyncTask 的源代码,正是集合我们之前考虑的这些东西。
一个全局的线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
一个绑定主线程的 Handler ,在线程中处理传递的消息
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
小结
Android 提供的 Looper 和 Handler 可以让我们非常方便的在各线程中安排可执行的任务
㈢ android中handle在内部类中为什么不执行
当Android应用启动的时候,会先创建一个应用主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
当在主线程中初始化Handler时,该Handler和Looper的消息队列关联。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。
如果外部类是Activity,则会引起Activity泄露 。
当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
要修改该问题,只需要按照Lint提示的那样,把Handler类定义为静态即可,然后通过WeakReference 来保持外部的Activity对象。
㈣ android在主线程中使用handle.postdelay做延时操作对主线程资源消耗大吗
android在主线程中使用handle.postdelay做延时操作对主线程资源消耗不大,因为handler中有一个消息池,是静态的消息池, 建议去了解一下Android中的Handler, Looper, MessageQueue之间的关系就知道了.