Android实现监听APP启动、前台和后台

Android 实时监听APP进入前台或后台

前言
在我们开发的过程中,经常会遇到需要我们判断app进入后台,或者切换到前台的情况。比如我们想判断app切换到前台时,显示一个解锁界面,要求用户输入解锁密码才能继续进行操作;我们想判断app切换到后台,记录一下log;或者当用户切换回前台时,我们想刷新一下页面的数据等等…

android里面监听app前后台的方案很多(这还是得归根于安卓提供了丰富的api和强大的架构支撑,呵呵~),比如可以通过ActivityManager提供的getRunningAppProcesses()获取系统当前运行的app,从而判断app是否处于前台。或者通过监听点击Home键,判断app是否回到了后台。我列出下列几种方案,推荐方案三

方案一 利用ActivityManager的RunningAppProcessInfo类


ActivityManager在整个系统里面起着非常重要的作用,主要为系统中运行着的activity交互提供接口,其中 RunningAppProcessInfo类 则封装了正在运行着的进程信息,当然也包含了正在运行的app的包名,因此我们可以 activitymanager.getRunningAppProcesses() 获取当前运行的app列表,对比自身的包名,来判断本身app是否处于前台运行。

这打断一下,ActivityManager框架是Android系统十分重要的一部分,在以后有时间,笔者会好好学习整理ActivityManager框架的分析。

回到这里,下面给出部分关键代码。
 

/**
     * App前后台状态
     */
    public boolean isForeground = false;
    @Override
    protected void onResume() {
        ......
        if (isForeground == false) {
            //由后台切换到前台
            isForeground = true;
        }
    }
 
    @Override
    protected void onPause() {
        ......
        if (!isAppOnForeground()) {
            //由前台切换到后台
            isForeground = false;
        }
    }
    /**
     * 判断app是否处于前台
     *
     * @return
     */
    public boolean isAppOnForeground() {
 
        ActivityManager activityManager = (ActivityManager) getApplicationContext()
                .getSystemService(Context.ACTIVITY_SERVICE);
        String packageName = getApplicationContext().getPackageName();
        /**
         * 获取Android设备中所有正在运行的App
         */
        List<RunningAppProcessInfo> appProcesses = activityManager
                .getRunningAppProcesses();
        if (appProcesses == null)
            return false;
 
        for (RunningAppProcessInfo appProcess : appProcesses) {
            // The name of the process that this object is associated with.
            if (appProcess.processName.equals(packageName)
                    && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
 
        return false;
    }

小结: 通过ActivityManager来获取当前运行的app列表,然后判断我们的app是否处于前台,能基本达到我们的预期需求。但如果将上面代码放到每一个activity,或者activity基类里面,这消耗还是挺大的。而且而且,ActivityManager通过.getRunningAppProcesses()获取当前运行列表这个方法,在5.0以后已经被deprecated掉了…(心中万马奔腾…)

方案二:监听Home键点击


说起home键的监听,也算是android里面的一个梗。这看上去简单的功能,实际上实现起来却十分的曲折,这跟android系统的设计有很大的关系。当home键被点击的时候,会发出一个系统广播,在系统收到这个广播以后,会在framework层做一系列操作将当前的app退到后台,然后把事件消费掉不传给应用层,所以这时候 onKeyDown事件也接收不到了…用官方的解释就是 ——“Home key. This key is handled by the framework and is never delivered to applications.”。实际上这也是为了安全性的考虑,不然每家的app都监听home键,然后禁掉响应,不都成了流氓软件了。

官方不支持,可是这难不到我们万能的攻城狮们的,毕竟有很多想我们正规的开发者,还是需要监听home键来做一些如写日志之类的操作的。网上谷歌百度会有很多类似的解决方案,在这里就不展开了。(不过这里可以推荐一下)

小结: 我们能监听到home键点击,当然就知道app处于前台还是后台了。但毕竟这个方案是基于官方不支持的前提下的,而且home键的监听在很多设备都会有兼容性的问题,因此我们不大推荐这样做。

方案三:利用ActivityLifecycleCallbacks监听所有activity的生命周期


通过监听所有activity的onStart、onStop调用,然后统计当前是不是所有的activity都调用了onStop,确实可以判断app处于了后台,不过让我们重写每一个activity的onStop,并加这段奇怪的代码,实在不大优雅,即使在activity基类里面统一写,那天如果忘了或者不需要继承基类的activity,就不大好了。

因此,这里推荐一个新的接口ActivityLifecycleCallbacks,说新也不新,其实早在API 14 (android 4.0)就已经推出了。ActivityLifecycleCallbacks接口在Application类里面,因此需要我们自己继承Application,自定义一个MyApplication,然后注册接口。ActivityLifecycleCallbacks为application提供了对所有activity生命周期的监听,因此我们通过重写ActivityLifecycleCallbacks的onActivityStarted和onActivityStopped方法,定义一个变量,来统计当前有几个activity处于前台。

/**
     * 当前Acitity个数
     */
    private int activityAount = 0;
    
    @Override
    public void onCreate() {
        ......
        registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
        ......
    }
 
    /**
     * Activity 生命周期监听,用于监控app前后台状态切换
     */
    ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }
 
        @Override
        public void onActivityStarted(Activity activity) {
//            if (activityAount == 0) {
//                //app回到前台
//                isForeground = true;
//            }
            activityAount++;
        }
 
        @Override
        public void onActivityResumed(Activity activity) {
        }
        @Override
        public void onActivityPaused(Activity activity) {
        }
 
        @Override
        public void onActivityStopped(Activity activity) {
            activityAount--;
            if (activityAount == 0) {
                isForeground = false;
            }
        }
 
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }
        @Override
        public void onActivityDestroyed(Activity activity) {
        }
    };

以上代码写在MyApplication里面。怎么样,是不是很简单快捷!而且准确无误。

总结:
1、利用ActivityManager的RunningAppProcessInfo类,直接粗暴,官方摒弃,不推荐;
2、监听Home键点击,官方不支持,兼容性差,不稳定,不推荐;
3、利用ActivityLifecycleCallbacks监听所有activity的生命周期,官方指定饮品,哦,不对,官方指定接口,大力推荐!我们举一反三,利用ActivityLifecycleCallbacks监听,我们还能控制我们的activity堆栈,甚至还可以在里面做日志统计…想想还是很强大的。

PS:虽则利用ActivityLifecycleCallbacks接口监听的方案最优,但这毕竟是4.0以后的产品,因此对于4.0以下的,可以考虑增加方案一判断。


这里提示一下
Android是在API14之后添加了用于监听应用Activity生命周期的回调接口ActivityLifecycleCallbacks,使用时需要在Application中进行注册。在Activity会报错

所以使用时,请自行转换或继承Application类来注册

参考:https://www.cnblogs.com/zhujiabin/p/9336663.html

Android实现APP启动监听和拦截

有时候我们需要监听app的启动,并在一定时间进行拦截,其实系统是有提供相应的监听方法的

我们需要申明以下权限

 <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />

其次我们需要进行注册

import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.os.RemoteException;
 
setActivityController();
 
private void setActivityController() {
    IActivityManager am = ActivityManagerNative.getDefault();
    try {
        Log.i("ActivityController", "setActivityController");
        am.setActivityController(new ActivityController(this),true);
    } catch (RemoteException e) {
        Log.i("ActivityController", "setActivityController RemoteException");
        e.printStackTrace();
    }
}

然后我们就去实现对应回调方法

 
import android.app.IActivityController;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.util.Log;
import android.os.SystemProperties;
import android.provider.Settings;
import android.app.ActivityManager;
import android.content.ComponentName;
 
import android.os.SystemProperties;
 
public class ActivityController extends IActivityController.Stub {
 
    public ActivityController(Context context){
    }
    /**activityStarting:当系统正在启动一个activity时会触发,当返回true,表示允许启动。当返回状态noraml/false分别表示停止/拒绝启动activity
    activityResuming:当系统正在返回一个activity时会触发,当返回true,表示允许返回。当返回状态noraml/false分别表示停止/拒绝返回activity
    appCrashed:当一个应用进程已经崩溃会触发,当返回true时,表示可以重启,当返回false时,表示立即杀死它(进程)。
    appEarlyNotResponding:当一鉴定为ANR时就很早触发;
    appNotResponding:当一个应用进程出现ANR时就会触发,当返回0时,表示会弹出应用无响应的dialog,如果返回1时,表示继续等待,如果返回-1时,表示立即杀死进程。
    systemNotResponding:当系统看门狗已经监测到系统似乎挂起就会触发,如果放回1时,表示继续等待,如果返回-1时,就让系统进行正常的自杀(这里的正常自杀,我的理解是系统自己主动自杀,该保存的数据先保存等然后就自杀,并不是因为其他原因导致的自杀)**/
 
    public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
        return true;
    }
 
    public boolean activityResuming(String pkg) throws RemoteException {
        return true;
    }
 
    public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace) throws RemoteException {
        return true;
    }
 
    public int appEarlyNotResponding(String processName, int pid, String annotation) throws RemoteException {
        return 0;
 
    }
 
    public int appNotResponding(String processName, int pid, String processStats) throws RemoteException {
 
        return 0;
 
    }
 
    public int systemNotResponding(String msg) throws RemoteException {
 
        return 0;
 
    }
 
}

以上方法只能系统级应用才能监听

深入理解IActivityController

一、研究背景

最近在学习别人代码,在代码中使用IActivityController.aidl统计设备中所有app的状态,包括activityStarting、activityResuming、appCrashed、appEarlyNotResponding、appNotResponding、systemNotResponding。后面我们会对这些方法单独介绍。发现这个写法很神奇,故想学习一下,这里对这个方法进行源码级分析,深入理解。

二、IActivityController.aidl简介

IActivityController.aidl是系统自带的aidl,在Am的内部类MyActivityController有实现这个aidl接口,主要用于app状态监听控制。对于应用开发者来说,此接口为给我们提供了各种可能性,比如统计每个app启动次数,crash次数等。这里我们先看下他的方法:

activityStarting:当系统正在启动一个activity时会触发,当返回true,表示允许启动。当返回状态noraml/false分别表示停止/拒绝启动activity
activityResuming:当系统正在返回一个activity时会触发,当返回true,表示允许返回。当返回状态noraml/false分别表示停止/拒绝返回activity
appCrashed:当一个应用进程已经崩溃会触发,当返回true时,表示可以重启,当返回false时,表示立即杀死它(进程)。
appEarlyNotResponding:当一鉴定为ANR时就很早触发;
appNotResponding:当一个应用进程出现ANR时就会触发,当返回0时,表示会弹出应用无响应的dialog,如果返回1时,表示继续等待,如果返回-1时,表示立即杀死进程。
systemNotResponding:当系统看门狗已经监测到系统似乎挂起就会触发,如果放回1时,表示继续等待,如果返回-1时,就让系统进行正常的自杀(这里的正常自杀,我的理解是系统自己主动自杀,该保存的数据先保存等然后就自杀,并不是因为其他原因导致的自杀)


三、系统内部IActivityController.class如何编译生成

我们应用开发知道aidl文件只有生成java或者class才可以给其他应用调用,由于系统在编译时就已经将IActivityController.aidl编译成IActivityController.class并打包到framework.jar中。先看下源码中如何将IActivityController.aidl编译的。 在源码的framworks/base/Android.mk中:
 

LOCAL_SRC_FILES += \
	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
	core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
	core/java/android/accounts/IAccountManager.aidl \
	core/java/android/accounts/IAccountManagerResponse.aidl \
	core/java/android/accounts/IAccountAuthenticator.aidl \
	core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
	core/java/android/app/IActivityContainer.aidl \
	core/java/android/app/IActivityContainerCallback.aidl \
	core/java/android/app/IActivityController.aidl \
	core/java/android/app/IActivityPendingResult.aidl \
	core/java/android/app/IAlarmManager.aidl \
	core/java/android/app/IAppTask.aidl \
	core/java/android/app/ITaskStackListener.aidl \
    ....................................................\
 
 
 
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
 
LOCAL_INTERMEDIATE_SOURCES := \
			$(framework_res_source_path)/android/R.java \
			$(framework_res_source_path)/android/Manifest.java \
			$(framework_res_source_path)/com/android/internal/R.java
 
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext
 
LOCAL_MODULE := framework
 
LOCAL_DX_FLAGS := --core-library --multi-dex
 
LOCAL_RMTYPEDEFS := true
 
include $(BUILD_JAVA_LIBRARY)
framework_module := $(LOCAL_INSTALLED_MODULE)
 
# Make sure that R.java and Manifest.java are built before we build
# the source for this library.
framework_res_R_stamp := \
	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
$(full_classes_compiled_jar): $(framework_res_R_stamp)
 
$(framework_module): | $(dir $(framework_module))framework-res.apk
 
framework_built := $(call java-lib-deps,framework)

文件中我们可以看到

core/java/android/app/IActivityController.aidl \
IActivityController.aidl已经被添加到LOCAL_SRC_FILES,接下来就被打包jar。

LOCAL_MODULE := framework
 
LOCAL_DX_FLAGS := --core-library --multi-dex
 
LOCAL_RMTYPEDEFS := true
 
include $(BUILD_JAVA_LIBRARY)
framework_module := $(LOCAL_INSTALLED_MODULE)
 

这里可以看到最终会被编译到framework.jar中去。

编译后framework.jar我们打开就会发现:

四、IActivityController源码分析

想要完全理解IActivityController的使用,必须要对源码有所了解,在源码中这四个类对其使用理解尤为重要,这四个类分别是IActivityManager、ActivityManagerProxy、ActivityManagerNative、ActivityManagerService。接下来先看下这四个类分别是做什么的。

IActivityManager:主要是提供Activity管理的一些接口


ActivityManagerProxy:是ActivityManagerNative的内部类,实现了IActivityManager的接口
ActivityManagerNative:其实是实现了IActivityManager.aidl的java文件,这里并不是自动生成的,而是按照aidl生成java格式写的,后面我们比较一下自动生成的java的。其他类通过集成此类就可以获取到响应的binder,然后对activity进行管理操作,我们从上图中也可以看到生成的class文件在framework.jar,也就是说系统级别的应用是可以调用到这个接口。
ActivityManagerService:继承了ActivityManagerNative类,通过ActivityManagerNative可以拿到binder。
1. ActivityManagerNative文件对比
先看下我自己实现的aidl文件
 

package com.tcl.myaidl;
interface IMyAidlInterface{
	void sayHello(String string);
	String getHello();
}

 再看下自动生成的java文件

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\MyAndroid\\MyAidlTestDemo\\src\\com\\tcl\\aidl\\IMyAidlInterface.aidl
 */
package com.tcl.aidl;
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.tcl.aidl.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.tcl.aidl.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.tcl.aidl.IMyAidlInterface interface,
 * generating a proxy if needed.
 */
public static com.tcl.aidl.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.tcl.aidl.IMyAidlInterface))) {
return ((com.tcl.aidl.IMyAidlInterface)iin);
}
return new com.tcl.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setHello:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setHello(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getHello:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getHello();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.tcl.aidl.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void setHello(java.lang.String string) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(string);
mRemote.transact(Stub.TRANSACTION_setHello, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String getHello() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getHello, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void setHello(java.lang.String string) throws android.os.RemoteException;
public java.lang.String getHello() throws android.os.RemoteException;
}
 

再看下ActivityManagerNative.java文件,这里只贴出一部分对比一下。

/** {@hide} */
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
 
        return new ActivityManagerProxy(obj);
    }
 
 
    ....................
    
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
            ..............
        }
    }
    
     public IBinder asBinder() {
        return this;
    }
 
    class ActivityManagerProxy implements IActivityManager
    {
        public ActivityManagerProxy(IBinder remote)
        {
            mRemote = remote;
        }
 
        public IBinder asBinder()
        {
            return mRemote;
        }
        ................
        
       private IBinder mRemote;
    }
}

对比后发现,这个ActivityManagerNative.java文件就是IActivityManager.aidl对应的文件,供外部调用的。

2. ActivityManagerNative.java分析

在ActivityManagerNative.java文件中有两个class,分别是ActivityManagerNative和ActivityManagerProxy:

ActivityManagerNative类,其主要实现是在service端实现的.
ActivityManagerProxy内部类主要是作为service端的一个代理类。
在这两个类中有这几个方法和变量尤为重要,ActivityManagerProxy类内部的asBinder() 和对象mRemote 、ActivityManagerNative类内部的onTransact()。

我们可以先看下ActivityManagerProxy的mRemote 其赋值在这里:

 public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }
外部其他类也可以通过以下方法获取mRemote :

 public IBinder asBinder()
    {
        return mRemote;
    }
mRemote 其实就是service的一个实体对象,当客户端拿到这个代理类内部的实体对象,就可以调用远程service的方法,主要方法有startActivity等等。

就拿startActivity举个列子,可以看到会调用mRemote发送一个消息START_ACTIVITY_TRANSACTION给service端:
 

 public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

在service端,有处理这个消息的方法,也就是onTransact() 。可以看下具体的如何处理的:

 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String callingPackage = data.readString();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            ProfilerInfo profilerInfo = data.readInt() != 0
                    ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
        }
            
    }

可以看到不同的消息有不同的处理方式,那我们回过头来想想我们主要是要看IActivityController如何调用的。
在onTransact() 方法中,我们恰好也看到这样一个消息:

        case SET_ACTIVITY_CONTROLLER_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IActivityController watcher = IActivityController.Stub.asInterface(
                    data.readStrongBinder());
            setActivityController(watcher);
            reply.writeNoException();
            return true;
        }

根据我们之前的分析,service端有处理这个信息的地方,相应的ActivityManagerProxy就有发送这个消息的地方:

  public void setActivityController(IActivityController watcher) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
        mRemote.transact(SET_ACTIVITY_CONTROLLER_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

IActivityController调用的地方找到的,处理的地方也找到了。

五、在ActivityManagerNative类内部有关IActivityController的消息是如何被处理的

回过头我们看下被处理的地方:

  case SET_ACTIVITY_CONTROLLER_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IActivityController watcher = IActivityController.Stub.asInterface(
                    data.readStrongBinder());
            setActivityController(watcher);
            reply.writeNoException();
            return true;
        }

我们看到service端会从data里面解析一个Binder,而且会通过IActivityController.Stub.asInterface()的方法,将此data中数据转换成IActivityController的一个Binder,这里也就是参数watcher,其实就是IActivityController的一个代理。

上述代码由将watcher对象作为参数传给setActivityController()方法,我们再追溯setActivityController(),发现其最终调用到ActivityManagerService的setActivityController()方法,也就是说最终使用IActivityController代理的地方是在ActivityManagerService类内部。 我们看下ActivityManagerService的setActivityController()方法源码:

    @Override
    public void setActivityController(IActivityController controller) {
        enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                "setActivityController()");
        synchronized (this) {
            mController = controller;
            Watchdog.getInstance().setActivityController(controller);
        }
    }

在ActivityManagerNative的获取到的对象watcher,最终会在ActivityManagerService类内部赋给mController和调用到Watchdog.getInstance().setActivityController()方法。看下Watchdog.java的源码:
 

  public void setActivityController(IActivityController controller) {
        synchronized (this) {
            mController = controller;
        }
    }

Watchdog也会将其赋给类内部的变量mController。

结论:在ActivityManagerNative类内部有关IActivityController的消息被处理后,最终会调用到ActivityManagerService和Watchdog。在ActivityManagerService类和Watchdog类内部都有获得一个IActivityController的代理对象

六、在ActivityManagerProxy类内部有关IActivityController的消息是被触发的

在ActivityManagerProxy类内部可以看到这样一个方法:

    public void setActivityController(IActivityController watcher) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
        mRemote.transact(SET_ACTIVITY_CONTROLLER_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

外部类拿到IActivityManager的代理类对象(也就是ActivityManagerProxy对象)时,通过该对象调用时,会发送一个消息SET_ACTIVITY_CONTROLLER_TRANSACTION给service端。

结论:综合来看,外部类拿到IActivityManager的代理对象,通过其代理对象的setActivityController()方法,会将IActivityControllerd代理对象序列化打包,发送到Binder通信的Service端,Service端接收到这消息,会将解析出IActivityControllerd代理对象,分别将其赋给ActivityManagerService和Watchdog类内部的mController的对象。

七、通过源码分析方式分析StartActivity如何触发IActivityController的activityStarting方法

我们先从Context.StartActivity看起,Context的StartActivity的源码:
 

    /**
     * Launch a new activity.  You will not receive any information about when
     * the activity exits.
     *
     * <p>Note that if this method is being called from outside of an
     * {@link android.app.Activity} Context, then the Intent must include
     * the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag.  This is because,
     * without being started from an existing Activity, there is no existing
     * task in which to place the new activity and thus it needs to be placed
     * in its own separate task.
     *
     * <p>This method throws {@link ActivityNotFoundException}
     * if there was no Activity found to run the given Intent.
     *
     * @param intent The description of the activity to start.
     * @param options Additional options for how the Activity should be started.
     * May be null if there are no options.  See {@link android.app.ActivityOptions}
     * for how to build the Bundle supplied here; there are no supported definitions
     * for building it manually.
     *
     * @throws ActivityNotFoundException &nbsp;
     *
     * @see #startActivity(Intent)
     * @see PackageManager#resolveActivity
     */
    public abstract void startActivity(Intent intent, @Nullable Bundle options);

我们可以看到这个方法是个虚方法,必须由子类实现,我们接下来看下子类ContextImpl的StartActivity的实现:

 
    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity)null, intent, -1, options);
    }
 

再继续追踪,我们会发现会调用到Instrumentation类的execStartActivity的方法:

 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options, UserHandle user) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options, user.getIdentifier());
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    }

我们可以看到这个方法可以调用到ActivityManagerNative.getDefault().startActivityAsUser()方法。先看下ActivityManagerNative.getDefault()的方法实现:

 /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
   private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

可以看到ActivityManagerNative.getDefault()得到的是IActivityManager代理对象,由于ActivityManagerNative是个虚类,具体的方法实现是在ActivityManagerService类中,上面我们看到调用到startActivityAsUser(),我们也可以猜到此方法具体实现是在ActivityManagerService类中:

 
    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, options, userId, null, null);
    }

继续追踪代码,发现调用的ActivityStackSupervisor类的startActivityMayWait()方法:

final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        boolean componentSpecified = intent.getComponent() != null;
 
        // Don't modify the client's object!
        intent = new Intent(intent);
 
        // Collect information about the target of the Intent.
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profilerInfo, userId);
 
        ActivityContainer container = (ActivityContainer)iContainer;
        synchronized (mService) {
            final int realCallingPid = Binder.getCallingPid();
            final int realCallingUid = Binder.getCallingUid();
            int callingPid;
            if (callingUid >= 0) {
                callingPid = -1;
            } else if (caller == null) {
                callingPid = realCallingPid;
                callingUid = realCallingUid;
            } else {
                callingPid = callingUid = -1;
            }
 
            final ActivityStack stack;
            if (container == null || container.mStack.isOnHomeDisplay()) {
                stack = getFocusedStack();
            } else {
                stack = container.mStack;
            }
            stack.mConfigWillChange = config != null
                    && mService.mConfiguration.diff(config) != 0;
            if (DEBUG_CONFIGURATION) Slog.v(TAG,
                    "Starting activity when config will change = " + stack.mConfigWillChange);
 
            final long origId = Binder.clearCallingIdentity();
            ...............................................
            ...........................................
            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options,
                    componentSpecified, null, container, inTask);
 
            Binder.restoreCallingIdentity(origId);
 
            .........................................
            ....................................
 
            return res;
        }
    }

上述代码中可以看到调用到startActivityLocked方法,追踪代码需要有点耐心。

 final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
            TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;
 
       
        ...............................
        .............................
 
        if (mService.mController != null) {
            try {
                // The Intent we give to the watcher has the extra data
                // stripped off, since it can contain private information.
                Intent watchIntent = intent.cloneFilter();
                abort |= !mService.mController.activityStarting(watchIntent,
                        aInfo.applicationInfo.packageName);
            } catch (RemoteException e) {
                mService.mController = null;
            }
        }
 
        ...............................
        .............................
        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);
 
        if (err < 0) {
            // If someone asked to have the keyguard dismissed on the next
            // activity start, but we are not actually doing an activity
            // switch...  just dismiss the keyguard now, because we
            // probably want to see whatever is behind it.
            notifyActivityDrawnForKeyguard();
        }
        return err;
    }

经过千辛万苦终于找到他了,我们发现此段代码中,我们找到了mController。这个变量应该不是很陌生吧,我们再看下是mController是哪个类内部的,我们会发现是ActivityManagerService类内部的变量。

结论:当我们通过外部类拿到IActivityManager的代理对象,通过其代理对象的setActivityController()后,此时ActivityManagerService和Watchdog会给类内部mController变量赋值,当调用Context.startActivity()方法是会调用到mController的activityStarting。此时实现了IActivityController接口的类就会回调此方法

注意:其他几个方法也是可以同样的方式追踪到,这里就不多介绍了

八、应用如何使用IActivityController

应用如果实现此接口,必须这个应用是个系统级别的应用,因为这些接口默认是hide。我们具体看下实现步骤:


1.自定义类实现IActivityController.Stub


由于IActivityController.class已经编译生成,并打包在framework.jar,故这里只需要继承IActivityController.Stub这个虚类,并实现其方法,由于其方法都被hide,这里需要手动添加这些方法:

class MyActivityController extends IActivityController.Stub {
		
		public boolean activityResuming(String pkgName) throws RemoteException {
			synchronized (this) {
//				SoftwareManagerLog.show(TAG, "Activity resuming: " + pkgName);
				if (prePackageName.equals(pkgName)) {
				} else {
					prePackageName = pkgName;
					// pushAppToHistory(curActivityInfo);
				}
			}
			return true;
		}
 
		public boolean activityStarting(Intent intent, String pkgName)
				throws RemoteException {
			synchronized (this) {
				ActivityInfo activityinfo = new ActivityInfo();
				activityinfo.setIntent(intent);
				activityinfo.setPackageName(pkgName);
				Log.i(TAG, "** Activity Starting: " + pkgName);
				Message message = new Message();
				message.what = ACTVITY_START;
				message.obj = activityinfo;
				mHandler.sendMessage(message);
			}
			return true;
		}
 
		public boolean appCrashed(String arg0, int arg1, String arg2,
				String arg3, long arg4, String arg5) throws RemoteException {
			return false;
		}
 
		public int appEarlyNotResponding(String arg0, int arg1, String arg2)
				throws RemoteException {
			return 0;
		}
 
		public int appNotResponding(String arg0, int arg1, String arg2)
				throws RemoteException {
			return 0;
		}
		
		public int systemNotResponding(String arg0) 
				throws RemoteException {
			return 0;
		}
 
	}
2.获取IActivityManager代理对象并调用setActivityController

mIActivityManager = ActivityManagerNative.getDefault();
mIActivityManager.setActivityController(new MyActivityController());
这里MyActivityController类对象就相当于一个回调,当有其他应用启动、崩溃等场景下,就会回调MyActivityController类

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值