前言
-
目标
有些时候Android应用在没有Activity实例的情况下,需要回调Activity生命周期及权限、ActivityResult以及屏幕旋转事件。生命周期回调可以从Application着手,可以采用如下方式
Application application = activity.getApplication();
ActivityLifecycleCallbacksImpl activityLifecycleCallbacks = new ActivityLifecycleCallbacksImpl();
application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
public class ActivityLifecycleCallbacksImpl implements Application.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
}
屏幕旋转事件如何捕获呢,这就是我要跟大家分享的内容了。
实现
1.了解onConfigurationChanged实现过程
通过查看源码发现,屏幕旋转的实现主要涉及Activity、ActivityThread两个类。Activity中onConfigurationChanged方法如下
public void onConfigurationChanged(@NonNull Configuration newConfig) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onConfigurationChanged " + this + ": " + newConfig);
mCalled = true;
mFragments.dispatchConfigurationChanged(newConfig);
if (mWindow != null) {
// Pass the configuration changed event to the window
mWindow.onConfigurationChanged(newConfig);
}
if (mActionBar != null) {
// Do this last; the action bar will need to access
// view changes from above.
mActionBar.onConfigurationChanged(newConfig);
}
}
那Activity中这个方法是谁来调用呢,在ActivityThread这个类中找到了结果
private Configuration performActivityConfigurationChanged(Activity activity,
Configuration newConfig, Configuration amOverrideConfig, int displayId,
boolean movedToDifferentDisplay) {
......
......
if (shouldChangeConfig) {
activity.mCalled = false;
activity.onConfigurationChanged(configToReport);
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
" did not call through to super.onConfigurationChanged()");
}
}
return configToReport;
}
@Override
public void handleActivityConfigurationChanged(IBinder activityToken,
Configuration overrideConfig, int displayId) {
......
......
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
ComponentCallbacks2 cb = callbacks.get(i);
if (cb instanceof Activity) {
// If callback is an Activity - call corresponding method to consider override
// config and avoid onConfigurationChanged if it hasn't changed.
Activity a = (Activity) cb;
performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
config);
} else if (!equivalent) {
performConfigurationChanged(cb, config);
}
}
}
}
@Override
public void handleConfigurationChanged(Configuration config) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = config.densityDpi;
mUpdatingSystemConfig = true;
try {
handleConfigurationChanged(config, null /* compat */);
} finally {
mUpdatingSystemConfig = false;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,
Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) {
r.tmpConfig.setTo(newBaseConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,
r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
return reportedConfig;
}
private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
......
......
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
ComponentCallbacks2 cb = callbacks.get(i);
if (cb instanceof Activity) {
// If callback is an Activity - call corresponding method to consider override
// config and avoid onConfigurationChanged if it hasn't changed.
Activity a = (Activity) cb;
performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
config);
} else if (!equivalent) {
performConfigurationChanged(cb, config);
}
}
}
}
class H extends Handler {
......
......
public void handleMessage(Message msg) {
......
......
case CONFIGURATION_CHANGED:
handleConfigurationChanged((Configuration) msg.obj);
break;
......
}
}
}
......
......
final H mH = new H();
2.通过上面的源码追溯,我们发现onConfigurationChanged(Configuration newConfig)方法是ActivityThread的Handler下触发的,OK,我们可以获取当前的ActivityThread,而后替换mH为自己的,这样我们就可以获取到系统下发的消息了。
public void process(Activity activity) {
this.mActivity = activity;
try {
Handler mH = getHandlerFormActivityThread();
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, mHCallback);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取ActivityThread.class
private Class<?> getActivityThreadClazz() throws ClassNotFoundException {
return Class.forName("android.app.ActivityThread");
}
// 先获取到当前的ActivityThread对象
private Object getCurrentActivityThread() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Field currentActivityThreadField = getActivityThreadClazz().getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
return currentActivityThreadField.get(null);
}
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
private Handler getHandlerFormActivityThread() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Field mHField = getActivityThreadClazz().getDeclaredField("mH");
mHField.setAccessible(true);
return (Handler) mHField.get(getCurrentActivityThread());
}
private boolean isHostActivity() {
ActivityManager am = (ActivityManager) mActivity.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Log.d(TAG, mActivity.getClass().getName() + "/" + cn.getClassName());
return (mActivity.getClass().getName()).equals(cn.getClassName());
}
实现Handler接收系统消息,拦截屏幕旋转事件。
private Handler.Callback mHCallback = new Handler.Callback() {
private final static int CONFIGURATION_CHANGED = 118; //CONFIGURATION_CHANGED
@Override
public boolean handleMessage(Message msg) {
//过滤其他Activity消息
if (!isHostActivity()) {
return false;
}
if (msg.what == CONFIGURATION_CHANGED) {
onConfigurationChanged(msg);
}
return false;
}
private void onConfigurationChanged(Message msg) {
ResultCallback.getInstance().onConfigurationChanged(mActivity, (Configuration) msg.obj);
}
}
以上是通过Hook方式拦截到屏幕旋转事件,如果大家有更好的方法欢迎留言,后面我会把如果通过hook方式拦截onActivityResult和onRequestPermissionsResult分享出来。谢谢观看~
网友评论