android插件化开发——加载广播

Posted 何以诚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android插件化开发——加载广播相关的知识,希望对你有一定的参考价值。

阅读本文前,先阅读前面几篇:
http://blog.csdn.net/u013022222/article/details/51171720

引言

android开发过程中,我们不可避免的会使用广播,比如,侦听开机,侦听短信。
而对于广播,我想很多人都知道他有两种类型,动态广播,通过代码在runtime进行register,
像这样:

   IntentFilter intentFilter = new IntentFilter("com.chan.plugin.receiver");
        registerReceiver(new PluginReceiver(), intentFilter);

而静态广播呢,是直接在AndroidManifest.xml中进行注册,像这样:

   <receiver
            android:name=".PluginReceiver"
            android:enabled="true"
            android:exported="true">
   </receiver>

因此,如果我们想加载插件中的广播,必然是要区分动态广播和静态广播。他们使用时,区别就只在是否需要注册了,所以我们只要搞清楚他们是如何让AMS“认识的”,问题就解决了一大步。那么我们就先来分析系统是如何注册广播的

我们先考虑动态广播的情况:

动态广播

我们在前文提到,如果我们在项目中使用动态广播,我们肯定有这几步:
1:注册 registerReceiver
2:注销 unregisterReceiver
3:使用 sendBroadcast

我们先看看注册:

 IntentFilter intentFilter = new IntentFilter("com.chan.plugin.receiver");
        registerReceiver(new PluginReceiver(), intentFilter);

我们先到registerReceiver中去查看:

    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) 
        return mBase.registerReceiver(receiver, filter);
    

看到这里的mBase,你就该回想到我们前几篇博文中提及的——关于Context的startActivity或者registerReceiver等method其实都是在ContextImpl中真正实现的。

我们查看ContextImpl的代码:

可见真正实现业务逻辑的地方还是在registerReceiverInternal这个函数中:

     private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) 

        //用于广播分发 aidl定义的接口
        IIntentReceiver rd = null;

        //显然如果我们在代码中使用的话这里的 receiver肯定是不为空的
        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 

            //调用AMS的方法 注册receiver
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
         catch (RemoteException e) 
            return null;
        
    

之后的调用都是在AMS中,我们可以看下AMS的registerReceiver函数:

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) 
        enforceNotIsolatedCaller("registerReceiver");
        int callingUid;
        int callingPid;
        synchronized(this) 

            ....

            // Look for any matching sticky broadcasts...
            Iterator actions = filter.actionsIterator();
            if (actions != null) 
                 ...
             else 
                 ...
            

            ...

            //一个receiver肯定注册了多个广播  这个是用来存放那些BroadcastFilter的      
            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;
                

                //这是很重要的 receiver 是我们上文说的IIntentReceiver对象 它被作为key在AMS保存
                //起来,注意IIntentReceiver 就是个Binder, 这是我们上文就说了的
                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) 
                   ...
             else if (rl.userId != userId) 
                   ...
            

            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);

            //存放到mReceiverResolver中
            mReceiverResolver.addFilter(bf);

            ...
            return sticky;
        
    

静态广播

刚刚我们已经提到了,静态广播是通过读取AndroidManifest.xml获得。关于这里的操作,是在PackageManagerService(一下简称PMS)中完成的,我们看下相关的代码:

            // Collect all OEM packages.
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

我们看下PMS的scanDirLI:

   private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) 
        final File[] files = dir.listFiles();
        ...
        for (File file : files) 
            ...
            try 
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
             catch (PackageManagerException e) 
                  ...
            
        
    

接着又是调用scanPackageLI:


    /*
     *  Scan a package and return the newly parsed package.
     *  Returns null in case of errors and the error code is stored in mLastScanError
     */
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException 

        ...
        final PackageParser.Package pkg;
        try 
            pkg = pp.parsePackage(scanFile, parseFlags);
         catch (PackageParserException e) 
            throw PackageManagerException.from(e);
        
        ...
    

之后便是调用PackageParser的parsePackage方法:

  /**
     * Parse the package at the given location. Automatically detects if the
     * package is a monolithic style (single APK file) or cluster style
     * (directory of APKs).
     * <p>
     * This performs sanity checking on cluster style packages, such as
     * requiring identical package name and version codes, a single base APK,
     * and unique split names.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in @link #collectCertificates(Package, int).
     *
     * @see #parsePackageLite(File, int)
     */
    public Package parsePackage(File packageFile, int flags) throws PackageParserException 
        if (packageFile.isDirectory()) 
            return parseClusterPackage(packageFile, flags);
         else 
            //如果是单个apk则调用这个方法
            return parseMonolithicPackage(packageFile, flags);
        
    

parseMonolithicPackge:


    /**
     * Parse the given APK file, treating it as as a single monolithic package.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in @link #collectCertificates(Package, int).
     *
     * @deprecated external callers should move to
     *             @link #parsePackage(File, int). Eventually this method will
     *             be marked private.
     */
    @Deprecated
    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException 

        ...
        final AssetManager assets = new AssetManager();
        try 
            //调用parseBaseApk
            final Package pkg = parseBaseApk(apkFile, assets, flags);
            pkg.codePath = apkFile.getAbsolutePath();
            return pkg;
         finally 
            IoUtils.closeQuietly(assets);
        
    

parseBaseApk:

 /**
     * Parse the manifest of a <em>base APK</em>.
     * <p>
     * When adding new features, carefully consider if they should also be
     * supported by split APKs.
     */
    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException 
        ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) 

            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) 
                continue;
            

            String tagName = parser.getName();
            //receiver肯定是在application tag下面的  所以我们只看这一段就行了
            if (tagName.equals("application")) 
                if (foundApp) 
                    if (RIGID_PARSER) 
                        outError[0] = "<manifest> has more than one <application>";
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                        return null;
                     else 
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    
                

                foundApp = true;
                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) 
                    return null;
                
             
            ...
        
   /**
     * Parse the @code application XML tree at the current parse location in a
     * <em>base APK</em> manifest.
     * <p>
     * When adding new features, carefully consider if they should also be
     * supported by split APKs.
     */
    private boolean parseBaseApplication(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
        throws XmlPullParserException, IOException 
        ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) 
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) 
                continue;
            

            String tagName = parser.getName();
            if (tagName.equals("activity")) 
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) 
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                

                owner.activities.add(a);

             else if (tagName.equals("receiver")) 
                //有点震惊的是 receiver 居然 是当做activity处理的
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                if (a == null) 
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                
                //然后添加到Package的receivers中
                owner.receivers.add(a);

             
         
            ...

        return true;
    

也就是说,当我们获得了receiver之后,我们是把它存在Package的receivers中的

我们分析完了注册,现在看看AMS是如何响应我们sendBoardcast的

发送广播

通常我们都是使用如下的方式发送广播:

      Intent intent = new Intent("com.chan.plugin.receiver");
      sendBroadcast(intent);

我们看下sendBroadcast:

    @Override
    public void sendBroadcast(Intent intent) 
        mBase.sendBroadcast(intent);
    

显然要查看ContextImpl了:

    @Override
    public void sendBroadcast(Intent intent) 
        mBase.sendBroadcast(intent);
    
    @Override
    public void sendBroadcast(Intent intent) 
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try 
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
                getUserId());
         catch (RemoteException e) 
        
    

之后的调用便到了AMS中:

  public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) 

            //调用broadcastIntentLocked
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo,
                    resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
                    callingPid, callingUid, userId);
            ...
            return res;
        
    

AMS的broadcastIntent:

 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) 
        ...

        // Figure out who all will receive this broadcast.
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;

        if (intent.getComponent() == null) 
            if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) 
                // Query one target user at a time, excluding shell-restricted users
                UserManagerService ums = getUserManagerLocked();
                for (int i = 0; i < users.length; i++) 
                    ...
                    //回忆之前注册广播时候讲的内容,我们把IIntentReceiver
                    //存在mReceiverResolver中
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false, users[i]);
                    if (registeredReceivers == null) 
                        registeredReceivers = registeredReceiversForUser;
                     else if (registeredReceiversForUser != null) 
                        registeredReceivers.addAll(registeredReceiversForUser);
                    
                
             else 
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            
        

        ...

        //开始查找要发送给谁
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        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是BroadcastQueue实例 从名字可以得出他的作用 就是存放广播队列
                //将要分发的广播记录入列
                queue.enqueueParallelBroadcastLocked(r);
                //准备分发广播
                queue.scheduleBroadcastsLocked();
            
            registeredReceivers = null;
            NR = 0;
        

        ...
        return ActivityManager.BROADCAST_SUCCESS;
    

Broadcast的enqueueParallelBroadcastLocked:

 //很简单 只是简单的入列
 public void enqueueParallelBroadcastLocked(BroadcastRecord r) 
        mParallelBroadcasts.add(r);
    

而scheduleBroadcastLocked:

  public void scheduleBroadcastsLocked() 
        if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) 
            return;
        

        //只是发送一个Message就结束了
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    

我们看下Message处理的地方:

  private final class BroadcastHandler extends Handler 
        public BroadcastHandler(Looper looper) 
            super(looper, null, true);
        

        @Override
        public void handleMessage(Message msg) 
            switch (msg.what) 
                case BROADCAST_INTENT_MSG: 
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG, "Received BROADCAST_INTENT_MSG");
                    //调用了processNextBroadcast
                    processNextBroadcast(true);
                 break;
                case BROADCAST_TIMEOUT_MSG: 
                    synchronized (mService) 
                        broadcastTimeoutLocked(true);
                    
                 break;
            
        
    ;

processNextBroadcast:

    final void processNextBroadcast(boolean fromMsg) 
        synchronized(mService) 
            BroadcastRecord r;

            // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) 
                ...
                final int N = r.receivers.size();
                ...
                for (int i=0; i<N; i++) 
                    Object target = r.receivers.get(i);
                    //把广播传给注册过的人
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                
                ...
            
  

这里遍历了下注册者的信息,然后通过deliverToRegisteredReceiverLocked将广播传给注册过的人

  private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered) 
        boolean skip = false;
        ...做一些安全检查

        if (!skip) 
            // If this is not being sent as an ordered broadcast, then we
            // don't want to touch the fields that keep track of the current
            // state of ordered broadcasts.
            if (ordered) 
                r.receiver = filter.receiverList.receiver.asBinder();
                r.curFilter = filter;
                filter.receiverList.curBroadcast = r;
                r.state = BroadcastRecord.CALL_IN_RECEIVE;
                if (filter.receiverList.app != null) 
                    // Bump hosting application to no longer be in background
                    // scheduling class.  Note that we can't do that if there
                    // isn't an app...  but we can only be in that case for
                    // things that directly call the IActivityManager API, which
                    // are already core system stuff so don't matter for this.
                    r.curApp = filter.receiverList.app;
                    filter.receiverList.app.curReceiver = r;
                    mService.updateOomAdjLocked(r.curApp);
                
            
            try 
                if (DEBUG_BROADCAST_LIGHT) 
                    int seq = r.intent.getIntExtra("seq", -1);
                    Slog.i(TAG, "Delivering to " + filter
                            + " (seq=" + seq + "): " + r);
                

                //现在准备真正开始发送广播了
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                    new Intent(r.intent), r.resultCode, r.resultData,
                    r.resultExtras, r.ordered, r.initialSticky, r.userId);
                if (ordered) 
                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
                
             catch (RemoteException e) 
                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
                if (ordered) 
                    r.receiver = null;
                    r.curFilter = null;
                    filter.receiverList.curBroadcast = null;
                    if (filter.receiverList.app != null) 
                        filter.receiverList.app.curReceiver = null;
                    
                
            
        
    
    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的主线程处理广播了
                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 
             ...
        
    

app.thread是一个binder对象(ApplicationThread),我们之前就讲过,AMS通过他和主线程通信。

到这里AMS已经准备好一些,就等着应用的主线程响应广播,我们看下先关的代码:

  // This function exists to make sure all receiver dispatching is
        // correctly ordered, since these are one-way calls and the binder driver
        // applies transaction ordering per object for such calls.
        public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException 
            ...
            //还记得IIntenetReceiver吗,我们在广播注册的时候生成了他,然后把它传给AMS
            //而AMS把它记录下来,然后如果发现匹配的广播,这个对象是要被再次传回主线程的
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        

我们看下IIntentReceiver的实现,那是在ReceiverDisptcher中实现的,不记得的请回过头看下上文

static final class ReceiverDispatcher 

        final static class InnerReceiver extends IIntentReceiver.Stub 
            ...
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) 
                LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
                ...
                if (rd != null) 
                    //又转到了这里
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                 else 
                    ...
                
                ...
            
        
        ...
    
   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)) 
                 ...
            
        
  /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    
       return  sendMessageDelayed(getPostMessage(r), 0);
    

可以看到其实Arg是Runnable类型的,然后刚刚的函数发送了一个Message,都到的人肯定也是通过这个Runnable执行实际的业务逻辑,这个我们在前几期博客中有涉及,不懂的可以翻看前几篇。

我们看下Arg类:

            public void run() 
                final BroadcastReceiver receiver = mReceiver;
                final boolean ordered = mOrdered;

                ...
                try 
                    //加载了广播类, 并且通过调用广播类的onReceive!
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                 catch (Exception e) 
                    ...
                
                ...
            
        

分析

到这里,我们所有的分析都结束了,我们总结一下,动态广播和静态广播,区别就在于其注册过程,对于接受广播并没有区别。但是静态广播是在PMS被解析的(receiver的Intent-Filter,决定了它对什么感兴趣),而PMS是在另一个进程,我们无法修改它(修改PMS中关于receiver的intent-filter),幸运的是,广播并不像activity一样拥有生命周期,我们从上文就可以看出,系统也就是动态加载了下receiver。我们不如就把静态广播当做动态广播处理,毕竟能做的就这些了

实现 & 源码剖析

初始化阶段:

package com.chan.host;

import android.app.Application;

import com.chan.host.utils.PluginReceiverLoader;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dalvik.system.DexClassLoader;

/**
 * Created by chan on 16/5/15.
 */
public class HostApplication extends Application 

    private File m_apk;
    private DexClassLoader m_dexClassLoader;


    @Override
    public void onCreate() 
        super.onCreate();

        try 
            //模拟从服务器拉取apk
            obtainApkFromServer();
            //初始化classLoader
            setupClazzLoader();
            //开始安装插件加载器
            setupPluginReceiverLoader();
         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         catch (ClassNotFoundException e) 
            e.printStackTrace();
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (IllegalAccessException e) 
            e.printStackTrace();
         catch (InstantiationException e) 
            e.printStackTrace();
         catch (InvocationTargetException e) 
            e.printStackTrace();
         catch (NoSuchFieldException e) 
            e.printStackTrace();
        
    

    private void obtainApkFromServer() throws IOException 
        InputStream inputStream = getResources().openRawResource(R.raw.plugin);
        byte[] bytes = new byte[256];
        int length = -1;


        File dir = getDir("plugin", MODE_PRIVATE);
        if (!dir.exists()) 
            dir.mkdirs();
        

        m_apk = new File(dir, "plugin.apk");
        FileOutputStream fileOutputStream = new FileOutputStream(m_apk);

        while ((length = inputStream.read(bytes)) != -1) 
            fileOutputStream.write(bytes, 0, length);
        

        fileOutputStream.flush();
        fileOutputStream.close();
    

    private void setupClazzLoader() 
        m_dexClassLoader = new DexClassLoader(
                m_apk.getAbsolutePath(),
                getDir("pluginOpt", MODE_PRIVATE).getAbsolutePath(),
                null, getClassLoader());
    

    private void setupPluginReceiverLoader() throws
            ClassNotFoundException, NoSuchFieldException, IllegalAccessException,
            NoSuchMethodException, InstantiationException, InvocationTargetException 
        Class<?> packageParserClazz = Class.forName("android.content.pm.PackageParser", false, getClassLoader());

        /**
         * Parse the package at the given location. Automatically detects if the
         * package is a monolithic style (single APK file) or cluster style
         * (directory of APKs).
         * <p>
         * This performs sanity checking on cluster style packages, such as
         * requiring identical package name and version codes, a single base APK,
         * and unique split names.
         * <p>
         * Note that this <em>does not</em> perform signature verification; that
         * must be done separately in @link #collectCertificates(Package, int).
         *
         * @see #parsePackageLite(File, int)
         */
        Method parsePackageMethod = packageParserClazz.getDeclaredMethod("parsePackage", File.class, int.class);

        //生成一个package parser 对象
        Object packageParserObject = packageParserClazz.newInstance();

        //获得Package对象
        Object packageObject = parsePackageMethod.invoke(packageParserObject, m_apk, 0);

        //获得package的receivers域
        Class<?> packageClazz = Class.forName("android.content.pm.PackageParser$Package", false, getClassLoader());
        Field receiversField = packageClazz.getDeclaredField("receivers");
        receiversField.setAccessible(true);

        //获得所有的receivers 他是PackageParser$Activity类型的
        List<?> receiversList = (List<?>) receiversField.get(packageObject);

        /*
         * 现在已经获得了所有的receiver
         * 就只剩获得receiver的intent filter
         * 下面就开始获得receiver的intent filter
         * 其中receiversList容器的模板实参类型是 android.content.pm.PackageParser$Activity
        * */



        /*
        * android.content.pm.PackageParser$Activity 其实是 android.content.pm.PackageParser$Component
        * public final static class Activity extends Component<ActivityIntentInfo>
        * 其中域 intents存放的是action信息
        * public final ArrayList<II> intents;
        *
        * 而II类型是 Component<II extends IntentInfo> 模板参数
        *
        * */
        Class<?> componentClazz = Class.forName("android.content.pm.PackageParser$Component");
        Field intentsField = componentClazz.getDeclaredField("intents");

        Class<?> packageParser$ActivityIntentInfoClazz = Class.forName(
                "android.content.pm.PackageParser$ActivityIntentInfo",
                false, getClassLoader());


        Method countActionsMethod = packageParser$ActivityIntentInfoClazz.getMethod("countActions");
        Method getActionMethod = packageParser$ActivityIntentInfoClazz.getMethod("getAction", int.class);

        Map<String, List<String>> receiverAndIntentFilterMap = new HashMap<>();

        /*
        * 下面的receiver 其实是 android.content.pm.PackageParser$Activity
        * 他有一个field 名为className 就是存放的receiver的className
        * 我们获得 这个className就能通过反射获得receiver对象
        * */
        Class<?> packageParser$ActivityClazz = Class.forName("android.content.pm.PackageParser$Activity", false, getClassLoader());
        Field classNameField = packageParser$ActivityClazz.getField("className");



        for (Object receiver : receiversList) 

            List<?> activityIntentInfoList = (List<?>) intentsField.get(receiver);

            if (activityIntentInfoList != null) 

                List<String> intentFilter = new ArrayList<>();
                for (Object activityIntentInfo : activityIntentInfoList) 

                    //添加所有的action 到intent filter中
                    final int count = (int) countActionsMethod.invoke(activityIntentInfo);
                    for (int i = 0; i < count; ++i) 
                        intentFilter.add((String) getActionMethod.invoke(activityIntentInfo, i));
                    
                

                //记录下来
                receiverAndIntentFilterMap.put((String) classNameField.get(receiver), intentFilter);
            
        

        PluginReceiverLoader.init(m_dexClassLoader, receiverAndIntentFilterMap);
    
package com.chan.host.utils;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import dalvik.system.DexClassLoader;

/**
 * Created by chan on 16/5/15.
 */
public class PluginReceiverLoader 

    private DexClassLoader m_dexClassLoader;
    private Map<String, List<String>> m_receivers;

    private static PluginReceiverLoader s_pluginReceiverLoader;

    public PluginReceiverLoader(DexClassLoader dexClassLoader, Map<String, List<String>> receivers) 
        m_dexClassLoader = dexClassLoader;
        m_receivers = receivers;
    

    public List<BroadcastReceiver> registerReceivers(Context context)
            throws ClassNotFoundException,
            IllegalAccessException, InstantiationException 

        List<BroadcastReceiver> list = new ArrayList<>();
        if (m_dexClassLoader == null) 
            return list;
        

        //依次注册
        for (Map.Entry<String, List<String>> entry : m_receivers.entrySet()) 
            BroadcastReceiver broadcastReceiver = (BroadcastReceiver) m_dexClassLoader.loadClass(entry.getKey()).newInstance();
            IntentFilter intentFilter = new IntentFilter();
            for (String action : entry.getValue()) 
                intentFilter.addAction(action);
            

            context.registerReceiver(broadcastReceiver, intentFilter);
            list.add(broadcastReceiver);
        

        return list;
    

    public void unregisterReceivers(Context context, List<BroadcastReceiver> broadcastReceiverList) 
        for (BroadcastReceiver broadcastReceiver : broadcastReceiverList) 
            context.unregisterReceiver(broadcastReceiver);
        
    

    public static void init(DexClassLoader dexClassLoader, Map<String, List<String>> receivers) 
        s_pluginReceiverLoader = new PluginReceiverLoader(dexClassLoader, receivers);
    

    public static PluginReceiverLoader getInstance() 
        return s_pluginReceiverLoader;
    

使用:

public class MainActivity extends AppCompatActivity 

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

        try 
            PluginReceiverLoader.getInstance().registerReceivers(this);
         catch (ClassNotFoundException e) 
            e.printStackTrace();
         catch (IllegalAccessException e) 
            e.printStackTrace();
         catch (InstantiationException e) 
            e.printStackTrace();
        

        findViewById(R.id.id_button).setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                Intent intent = new Intent("com.chan.plugin.receiver");
                sendBroadcast(intent);
            
        );


    

效果

以上是关于android插件化开发——加载广播的主要内容,如果未能解决你的问题,请参考以下文章

Android 插件化“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )

android 广播的插件化

Android 插件化开发:资源插件化

Classloader、插件化开发(结合Presto)

android插件化(四)Hook加载插件APK(ClassLoader方式)

android插件化开发——通过hook加载插件apk