Android开发App启动流程与消息机制详解

 更新时间:2022年08月24日 16:29:44   作者:好男人中国造  
这篇文章主要为大家介绍了Android开发App启动流程与消息机制详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

相信很多人对这个问题不陌生,但是大家回答的都比较简单,如谈到app启动流程有人就会是app的生命周期去了,谈到消息机制有人就会说looper循环消息进行分发,如果是面试可能面试官不会满意,今天我们搞一篇完善的源码解析来进行阐述上面的问题

1、第一步了解 ThreadLocal

什么是ThreadLocal呢,专业的来讲,ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有再指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,是共享数据变量存储,通俗的来讲,就是保存每个线程的数据,肯定大家都没听懂,没事的,接下来我们通过代码来解释ThreadLocal的具体作用

首先看一个例子

public static void main(String[] args) throws InterruptedException {
        ThreadLocal<String> threadLocal = new ThreadLocal<String>(){
            @Override
            protected String initialValue() {
                return "data--1";
            }
        };
        System.out.println("1主线程-->  "+threadLocal.get());
        Thread t1 = new Thread(()->{
        });
        t1.start();
        threadLocal.set("data--2");
        System.out.println("2主线程-->  "+threadLocal.get());
        Thread t2 = new Thread(()->{
            System.out.println("线程2--->  "+threadLocal.get());
        });
        t2.start();
        Thread t3 = new Thread(()->{
            threadLocal.set("data-->3");
            System.out.println("线程3--->  "+threadLocal.get());
        });
        t3.start();
        System.out.println("3主线程-->  "+threadLocal.get());
        Thread.sleep(1000);
    }

打印结果

1主线程-->  data--1
2主线程-->  data--2
线程2--->  data--1
3主线程-->  data--2
线程3--->  data-->3

从上面的例子我们可以看到,ThreadLocal保存一个String这个变量,这个变量初始化会有一个值,在接下来的线程种,每个线程都会拥有一个初始值,这个初始值在主线程中,一旦这个初始值发生改变,如果是在主线程种改变如进行set,则后面的子线程获取的都是这个改变后的值,但是如果子线程种也改变了这个值,则只在当前子线程种有此值 没其子线程还是获取的主线程种那个值,我们来简单画个图给大家

ThreadLocal种的三个重要方法

//默认情况下initialValue是返回为空的
    protected T initialValue() {
        return null;
    }
//在get的时候如果没有调用set方法  getMap(t);是返回为空的,所以,返回的是setInitialValue(),这些方法请看后面的介绍,而setInitialValue方法返回的其实就是初始值   
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    //这个方法在在调用的时候实际上getMap(t)是为空的,所以就会调用createMap,这个方法会把当前的线程作为值,保证getMap再调用就不会为空
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
         public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
         ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
        void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

简单来讲,就是自己有的,用自己的,自己没有就用初始化的,初始化改变了,后面的也改变,但是自己设置的,还是用自己的,就这么简单,好了,接下来进行下一步

2、App的启动流程

我们看下Android的源码

//这是main函数的入口
 public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("&lt;pre-initialized&gt;");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我们重点看下 Looper.prepareMainLooper();这个方法

 /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

我们再点击去看,myLooper

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

很惊讶的看见了 sThreadLocal,这里是调用get方法

  // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这里我们可以看到ThreadLocal保存的是Looper这个对象

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

这里调用了set方法,创建了一个全局唯一的Looper

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

创建了一个全局唯一的主线程消息队列

3、Activity中创建Handler

  • 创建一个handler,重写handleMessage方法
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
  • 发送消息
        Message message = new Message();
        handler.sendMessage(message);
	//点击去
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
	//点击去
	    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis &lt; 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
//继续看sendMessageAtTime
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    //我们看到了enqueueMessage,我们看这个queue在哪里获取的
        public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

到这里我们明白了,也就是再app启动后那个唯一的Queue,好了我们整理下Handler的消息机制

hander发送消息的时候,调用sendMessage方法,handler种会讲消息放到全局的消息队列中queue.enqueueMessage(msg, uptimeMillis)接着就会在MessageQueue种赋值全局消息

消息处理

消息消费

以上就是Android开发App启动流程与消息机制详解的详细内容,更多关于Android App启动流程消息机制的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Android的.aar文件生成方法以及使用技巧

    详解Android的.aar文件生成方法以及使用技巧

    给大家详细分析一下在Android中关于.aar文件生成的方法以及相关是使用技巧分析,需要的朋友学习参考下吧。
    2017-12-12
  • Android自定义单例AlertDialog详解

    Android自定义单例AlertDialog详解

    这篇文章主要为大家详细介绍了Android自定义单例AlertDialog的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • android中实现OkHttp下载文件并带进度条

    android中实现OkHttp下载文件并带进度条

    本篇文章主要介绍了android中实现OkHttp下载文件并带进度条,OkHttp是比较火的网络框架,它支持同步与异步请求,支持缓存,可以拦截,更方便下载大文件与上传文件的操作,有兴趣的可以了解一下
    2017-07-07
  • Android布局之表格布局TableLayout详解

    Android布局之表格布局TableLayout详解

    这篇文章主要为大家详细介绍了Android布局之表格布局TableLayout,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Android网络状态实时监听实例代码(二)

    Android网络状态实时监听实例代码(二)

    这篇文章主要介绍了Android网络状态实时监听实例代码(2)的相关资料,需要的朋友可以参考下
    2016-03-03
  • Android中增加新字库的方法

    Android中增加新字库的方法

    这篇文章主要介绍了Android中增加新字库的方法,本文分别讲解了不需要保留Google默认的中文字库、有需要保留Google默认的中文字库并在此基础上增加自己需要的新字库两种情况下的处理方法,需要的朋友可以参考下
    2015-04-04
  • android实现手机传感器调用

    android实现手机传感器调用

    这篇文章主要为大家详细介绍了android实现手机传感器调用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • Android编程实现popupwindow定时消失的方法

    Android编程实现popupwindow定时消失的方法

    这篇文章主要介绍了Android编程实现popupwindow定时消失的方法,结合实例形式分析了Android使用定时器实现popupwindow定时消失的相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Android 几种屏幕间跳转的跳转Intent Bundle

    Android 几种屏幕间跳转的跳转Intent Bundle

    这篇文章主要介绍了Android 几种屏幕间跳转的跳转Intent Bundle,有需要的朋友可以参考一下
    2013-12-12
  • Android嵌套滚动和协调滚动的多种实现方法

    Android嵌套滚动和协调滚动的多种实现方法

    嵌套的滚动主要方式就是这些,这些简单的效果我们用协调滚动,如 CoordinatorLayout 也能实现同样的效果,这篇文章主要介绍了Android嵌套滚动和协调滚动的多种实现方法,需要的朋友可以参考下
    2022-06-06

最新评论