Activity
生命周期
// public static void main
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
[ActivityThread].attach()
[ActivityManagerService].attachApplication()
[ActivityManagerService].attachApplicationLocked()
[ActivityThread].bindApplication()
[ActivityThread].sendMessage(H.BIND_APPLICATION, data);
// --> 然后ActivityThread中的Handler处理这条消息
[ActivityThread].handleBindApplication()
Application app;
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
cl.loadClass(className).newInstance() // 反射创建app实例
app.attach(context)
instrumentation.callApplicationOnCreate(app);
app.onCreate();
[ActivityThread].installContentProviders()
// 遍历Manifest中申明的所有的 ContentProvider 组件
for (ProviderInfo cpi : providers) {
ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/)
// installProvider函数作用:实例化ContentProvider,并调用onCreate方法
// ContentProvider localProvider = null;
// localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);
// localProvider.attachInfo(c, info);
// ContentProvider.this.onCreate();
}
Looper.loop();
schedulePauseActivity
ClientLifecycleManager.scheduleTransaction
ClientTransactionHandler.sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
处理handler消息
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
TransactionExecutor.execute
executeLifecycleState
// Cycle to the state right before the final requested state.
cycleToPath
TransactionExecutor.performLifecycleSequence
定义了activity的自动流程
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
ClientTransaction transaction) {
final int size = path.size();
for (int i = 0, state; i < size; i++) {
state = path.get(i);
if (DEBUG_RESOLVER) {
Slog.d(TAG, tId(transaction) + "Transitioning activity: "
+ getShortActivityName(r.token, mTransactionHandler)
+ " to state: " + getStateName(state));
}
switch (state) {
case ON_CREATE:
mTransactionHandler.handleLaunchActivity(r, mPendingActions,
Context.DEVICE_ID_INVALID, null /* customIntent */);
break;
case ON_START:
mTransactionHandler.handleStartActivity(r, mPendingActions,
null /* activityOptions */);
break;
case ON_RESUME:
mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
r.isForward, false /* shouldSendCompatFakeFocus */,
"LIFECYCLER_RESUME_ACTIVITY");
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r, false /* finished */,
false /* userLeaving */, 0 /* configChanges */,
false /* autoEnteringPip */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r, false /* start */);
break;
default:
throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
}
}
}
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
// 下面的activity执行顺序是最关键的
ActivityThread.handleLaunchActivity
mConfigurationController.handleConfigurationChanged(null, null)
performConfigurationChanged(cb, config)
onConfigurationChanged(configToReport)
ActivityThread.performLaunchActivity
// 反射创建activity对象
mInstrumentation.newActivity
// androidx.fragment.app.FragmentActivity
// 绑定一个fragment manager,用来管理fragment
// FragmentController mFragments = FragmentController.createController(new HostCallbacks()); // HostCallbacks是FragmentHostCallback的实现类
反射创建Applicatin实例,并调用app.attach(context);app.onCreate()
activity.attach()
PhoneWindow初始化
WindowManagerImpl初始化
mInstrumentation.callActivityOnCreate
activity.performCreate
activity.onCreate()
mFragments.dispatchCreate()
mFragmentManager(FragmentManagerImpl).dispatchCreate();
fragment.onCreateView(layoutInflater,container)
layoutInflater.inflat的时候,会触发activity的onCreateView,传递到fragmentmanager的onCreateView,这里面findFragmentById(id)然后把fragment添加到fragmentmanager中统一管理
container.addView // 这个container就是我们给fragment设置的容器,例如xml中的<fragment>
fragment.onViewCreated()
androidx.activity.ComponentActivity->ReportFragment.injectIfNeededIn(this)
mFragments.dispatchActivityCreated();
setContent()
PhoneWindow.installDecor()
DecorView初始化
// generateLayout:这里把Manifest或者setContent方法之前调用setFlags设置的window属性,设置到decorView中
// 比如windowBackground,windowFullscreen...
// activity冷启动白屏就是在这里设置的,activity默认的背景颜色就是在Theme里面设置的
generateLayout()
ActivityThread.handleStartActivity
activity.performStart("handleStartActivity");
activity.onStart
mFragments.dispatchStart();
mInstrumentation.callActivityOnRestoreInstanceState
mInstrumentation.callActivityOnPostCreate
updateVisibility(r, true /* show */) // 页面可见,显示白色的decorview
ActivityThread.handleResumeActivity
performResumeActivity
deliverNewIntents
mInstrumentation.callActivityOnNewIntent(r.activity, intent);
deliverResults
activity.dispatchActivityResult
activity.performResume(r.startsNotResumed, reason);
mFragments.execPendingActions();
mInstrumentation.callActivityOnResume(this);
activity.onResume();
mFragments.dispatchResume();
mFragments.execPendingActions();
onPostResume();
decor = window.getDecorView()
if(mDecor==null) installDecor()
decor.setVisibility(View.INVISIBLE);// decor设置不可见
WindowManagerImpl.addView->WindowManagerGlobal.addView(decor)
root = new ViewRootImpl(view.getContext(), display)
BaseSurfaceHolder mSurfaceHolder;
Surface mSurface = new Surface();
SurfaceControl mSurfaceControl = new SurfaceControl();
SurfaceSession mSurfaceSession = new SurfaceSession();
root.setView(decor)
mSurfaceHolder = new TakenSurfaceHolder();
requestLayout()
scheduleTraversals()
// 开启同步屏障,监听vsync信号,执行回调。https://ljd1996.github.io/2020/09/07/Android-Choreographer原理
doTraversal()
performTraversals() // 这里执行view绘制三大流程 measure、layout、draw
if (mFirst) {
host.dispatchAttachedToWindow() // 这里的host其实就是decorView,DecorView中的所有子View都会收到onAttachedToWindow回调
}
c.surfaceCreated(mSurfaceHolder);
performMeasure()
performLayout()
notifySurfaceCreated(mTransaction)
// 通知其他SurfaceView生命周期,比如我们自定义的ServiceView里面就自动帮我们这册了这个callback
private void notifySurfaceCreated(Transaction t) {
for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) {
mSurfaceChangedCallbacks.get(i).surfaceCreated(t);
}
}
notifySurfaceReplaced(mTransaction);//同理
notifySurfaceDestroyed();//同理
performDraw()
private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (isHardwareEnabled()) {
...
// 硬件draw
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
...
// 软件draw
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
...
}
// 至于onWindowFocusChanged如何被调用了,链路太长了,有空可以去看看
onWindowFocusChanged(true)
r.activity.makeVisible();
mDecor.setVisibility(View.VISIBLE); // activity可见
activity跳转生命周期变化
正常跳转
1.Activity1启动:
Activity1: onCreate()
Activity1: onStart()
Activity1: onResume()
2.点击按钮跳转到Activity2:
Activity1: onPause()
Activity2: onCreate()
Activity2: onStart()
Activity2: onResume()
Activity1: onStop()
3.从Activity2中返回:
Activity2: onPause()
Activity1: onRestart()
Activity1: onStart()
Activity1: onResume()
Activity2: onStop()
Activity2: onDestroy()
4.Activity1退出
Activity1: onPause()
Activity1: onStop()
Activity1: onDestroy()
假设Activity2为一个透明的activity,activity跳转的生命的周期如下
1.Activity1启动:
Activity1: onCreate()
Activity1: onStart()
Activity1: onResume()
2.点击按钮跳转到Activity2:
Activity1: onPause()
Activity2: onCreate()
Activity2: onStart()
Activity2: onResume()
3.从Activity2中返回:
Activity2: onPause()
Activity1: onResume()
Activity2: onStop()
Activity2: onDestroy()
4.Activity1退出
Activity1: onPause()
Activity1: onStop()
Activity1: onDestroy()
那在Activity之上弹出一个系统对话框,生命周期变化呢
1.Activity启动:
Activity1: onCreate()
Activity1: onStart()
Activity1: onResume()
2.点击按钮弹出系统对话框
这里发现并没有任何生命周期变化 ,原本还以为会走 onPause() 这个方法。
锁屏时只会调用onPause(),而不会调用onStop方法,开屏后则调用onResume()。
横竖屏切换时的生命周期? 如果清单文件中没有设置android:configChanges属性时,生命周期:先销毁onPause()、onStop()、onDestroy()再重新创建onCreate()、onStart()、onResume()方法。 设置orientation|screenSize(一定要同时出现)属性值时,不走生命周期方法,只会执行onConfigurationChanged()方法。
Activity pause状态,不执行onConfigurationChanged,onResume时再重新执行onConfigurationChange
启动模式
- standard:默认值,启动Activity都会重新创建一个Activity的实例进行入栈。此时Activity可能存在多个实例。
- singleTop:栈顶复用,当Activity处于栈顶时,再启动此Activity,不会重新创建实例入栈,而是会使用已存在的实例。在栈顶再次进入:onNewIntent;不在栈顶再次进入:onCreate=>onStart
- singleTask:栈内复用,根据taskAffinity去查找是否存在这个任务栈,默认情况下taskAffinity为应用package name,也就是应该默认创建的任务栈,之后在这个任务栈中查找是否存在Activity,当Activity在栈中已经存在,再启动此Activity,会使用已存在实例,并且会将栈中此Activity上面的所有Activity进行出栈销毁,使得此Activity处于栈顶。如果不存在taskAffinity相同的任务栈,则创建任务栈,并且将此Activity入栈。在栈顶再次进入:onNewIntent;不在栈顶再次进入:onNewIntent => onRestart => onStart
- singleInstance:启动的Activity,会重新创建一个任务栈,并且此任务栈中只有一个Activity。在栈顶再次进入: onNewIntent;不在栈顶再次进入:onNewIntent => onRestart => onStart
如何在Service中启动一个Activity
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent activityIntent = new Intent(this, MyActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 添加标志以在新任务中启动Activity
startActivity(activityIntent);
return START_STICKY;
}
ANR 的四种场景:
- Service TimeOut: service 未在规定时间执行完成:前台服务 20s,后台 200s
- BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内
- ContentProvider TimeOut: publish 在 10s 内没有完成
- Input Dispatching timeout: 5s 内未响应键盘输入、触摸屏幕等事件
Activity跟window,view之间的关系?
Activity在创建时会调用 attach() 方法初始化一个PhoneWindow(继承于Window),每一个Activity都包含了唯一一个PhoneWindow Activity通过setContentView实际上是调用的getWindow().setContentView将View设置到PhoneWindow上而PhoneWindow内部是通过WindowManager的addView、removeView、updateViewLayout这三个方法来管理View,WindowManager本质是接口,最终由WindowManagerImpl实现
onActivityResult 在哪两个生命周期之间回调
B.onPause -> A.onActivityResult -> A.onRestart -> A.onStart -> A.onResume
Intent为什么不能传输大数据
Intent是通过Parceable对象传输的,底层是Binder缓冲区,而这个缓冲区的大小不是无限大的。报错:TransactionTooLargeException
Serializable 和Parcelable 的区别
Serializable底层实现需要用到反射,Parcelable底层实现则不需要反射,而且它是内存操作。IPC的时候用Parcelable,是因为它效率高,虽然我们在使用Intent传递的时候也可以使用Serializable的对象
Android中的动画类型
1、帧动画(Frame Animation): 通过顺序播放一系列图像来创建动画效果,适用于简单的动画效果,如加载指示器。
2、补间动画(Tween Animation): 对象的属性值(如位置、大小、透明度)进行动态变化。适用于视图的平滑过渡效果,如淡入淡出、缩放等。
3、属性动画(Property Animation): Android 3.0(API级别11)引入,提供了对任何对象属性的动画支持,比补间动画更灵活强大。适用于复杂的动画效果,如弹跳、旋转等。
4、布局动画(Layout Animation): 对布局容器的子视图进行动画效果的设置,适用于对一组UI元素进行统一的动画效果处理,如列表项的动画。
Activity.runOnUiThread
从代码上看,只是handler的简单封装
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
Fragment
重叠问题
出现场景:内存不足,或者屏幕旋转时,Activity被销毁并重建,再次打开Activity后,fragment出现重叠现象
原因:activity被销毁前会执行onSaveInstanceState,这个方法会执行一个saveViewHirekuy这个方法,目的是应用重启后恢复页面。当应用重新走onCreate方法时,用户会重新生成Fragment对象
解决办法:在onCreate(Bundle saveBundle)方法判断,如果是应用重启,saveBundle为null,同时还要用findFragmentById判断fragment对象是否已经存在
hide和show不执行生命周期
略
Service
生命周期
onStartCommand返回值类型
- START_STICKY:Service 在被系统杀死后会自动重启并尝试重新创建。
- START_NOT_STICKY:Service 在被系统杀死后不会自动重启。
- START_REDELIVER_INTENT:Service 在被系统杀死后会自动重启,并使用最后一个 Intent 重新启动或调用 onStartCommand()。
Service运行在哪个线程/进程
Service默认运行在当前进程的主线程中的,如果需要执行耗时操作,记得在Service里创建新线程; Service用来提高优先级:在后台场景下,Service的优先级是高于后台挂起的Activity的,也高于Activity所创建的线程
Service如何更新UI
在Android 中,Service 是一个运行在后台的组件,它不能直接更新UI。 如果您需要在Service 中更新UI,您可以创建一个隐式Intent,并将其发送到Activity
Service 和 IntentService 的区别?
IntentService是Service 的子类,默认给我们开启了一个工作线程执行耗时任务,并且执行完任务后自 动停止服务。内部使用HandlerThread+Looper实现
如何保证Service不被杀死
1.用前台service,或者manifest提高进程优先级,降低进程被杀死的概率 2.双进程相互唤起 3.用broadcastreceiver给service发送广播,唤醒service
Service里面可以弹Toast么?
可以的。弹吐司有个条件就是得有一个Context上下文,而Service本身就是Context的子类,因此在Service里面弹Toast是完全可以的。比如我们在Service中完成下载任务后可以弹一个Toast通知用户。
特别注意
- startService()和stopService()只能开启和关闭Service,无法操作Service;bindService()和unbindService()可以操作Service
- startService开启的Service,调用者退出后Service仍然存在;BindService开启的Service,调用者退出后,Service随着调用者销毁。
Broadcast Receiver
注册方式
静态注册,生命周期与应用的生命周期相同
<receiver android:name=".XXXReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
exported属性表示是否暴露给其他应用,设置为true, 则可以接收到其他应用发送的广播
动态注册,生命周期与注册它的组件(Activity或Service)的生命周期相同
IntentFilter mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//调用Context的registerReceiver()方法进行动态注册
registerReceiver(mBroadcastReceiver, intentFilter);
广播分类
- 无序广播:也就是最常见的标准广播,通过SendBroadcast()方法发送。
- 有序广播:针对广播接收方而言,通过sendOrderedBroadcast(intent)发送,发送出去的广播被广播接收者按照优先级先后顺序接收,相同优先级的动态注册的广播优先,每次只能有一个接受者收到,接受者收到广播后,可以通过setResultData来传递数据给下一个接收者,也可以通过abortBroadcast()来终止广播继续向下传递。
- 粘性广播:调用SendStickyBroadcast()方法发送,需要android.Manifest.permission.BROADCAST_STICKT权限,注册者可以接受到注册广播前发送者发送的最后一次广播。目前API 21中已标记为Deprecated,不推荐使用。系统中电量的广播就是使用粘性广播发送的。
- 本地广播:通过系统LocalBroadcastManager发送,只能在当前应用内接收。相对于其他类型广播而言,安全性高&效率高。本地广播只能通过LocalBroadcastManager动态注册。
- 系统广播:有的地方把这个也算一个分类,这里也提一下,系统广播就是Android系统内置的广播,用来通知应用一些系统状态的改变,如:息屏亮屏,电量变化,网络状态变化。使用者只需注册对应的Action, 系统有相关操作时会自动广播。
View绘制
根节点是DecorView,整个View体系就是一棵以DecorView为根的View树,依次通过遍历来完成measure、layout和draw过程。而如果要自定义view,一般都是通过重写onMeasure(),onLayout(),onDraw()来完成要自定义的部分,整个绘制流程也基本上是围绕着这几个核心的地方来展开的。
onMeasure
MeasureSpec
UNSPECIFIED:未指定尺寸模式。父布局没有对子view强加任何限制。它可以是任意想要的尺寸。(笔者注:这个在工作中极少碰到,据说一般在系统中才会用到,后续会讲得很少)
EXACTLY:精确值模式。父布局决定了子view的准确尺寸。子view无论想设置多大的值,都将限定在那个边界内。(笔者注:也就是layout_width属性和layout_height属性为具体的数值,如50dp,或者设置为match_parent,设置为match_parent时也就明确为和父布局有同样的尺寸,所以这里不要以为笔者搞错了。当明确为精确的尺寸后,其也就被给定了一个精确的边界)
AT_MOST:最大值模式。子view可以一直大到指定的值。(笔者注:也就是其宽高属性设置为wrap_content,那么它的最大值也不会超过父布局给定的值,所以称为最大值模式)
MeasureSpec被实现为int型来减少对象分配。该类用于将size和mode元组装包和拆包到int中。(笔者注:也就是将size和mode组合或者拆分为int型数据)
LayoutParams
该LayoutParams基类仅仅描述了view希望宽高有多大。对于每一个宽或者高,可以指定为以下三种值中的一个:MATCH_PARENT,WRAP_CONTENT,an exact number
onLayout
略
onDraw
略
布局
在Android中,有多种布局用于组织和定位UI元素,以实现不同的界面布局效果。以下是Android中常用的几种布局:
- LinearLayout(线性布局):LinearLayout是最简单的布局,它按照水平或垂直方向排列子视图。可以通过android:orientation属性指定排列方向为horizontal(水平)或vertical(垂直)。
- RelativeLayout(相对布局):RelativeLayout允许子视图相对于父视图或其他子视图定位。通过指定视图之间的相对关系,可以实现灵活的布局效果。
- FrameLayout(帧布局):FrameLayout将子视图堆叠在一起,每个子视图位于最顶层的位置。常用于覆盖显示或切换视图。
- ConstraintLayout(约束布局):ConstraintLayout是一个灵活强大的布局,可以实现复杂的界面布局。它使用约束将子视图相对于父视图或其他子视图进行定位。
- TableLayout(表格布局):TableLayout可以将子视图组织成表格形式,类似于HTML的表格布局。它包含多个TableRow,每个TableRow包含多个子视图。
- GridLayout(网格布局):GridLayout将子视图组织成网格形式,类似于表格布局。可以通过android:layout_row和android:layout_column属性指定子视图的行和列位置。
- CoordinatorLayout(协调布局):CoordinatorLayout是用于处理子视图之间的协调动作的特殊布局。它常用于实现一些复杂的交互效果,如滚动时的视图协调。
- ScrollView(滚动布局):ScrollView允许在视图内容超过屏幕时进行滚动查看。它只能包含一个直接子视图。
- ConstraintSet:ConstraintSet是用于在ConstraintLayout中动态修改约束的类。可以通过ConstraintSet在运行时改变界面布局。
RelativeLayout需要对其子View进行两次measure过程。而LinearLayout则只需一次measure过程
如何让View刷新
View重绘和更新可以使用invalidate()和requestLayout()方法,其主要区别如下:
- invalidate()方法只会执行onDraw方法
- requestLayout()只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法 所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate()方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout()方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout()方法再调用invalidate()方法。
与invalidate()方法类似的还有一个postInvalidate(),两者作用都是刷新View,区别在于:
- invalidate方法用于UI线程中重新刷新View
- postInvalidate方法用于非UI线程中重新刷新View,这里借助了ViewRootHandler来达成目的 ViewRootHandler看着比较陌生,其实我们经常接触到。比如我们调用View.post(Runnable)方法,处理Runnable的就是这个ViewRootHandler了。
Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?
PhoneWindow 其实是 Window 的唯一子类,是 Activity 和 View 交互系统的中间层,而 DecorView 是整个 View 层级的最顶层,ViewRootImpl 是 DecorView 的 parent,但是他并不是一个真正的 View,只是继承了 ViewParent 接口,用来掌管 View 的各种事件,包括 requestLayout、invalidate、dispatchInputEvent 等等
事件分发机制
大概流程:在程序的主界面情况下,布局的顶层view是DecorView,他会先把事件交给Activity,Activity调用PhoneWindow的方法进行分发,PhoneWindow会调用DecorView的父类ViewGroup的dispatchTouchEvent方法进行分发。也就是Activity->Window->ViewGroup的流程。ViewGroup则会向下去寻找合适的控件并把事件分发给他
onTouchEvent:true表示消费事件,false表示继续传递事件。先调用onTouchListener,再调用onClickListener和onLongClickListener。
onInterceptTouchEvent判断是否拦截事件。如果拦截,则调用自身的onTouchEvent方法;如果不拦截则调用子view的dispatchTouchEvent方法
触摸事件是如何产生的:手指点击屏幕后,屏幕会产生特定的数字信号,比如按压的位置,传给驱动程序,然后会把这个信息交给InputServiceManager去处理,最后通过WindowManagerService找到符合的window,并把触摸信息发送给viewRootImpl,viewRootImpl经过层层封和处理之后,产生一个MotionEvent事件分发给view。
如果父view中不拦截down事件,拦截move,up事件,在子view中设置了requestDisallowInterceptTouchEvent(true);(请求父view不拦截事件)这个标志后,子view能收到move,up事件吗?(1)如果子View消费了Down事件,并且设置了requestDisallowInterceptTouchEvent(true)那么后续的move、up事件会正常传过来(2)如果子view不消费Down事件,并且设置了requestDisallowInterceptTouchEvent(true),则只会收到Down事件,后续的move、up事件收不到。主要用于解决滑动冲突,比如:水平和垂直滑动冲突,嵌套滑动冲突
Handler
如何在子线程更新UI
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
ViewRootImpl类的checkThread方法判断了当前线程是否是ViewRootImpl创建时候的线程,如果不是,就会崩溃。 而ViewRootImpl创建的时机就是界面被绘制的时候,也就是onResume之后,所以如果在子线程进行UI更新,就会发现当前线程(子线程)和View创建的线程(主线程)不是同一个线程,发生崩溃。解决办法就是在ViewRootImpl创建之前进行子线程的UI更新,比如onCreate方法中进行子线程更新UI。
handler缺点,有可能造成内存泄漏。解决办法:(1)静态内部类,持有activity的弱饮用。(2)在相应生命周期解绑
消息队列中的消息是如何存储的
虽然叫MessageQueue,但实际上是单链表,用next参数来连接下一个节点
ThreadLocal
内部是map结构,保证每个线程只有一个Looper,key是Thread对象
Binder机制
线程池
- FixedThreadPool:它是一种数量固定的线程池,当线程处于空闲状态时也不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有空闲线程出来。FixedThreadPool只有核心线程并且不会被回收因此它可以更加快速的响应外界的请求。
- CacheThreadPool:它是一种线程数量不定的线程池且只有非核心线程,线程的最大数量是Integer.MAX_VALUE,当线程池中的线程都处于活动状态时如果有新的任务进来就会创建一个新的线程去执行任务,同时它还有超时机制,当一个线程闲置超过60秒时就会被回收。
- ScheduleThreadPool:它是一种拥有固定数量的核心线程和不固定数量的非核心线程的线程池,当非核心线程闲置时会立即被回收。
- SignleThreadExecutor:它是一种只有一个核心线程的线程池,所有任务都在同一个线程中按顺序执行。
Android系统启动流程
-
启动电源 当电源被启动时,引导芯片载入了预定义的引导程序(固化在ROM中),并将引导程序BootLoader加载到RAM中开始执行。
-
引导程序BootLoader BootLoader是一个小程序,其主要作用是引导安卓系统,并将其运行起来。
-
Linux内核启动 在内核启动时,系统设置了缓存、保护存储器、调度列表,并加载了各种驱动。完成系统设置后,内核会搜索init.rc文件,并启动init进程。
-
init进程启动 init进程是系统启动时的第一个用户空间进程。它完成了许多重要的初始化工作,包括1.创建和挂载启动所需的文件目录、2.初始化和启动属性服务,以及3.启动Zygote进程。
下面详细说明三个重要的步骤: 步骤一:创建和挂载启动所需的文件目录: 在系统启动过程中,init进程首先会创建和挂载所需的文件目录,这些目录包括根目录、/proc、/sys等,以确保系统能够正常运行和提供所需的资源和环境。 初始化和启动属性服务: 步骤二:Android系统中的属性服务用于存储系统的属性信息,这些信息可以用于配置系统的行为和特性。init进程在启动时会初始化并启动属性服务,这确保了系统能够根据需要正确配置自身,并提供正确的服务。 步骤三:解析init.rc配置文件并启动Zygote进程: init进程会解析init.rc配置文件,该文件描述了系统在启动时需要进行的各种操作,包括启动系统服务、挂载文件系统等。其中,init.rc文件中的一项重要任务是启动Zygote进程。Zygote进程是Android系统中的一个重要进程,它是所有应用程序的起点,任何新应用程序的进程都是由Zygote进程来孵化和启动的。
-
Zygote进程启动 Zygote进程是Android系统的关键进程,其主要工作包括创建Java虚拟机并为其注册JNI方法,创建服务器端Socket以便接收请求,并启动SystemServer进程。
Zygote进程的启动过程涉及了一系列关键步骤,这些步骤为Android应用程序的快速启动和运行奠定了基础。 init启动zygote时主要是调用app_main.cpp的main函数中的AppRuntime的start方法来启动zygote进程,AppRuntime负责管理应用程序的运行环境,包括应用程序的启动和生命周期管理。通过调用start方法,Zygote进程开始正式启动,为后续应用程序的启动和运行做好准备。 创建Java虚拟机并为Java虚拟机注入JNI方法: 作为Android应用程序的基石,Java虚拟机(JVM)是整个Android系统的核心组成部分。在Zygote启动过程中,会创建Java虚拟机,并为其注入JNI(Java Native Interface)方法,这些方法用于实现Java与本地代码(C/C++)的互操作。通过这一步骤,Zygote为后续应用程序提供了Java运行环境的支持。 通过JNI调用ZygoteInit的main函数进入Zygote的Java框架层: 通过JNI调用ZygoteInit的main函数,Zygote进程进入其Java框架层。ZygoteInit负责系统服务和应用进程的启动,它负责在应用程序启动时进行一系列初始化操作,为新应用程序的进程创建做好准备。 通过registerZygoteSocket方法创建服务器端Socket,并通过runSelectLoop方法等待AMS的请求来创建新的应用程序进程: Zygote进程通过registerZygoteSocket方法创建服务器端Socket,这允许ActivityManagerService(AMS)向Zygote进程发送请求,请求创建新的应用程序进程。随后,Zygote进程通过runSelectLoop方法等待AMS的请求,一旦收到请求,就会创建新的应用程序进程,从而实现应用程序的快速启动。 当 Zygote 进程完成前述步骤后,其接下来的工作便是启动 SystemServer 进程。SystemServer 是 Android 系统中一个非常重要的进程,负责启动并管理系统的各项核心服务。
-
SystemServer进程启动 SystemServer是Android系统中的一个重要进程,负责启动并管理系统的各项核心服务。它负责启动Binder线程池和SystemServiceManager,并启动各种系统服务。里面包含的服务包括:WindowManagerServer(WMS)、ActivityManagerService(AMS)和PackageManagerServer(PMS)......PowerManagerService...
详细来说启动通常包括以下几个主要步骤: 创建 SystemServer 实例: Zygote 进程在启动 SystemServer 之前会创建 SystemServer 的实例。 调用 SystemServer 的 main 方法: Zygote 进程通过调用 SystemServer 实例的 main 方法来启动 SystemServer 进程。 初始化系统服务: SystemServer 进程在启动时会初始化各种系统服务,包括但不限于包管理器(PackageManagerService)、窗口管理器(WindowManagerService)、通知管理器(NotificationManagerService)、ActivityManager 等。 注册核心系统服务: SystemServer 进程负责注册核心的系统服务,以便其他应用程序或组件可以与这些服务进行通信和交互。 启动其他关键进程: 除了系统服务外,SystemServer 进程还可能启动其他重要的系统组件和进程,例如安全性相关的服务或者底层系统管理进程。 通过这些步骤,SystemServer 进程在 Android 系统启动过程中扮演着至关重要的角色。它的启动是 Android 系统初始化过程中的最后一步,确保系统各项核心服务的正常运行,并为后续应用程序和用户体验提供了强大的基础支持。
-
Launcher启动。SystemServer进程启动后,会启动ActivityManagerService(AMS),并由AMS启动Launcher。Launcher负责将已安装的应用程序的快捷图标显示到用户界面上,使用户可以轻松访问和启动应用程序。
SharedPreferences
commit和apply的区别
- apply没有返回值而commit返回boolean表明修改是否提交成功
- apply是将修改数据原子提交到内存,而后异步真正提交到硬件磁盘;而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内存,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
- apply方法不会提示任何失败的提示
线程安全吗?
不安全
Android混淆
混淆原理:把包名,类名,变量名,改成a,b,c,在不影响程序正常功能的情况下保护源代码安全
以下情况不能被混淆
- AndroidManifest中的类不能混淆,包括四大组件
- 与服务端交互的gson数据类
- 反射用到的类不能混淆
- jni方法
- sdk,so库
- webview中的js接口
Java面试题
基本数据类型
byte, boolean, short, char int, float, long double
StringBuffer和StringBuilder区别
StringBuffer:线程安全
StringBuilder:线程不安全
进程和线程的区别?
- 进程切换的开销大于线程切换
- 进程拥有独立的地址空间,进程之间互不干扰。线程共享内存空间
初始化块、静态初始化块和构造方法的执行顺序是怎样的?
静态初始化块在类加载时执行,且只执行一次。它在类的静态成员初始化之前执行。如果有多个静态初始化块,它们按照在类中出现的顺序依次执行。
实例初始化块在每次创建类的实例时执行。它在构造方法之前执行。如果有多个实例初始化块,它们按照在类中出现的顺序依次执行。
构造方法在创建类的实例时执行。它用于完成对象的初始化操作。如果有多个构造方法,根据使用的构造方法不同,执行相应的构造方法。
范型
Java内置注解内置注解
- @Override内置注解
- @Deprecated内置注解
- @SuppressWarnings元注解
元注解
- @Target元注解
- @Retention & @RetentionTarget元注解
- @Documented元注解
- @Inherited元注解
- @Repeatable
(Java8)元注解
- @Native (Java8)
异常
Throwable 是 Java 语言中所有错误与异常的超类。Throwable 包含两个子类:Error(错误)和 Exception(异常)
Java常见异常有哪些?
- java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
- java.lang.StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。
- java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。
- java.lang.ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
- java.lang.IndexOutOfBoundsException:索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
- java.lang.NullPointerException:空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。
集合框架
ArrayList:底层结构是一个数组,初始长度为10 容量不足时,扩展为原来的1.5倍也就是扩展为15 LinkedArrayList底层是一个双向链表,好处是不用扩容,坏处是当你要寻找第N个元素时,实践复杂度为O(n),就是遍历N个元素去找到他 而ArrayList的时间复杂度是 O(1)
ArrayList的扩容,在原有基础上增长50%
HashMap:底层结构是 一个元素为链表的数组 , 虽然是数组 但是是无序插入数组的。根据哈希值来插入。 当hash碰撞则需要用到链表结构 , 把新插入的但 hashcode值相同的 链在之前插入的后面形成链表,当连得太多 就会形成红黑树,新加入的元素形成连头,第一存放在位置上的就成链尾。对象需要重写equals和hashcode。java1.8之后,改为红黑树,红黑树的查询效率高。扩容机制:负载因子0.75。元素插入时,用&与运算计算下标,聊表长度大于8时,转为红黑树
hashmap和hashtable的区别?(1)HashTable线程安全,HashMap线程不安全(2)HashMap的key、value都可为null,hashtablke则不行
如果hashmap的key是int,用sparsearray的性能优于hashmap,不用对int装箱
ConcurrentHashMap和HashMap是Java中两种常用的哈希表实现。 它们的主要区别在于线程安全性。 HashMap是非线程安全的,如果多个线程同时读写HashMap,可能会导致数据不一致的问题。 而ConcurrentHashMap则是线程安全的,可以在多个线程之间安全地共享数据,synchronize关键字。为了保证线程安全,ConcurrentHashMap 采用了分段锁机制。 它将整个哈希表分成多个段(Segment),每个段都是一个独立的锁,只控制对应段的数据访问。 当多个线程访问同一个段时,会互相竞争该段的锁,从而实现线程安全
Java是值传递还是引用传递
基本数据类型,值传递;对象,引用传递
抽象类和接口的区别
抽象类可以有构造方法和普通成员变量,一个类只能继承一个抽象类。
接口只包含方法的声明,不能有构造方法和普通成员变量,一个类可以实现多个接口
多线程同步
synchronize关键字 CountDownLatch类
Serializable和Parcelable的区别?
当我们需要将我们的对象进行网络传输或存储到文件中时,使用Serializable;当我们需要将我们的对象在进程间传递通信时,使用Parcelable
设计模式
常用的设计模式
- 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
- 工厂模式(Factory Pattern):定义一个创建对象的接口,但将实际创建对象的过程延迟到子类中。
- 建造者模式(Builder Pattern):通过多个简单的对象逐步构建复杂对象。
- 适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口。
- 装饰器模式(Decorator Pattern):动态地给对象添加额外的职责。
- 观察者模式(Observer Pattern):定义对象之间的一对多依赖关系,当一个对象状态改变时,其依赖者会收到通知。
- 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,使它们可以相互替换。
HTTP面试题
状态码
1xx(信息性状态码):表示接收的请求正在处理。
2xx(成功状态码):表示请求正常处理完毕。
3xx(重定向状态码):需要后续操作才能完成这一请求。
4xx(客户端错误状态码):表示请求包含语法错误或无法完成。
5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误
常用状态码及含义
200 请求成功
301 永久重定向
304 资源未修改
307 临时重定向
400 Bad Request
401 Unauthorized 用户未认证
403 Forbidden
404 Not Found
500 服务器内部错误
502 Bad Gateway
网络模型
⑴ 应用层 HTTP、DNS、SSH、SMTP web、tmp、war chrome、firefox、email
⑵ 表示层 tsl / ssl zip、pgn、mpv4 翻译、压缩、加密/解密
⑶ 会话层 双工、双向传递 / 身份验证、授权
⑷ 传输层 【段】 TCP、UDP、SCTP / 分段、流量控制、差错控制
⑸ 网络层 【包】IPV4、IPV6 dzp、szp 确定路径
⑹ 数据链路层 【帧】以太网eth、LAN、ARP mac、dmac、smac 由网络层携带自己IP地址和对方IP地址
⑺ 物理层 【BIT】网线、无线 二进制数(1001011…) 携带完整的请求信息
WebSocket
在建立连接时,在请求头header中加入特定的header,升级到websocket协议。就不会像http一样,使用完就断开
Upgrade: websocket
Connection: Upgrade
Android内存泄漏场景
比如,子线程引用了四大组件,handler未解绑,静态方法持有四大组件,长生命周期持有了短生命周期的引用
第三方框架面试题
Bitmap
inJustDecodeBounds是什么?
上面的例子大家应该发现了,其中有个inJustDecodeBounds,又设置为true,又设置成false的,总感觉多此一举,那么他到底是干嘛呢?
因为我们要获取图片本身的大小,如果直接decodeResource加载一遍的话,那么就会增加内存了,所以官方提供了这样一个参数inJustDecodeBounds。如果inJustDecodeBounds为ture,那么decode的bitmap为null,也就是不返回实际的bitmap,只把图片的大小信息放到了options的值中。
Kotlin
apply:上下文对象this,返回对象本身
also:上下文对象it,返回对象本身
let:上下文对象it,返回lambda表达式结果
run:上下文对象this,返回lambda表达式结果
with:上下文对象this,返回lambda表达式结果(with和run的效果一样,区别就是run可以用点表达式,因为run是扩展函数)
这些关键字可以用点表达式随意组合
fun main() {
//sampleStart
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
//sampleEnd
println(numberList)
}
JVM面试题
垃圾回收算法:标记-清除(优点是实现简单,缺点是回收过程中会产生内存碎片)、标记-复制(解决标记-清除算法的内存碎片问题,因为它将内存空间划分为两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后清理掉这一块,缺点是浪费了一半的内存空间。)、标记-整理(标记-清除复制算法的升级版,它不再划分内存空间,而是将存活的对象向内存的一端移动,然后清理边界以外的内存)