Android 實時監(jiān)聽APP進入前臺或后臺
前言
在我們開發(fā)的過程中,經(jīng)常會遇到需要我們判斷app進入后臺,或者切換到前臺的情況。比如我們想判斷app切換到前臺時,顯示一個解鎖界面,要求用戶輸入解鎖密碼才能繼續(xù)進行操作;我們想判斷app切換到后臺,記錄一下log;或者當用戶切換回前臺時,我們想刷新一下頁面的數(shù)據(jù)等等…
android里面監(jiān)聽app前后臺的方案很多(這還是得歸根于安卓提供了豐富的api和強大的架構(gòu)支撐,呵呵~),比如可以通過ActivityManager提供的getRunningAppProcesses()獲取系統(tǒng)當前運行的app,從而判斷app是否處于前臺?;蛘咄ㄟ^監(jiān)聽點擊Home鍵,判斷app是否回到了后臺。我列出下列幾種方案,推薦方案三
方案一 利用ActivityManager的RunningAppProcessInfo類
ActivityManager在整個系統(tǒng)里面起著非常重要的作用,主要為系統(tǒng)中運行著的activity交互提供接口,其中 RunningAppProcessInfo類 則封裝了正在運行著的進程信息,當然也包含了正在運行的app的包名,因此我們可以 activitymanager.getRunningAppProcesses() 獲取當前運行的app列表,對比自身的包名,來判斷本身app是否處于前臺運行。
這打斷一下,ActivityManager框架是Android系統(tǒng)十分重要的一部分,在以后有時間,筆者會好好學習整理ActivityManager框架的分析。
回到這里,下面給出部分關(guān)鍵代碼。
?
/**
* App前后臺狀態(tài)
*/
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設(shè)備中所有正在運行的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;
}
小結(jié): 通過ActivityManager來獲取當前運行的app列表,然后判斷我們的app是否處于前臺,能基本達到我們的預(yù)期需求。但如果將上面代碼放到每一個activity,或者activity基類里面,這消耗還是挺大的。而且而且,ActivityManager通過.getRunningAppProcesses()獲取當前運行列表這個方法,在5.0以后已經(jīng)被deprecated掉了…(心中萬馬奔騰…)
方案二:監(jiān)聽Home鍵點擊
說起home鍵的監(jiān)聽,也算是android里面的一個梗。這看上去簡單的功能,實際上實現(xiàn)起來卻十分的曲折,這跟android系統(tǒng)的設(shè)計有很大的關(guān)系。當home鍵被點擊的時候,會發(fā)出一個系統(tǒng)廣播,在系統(tǒng)收到這個廣播以后,會在framework層做一系列操作將當前的app退到后臺,然后把事件消費掉不傳給應(yīng)用層,所以這時候 onKeyDown事件也接收不到了…用官方的解釋就是 ——“Home key. This key is handled by the framework and is never delivered to applications.”。實際上這也是為了安全性的考慮,不然每家的app都監(jiān)聽home鍵,然后禁掉響應(yīng),不都成了流氓軟件了。
官方不支持,可是這難不到我們?nèi)f能的攻城獅們的,畢竟有很多想我們正規(guī)的開發(fā)者,還是需要監(jiān)聽home鍵來做一些如寫日志之類的操作的。網(wǎng)上谷歌百度會有很多類似的解決方案,在這里就不展開了。(不過這里可以推薦一下)
小結(jié): 我們能監(jiān)聽到home鍵點擊,當然就知道app處于前臺還是后臺了。但畢竟這個方案是基于官方不支持的前提下的,而且home鍵的監(jiān)聽在很多設(shè)備都會有兼容性的問題,因此我們不大推薦這樣做。
方案三:利用ActivityLifecycleCallbacks監(jiān)聽所有activity的生命周期
通過監(jiān)聽所有activity的onStart、onStop調(diào)用,然后統(tǒng)計當前是不是所有的activity都調(diào)用了onStop,確實可以判斷app處于了后臺,不過讓我們重寫每一個activity的onStop,并加這段奇怪的代碼,實在不大優(yōu)雅,即使在activity基類里面統(tǒng)一寫,那天如果忘了或者不需要繼承基類的activity,就不大好了。
因此,這里推薦一個新的接口ActivityLifecycleCallbacks,說新也不新,其實早在API 14 (android 4.0)就已經(jīng)推出了。ActivityLifecycleCallbacks接口在Application類里面,因此需要我們自己繼承Application,自定義一個MyApplication,然后注冊接口。ActivityLifecycleCallbacks為application提供了對所有activity生命周期的監(jiān)聽,因此我們通過重寫ActivityLifecycleCallbacks的onActivityStarted和onActivityStopped方法,定義一個變量,來統(tǒng)計當前有幾個activity處于前臺。
/**
* 當前Acitity個數(shù)
*/
private int activityAount = 0;
@Override
public void onCreate() {
......
registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
......
}
/**
* Activity 生命周期監(jiān)聽,用于監(jiān)控app前后臺狀態(tài)切換
*/
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里面。怎么樣,是不是很簡單快捷!而且準確無誤。
總結(jié):
1、利用ActivityManager的RunningAppProcessInfo類,直接粗暴,官方摒棄,不推薦;
2、監(jiān)聽Home鍵點擊,官方不支持,兼容性差,不穩(wěn)定,不推薦;
3、利用ActivityLifecycleCallbacks監(jiān)聽所有activity的生命周期,官方指定飲品,哦,不對,官方指定接口,大力推薦!我們舉一反三,利用ActivityLifecycleCallbacks監(jiān)聽,我們還能控制我們的activity堆棧,甚至還可以在里面做日志統(tǒng)計…想想還是很強大的。
PS:雖則利用ActivityLifecycleCallbacks接口監(jiān)聽的方案最優(yōu),但這畢竟是4.0以后的產(chǎn)品,因此對于4.0以下的,可以考慮增加方案一判斷。
這里提示一下
Android是在API14之后添加了用于監(jiān)聽應(yīng)用Activity生命周期的回調(diào)接口ActivityLifecycleCallbacks,使用時需要在Application中進行注冊。在Activity會報錯
所以使用時,請自行轉(zhuǎn)換或繼承Application類來注冊
參考:https://www.cnblogs.com/zhujiabin/p/9336663.html
Android實現(xiàn)APP啟動監(jiān)聽和攔截
有時候我們需要監(jiān)聽app的啟動,并在一定時間進行攔截,其實系統(tǒng)是有提供相應(yīng)的監(jiān)聽方法的
我們需要申明以下權(quán)限
?<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();
}
}
然后我們就去實現(xiàn)對應(yīng)回調(diào)方法
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:當系統(tǒng)正在啟動一個activity時會觸發(fā),當返回true,表示允許啟動。當返回狀態(tài)noraml/false分別表示停止/拒絕啟動activity
activityResuming:當系統(tǒng)正在返回一個activity時會觸發(fā),當返回true,表示允許返回。當返回狀態(tài)noraml/false分別表示停止/拒絕返回activity
appCrashed:當一個應(yīng)用進程已經(jīng)崩潰會觸發(fā),當返回true時,表示可以重啟,當返回false時,表示立即殺死它(進程)。
appEarlyNotResponding:當一鑒定為ANR時就很早觸發(fā);
appNotResponding:當一個應(yīng)用進程出現(xiàn)ANR時就會觸發(fā),當返回0時,表示會彈出應(yīng)用無響應(yīng)的dialog,如果返回1時,表示繼續(xù)等待,如果返回-1時,表示立即殺死進程。
systemNotResponding:當系統(tǒng)看門狗已經(jīng)監(jiān)測到系統(tǒng)似乎掛起就會觸發(fā),如果放回1時,表示繼續(xù)等待,如果返回-1時,就讓系統(tǒng)進行正常的自殺(這里的正常自殺,我的理解是系統(tǒng)自己主動自殺,該保存的數(shù)據(jù)先保存等然后就自殺,并不是因為其他原因?qū)е碌淖詺ⅲ?*/
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;
}
}
以上方法只能系統(tǒng)級應(yīng)用才能監(jiān)聽
深入理解IActivityController
一、研究背景
最近在學習別人代碼,在代碼中使用IActivityController.aidl統(tǒng)計設(shè)備中所有app的狀態(tài),包括activityStarting、activityResuming、appCrashed、appEarlyNotResponding、appNotResponding、systemNotResponding。后面我們會對這些方法單獨介紹。發(fā)現(xiàn)這個寫法很神奇,故想學習一下,這里對這個方法進行源碼級分析,深入理解。
二、IActivityController.aidl簡介
IActivityController.aidl是系統(tǒng)自帶的aidl,在Am的內(nèi)部類MyActivityController有實現(xiàn)這個aidl接口,主要用于app狀態(tài)監(jiān)聽控制。對于應(yīng)用開發(fā)者來說,此接口為給我們提供了各種可能性,比如統(tǒng)計每個app啟動次數(shù),crash次數(shù)等。這里我們先看下他的方法:
activityStarting:當系統(tǒng)正在啟動一個activity時會觸發(fā),當返回true,表示允許啟動。當返回狀態(tài)noraml/false分別表示停止/拒絕啟動activity
activityResuming:當系統(tǒng)正在返回一個activity時會觸發(fā),當返回true,表示允許返回。當返回狀態(tài)noraml/false分別表示停止/拒絕返回activity
appCrashed:當一個應(yīng)用進程已經(jīng)崩潰會觸發(fā),當返回true時,表示可以重啟,當返回false時,表示立即殺死它(進程)。
appEarlyNotResponding:當一鑒定為ANR時就很早觸發(fā);
appNotResponding:當一個應(yīng)用進程出現(xiàn)ANR時就會觸發(fā),當返回0時,表示會彈出應(yīng)用無響應(yīng)的dialog,如果返回1時,表示繼續(xù)等待,如果返回-1時,表示立即殺死進程。
systemNotResponding:當系統(tǒng)看門狗已經(jīng)監(jiān)測到系統(tǒng)似乎掛起就會觸發(fā),如果放回1時,表示繼續(xù)等待,如果返回-1時,就讓系統(tǒng)進行正常的自殺(這里的正常自殺,我的理解是系統(tǒng)自己主動自殺,該保存的數(shù)據(jù)先保存等然后就自殺,并不是因為其他原因?qū)е碌淖詺ⅲ?/p>
三、系統(tǒng)內(nèi)部IActivityController.class如何編譯生成
我們應(yīng)用開發(fā)知道aidl文件只有生成java或者class才可以給其他應(yīng)用調(diào)用,由于系統(tǒng)在編譯時就已經(jīng)將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已經(jīng)被添加到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我們打開就會發(fā)現(xiàn):
四、IActivityController源碼分析
想要完全理解IActivityController的使用,必須要對源碼有所了解,在源碼中這四個類對其使用理解尤為重要,這四個類分別是IActivityManager、ActivityManagerProxy、ActivityManagerNative、ActivityManagerService。接下來先看下這四個類分別是做什么的。
IActivityManager:主要是提供Activity管理的一些接口
ActivityManagerProxy:是ActivityManagerNative的內(nèi)部類,實現(xiàn)了IActivityManager的接口
ActivityManagerNative:其實是實現(xiàn)了IActivityManager.aidl的java文件,這里并不是自動生成的,而是按照aidl生成java格式寫的,后面我們比較一下自動生成的java的。其他類通過集成此類就可以獲取到響應(yīng)的binder,然后對activity進行管理操作,我們從上圖中也可以看到生成的class文件在framework.jar,也就是說系統(tǒng)級別的應(yīng)用是可以調(diào)用到這個接口。
ActivityManagerService:繼承了ActivityManagerNative類,通過ActivityManagerNative可以拿到binder。
1. ActivityManagerNative文件對比
先看下我自己實現(xiàn)的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;
}
}
對比后發(fā)現(xiàn),這個ActivityManagerNative.java文件就是IActivityManager.aidl對應(yīng)的文件,供外部調(diào)用的。
2. ActivityManagerNative.java分析
在ActivityManagerNative.java文件中有兩個class,分別是ActivityManagerNative和ActivityManagerProxy:
ActivityManagerNative類,其主要實現(xiàn)是在service端實現(xiàn)的.
ActivityManagerProxy內(nèi)部類主要是作為service端的一個代理類。
在這兩個類中有這幾個方法和變量尤為重要,ActivityManagerProxy類內(nèi)部的asBinder() 和對象mRemote 、ActivityManagerNative類內(nèi)部的onTransact()。
我們可以先看下ActivityManagerProxy的mRemote 其賦值在這里:
?public ActivityManagerProxy(IBinder remote)
? ? {
? ? ? ? mRemote = remote;
? ? }
外部其他類也可以通過以下方法獲取mRemote :
?public IBinder asBinder()
? ? {
? ? ? ? return mRemote;
? ? }
mRemote 其實就是service的一個實體對象,當客戶端拿到這個代理類內(nèi)部的實體對象,就可以調(diào)用遠程service的方法,主要方法有startActivity等等。
就拿startActivity舉個列子,可以看到會調(diào)用mRemote發(fā)送一個消息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如何調(diào)用的。
在onTransact()?方法中,我們恰好也看到這樣一個消息:
case SET_ACTIVITY_CONTROLLER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IActivityController watcher = IActivityController.Stub.asInterface(
data.readStrongBinder());
setActivityController(watcher);
reply.writeNoException();
return true;
}
根據(jù)我們之前的分析,service端有處理這個信息的地方,相應(yīng)的ActivityManagerProxy就有發(fā)送這個消息的地方:
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調(diào)用的地方找到的,處理的地方也找到了。
五、在ActivityManagerNative類內(nèi)部有關(guān)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中數(shù)據(jù)轉(zhuǎn)換成IActivityController的一個Binder,這里也就是參數(shù)watcher,其實就是IActivityController的一個代理。
上述代碼由將watcher對象作為參數(shù)傳給setActivityController()方法,我們再追溯setActivityController(),發(fā)現(xiàn)其最終調(diào)用到ActivityManagerService的setActivityController()方法,也就是說最終使用IActivityController代理的地方是在ActivityManagerService類內(nèi)部。 我們看下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類內(nèi)部賦給mController和調(diào)用到Watchdog.getInstance().setActivityController()方法??聪耊atchdog.java的源碼:
?
public void setActivityController(IActivityController controller) {
synchronized (this) {
mController = controller;
}
}
Watchdog也會將其賦給類內(nèi)部的變量mController。
結(jié)論:在ActivityManagerNative類內(nèi)部有關(guān)IActivityController的消息被處理后,最終會調(diào)用到ActivityManagerService和Watchdog。在ActivityManagerService類和Watchdog類內(nèi)部都有獲得一個IActivityController的代理對象
六、在ActivityManagerProxy類內(nèi)部有關(guān)IActivityController的消息是被觸發(fā)的
在ActivityManagerProxy類內(nèi)部可以看到這樣一個方法:
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對象)時,通過該對象調(diào)用時,會發(fā)送一個消息SET_ACTIVITY_CONTROLLER_TRANSACTION給service端。
結(jié)論:綜合來看,外部類拿到IActivityManager的代理對象,通過其代理對象的setActivityController()方法,會將IActivityControllerd代理對象序列化打包,發(fā)送到Binder通信的Service端,Service端接收到這消息,會將解析出IActivityControllerd代理對象,分別將其賦給ActivityManagerService和Watchdog類內(nèi)部的mController的對象。
七、通過源碼分析方式分析StartActivity如何觸發(fā)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
*
* @see #startActivity(Intent)
* @see PackageManager#resolveActivity
*/
public abstract void startActivity(Intent intent, @Nullable Bundle options);
我們可以看到這個方法是個虛方法,必須由子類實現(xiàn),我們接下來看下子類ContextImpl的StartActivity的實現(xiàn):
@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);
}
再繼續(xù)追蹤,我們會發(fā)現(xiàn)會調(diào)用到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;
}
我們可以看到這個方法可以調(diào)用到ActivityManagerNative.getDefault().startActivityAsUser()方法。先看下ActivityManagerNative.getDefault()的方法實現(xiàn):
/**
* 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是個虛類,具體的方法實現(xiàn)是在ActivityManagerService類中,上面我們看到調(diào)用到startActivityAsUser(),我們也可以猜到此方法具體實現(xiàn)是在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);
}
繼續(xù)追蹤代碼,發(fā)現(xiàn)調(diào)用的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;
}
}
上述代碼中可以看到調(diào)用到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;
}
經(jīng)過千辛萬苦終于找到他了,我們發(fā)現(xiàn)此段代碼中,我們找到了mController。這個變量應(yīng)該不是很陌生吧,我們再看下是mController是哪個類內(nèi)部的,我們會發(fā)現(xiàn)是ActivityManagerService類內(nèi)部的變量。
結(jié)論:當我們通過外部類拿到IActivityManager的代理對象,通過其代理對象的setActivityController()后,此時ActivityManagerService和Watchdog會給類內(nèi)部mController變量賦值,當調(diào)用Context.startActivity()方法是會調(diào)用到mController的activityStarting。此時實現(xiàn)了IActivityController接口的類就會回調(diào)此方法
注意:其他幾個方法也是可以同樣的方式追蹤到,這里就不多介紹了
八、應(yīng)用如何使用IActivityController
應(yīng)用如果實現(xiàn)此接口,必須這個應(yīng)用是個系統(tǒng)級別的應(yīng)用,因為這些接口默認是hide。我們具體看下實現(xiàn)步驟:
1.自定義類實現(xiàn)IActivityController.Stub
由于IActivityController.class已經(jīng)編譯生成,并打包在framework.jar,故這里只需要繼承IActivityController.Stub這個虛類,并實現(xiàn)其方法,由于其方法都被hide,這里需要手動添加這些方法:文章來源:http://www.zghlxwxcb.cn/news/detail-679037.html
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代理對象并調(diào)用setActivityController
mIActivityManager = ActivityManagerNative.getDefault();
mIActivityManager.setActivityController(new MyActivityController());
這里MyActivityController類對象就相當于一個回調(diào),當有其他應(yīng)用啟動、崩潰等場景下,就會回調(diào)MyActivityController類文章來源地址http://www.zghlxwxcb.cn/news/detail-679037.html
到了這里,關(guān)于Android實現(xiàn)監(jiān)聽APP啟動、前臺和后臺的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!