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

    }

效果

时间: 2024-11-07 09:34:49

android插件化开发——加载广播的相关文章

Android插件化开发---运行未安装apk中的Service

如果你还不知道什么叫插件化开发,那么你应该先读一读之前写的这篇博客:Android插件化开发,初入殿堂 上一篇博客主要从整体角度分析了一下Android插件化开发的几个难点与动态加载没有被安装的apk中的Activity和资源的方法.其实一般的插件开发主要也就是加载个Activity,读取一些资源图片之类的.但是总有遇到特殊情况的时候,比如加载Service. 要动态加载Service,有两种思路:一是通过NDK的形式,将Service通过C++运行起来(这种方法我没有尝试,只听群里的朋友说实现

Android 插件化开发-主题皮肤更换

参考 http://www.2cto.com/kf/201501/366859.html 本项目是以插件化开发思想进行的,主要工作和代码如下 资源文件,这里以color资源为例 1.首先我们需要准备一个皮肤包,这个皮肤包里面不会包含任何Activity,里面只有资源文件,这里我为了简单,仅仅加入一个color.xml(其实就相当于Android系统中的framework_res.apk) <!--?xml version="1.0" encoding="utf-8&qu

Android插件化开发,初入殿堂

好久没有写博客了,这次准备写写我这几天的研究成果--Android插件化开发框架CJFrameForAndroid. 好久没有写博客了,这次准备写写我这几天的研究成果--Android插件化开发框架CJFrameForAndroid. 背景交代 首先,你需要知道什么是插件化开发.就拿最常见的QQ来说,在第三个界面动态那里有个管理,点开后可以选择很多的增植功能,这里腾讯只放了一些网页应用,那么如果未来想加入一个打飞机游戏,要怎么做?让用户重新安装吗,这就是插件化开发所解决的问题. 用一句话来概括插

详解Android插件化开发-资源访问

动态加载技术(也叫插件化技术),当项目越来越庞大的时候,我们通过插件化开发不仅可以减轻应用的内存和CPU占用,还可以实现热插拔,即在不发布新版本的情况下更新某些模块. 通常我们把安卓资源文件制作成插件的形式,无外乎有一下几种: zip.jar.dex.APK(未安装APK.安装APK) 对于用户来讲未安装的APK才是用户所需要的,不安装.不重启,无声无息的加载资源文件,这正是我们开发者追求的结果. 但是,开发中宿主程序调起未安装的插件apk,一个很大的问题就是资源如何访问,这些资源文件的ID都映

Android插件化开发之解决Atlas组件在宿主的注册问题

OpenAtlas有一个问题,就是四大组件必须在Manifest文件中进行注册,那么就必然带来一个问题,插件中的组件都要重复在宿主中注册.像Service,ContentProvider等组件目前没有什么好的解决方法,只能在宿主中注册.但是像Activity,显然是有解决方法的,就是使用Fragment代替Activity,Activity只是作为一个放Fragment的容器,那么不仅在插件中不用再清单文件中注册,就连宿主的注册问题也一并解决了.那么,解决方案呢,没错,就是之前写的一篇博文And

Android 插件化开发(三):资源插件化

在前面的文章中我们成功的加载了外部的Dex(Apk)并执行了插件的Bean代码.这时我们会想,能不能加载并运行插件Apk的Activity.答案当然是能,否则后续我们的研究就没意义了,但是想实现Activity的插件化运行,我们必须要解决一个问题——如何使用插件中的资源. 本文我们就讲一下插件的资源加载机制,并讲述一下如何实现资源的插件化. 一.资源的加载机制 Android的资源文件分为两类: 第一类是res目录下存放的可编辑的资源文件,这类文件在编译时系统会自动在R文件中生成资源文件的16进

Android插件化开发-hook动态代理

首先,我们阐述为什么android需要插件化: 1:由于业务的增长,app的方法数逐渐达到65535(有人说用于检索方法数的列表大小使用short存储的,其实我看了源码之后并没有发现相关信息,并对此说法产生了怀疑,不过最后找到的结果就是,65535这个限制可能是由于dalvik的bytecode大小限制的,具体的可以查看官方文档). 2:一个模块的变化都要整体编译一次app,维护成本太大了,用插件开发app会好很多 对于以上问题解决方案不少,著名的有h5,hybird,不过这些都没有native

Android插件化开发之DexClassLoader动态加载dex、jar小Demo

一.温故动态加载ClassLoader机制 如果对Android的ClassLoader加载机制不熟悉,猛戳Android插件化开发动态加载基础之ClassLoader工作机制 http://blog.csdn.net/u011068702/article/details/53248960 二.介绍 我们知道在Android中可以跟java一样实现动态加载jar,但是Android使用德海Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik

Android插件化开发之OpenAtlas插件启动方式与插件启动广播

到现在为止已经写了6篇文章了 Android插件化开发之OpenAtlas初体验 Android插件化开发之OpenAtlas生成插件信息列表 Android插件化开发之OpenAtlas资源打包工具补丁aapt的编译 Android插件化开发之OpenAtlas插件适配 Android插件化开发之解决OpenAtlas组件在宿主的注册问题 Android插件化开发之OpenAtlas中四大组件与Application功能的验证 这篇文章主要介绍一下OpenAtlas插件的几种启动方式,在Atl