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;@Overrideprotected void onResume() {......if (isForeground == false) {//由后台切换到前台isForeground = true;}}@Overrideprotected 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;@Overridepublic void onCreate() {......registerActivityLifecycleCallbacks(activityLifecycleCallbacks);......}/*** Activity 生命周期监听,用于监控app前后台状态切换*/ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {}@Overridepublic void onActivityStarted(Activity activity) {
//            if (activityAount == 0) {
//                //app回到前台
//                isForeground = true;
//            }activityAount++;}@Overridepublic void onActivityResumed(Activity activity) {}@Overridepublic void onActivityPaused(Activity activity) {}@Overridepublic void onActivityStopped(Activity activity) {activityAount--;if (activityAount == 0) {isForeground = false;}}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Overridepublic 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分别表示停止/拒绝启动activityactivityResuming:当系统正在返回一个activity时会触发,当返回true,表示允许返回。当返回状态noraml/false分别表示停止/拒绝返回activityappCrashed:当一个应用进程已经崩溃会触发,当返回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.javaLOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle extLOCAL_MODULE := frameworkLOCAL_DX_FLAGS := --core-library --multi-dexLOCAL_RMTYPEDEFS := trueinclude $(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.apkframework_built := $(call java-lib-deps,framework)

文件中我们可以看到

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

LOCAL_MODULE := frameworkLOCAL_DX_FLAGS := --core-library --multi-dexLOCAL_RMTYPEDEFS := trueinclude $(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);}....................@Overridepublic 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()方法源码:

    @Overridepublic 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的实现:

 @Overridepublic 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类中:

 @Overridepublic 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 descriptorsif (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类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/110040.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同子网络中的通信过程

从输入www.baidu.com经历了什么 一、DNS&#xff08;网址->IP&#xff09; 二、ARP&#xff08;IP->MAC&#xff09; A->B&#xff1a;有数据发送&#xff0c;数据封装ip之后发现没有主机B的mac地址。然后ARP在本网段广播&#xff1a;检查目标地址和源地址是否在同一…

抖音矩阵,矩阵账号开发,抖音矩阵源码搭建

抖音矩阵&#xff0c;矩阵账号开发&#xff0c;抖音矩阵源码搭建&#xff1a; 1、账号矩阵系统搭建首先需要注意的是支持多平台&#xff0c;多账号&#xff0c;可以实现流量互通&#xff0c;账号矩阵多个账号联动形成账号矩阵形式分发开发。 2、账号矩阵系统需要可以查看分发…

Node与Express后端架构:高性能的Web应用服务

在现代Web应用开发中&#xff0c;后端架构的性能和可扩展性至关重要。Node.js作为一个基于事件驱动、非阻塞I/O的平台&#xff0c;以及Express作为一个流行的Node.js框架&#xff0c;共同构建了高性能的Web应用服务。 在本文中&#xff0c;我们将深入探讨Node与Express后端架构…

基于梯度算法优化的BP神经网络(预测应用) - 附代码

基于梯度算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于梯度算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.梯度优化BP神经网络2.1 BP神经网络参数设置2.2 梯度算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

Bootstrap 源代码目录结构一览

目录 前言 Bootstrap 目录结构 Bootstrap 内容简介 Bootstrap 编译文件 CSS文件 | CSS 文件功能对比与清单 JS文件 | JS 文件功能对比与清单 Bootstrap 源码码目录 | 资源清单 前言 Bootstrap是Twitter推出的一个用于前端开发的开源工具包。它由Twitter的设计师Mark Ot…

Visual Studio编译出来的程序无法在其它电脑上运行

在其它电脑&#xff08;比如Windows Server 2012&#xff09;上运行Visual Studio编译出来的应用程序&#xff0c;结果报错&#xff1a;“无法启动此程序&#xff0c;因为计算机中丢失VCRUNTIME140.dll。尝试重新安装该程序以解决此问题。” 解决方法&#xff1a; 属性 -> …

ConsoleApplication815项目(直接加载+VEH Hook Load)

上线图 ConsoleApplication815.cpp #include <iostream> #include<Windows.h> #include "detours.h" #include "detver.h" #pragma comment(lib,"detours.lib")#pragma warning(disable:4996)LPVOID Beacon_address; SIZE_T Beacon…

机器视觉工程师永不为奴,他们是肯干肯出差肯加班肯拼命肯被使唤肯被叼

​ 永不为奴&#xff0c;为什么这样呐喊&#xff0c;真的很难做到。我们职业机器视觉工程师&#xff0c;本身职业具有一大特点就是专业性。 但是我们机器视觉工程师是专业技术绝不苟同于不是技术人员言语&#xff0c;我们很专业。 肯出差&#xff1a; 设备去那里&#xff0c;…

C#,《小白学程序》第七课:列表(List)应用之一“编制高铁车次信息表”

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…

【优化算法】Python实现面向对象的遗传算法

遗传算法 遗传算法(Genetic Algorithm)属于智能优化算法的一种&#xff0c;本质上是模拟自然界中种群的演化来寻求问题的最优解。与之相似的还有模拟退火、粒子群、蚁群等算法。 在具体介绍遗传算法之前&#xff0c;我们先来了解一些知识&#x1f9c0; DNA&#xff1a; 携带有…

性能优化之分库分表

1、什么是分库分表 1.1、分表 将同一个库中的一张表&#xff08;比如SPU表&#xff09;按某种方式&#xff08;垂直拆分、水平拆分&#xff09;拆分成SPU1、SPU2、SPU3、SPU4…等若干张表&#xff0c;如下图所示&#xff1a; 1.2、分库 在表数据不变的情况下&#xff0c;对…

图像处理 信号处理板 设计原理图:367-基于zynq XC7Z100 FMC接口通用计算平台

基于zynq XC7Z100 FMC接口通用计算平台 一、板卡概述 板卡由SoC XC7Z100-2FFG900I芯片来完成卡主控及数字信号处理&#xff0c;XC7Z100内部集成了两个ARM Cortex-A9核和一个kintex 7的FPGA&#xff0c;通过PL端FPGA扩展FMC、光纤、IO等接口&#xff0c;PS端ARM扩展网络、USB、R…

exchange实战

未得到exchange服务器权限 确定exchange服务器ip地址 setspn -T example.domain.com -F -Q */* | findstr exchangeMailSniper 爆破用户名和密码 爆破Exchange邮箱用户名密码&#xff0c;为了防止账号被锁定&#xff0c;所以我们使用密码喷洒攻击&#xff0c;即只使用一个密…

dolphinschedule配置企微告警服务(WeChat群组)

一、前置说明 ds配置好工作流后&#xff0c;比较重要的一个就是上线后的监控报警服务&#xff0c;如果你是基于企微作为协同办公的&#xff0c;WeChat群组预警必须是要安排上的&#xff0c;文章基于自建应用配合群组方式构建预警群&#xff0c;接入后&#xff0c;任务成功或者…

sql:SQL优化知识点记录(五)

&#xff08;1&#xff09;explain之例子 &#xff08;2&#xff09;索引单表优化案例 上面的功能已经实现&#xff0c;但是分析功能&#xff0c; 使用explain分析这条sql&#xff1a; 发现type为All Extra&#xff1a;有Using filesort &#xff08;文件内排序&#xff09; 这…

创作2周年纪念日-特别篇

创作2周年纪念日-特别篇 1. 与CSDN的机缘2. 收获3. 憧憬 1. 与CSDN的机缘 很荣幸&#xff0c;在大学时候&#xff0c;能够接触到CSDN这样一个平台&#xff0c;当时对嵌入式开发、编程、计算机视觉等内容比较感兴趣。后面一个很偶然的联培实习机会&#xff0c;让我接触到了Pych…

浅析Linux SCSI子系统:设备管理

文章目录 概述设备管理数据结构scsi_host_template&#xff1a;SCSI主机适配器模板scsi_host&#xff1a;SCSI主机适配器主机适配器支持DIF scsi_target&#xff1a;SCSI目标节点scsi_device&#xff1a;SCSI设备 添加主机适配器构建sysfs目录 添加SCSI设备挂载LunIO请求队列初…

基于Spring Boot的软件缺陷追踪系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的软件缺陷追踪系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spri…

【HTML】基础语法讲解

基础语法 1. HTML 结构1.1 认识HTML标签1.2 HTML 文件基本结构1.3 标签层次结构1.4 快速生成代码框架 2. HTML 常见标签2.1 注释标签2.2 标题标签:h1-h62.3 段落标签:p2.4 <br>换行标签2.5 格式化标签2.6 图片标签&#xff1a;img2.7 超链接标签&#xff1a;a2.8 表格标签…

Flutter问题记录 - Unable to find bundled Java version

新版本的Android Studio真的移除了JRE&#xff0c;jre目录找不到&#xff0c;怪不得报错了&#xff0c;不过多了一个jbr目录&#xff0c;找了个以前的Android Studio版本对比 搜了一下jbr&#xff08;JetBrains Runtime&#xff09;&#xff0c;原来IDEA老早就开始用了&#xf…