2017-01-17 22:06:54 loongago 阅读数 436
  • Android移植基础

    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android源码中。

    26252 人正在学习 去看看 钟文昌

之前在ubuntu中下载了Android源码,但当时只限于编译系统。现在想想如果能顺便研究一下源码岂不美哉,说做就做。开发Android APP用的是Android studio,用它来看Android源码其实也很方便的,所以我也在ubuntu上选择Android studio这样的IDE来看源码。下面将讲述搭建环境用Android studio看源码。

一、编译idegen模块
idegen一看名字就知道是专门是为IDE而生的。目录地址在development/tools/idegen/
先cd进源码根目录,然后执行

source build/envsetup.sh
lunch

lunch选好你的cpu架构,接着

mmm development/tools/idegen/  

这个命令是编译idegen这个模块项目,然后生成idegen.jar文件

如果你最后看到make completed successfully这样的字样说明你编译成了。
执行

development/tools/idegen/idegen.sh  

它将会在根目录生成对应的android.ipr、android.iml IEDA工程配置文件。
我的输出结果是

Read excludes: 3ms  
Traversed tree: 118419ms 

这可能有两三分钟的时间,请耐心等候。

然后在Android studio打开Android源码根目录位置,你就可以看到Android源码了。
来一个效果图:
AS打开aosp

2017-05-03 17:47:54 hzqtby 阅读数 174
  • Android移植基础

    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android源码中。

    26252 人正在学习 去看看 钟文昌

粗读 Android Handler 源码

Handler源码和实现机制主要包含了几个关键的类Handler、Message、ThreadLocal、Looper、MessageQueue、ActivityThread。
当我一次无意中在子线程中尝试创建一个Handler实例的时候,果断程序就奔溃报错了。

“Can’t create handler inside thread that has not called Looper.prepare()”

意思大概是说不能在Looper.prepare()方法没执行前创建Handler对象。当时也没在意就把它放到了外面初始化了。后面看源码的时候自己就思考这个问题为啥在外面可以创建呢?原来报错的原因是因为因为mLooper对象为空值


        //获得一个Looper对象实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            //说明Looper对象初始化失败了(没有初始化)
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

````
那按这样推倒意思就是说Looper.prepare()方法的必须在之前执行过啊。看下源码





<div class="se-preview-section-delimiter"></div>

``` java

 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));
    }


      /**
     * 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();
    }

果然如我们所料啊。原来Looper.prepare()方法的作用是做了初始化的工作。那我们回到前面说的,那我在在线程中创建Handler实例之前执行下这个方法是不是就可以呢?答案是肯定的。那有人肯定会问为啥直接在主线程中创建Handler实例就不会报异常呢,按这个来推倒的话那肯定是在主线程某个地方做了这件事啊。这时候我们的ActivityThread就登场了,咋们直接定位到他的main()函数。查看里面的代码

public static void main(String[] args) {

       ....

        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()其实最终也是调用了啊Looper.prepare(),除此之外我们还有意外收获那就是另一个很重要的方法 Looper.loop()调用了这个方法。
这方法调用的时机一般是在Handler初始化完成之后,那我们就可以判断出Activity的一些创建 绘制什么的肯定也是在Looper.prepareMainLooper()和Looper.loop()进行的至于为什么这里就不细说了。出门百度 O(∩_∩)O哈哈~
好那我们去看下这个核心方法吧


/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //轮询处理消息
        for (;;) {
        //获取一个消息对象
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

这里用了一个for(;;)循环对消息队列进行无限轮询,我们只要关心主要的代码就可以了
msg.target.dispatchMessage(msg),这个方法就是分发我们的消息的进去我们可以看到如下


    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

   /**
     * 当以Post形式发送消息的时候回调其
     * run方法
     * @param message
     */
    private static void handleCallback(Message message) {
        message.callback.run();
    }

至此我们熟悉的终于出现了,这里主要体现了一个优先级Handler消息接收也有这两种方法。接口优先然后才是重写的方法。如果接口为空则调用handleMessage(msg);最终就到了我们这里了

    private  Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                String s= (String) msg.obj;
                tv1.setText(s);
                break;
            }

        }
    };

* 所以我们要在子线程中模仿上面的步骤自己实现一个Handler除了在创建Hnadler实例之前需要调用Looper.prepare()方法还需在实例创建完成后执行Looper.poop()方法启动消息队列轮询 *

让我们回到开始,我们使用Handler发送消息一般有两种方式分别是是handler.sendXXX()和handler.postXXX(),但是不管哪一种最终都是调用了sendMessageAtTime(Message msg,long uptimeMillis)方法,然后在调用MessageQueue的enqueueMessage()方法,作用是往队列里面插入一条Message,然后再loop中通过next方法不断地取出Message进行处理。

盗用网上一张图描述这几者之间的关系网(希望作者不要跟我一般计较):

mark

以上就是我看源码的一些过程,期间也有参阅一些资料。感谢那些乐于分享的前辈!!
可能有点乱,全当记录一下。供以后翻阅吧,如有错…请指出。轻喷哈

2015-12-13 18:03:58 bz419927089 阅读数 16517
  • Android移植基础

    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android源码中。

    26252 人正在学习 去看看 钟文昌

断断续续的,《Android源码设计模式解析》也看了一遍,书中提到了很多的设计模式,但是有部分在开发中见到的几率很小,所以掌握不了也没有太大影响。

我觉得这本书的最大价值有两点,一个是从设计模式的角度去理解Android源码,结合着日常开发中的常用类,对设计模式的理解会更加的深刻;另外一个好处就是了解常用模式,再看其他人写的代码的时候,更容易理解代码思路。下面是我的读书笔记和一些思考,设计模式只整理我认为重要的部分。

建造者模式

建造者模式最明显的标志就是Build类,而在Android中最常用的就是Dialog的构建,Notification的构建也是标准的建造者模式。

建造者模式很好理解,如果一个类的构造需要很多参数,而且这些参数并不都是必须的,那么这种情况下就比较适合Builder。

比如构建一个AlertDialog,标题、内容、取消按钮、确定按钮、中立按钮,你可能只需要单独设置几个属性即可;另外在我的OkHttpPlus项目中,构造一个Http请求也是这样的,有可能你只需要设置URL,有可能需要添加请求参数、Http Header等,这个时候建造者模式也是比较合适的。

单例模式

单例在Android开发中经常用到,但是表现形式可能不太一样。

以ActivityManager等系统服务来说,是通过静态代码块的形式实现单例,在首次加载类文件时,生成单例对象,然后保存在Cache中,之后的使用都是直接从Cache中获取。

class ContextImpl extends Context {

    static {
        registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(),       ctx.mMainThread.getHandler());
                }});
    }
}

当然,还有更加明显的例子,比如AccessibilityManager内部自己也保证了单例,使用getInstance获取单例对象。

 public static AccessibilityManager getInstance(Context context) {
        synchronized (sInstanceSync) {
            if (sInstance == null) {

               ......

                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
                IAccessibilityManager service = iBinder == null
                        ? null : IAccessibilityManager.Stub.asInterface(iBinder);
                sInstance = new AccessibilityManager(context, service, userId);
            }
        }
        return sInstance;
    }

除此之外,还有一些伪单例,比如Application,默认情况下在一个进程中只存在一个实例,但是Application不能算是单例,因为它的构造方法未私有,你可以生成多个Application实例,但是没有用,你没有通过attach()绑定相关信息,没有上下文环境。

public Application() {
        super(null);
    }

单例的使用场景也很简单,就是一个App只需要存在一个类实例的情况,或者是类的初始化操作比较耗费资源的情况。在很多开源框架中,我们只需要一个对象即可完成工作,比如各种网络框架和图片加载库。

除此之外,因为单例的实现方式很多,比如懒汉式、饿汉式、静态内部类、双重锁检查、枚举等方式,所以要清楚每种实现方式的主要特点和使用场景。

原型模式

原型模式在开发中使用的并不多,但是在源码中却有所体现。

书中以Intent介绍了原型模式,是通过实现Cloneable接口来做的

public class Intent implements Parcelable, Cloneable {
    @Override
        public Object clone() {
         return new Intent(this);
        }
    }

其实这样来看的话,原型模式也比较好理解,就是你想更快的获取到一个相同属性的对象,那么就可以使用原型模式,比如这里就获取到了一个Intent对象,Intent里面的属性与被clone的相同,但是两者并无关联,可以单独使用。

除了实现Cloneable接口,你完全可以自己定义一个方法,来获取一个对象。我这里以PhoneLayoutInflater为例子介绍。

PhoneLayoutInflater是LayoutInflater的子类,如果我们在Activity中获取LayoutInflate的话,是通过下面方法

 @Override public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

可以看到,如果为null,就会调用cloneInContext(),这个方法在LayoutInflate是抽象方法,具体实现在PhoneLayoutInflater中

  public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }

可以看到,这也是一个原型模式,所以我们不要太纠结于形式,更重要的是理解这样做的好处。

除了在源码中可以看到原型模式,在开源框架中也可以看到,比如OkHttpClient中就存在着下面的方法

/** Returns a shallow copy of this OkHttpClient. */
  @Override public OkHttpClient clone() {
    return new OkHttpClient(this);
  }

可以看到,实现和前面的完全相同,也是new了一个对象返回,因为OkHttpClient的构造过程比较复杂,参数众多,所以用这种方式来直接生成新对象,成本很低,而且能保留之前对象的参数设置。

工厂方法模式

书中对于工厂方法模式的一个观点很新奇,就是Activity.onCreate()可以看做是工厂方法模式,来生成不同的View对象填充界面。

但是我对这个说法不太苟同,原因有两点:一是这种形式不太符合工厂方法,没有抽象,没有实现,不符合一般格式,也不是静态方法,不可看做是静态工厂方法;二是没有以生成对象为结果,即不是return view来生成对象,只是通过setContentView()来设置了属性而已。这就像是给一个Activity设置了背景颜色一样。当然,设计模式这东西一个人有一个人的看法。

静态工厂方法在Android中比较明显的例子应该就是BitmapFactory了,通过各种decodeXXX()就可以从不同渠道获得Bitmap对象,这里不再赘述。

策略模式

在书中策略模式讲得非常好,结合动画的插值器用法,我们可以很好的理解策略模式的形式和用法。

在我看来,策略模式就相当于一个影碟机,你往里面插什么碟子,就能放出什么电影。

同样,在OkHttpPlus的封装中,为了对网络返回值进行解析,我使用了策略模式。当然我写代码的时候还不知道策略模式,是写完了之后突然想到,这就是策略模式啊!

策略模式的精髓就在于,你传入一个类,后面的处理就能按照这个类的实现去做。以动画为例,设置不同的插值器对象,就可以得到不同的变化曲线;以返回值解析为例,传入什么样的解析器,就可以把二进制数据转换成什么格式的数据,比如String、Json、XML。

责任链模式

书中对于责任链模式选取的例子非常有代表性,那就是Android的触摸机制,这个看法让我从另一个维度去理解Android中的触摸事件传递。

我在这里提到这个模式,并不想说太多,只是简单的推荐你读一下这一章的内容,相信你也会有收获的。

观察者模式

Android中的观察者模式应该是用的非常频繁的一种模式了,对于这个模式的使用场景就一句话:你想在某个对象发生变化时,立刻收到通知。

书中介绍观察者模式使用的是ListView的Adapter为例子,我之前知道Adapter属于适配器模式,不知道这里还有观察者模式的身影,学到了。

Android里面的各种监听器,也都属于观察者模式,比如触摸、点击、按键等,ContentProvider和广播接收者也有观察者模式的身影,可以说是无处不在。

除此之外,现在很多基于观察者模式的第三方框架也是非常多,比如EventBus、RxJava等等,都是对观察者模式的深入使用,感兴趣的同学可以研究一下。

模板方法模式

这个模式我之前见的比较少,但是理解之后,就会发现这个模式很简单。

我觉得,模板方法模式的使用场景也是一句话:流程确定,具体实现细节由子类完成。

这里要关注一下『流程』这个关键字,随便拿一个抽象类,都符合”具体实现细节由子类完成”的要求,关键就在于是否有流程,有了流程,就叫模板方法模式,没有流程,就是抽象类的实现。

书中讲这个模式用的是AsyncTask,各个方法之间的执行符合流程,具体实现由我们完成,非常经典。

另外一个方面,Activity的生命周期方法可以看做是模板方法模式,各个生命周期方法都是有顺序的,具体实现我们可以重写,是不是和前面的要求很符合?关于这方面的理解,可以参考我的这篇文章:对Activity生命周期的理解

除了Android里面的模板方法模式,在其他开源项目中也存在着这个模式的运用。比如鸿洋的OkHttp-Utils项目,就是模板方法模式的典型实现。将一个Http请求的过程分割成几部分,比如获取URL,获取请求头,拼接请求信息等步骤,这几个步骤之前有先后顺序,就可以这样来做。

代理模式和装饰器模式

之所以把这两个放在一起说,是因为这两种模式很像,所以这里简单介绍下他们之间的区别,主要有两点。

  1. 装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问
  2. 代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。而当我们使用装饰器模式的时候,通常的做法是将原始对象作为一个参数传给装饰者的构造器

这两句话可能不太好理解,没关系,下面看个例子。

代理模式会持有被代理对象的实例,而这个实例一般是作为成员变量直接存在于代理类中的,即不需要额外的赋值。

比如说WindowManagerImpl就是一个代理类,虽然名字上看着不像,但是它代理的是WindowManagerGlobal对象。从下面的代码中就可以看出来。

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;

    ......

    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

从上面的代码中可以看出,大部分WindowManagerImpl的方法都是通过WindowManagerGlobal实现的,而WindowManagerGlobal对象不需要额外的赋值,就存在于WindowManagerImpl中。另外,WindowManagerGlobal中其实有大量的方法,但是通过WindowManagerImpl代理之后,都没有暴露出来,对开发者是透明的。

我们再来看一下装饰器模式。装饰器模式的目的不在于控制访问,而是扩展功能,相比于继承基类来扩展功能,使用装饰器模式更加的灵活。

书中是以Context和它的包装类ContextWrapper讲解的,也非常的典型,我这里就不在赘述了,贴出一些代码来说明装饰器模式的形式。

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
}

但是还有一个问题,就是在ContextWrapper中,所有方法的实现都是通过mBase来实现的,形式上是对上号了,说好的扩展功能呢?

功能扩展其实是在ContextWrapper的子类ContextThemeWrapper里面。

在ContextWrapper里面,获取系统服务是直接通过mBase完成的

@Override
    public Object getSystemService(String name) {
        return mBase.getSystemService(name);
    }

但是在ContextThemeWrapper里面,对这个方法进行了重写,完成了功能扩展

@Override public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

当然,如果不存在功能扩展就不算是装饰器模式了吗?其实设计模式本来就是『仁者见仁,智者见智』的事情,只要你能理解这个意思就好。

外观模式

外观模式可能看到的比较少,但是其实不经意间你就用到了。

这里以我的一个开源项目KLog来说吧,在最开始写这个类的时候,就只有KLog这一个类,完成基本的Log打印功能,后来又添加了JSON解析、XML解析、Log信息存储等功能,这个时候一个类就不太合适了,于是我把JSON、XML、FILE操作相关的代码抽取到单独的类中,比如JSON打印的代码

public class JsonLog {

    public static void printJson(String tag, String msg, String headString) {

        String message;

        try {
            if (msg.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(msg);
                message = jsonObject.toString(KLog.JSON_INDENT);
            } else if (msg.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(msg);
                message = jsonArray.toString(KLog.JSON_INDENT);
            } else {
                message = msg;
            }
        } catch (JSONException e) {
            message = msg;
        }

        Util.printLine(tag, true);
        message = headString + KLog.LINE_SEPARATOR + message;
        String[] lines = message.split(KLog.LINE_SEPARATOR);
        for (String line : lines) {
            Log.d(tag, "║ " + line);
        }
        Util.printLine(tag, false);
    }
}

代码很简单,就一个方法,但是在使用的时候,无论打印哪种格式,都是这样使用的

//普通打印
 KLog.d(LOG_MSG);
 //JSON格式打印
 KLog.json(JSON);
 //XML格式打印
 KLog.xml(XML);

可以看到,虽然功能不同,但是都通过KLog这个类进行了封装,用户只知道用KLog这个类能完成所有需求即可,完全不需要知道代码实现是几个类完成的。

实际上,在KLog内部,是多个类共同完成打印功能的。

 private static void printLog(int type, String tagStr, Object... objects) {

        if (!IS_SHOW_LOG) {
            return;
        }

        String[] contents = wrapperContent(tagStr, objects);
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];

        switch (type) {
            case V:
            case D:
            case I:
            case W:
            case E:
            case A:
                BaseLog.printDefault(type, tag, headString + msg);
                break;
            case JSON:
                JsonLog.printJson(tag, msg, headString);
                break;
            case XML:
                XmlLog.printXml(tag, msg, headString);
                break;
        }
    }

但是通过外观模式,这些细节对用户隐藏了,这样如果以后我想更换JSON的解析方式,用户的代码不需要任何改动,这也是这个设计模式的优势所在。

总结

唠唠叨叨的,总算是把这几种设计模式介绍完了,看完这篇文章,你应该就会发现其实Android中的设计模式确实到处都存在,不是缺少设计模式,而是缺少一双发现的眼睛。

当然,设计模式的提出是为了解决特定的问题,当我们遇到类似问题的时候,可以从设计模式的角度思考和解决问题,这应该是我最大的收获吧。

关于我

江湖人称『凯子哥』,Android开发者,喜欢技术分享,热爱开源。

2017-09-22 10:15:45 qq_36791265 阅读数 80
  • Android移植基础

    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android源码中。

    26252 人正在学习 去看看 钟文昌

这几天,我一直在为如何读取anroid源码而发愁。在本地找取SDK的源码觉得每次都很麻烦,还好从网上找到一个网址。可以在线读android源码。

http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/


而在本地你可以看Android SDK

file:///D:/Android_studio/SDK/docs/guide/index.html


这里是你SDK所在位置(file:///D:/Android_studio/SDK/)

下一步:找到docs文件夹->guide文件夹->index.html,打开index.html 


2017-11-13 11:45:08 verymrq 阅读数 177
  • Android移植基础

    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android源码中。

    26252 人正在学习 去看看 钟文昌

一、先BB两句

Android四大组件源码已经分析完Activity和Service,已经攻克一半,现在读起源码越来越得心应手。
阅读本篇文章前建议先阅读我的前两篇博文:
【Android源码系列】Activity启动源码解析
【Android源码系列】Service启动源码解析
今天我们来看看BroadcastReceiver(广播)的源码逻辑,此前说过的流程在这里可能会简化(懒)。其实他们的流程大致都相同,这里先把流程图放上来。
哈哈
好的,本篇文章到此结束。
哈哈,开个玩笑,我们还是再来熟悉一遍流程避免忘得飞快。再次提醒,强烈推荐浏览本文时和着源码。
我们一般使用广播关键步骤分为两步:

//注册
registerReceiver(new MyReceiver(), new IntentFilter("com.source.broadcastReceiver"));
//发送广播
Intent intent = new Intent("com.source.broadcastReceiver");
                sendBroadcast(intent);

我们一个一个分析。

二、注册

点开registerReceiver源码,依然和Service一样,走到ContextImpl里,并调用了registerReceiverInternal方法。

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) {
            return null;
        }
    }

这个rd瞅着有点眼熟,感觉和Servive的connect一样,这里先不管,我们直接往下走。看到我们熟悉的ActivityManagerNative(为啥熟悉?看了前两篇文章就知道),也就是ActivityManagerService调用了registerReceiver,我们到AMS里去看看。

 public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
            //省略.......
ReceiverList rl
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId);
            }
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadast");
            }
            mReceiverResolver.addFilter(bf);
            //省略.......
            }

很明显,我们的AMS将receiver保存了起来。其实这里还做了Sticky(粘性广播)的发送工作,有兴趣的同学可以仔细看看源码,本文就不多说了。
好了,注册工作就完成了,步骤比较简单。我们接着看发送部分。

三、发送广播

发送广播和注册一样,都是到ContextImpl里直接调用了AMS的broadcastIntent方法,而AMS,又直接调用了自己的broadcastIntentLocked,过程我就不一一列出来了,我们直接看这个方法:

 private final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle map, String requiredPermission, int appOp,
            boolean ordered, boolean sticky, int callingPid, int callingUid,
            int userId) {
//......
if (!ordered && NR > 0) {
            // If we are not serializing this broadcast, then send the
            // registered receivers separately so they don't wait for the
            // components to be launched.
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
                    appOp, registeredReceivers, resultTo, resultCode, resultData, map,
                    ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }
//......
}

broadcastIntentLocked的内容很多,我们找到关键的地方

queue.scheduleBroadcastsLocked();

工作转移到了BroadcastQueue这个类里,看名字就知道这是一个储存和操作Revceiver的队列,进去后发现他内部有一个专门的BroadcastHandler,并将事件分发给了自己的processNextBroadcast方法

final void processNextBroadcast(boolean fromMsg) {
//.......
if (r.resultTo != null) {
                        try {
                            if (DEBUG_BROADCAST) {
                                int seq = r.intent.getIntExtra("seq", -1);
                                Slog.i(TAG, "Finishing broadcast ["
                                        + mQueueName + "] " + r.intent.getAction()
                                        + " seq=" + seq + " app=" + r.callerApp);
                            }
                            performReceiveLocked(r.callerApp, r.resultTo,
                                new Intent(r.intent), r.resultCode,
                                r.resultData, r.resultExtras, false, false, r.userId);
                            // Set this to null so that the reference
                            // (local and remote) isn't kept in the mBroadcastHistory.
                            r.resultTo = null;
                        } catch (RemoteException e) {
                            r.resultTo = null;
                            Slog.w(TAG, "Failure ["
                                    + mQueueName + "] sending broadcast result of "
                                    + r.intent, e);
                        }
                    }
}

一样省略大部分无光代码,找出之前存好的ProcessRecord,调用了自己的performReceiveLocked

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Send the intent to the receiver asynchronously using one-way binder calls.
        if (app != null) {
            if (app.thread != null) {
                // If we have an app thread, do the call through that so it is
                // correctly ordered with other one-way calls.
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
            } else {
                // Application has died. Receiver doesn't exist.
                throw new RemoteException("app.thread must not be null");
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

依旧是我们熟悉的ActivityThread

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException {
            updateProcessState(processState, false);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

调用了IIntentReceiver的performReceive,这个这很明显是一个Binder接口,有了Service的经验,很容易我们就找到了ReceiverDispatcher这个对象(上文注册时也提到过),他的内部类InnerReceiver继承了IIntentReceiver.Stub。InnerReceiver的performReceive方法直接调用了ReceiverDispatcher的performReceive方法。

public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            if (ActivityThread.DEBUG_BROADCAST) {
                int seq = intent.getIntExtra("seq", -1);
                Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
                        + " to " + mReceiver);
            }
            Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            if (!mActivityThread.post(args)) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing sync broadcast to " + mReceiver);
                    args.sendFinished(mgr);
                }
            }
        }

这里出现了一个Args,他实现了runable接口,mActivityThread是我们的H类(handler)。

mActivityThread.post(args)

所以又转到,Args的run方法里。

public void run() {
                //......
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                    if (mRegistered && ordered) {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing failed broadcast to " + mReceiver);
                        sendFinished(mgr);
                    }
                //.......
        }

好了,onReceive方法已经调用,意味着广播发送成功。

四、总结

1、最重要的依然是我们的Binder,通过一系列的IPC通信,最终传给了我们的BroadcastReceiver。
2、多次利用handler发送消息,虽然我们频繁使用它,或多或少对他的原理有些了解,但是Android的消息机制是究竟如何实现的,研究完四大组件,将他定为下一个研究对象。

没有更多推荐了,返回首页