深入浅出Service插件化原理

栏目: IOS · Android · 发布时间: 5年前

内容简介:关注公众号:顾林海Service插件化的重点是保证它的优先级,需要一个真正的Service来实现,当启动插件Service时,就会先启动代理Service,当这个代理Service运行起来后,在它的onStartCommand等方法里面进行分发,执行插件Service的onCreate等方法,这种方案叫代理分发。也就是在启动插件Service时替换为代理Service,什么时候替换?通过startService方法启动Service会调用ContextWrapper的startService方法,如下所示:
深入浅出Service插件化原理

关注公众号:顾林海

Service插件化的重点是保证它的优先级,需要一个真正的Service来实现,当启动插件Service时,就会先启动代理Service,当这个代理Service运行起来后,在它的onStartCommand等方法里面进行分发,执行插件Service的onCreate等方法,这种方案叫代理分发。

深入浅出Service插件化原理

也就是在启动插件Service时替换为代理Service,什么时候替换?通过startService方法启动Service会调用ContextWrapper的startService方法,如下所示:

//路径:/frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {
    
    Context mBase;
    
    ...
    
    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
    
    ...
}
复制代码

在ContextWrapper的startService方法中调用mBase的startService方法,mBase的类型是Context,而Context是一个抽象类,内部定义了很多方法以及静态常量,它的具体实现类是ContextImpl,进入ContextImpl的startService方法:

//路径:/frameworks/base/core/java/android/app/ContextImpl.java
    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
复制代码

ContextImpl的startService方法中又调用了startServiceCommon方法:

//路径:/frameworks/base/core/java/android/app/ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //注释1
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
复制代码

注释1处通过ActivityManager的getService方法获取ActivityManagerService的代理类IActivityManager,进入ActivityManager的getService方法:

//路径:/frameworks/base/core/java/android/app/ActivityManager.java
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
复制代码

getService方法通过IActivityManagerSingleton的get方法获取IActivityManager对象,IActivityManagerSingleton是一个单例类,在create方法中从ServiceManager中获取一个名叫“activity”的Service引用,同时也是IBinder类型的ActivityManagerService的引用,最后通过IActivityManager.Stub.asInterface方法将它转换成IActivityManager,看到IActivityManager.Stub.asInterface这段代码时可以知道这里采用的是AIDL方式来实现进程间通信,也就是说服务端ActivityManagerService会实现IActivityManager.Stub类并实现相应的方法。

继续回到ContextImpl的startServiceCommon方法:

//路径:/frameworks/base/core/java/android/app/ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //注释1
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
复制代码

在注释1处获取到ActivityManagerService的代理类IActivityManager,接着通过这个代理类向ActivityManagerService发送startService的消息。

将上面的知识点进行总结,如下图所示:

深入浅出Service插件化原理

替换代理Service可以通过Hook IActivityManager生成动态代理类来实现。

Service插件化需要一个真正的Service来实现,先在AndroidManifest.xml中注册代理ProxyService:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.book.demo">
    ...
    <application
        ...>
        ...
        <service android:name=".ProxyService" />
    </application>

</manifest>
复制代码

接着启动插件Service:

public class MainActivity extends AppCompatActivity {

    private Button mBtnNormal;

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

    private void initViews(){
        mBtnNormal=findViewById(R.id.btn_normal);
    }
    private void initEvent(){
        mBtnNormal.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,TargetService.class);
                startService(intent);
            }
        });
    }
}
复制代码

这个TargetService用来模拟插件Service,不能够直接启动,因此需要Hook IActivityManager,定义替换IActivityManager的代理类IActivityManagerProxy,代码如下:

public class IActivityManagerProxy implements InvocationHandler {

    private Object mActivityManager;

    public IActivityManagerProxy(Object activityManager){
        this.mActivityManager=activityManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("startService".equals(method.getName())){
            Intent intent=null;
            int index=0;
            for(int i=0,length=args.length;i<length;i++){
                if(args[i] instanceof Intent){
                    index=i;
                    break;
                }
            }
            intent= (Intent) args[index];

            Intent proxyIntent=new Intent();
            String packageName="com.book.demo";
            proxyIntent.setClassName(packageName,packageName+".ProxyService");
            //将插件Service的ClassName保存起来
            proxyIntent.putExtra("TARGET_SERVICE",intent.getComponent().getClassName());
            //替换为新建的proxyIntent
            args[index]=proxyIntent;
        }
        return method.invoke(mActivityManager,args);
    }
}
复制代码

上述代码中通过拦截startService方法,获取启动TargetService的Intent,再创建代理Service的Intent,将TargetService的Intent的相关信息保存在代理Service的Intent中,最后将启动的Intent替换成代理Service的Intent,也就是说最后启动的是ProxyService。

接着用IActivityManagerProxy替换系统的IActivityManager,代码如下:

public class HookUtil {

    public static void hookAMS() {
        Object defaultSingleton = null;
        try {
            if (Build.VERSION.SDK_INT >= 26) {
                Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
                Field field=activityManagerClass.getDeclaredField("IActivityManagerSingleton");
                field.setAccessible(true);
                defaultSingleton=field.get(null);
            } else {
                Class<?> activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative");
                Field field=activityManagerNativeClass.getDeclaredField("gDefault");
                field.setAccessible(true);
                defaultSingleton=field.get(null);
            }
            Class<?> singletonClass=Class.forName("android.util.Singleton");
            Field mInstanceField=singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            //获取IActivityManager
            Object iActivityManager=mInstanceField.get(defaultSingleton);
            
            Class<?> iActivityManagerClazz=Class.forName("android.app.IActivityManager");
            
            Object proxy=Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[]{iActivityManagerClazz},new IActivityManagerProxy(iActivityManager));
            
            mInstanceField.set(defaultSingleton,proxy);
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}
复制代码

上述代码中主要做了这些事:

  • 对版本进行区分,最终获取的是Singleton类型的IActivityManagerSingleton或者gDefault字段。
  • 获取Singleton类中的mInstance字段并获取系统的IActivityManager。
  • 创建代理类IActivityManagerProxy,来替换系统的IActivityManager。

接着在Application中调用这个方法:

public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        HookUtil.hookAMS();
    }
}
复制代码

到这里启动的Service不是插件的Service,而是代理的Service,接下来需要在ProxyService中进行代理分发。

public class ProxyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (null == intent || !intent.hasExtra("TARGET_SERVICE")) {
            return START_STICKY;
        }

        String serviceName = intent.getStringExtra("TARGET_SERVICE");
        if (TextUtils.isEmpty(serviceName)) {
            return START_STICKY;
        }

        //=============反射调用插件Service的attach方法===========================

        try {
            //获取attach方法需要的ActivityThread
            Class activityThreadClazz = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThread = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThread.setAccessible(true);
            Object activityThread = sCurrentActivityThread.get(null);

            //获取attach方法需要的token
            Method getActivityThreadMethod = activityThreadClazz.getDeclaredMethod("getApplicationThread");
            getActivityThreadMethod.setAccessible(true);
            Object applicationThread = getActivityThreadMethod.invoke(activityThread);

            Class iInterfaceClazz = Class.forName("android.os.IInterface");
            Method asBinderMethod = iInterfaceClazz.getDeclaredMethod("asBinder");
            asBinderMethod.setAccessible(true);

            Object token = asBinderMethod.invoke(applicationThread);

            //反射获取attach方法
            Class serviceClazz = Class.forName("android.app.Service");
            Method attachMethod = serviceClazz.getDeclaredMethod("attach",
                    Context.class, activityThreadClazz, String.class, IBinder.class, Application.class, Object.class);
            attachMethod.setAccessible(true);

            //获取IActivityManager
            Object defaultSingleton = null;
            if (Build.VERSION.SDK_INT >= 26) {
                Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
                Field field = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
                field.setAccessible(true);
                defaultSingleton = field.get(null);
            } else {
                Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
                Field field = activityManagerNativeClass.getDeclaredField("gDefault");
                field.setAccessible(true);
                defaultSingleton = field.get(null);
            }
            Class<?> singletonClass = Class.forName("android.util.Singleton");
            Field mInstanceField = singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            Object iActivityManager = mInstanceField.get(defaultSingleton);

            //反射执行插件Service
            Service targetService = (Service) Class.forName(serviceName).newInstance();
            attachMethod.invoke(targetService, this,
                    activityThread,
                    intent.getComponent().getClassName(),
                    token,
                    getApplication(),
                    iActivityManager);
            //执行插件Service的onCreate、onStartCommand方法
            targetService.onCreate();
            targetService.onStartCommand(intent,flags,startId);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
                       return START_STICKY;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            return START_STICKY;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return START_STICKY;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return START_STICKY;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            return START_STICKY;
        } catch (InstantiationException e) {
            e.printStackTrace();
            return START_STICKY;
        }
        return START_STICKY;
    }
}
复制代码

上诉代码主要做了以下几件事:

  • 判断参数条件不满足、出现异常和代码执行完毕返回START_STICKY,这样ProxyService会重新创建并执行onStartCommand方法。
  • 创建插件Service,并反射调用attach方法。
  • 进行代理分发,执行插件Service的onCreate和onStartCommand方法。

插件TargetService如下所示:

public class TargetService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("TargetService","----onCreate-----");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("TargetService","----onStartCommand-----");
        return super.onStartCommand(intent, flags, startId);
    }
}
复制代码

运行看是否会在onCreate和onStartCommand方法中打印Log:

深入浅出Service插件化原理
深入浅出Service插件化原理

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

裂变式创业

裂变式创业

宗毅、小泽 / 机械工业出版社 / 2016-1-1 / 39.84

互联网大潮汹涌来袭,传统企业增长乏力,互联网公司跨界冲击,转型之路迫在眉睫。“转型找死,不转型等死”这一坊间传说让多数企业徘徊不前,不少实体经济面临困境,敢问路在何方? 宗毅独创裂变式创业,用人民币投票选总经理,规定自己不投钱不能参与竞选;不相信干股,不使用职业经理人,用金融的方式管理现金流。用商业模式颠覆传统公益,打通南北充电之路;摇身一变成为自媒体,用产品建立社群。自己写故事,自己当导演......一起来看看 《裂变式创业》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具