Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android插件化開發——加載廣播

android插件化開發——加載廣播

編輯:關於Android編程

引言

在android開發過程中,我們不可避免的會使用廣播,比如,偵聽開機,偵聽短信。
而對於廣播,我想很多人都知道他有兩種類型,動態廣播,通過代碼在runtime進行register,
像這樣:

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

而靜態廣播呢,是直接在AndroidManifest.xml中進行注冊,像這樣:

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

因此,如果我們想加載插件中的廣播,必然是要區分動態廣播和靜態廣播。他們使用時,區別就只在是否需要注冊了,所以我們只要搞清楚他們是如何讓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這個函數中:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> 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).
     * 

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

* Note that this does not 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.
     * 

* Note that this does not 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 base APK.
     * 

* 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] = " has more than one "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, " has more than one "); 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
     * base APK manifest.
     * 

* 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 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 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

這裡遍歷了下注冊者的信息,然後通過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).
         * 

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

* Note that this does not 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域 ClasspackageClazz = Class.forName("android.content.pm.PackageParser$Package", false, getClassLoader()); Field receiversField = packageClazz.getDeclaredField("receivers"); receiversField.setAccessible(true); //獲得所有的receivers 他是PackageParser$Activity類型的 ListreceiversList = (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 * 其中域 intents存放的是action信息 * public final ArrayList intents; * * 而II類型是 Component 模板參數 * * */ ClasscomponentClazz = Class.forName("android.content.pm.PackageParser$Component"); Field intentsField = componentClazz.getDeclaredField("intents"); ClasspackageParser$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> receiverAndIntentFilterMap = new HashMap<>(); /* * 下面的receiver 其實是 android.content.pm.PackageParser$Activity * 他有一個field 名為className 就是存放的receiver的className * 我們獲得 這個className就能通過反射獲得receiver對象 * */ ClasspackageParser$ActivityClazz = Class.forName("android.content.pm.PackageParser$Activity", false, getClassLoader()); Field classNameField = packageParser$ActivityClazz.getField("className"); for (Object receiver : receiversList) { ListactivityIntentInfoList = (List) intentsField.get(receiver); if (activityIntentInfoList != null) { List 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> m_receivers;

    private static PluginReceiverLoader s_pluginReceiverLoader;

    public PluginReceiverLoader(DexClassLoader dexClassLoader, Map> receivers) {
        m_dexClassLoader = dexClassLoader;
        m_receivers = receivers;
    }

    public List registerReceivers(Context context)
            throws ClassNotFoundException,
            IllegalAccessException, InstantiationException {

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

        //依次注冊
        for (Map.Entry> 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 broadcastReceiverList) {
        for (BroadcastReceiver broadcastReceiver : broadcastReceiverList) {
            context.unregisterReceiver(broadcastReceiver);
        }
    }

    public static void init(DexClassLoader dexClassLoader, Map> 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);
            }
        });


    }

效果

這裡寫圖片描述

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved