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

news2025/1/22 9:25:33

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类

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pdf怎么转换成jpg图片?这几个方法值得一试

pdf怎么转换成jpg图片&#xff1f;PDF格式的文件在我们的日常生活和工作中十分常见&#xff0c;但有时候我们需要将PDF文件转换成图片格式&#xff0c;以便于在网页上进行展示或者存放到手机相册中。那么&#xff0c;PDF怎么转换成JPG图片呢&#xff1f;下面介绍几种方法。 第一…

Java【手撕滑动窗口】LeetCode 209. “长度最小子数组“, 图文详解思路分析 + 代码

文章目录 前言一、长度最小子数组1, 题目2, 思路分析3, 代码 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 &#x1f4d7; Java数据结构: 顺序表, 链…

达梦数据配置兼容

修改数据库实例的dm.ini配置文件COMPATIBLE_MODE的值&#xff0c;0:none, 1:SQL92, 2:Oracle, 3:MS SQL Server, 4:MySQL, 5:DM6, 6:Teradata, 7:PG COMPATIBLE_MODE 4 #Server compatible mode, 0:none, 1:SQL92, 2:Oracle, 3:MS SQL Server,…

如何设置让软件开机自启动

电脑重置&#xff0c;一些软件能正常使用&#xff0c;但是系统开机自启动选项中并没有它&#xff0c;这就很郁闷 有些极简软件没有直接设置开机自启&#xff0c;需要手动设置&#xff0c;所以我们就需要手动去设置一下它。 1、如果你在任务管理器里能找到它&#xff0c;也可以…

怎么把m4a转换成mp3?音频格式转换方法分享

M4A格式音频文件通常比MP3格式音频文件具有更高的音质&#xff0c;因为它使用了先进的编码技术。但是&#xff0c;M4A文件在某些设备上可能无法播放。将M4A文件转换为MP3格式可以增加音频文件的兼容性&#xff0c;并使其可以在更广泛的设备上播放&#xff0c;如移动设备和汽车音…

解决 git clone 时出现Failed to connect to 127.0.0.1 port 1573问题

今天去拉一个仓库代码&#xff0c;往常都是一下就拉下来了&#xff0c;今天却报错&#xff0c;报错信息如下&#xff1a; 原因&#xff1a;这种情况是因为代理在git中配置的&#xff0c;但是本身环境就有SSL协议了&#xff0c;所以取消git的https或者http代理即可 方法如下&…

文件夹无法删除?简单3招,轻松解决问题!

“我电脑里有一个文件夹占用了很大的内存&#xff0c;我想将它删除来释放一些内存&#xff0c;但是根本没法删除&#xff0c;为什么会这样呢&#xff1f;文件夹无法删除应该怎么办呢&#xff1f;” 在日常电脑使用中&#xff0c;有时候会遇到文件夹无法删除的情况&#xff0c;这…

iTween安装

1. 找到Package Manager面板&#xff0c;Packages选择MyAssets-右上角搜索iTween-找到后点DownLoad-点Import 导入 2. 导入后Assets面板结构如下图。 3. 编译器中输入iTween有提示&#xff0c;安装成功。

DataFrame.plot函数详解(六)

DataFrame.plot函数详解&#xff08;六&#xff09; 使用subplot()做子图&#xff0c;定位每一个子图&#xff0c;设置数据和图形&#xff0c;理解fig和ax(axs)的意义和作用。 1. subplot() matplotlib.pyplot.subplots(nrows1, ncols1, *, sharexFalse, shareyFalse, squee…

Spring Boot框架以及它的优势

文章目录 介绍1. **简化配置**2. **快速启动**3. **自动配置**4. **集成第三方库和框架**5. **微服务支持**6. **内嵌式数据库支持**7. **健康监控和管理**8. **可插拔的开发工具**9. **丰富的社区和生态系统**10. **良好的测试支持&#xff1a;** 核心特性**1. 依赖注入&#…

解决Python中的循环引用和内存泄漏问题

在Python编程中&#xff0c;循环引用和内存泄漏是两个常见的问题。本文将详细介绍如何识别和解决这些问题&#xff0c;并提供详细的代码示例。 1、什么是循环引用&#xff1f; 循环引用是指两个或多个对象之间相互引用的情况。这种情况可能导致内存泄漏&#xff0c;因为Python…

无涯教程-Android - 环境设置

您可以从Oracle的Java网站下载最新版本的Java JDK-Java SE下载&#xff0c;您将在下载的文件中找到有关安装JDK的说明,按照给定的说明安装和配置安装程序。最后,将PATH和JAVA_HOME环境变量设置为引用包含 java 和 javac 的目录,通常分别是java_install_dir/bin和java_install_d…

QML Book 学习基础3(动画)

目录 主要动画元素 例子&#xff1a; 非线性动画 分组动画 Qt 动画是一种在 Qt 框架下创建交互式和引人入胜的图形用户界面的方法&#xff0c;我们可以认为是对某个基础元素的多个设置 主要动画元素 PropertyAnimation-属性值变化时的动画 NumberA…

Sip分控管理主机 sip协议可视对讲话筒

Sip分控管理主机 sip协议可视对讲话筒 (型号:SV-3280) 产品特点 标准桌面主机&#xff0c;采用8寸高清IPS屏幕&#xff0c;屏幕分辨率1280*800&#xff0c;触摸控制设计&#xff0c;强化铝合金材质&#xff1b; 国产4核嵌入式CPU芯片1G内存&#xff0c;保证系统的整体稳定性&…

IPD集成产品开发进阶:新产品立项CDP流程

目录 前言 立项流程 专栏目录 CSDN学院 作者简介 前言 CDP 流程原本是 IPD 产品开发的前端流程。 之所以拿到《产品经理进阶专栏》中来讲解&#xff1a; 一是因为这个流程承接了市场管理&#xff08;也就是 MM 流程&#xff09;和产品开发这两个关键业务流。 这其实就…

《人生苦短,我学Python》——变量 常量 输入输出

今天&#xff0c;我们来学习变量&#xff0c;常量&#xff0c;以及字符串的输入输出。 文章目录 一、变量&#xff1a;二、常量&#xff1a;三、赋值&#xff1a;四、字符串的定义&#xff1a;五、格式化输出&#xff1a;六、转义字符&#xff1a;七、刷题练习&#xff1a;1. 小…

20 - 分页

分页相关方法 # paginate(当前页, 每页显示几条):分页;返回一个对象 pagination 模型类.query.order_by(-模型类.对象).paginate(page2, per_page3) print(pagination.items) # [<Article 2>, <Article 3>] :每页的数据对象 print(pagination.page) # 当前的页…

大势:从米哈游的估值远远超过B站说起

互联网怪盗团的新书《大势&#xff1a;站在十字路口的互联网行业》终于出版了。 本书的诞生堪称一波三折&#xff1a;差不多一年前&#xff0c;当我刚刚提笔时&#xff0c;想要撰写的是一本关于Web3及其对传统互联网平台影响的书。写到第三章时&#xff0c;ChatGPT横空出世&am…

常见前端面试之VUE面试题汇总十一

31. Vuex 有哪几种属性&#xff1f; 有五种&#xff0c;分别是 State、 Getter、Mutation 、Action、 Module state > 基本数据(数据源存放地) getters > 从基本数据派生出来的数据 mutations > 提交更改数据的方法&#xff0c;同步 actions > 像一个装饰器&a…

MyBatis 一个简单配置搞定加密、解密,不能太方便了~!TypeHandler

目录 一、背景 二、解决方案 三、需求 四、实现思路 五、实现代码 一、背景 在我们数据库中有些时候会保存一些用户的敏感信息&#xff0c;比如&#xff1a;手机号、银行卡等信息&#xff0c;如果这些信息以明文的方式保存&#xff0c;那么是不安全的。假如&#xff1a;黑客黑…