读书人

某APK中使用了动态注册BroadcastRecei

发布时间: 2013-09-08 15:21:21 作者: rapoo

某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法

在某APK中,通过如下方法动态注册了一个BroadcastReceiver,代码参考如下:

    @Override    protected void onAttachedToWindow()    {        super.onAttachedToWindow();                /* monitor time ticks, time changed, timezone */                if (mIntentReceiver == null)        {            mIntentReceiver = new TimeChangedReceiver(this);            IntentFilter filter = new IntentFilter();            filter.addAction(Intent.ACTION_TIME_TICK);            filter.addAction(Intent.ACTION_TIME_CHANGED);            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);            getContext().registerReceiver(mIntentReceiver, filter);
Launcher要动态加载此APK(动态加载APK的目的和实现思路,参见我的这个文章:Launcher中动态加载其它APK中Activity的问题解决思路),

出现了如下异常:

09-05 19:05:55.033: E/AndroidRuntime(28637): java.lang.SecurityException: Given caller package com.zhao3546.time is not running in process ProcessRecord{41e74e50 28637:com.zhao3546.launcher/u0a10142}

搜索了一下,很多人遇到过此类问题,但都没有提出解决方法,看来只能自己动手解决了。

动态加载不行,那在AndroidManifest.xml试试静态加载BroadcastReceiver是否可以?

如果是其它的BroadcastRecevier,验证了是ok的,但android.content.Intent.ACTION_TIME_TICK这个不行,Android的注释中已经明确说明了。

String android.content.Intent.ACTION_TIME_TICK = "android.intent.action.TIME_TICK"Broadcast Action: The current time has changed. Sent every minute. You can not receive this through components declared in manifests, only by exlicitly registering for it with Context.registerReceiver(). This is a protected intent that can only be sent by the system.

难道就真的没有解决方法了?

手头有一份Android源码,我根据“Given caller package”和“is not running in process ProcessRecord”这两个关键字全文搜索,

想看看抛出这个异常的代码是在哪里搞出来的,在*.cpp,*.c*,*.java中搜索都没有找到,先放弃此方法吧。

回头再来分析此异常,

09-05 19:05:55.033: E/AndroidRuntime(28637): java.lang.SecurityException: Given caller package com.zhao3546.time is not running in process ProcessRecord{41e74e50 28637:com.zhao3546.launcher/u0a10142}

我是在第三方APK中注册的BroadcastReceiver,这个APK在AndroidManifest.xml中声明的包名正好是“com.zhao3546.time”,而最终是和Launcher运行在同一个进程中的,这个进程对应的包名是“com.zhao3546.launcher”,因为Launcher先运行,而APK是被动态加载进来的。

那是不是只要我注册BroadcastReceiver对应的包名只要是“com.zhao3546.launcher”,就可以解决问题了?

Launcher中动态加载其它APK中Activity的问题解决思路,此文中已经通过将Launcher和第三方APK的AndroidManifest.xml中android:sharedUserId属性声明成为一致了,

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.zhao3546.time"    android:sharedUserId="zhao3546.launcher"

所以Launcher和第三方APK实际上是运行在同一个进程中的,那只要将Launcher的context对象传给这个APK,这个APK使用Launcher的context去注册BroadcastReceiver,是不是就可以解决问题了?

第三方APK的com.zhao3546.time包下新增如下类,并实现有public static void setContext(Context context)这个静态方法:

package com.zhao3546.time;import android.content.Context;public class ContextHolder{    private static Context context = null;        public static void setContext(Context context)    {        System.out.println("com.zhao3546.time.ContextHolder setContext");                ContextHolder.context = context;    }        public static Context getContext()    {        System.out.println("com.zhao3546.time.ContextHolder getContext() context="                + context);                return context;    }}

在Launcher代码中,通过反射机制动态地将launcher的context对象传到 com.zhao3546.time.ContextHolder 中,

    private void setContext2Plugin(ClassLoader pluginClassLoader,            String pluginPackageName, Context launcherContext)    {        String className = pluginPackageName + ".ContextHolder";        try        {            Method m = pluginClassLoader.loadClass(className)                    .getMethod("setContext", Context.class);            m.invoke(null, launcherContext);        }        catch (Exception e)        {            Log.w(TAG, "Fail to loadClass " + className + ", skip it", e);        }    }

修改之前注册BroadcastReceiver的地方,通过ContextHolder()来注册BroadcastReceiver,把APK重新部署验证,问题解决。

看似解决不了的问题,其实有时需要换个思路去尝试不同的方法,可能就会取得意想不到的效果。


读书人网 >移动开发

热点推荐