Android—ActivityThread与Handler

应用启动过程:

ActivityThread

ActivityThread就是主线程或UI线程,ActivityThread的main方法是整个APP的入口。

public final class ActivityThread {
    //... 
    final H mH = new H();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();

    final ApplicationThread mAppThread = new ApplicationThread();  
    private class ApplicationThread extends ApplicationThreadNative {    
       //...  
    }
    private class H extends Handler {
            //...
    }
    //...

}

Activity信息全部被存储在ActivityThread的成员变量mActivities中。mServices则保存了所有service的信息。

    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();

ActivityThread有三个重要的类,Instrumentation、H和ApplicationThread

  • mInstrumentation是Instrumentation类的对象,Instrumentation类为ActivityThread的一个工具类,在ActivityThread中初始化,一个进程只存在一个Instrumentation对象,在每个Activity初始化时,会通过Activity的Attach方法,将该引用传递给Activity。Activity所有生命周期的方法都有该类来执行。最后mInstrumentation调用了Application的onCreate方法。
  • H继承于Handler,mH负责处理ApplicationThread发送到消息队列的消息,Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施。
  • ApplicationThread是ActivityThread的内部类,ApplicationThread内部继承自Binder并实现IApplicationThread接口。Binder是C/S结构,Binder是Service,AMS是Client,ApplicationThread主要用于应用进程和AMS进程间通信,并用于AMS的调用,通过H类将消息发送到消息队列,然后进行相应的操作。

为什么App进程做服务端呢?

仔细想想,Activity的生命周期回调是谁调用的啊。肯定不是app本身控制,而远程服务通过监听到一系列的操作发起周期回调,AMS持有App的proxy,这个代理的协议是IApplicationThread,于是AMS监控系统需要onResume,则通过proxy发起IApplicationThread的onResume,也就是通知服务去执行onResume。所以这里作为Binder理解IPC,服务端就是客户端App而不是AMS,AMS作为请求端,持有App进程的代理。

    public static void main(String[] args) {
        //....

        //创建Looper和MessageQueue对象,用于处理主线程的消息
        Looper.prepareMainLooper();

        //创建ActivityThread对象
        ActivityThread thread = new ActivityThread(); 

        //建立Binder通道(创建新线程)
        thread.attach(false);

        Looper.loop(); //消息循环运行
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

private void attach(boolean system){
    ...
    final IActivityManager mgr = ActivityManager.getService();
    try {
    //将ApplicationThread这个Binder交给了ActivityManagerService
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ...
    BinderInternal.addGcWatcher(new Runnable() {
        @Override public void run() {
            if (!mSomeActivitiesChanged) {
                return;
            }
            Runtime runtime = Runtime.getRuntime();
            long dalvikMax = runtime.maxMemory();
            long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
            if (dalvikUsed > ((3*dalvikMax)/4)) {
                if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                        + " total=" + (runtime.totalMemory()/1024)
                        + " used=" + (dalvikUsed/1024));
                mSomeActivitiesChanged = false;
                try {
                    mgr.releaseSomeActivities(mAppThread);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    });
    ...
}

主线程(ActivityThread)的初始化:

  1. 开启消息循环。调用Looper.prepareLoop() Looper.loop(),开启主线程的消息循环,以便于ApplicationThread调用ActivityThread中的生命周期方法。
  2. 通知ActivityManagerService。调用ActivityThread.attach()方法,attach()方法在调用了attachApplication()将ApplicationThread这个Binder交给了ActivityManagerService,意味着ActivityManagerService可以通过ApplicationThread控制我们的应用,建立了服务器端对客户端的通信渠道。
  3. 添加GCWatcher。在attach()方法中,添加了监听dialvik内存使用情况得监听者GcWatcher,当内存使用超过总容量的3/4,则打印Log进行记录,并且调用ActivityManagerService的releaseSomeActivities()进行内存释放操作,以防止内存溢出导致应用崩溃。

attach()方法在调用了attachApplication()之后,经过一系列操作,最后调用了ApplicationThread的bindApplication()方法,bindApplication中通过消息机制,sendMessage到ActivityThread,handleMessage调用了ActivityThread的handleBindApplication()。通过反射创建了Application对象,然后onCreate创建activity。

 private void handleBindApplication(AppBindData data) {
       //创建appContext 
      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
      try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            //通过反射创建Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

           try {
                //调用Application的onCreate方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
            }
        }
}

 public void callApplicationOnCreate(Application app) {
     app.onCreate();
 }

总结:ActivityThread通过ApplicationThread和AMS进行进程间通讯,AMS接受 ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行。

Handler 线程间通信

从上面我们得知Activity的生命周期都是依靠主线程的Looper.loop,主线程Handler收到不同Message时则采用相应措施。接下来介绍Handler。

Handler作用:

进行子线程与主线程之间的通信。子线程可以通过Handler来通知主线程进行UI更新。

根据Looper.loop()源码可知里面是一个死循环在遍历消息队列取消息,queue.next()阻塞方法,从队列中获取消息。

    public static void loop() {
        final Looper me = myLooper();
        ......
        for (;;) {
            //获取消息队列中的消息
            Message msg = queue.next(); // might block
            ....
            //然后分发消息到Handler的处理中
            msg.target.dispatchMessage(msg);
            ...
            //释放消息
            msg.recycleUnchecked();
        }
    }

Android在子线程更新UI的三种方式 

 new Handler(mContext.getMainLooper()).post(new Runnable() {
      @Override
      public void run() {
          // 在这里执行你要想的操作 比如直接在这里更新ui或者调用回调在 在回调中更新ui
      }
});

常见常用的post()类方法汇总:

  • post(Runnable)
  • postAtTime(Runnable,long)   System.currentTimeMillis() + 100000 在设定的目标时间post
  • postDelayed(Runnable long)    延迟多少时间再post
((Activity) context).runOnUiThread(new Runnable() {
 
    @Override
    public void run() {
        // 在这里执行你要想的操作 比如直接在这里更新ui或者调用回调在 在回调中更新ui
    }
});
private Handler handler = new Handler() {
 
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
 
        switch (msg.what) {
        case ACTIVENUMBER:
       		 MaintainProtos.ActiveNumber activeNumber = (MaintainProtos.ActiveNumber) msg.obj;
             break;
        }
    }
};

 Message msg = Message.obtain();
 msg.what = ACTIVENUMBER;
 msg.obj = activeNumber;
 mHandler.sendMessage(msg);

常见常用的send类方法汇总:

  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message,long)
  • sendMessageDelayed(Message,long)

Handler内部如何获取到当前线程的Looper?

ThreadLocal。ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是①线 程是默认没有Looper的,如果需要使用Handler,就必须为线程创建Looper。我们经常提到的主线 程,也叫UI线程,它就是ActivityThread,②ActivityThread被创建时就会初始化Looper,这也是在主 线程中默认可以使用Handler的原因。

系统为什么不允许在子线程中访问UI?

这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那么为什么 系统不对UI控件的访问加上锁机制呢?缺点有两个: ①首先加上锁机制会让UI访问的逻辑变得复杂 ②锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。 所以最简单且高效的方法就是采用单线程模型来处理UI操作。

Looper.loop为什么不会阻塞主线程?

Activity的生命周期就是依靠Looper.loop(),Looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下。所以不存在主线程会被Looper.loop方法阻塞。

Handler优化:

实现静态内部类:实际项目中Handler很少采用上面匿名类的实现方式,因为会造成内存泄漏,大部分采用静态内部类、建一个单类或者在onDestroy方法中removeCallbacksAndMessages。

使用HandlerThread:Loop主线程已经有了,不需要我们自己创建,但是子线程默认是没有开启消息循环的。需要用到Looper.prepare()和Looper.loop(),当然也可以用到我们上面的实现方式传回主线程,但是这样做会增加主线程的工作量。

HandlerThread:本质上就是一个普通Thread,只不过内部建立了Looper。

public class MainActivity extends AppCompatActivity {

    private HandlerThread myHandlerThread ;
    private Handler handler ;

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

        //1、创建一个线程,线程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ;

        //2、开启一个线程
        myHandlerThread.start();

        //3、在这个线程中创建一个handler对象
        handler = new Handler( myHandlerThread.getLooper() ){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.d( "handler " , "消息: " + msg.what + "  线程: " + Thread.currentThread().getName()  ) ;

            }
        };

        //在主线程给handler发送消息
        handler.sendEmptyMessage( 1 ) ;

        //在子线程给handler发送数据
        new Thread(new Runnable() {
            @Override
            public void run() {
             handler.sendEmptyMessage( 2 ) ;
            }
        }).start() ;

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //4、释放资源
        myHandlerThread.quit() ;
    }
}

因为最后执行了quit()操作,所以内存泄漏的问题也得到解决。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值