所谓的实践出真知,没有理论的实践是瞎忙,没有实践的理论是纸上谈兵,只有学习了理论,用实践验证理论—非常有用的一句话。
涉及到的关键类:ActivityThread,ContextImp,ActivityManager
涉及到的技术:activity启动流程,hook(也就是动态代理),java反射,AMS的交互过程
ActivityThread位于android.app包下,他是安卓应用的真正入口,以下代码拷贝自android SDK 26中,去掉了注释部分
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
//准备ui线程的Looper,这也是为什么我们平时可以直接在ui线程中使用handler的原因,而子线程中必须手工进行初始化。
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
//就是mH,它是一个handler
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始loop,这里面就开始无限循环了,如果这里结束了那么就表示整个应用程序结束了
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
通过这段代码我们可以得到几个知识点,简单进行下总结:
安卓应用程序的入口是什么?
ActivityThread?Application?Activity? 想必大家也都知道答案了吧,真正的入口只有一个,就是ActivityThread
为什么在子线程中需要手工进行Looper的初始化?
因为在ui线程中,系统已经默认帮我们初始化了,而在子线程中必须手工进行初始化,可以参考HandlerThread
安卓不能在ui线程中执行耗时操作,但是在程序的入口处却开始了无限循环,这种现象如何解释?
准确的说不能再ui线程中进行耗时操作,指的是四大组件中的生命周期内不能进行耗时操作。而Looper的无限循环是Handler异步通讯的基础,不能结束,如果结束了,就代表应用程序结束了。ANR的异常检查是在AMS中进行校验的,可以参考 http://blog.csdn.net/vrix/article/details/54134124
安卓四大组件运行时的上下文环境,以下代码拷贝自android SDK 26中,去掉了注释部分
代码路径为ActivityThread.handleLaunchActivity() -> performLaunchActivity()
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
//开始创建ContextImp
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
......//忽略了好多代码
return activity;
}
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId;
try {
displayId = ActivityManager.getService().getActivityDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
//通过一个静态方法创建的ContextImp的
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
//开始实例化上下文
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
这个主要涉及到AMS交互,请参考这篇博客,作者写的确实很棒
http://blog.csdn.net/bjp000111/article/details/52232041
熟悉了上面的基础知识后并对activity的启动流程有一定的了解,就可以开始动手实践了。
坑位埋点是360公司最早提出来的一种解决方案,他们在插件化技术领域研究了好多年,是非常专业的,当然他们也开源了大名鼎鼎的RePlugin插件化框架 RePlugin
本次实践也是借鉴坑位思想实现的,下面开始逐步实现。
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//在程序入口处进行初始化工作,也就是hook
Plugin.init();
}
}
public class Plugin {
public static void init(){
try {
//反射获取ams并设置代理,兼容高低版本api差异的问题
Object singletonObject = null;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
Field iActivityManagerSingletonField = Class.forName("android.app.ActivityManagerNative").getDeclaredField("gDefault");
iActivityManagerSingletonField.setAccessible(true);
singletonObject = iActivityManagerSingletonField.get(null);
}else{
Field iActivityManagerSingletonField = Class.forName("android.app.ActivityManager").getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingletonField.setAccessible(true);
singletonObject = iActivityManagerSingletonField.get(null);
}
Field mInstanceField = Class.forName("android.util.Singleton").getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object iActivityManagerObject = mInstanceField.get(singletonObject);
Class> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
PluginInvocationHandler handler = new PluginInvocationHandler(iActivityManagerObject);
//开始动态代理,也就是hook
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{IActivityManagerIntercept}, handler);
mInstanceField.set(singletonObject, proxy);
//反射获取activityThread中的mH
Field sCurrentActivityThread = Class.forName("android.app.ActivityThread").getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread.setAccessible(true);
Object activityThread = sCurrentActivityThread.get(null);
Field mH = activityThread.getClass().getDeclaredField("mH");
mH.setAccessible(true);
Handler mHobject = (Handler) mH.get(activityThread);
Field mCallback = Handler.class.getDeclaredField("mCallback");
mCallback.setAccessible(true);
//利用handler的特殊机制,开始进行半路拦截
mCallback.set(mHobject,new PluginCallback());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public class PluginInvocationHandler implements InvocationHandler {
public static final String OLD_INTENT_FLAG = "old_intent_flag";
Object activityManagerImp;
public PluginInvocationHandler(Object activityManagerImp) {
this.activityManagerImp = activityManagerImp;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if(name.equals("startActivity")){//替换坑位的activity
Intent oldIntent = (Intent) args[2];
ComponentName componentName = new ComponentName("com.example.administrator.myapplication", StubActivity.class.getName());
Intent newIntent = new Intent();
newIntent.setComponent(componentName);
newIntent.putExtra(OLD_INTENT_FLAG,oldIntent);
args[2] = newIntent;
}else if(name.equals("startService")){//替换坑位的service
Intent oldIntent = (Intent) args[1];
ComponentName componentName = new ComponentName("com.example.administrator.myapplication", StubService.class.getName());
Intent newIntent = new Intent();
newIntent.setComponent(componentName);
newIntent.putExtra(OLD_INTENT_FLAG,oldIntent);
args[1] = newIntent;
}
return method.invoke(activityManagerImp,args);
}
}
public class PluginCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
if(msg.what == 100){
processStartActivity(msg);
}else if(msg.what == 114){
processCreateService(msg);
}
return false;
}
/**
* 换回原来的activity
* @param msg
*/
private void processStartActivity(Message msg){
try {
Object obj = msg.obj;
Field field = obj.getClass().getDeclaredField("intent");
field.setAccessible(true);
Intent intent = (Intent) field.get(obj);
Intent oldIntent = intent.getParcelableExtra(PluginInvocationHandler.OLD_INTENT_FLAG);
if(oldIntent != null){
ComponentName component = oldIntent.getComponent();
intent.setComponent(component);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
/**
* 换回原来的service
* @param msg
*/
private void processCreateService(Message msg){
try {
Object obj = msg.obj;
Field field = obj.getClass().getDeclaredField("info");
field.setAccessible(true);
ServiceInfo serviceInfo = (ServiceInfo) field.get(obj);
serviceInfo.name = TestService.class.getName();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
完成以上工作就开始对demo进行测试了,下面代码中TestActivity和TestService均未在清单文件进行注册,我们通过点击按钮开始进行界面的跳转
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(v.getContext(),TestActivity.class));
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(new Intent(v.getContext(),TestService.class));
}
});
}
}