Android-HandlerThread

在外部线程并不能拿到匿名内部类线程对象的Looper。比如主线程,创建了一个Thread对象,并不能通过Thread对象获取到该Thread的Looper对象。
而如果将Thread的Looper写全局对象,那么就存在耦合,并不会随着线程Thread的消失而消失。
HandlerThread就是一个线程。在HandlerThread中的run方法中,自动帮我们完成了Looper.prepare和Looper.loop()。
HandlerThread存在的意义主要是:
方便初始化,方便取线程Looper对象
保证了线程安全
解决有可能的异步问题。
面试:多线程的锁机制。
当有人通过HandlerThread的getLooper()方法获取线程对应的Looper对象的时候,如果Looper对象为null,那么就会调用wait()等待。而在HandlerThread的run方法中,如果Looper.prepare()成功之后,就会调用notifyAll()通知需要获取锁。
wait():会释放锁
notifyAll():不会释放当前持有的锁,只是会唤醒其他等待这个锁的线程,但是并不意味着会立马执行,需要等待notifyAll()所在的锁中的代码执行完成并且释放锁之后,被唤醒的其他线程去持有锁并且继续执行。

HandlerThread的应用:在IntentService中
IntentService初始化之后,实现onHandleIntent方法,而在IntentService中处理Handler消息的时候,就是调用onHandleIntent方法进行处理。
而IntentService就是可以在Service中实现耗时操作,其实就是在其内部有一个ServiceHandler,而ServiceHandler的创建,就是通过HandlerThread对象获取到子线程的Looper对象,然后创建了ServiceHandler对象。
IntentService.ServiceHandler源码:

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        // 处理消息之后,停止Service,自己停止自己。
        stopSelf(msg.arg1);
    }
}

IntentService.onCreate()源码:

@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.

    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    // 创建子线程HandlerThread对象的Handler
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

Service一般用于处理后台耗时任务。
IntentService会维持一个子线程独有的消息队列,保证每一个任务都是在同一个线程。这样就可以保证在IntentService一定是处理的同一个线程的任务。这样就可以保证任务在同一个线程中按照先后顺序执行。
应用需求:一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完成之后,这项任务才算成功。
这个需求可以用多个线程来处理,一个线程处理完->下一个线程->下一个线程
IntentService也可以帮助我们实现这个需求。而且,能够很好的管理线程,保证只有一个子线程处理工作,而且是一个一个的完成任务,有条不紊的进行对多个子任务的处理。
IntentService的使用:

public class MyIntentService extends IntentService {

  /** 
    * 在构造函数中传入线程名字
    **/  
    public myIntentService() {
        // 调用父类的构造函数
        // 参数 = 工作线程的名字
        super("myIntentService");
    }

   /** 
     * 复写onHandleIntent()方法
     * 根据 Intent实现 耗时任务 操作
     **/  
    @Override
    protected void onHandleIntent(Intent intent) {

        // 根据 Intent的不同,进行不同的事务处理
        String taskName = intent.getExtras().getString("taskName");
        switch (taskName) {
            case "task1":
                Log.i("myIntentService", "do task1");
                break;
            case "task2":
                Log.i("myIntentService", "do task2");
                break;
            default:
                break;
        }
    }

    @Override
    public void onCreate() {
        Log.i("myIntentService", "onCreate");
        super.onCreate();
    }
   /** 
     * 复写onStartCommand()方法
     * 默认实现 = 将请求的Intent添加到工作队列里
     **/  
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("myIntentService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("myIntentService", "onDestroy");
        super.onDestroy();
    }
}

在AndroidManifest.xml中注册

<service android:name=".myIntentService">
            <intent-filter >
                <action android:name="cn.scu.finch"/>
            </intent-filter>
        </service>

在Activity中启动IntentService

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

            // 同一服务只会开启1个工作线程
            // 在onHandleIntent()函数里,依次处理传入的Intent请求
            // 将请求通过Bundle对象传入到Intent,再传入到服务里

            // 请求1
            Intent i = new Intent("cn.scu.finch");
            Bundle bundle = new Bundle();
            bundle.putString("taskName", "task1");
            i.putExtras(bundle);
            startService(i);

            // 请求2
            Intent i2 = new Intent("cn.scu.finch");
            Bundle bundle2 = new Bundle();
            bundle2.putString("taskName", "task2");
            i2.putExtras(bundle2);
            startService(i2);

            startService(i);  //多次启动
        }
    }

除了在IntentService使用HandlerThread以外:
(1)在Fragment的生命周期管理
Fragment一般是通过FragmentManager事务提交,而提交的时候,是通过Handler发送消息执行提交过程。
FragmentManager中使用了Handler进行提交。

void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

(2)在Glide的生命周期管理也使用了类似的方式,即创建一个空的Fragment管理生命周期,如果Fragment是空的时候,先从缓存去中,如果缓存中还是空的,则重新创建一个新的Fragment,这里使用缓存的目的,是因为Fragment事务提交的时候,是通过Handler发送消息提交事务,而这个任务并不一定是立马执行,也不一定是在下一次任务来的时候就已经提交完成,因为Glide可以多线程使用。当一个线程使用创建了空的Fragment生命周期管理,那么下一个线程异步请求创建空的生命周期管理的时候,先去查询一个临时HashMap缓存,这是因为Fragment事务提交是Handler发送消息,并不能保证立马执行完成,而在临时HashMap缓存一个空的Fragment生命周期,然后经过两次的判空,而空的Fragment就算没有绑定,那么也会先在缓存中存在这个对象,那么第二个线程进来之后就不会去创建这个空的Fragment生命周期管理。

@NonNull
private RequestManagerFragment getRequestManagerFragment(
    @NonNull final android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart();
      }
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容