Android 面试题 应用程序结构 九

🔥 核心应用程序  Activity五个状态🔥

Starting-> running-> paused-> stopped-> killed

  • 启动状态(Starting):Activity的启动状态很短暂,当Activity启动后便会进入运行状态(Running)。
  • 运行状态(Running):Activity在此状态时处于屏幕最前端,它是可见、有焦点的,可以与用户进行交互。如单击、长按等事件。即使出现内存不足的情况,Android也会先销毁栈底的Activity,来确保当前的Activity正常运行。
  • 暂停状态(Paused):在某些情况下,Activity对用户来说仍然可见,但它无法获取焦点,用户对它操作没有没有响应,此时它处于暂停状态。
  • 停止状态(Stopped):当Activity完全不可见时,它处于停止状态,但仍然保留着当前的状态和成员信息。如系统内存不足,那么这种状态下的Activity很容易被销毁。
  • 销毁状态(Destroyed):当Activity处于销毁状态时,将被清理出内存。
  • 还有一种情况由于系统内存不足可能在Paused状态中直接被系统杀死达到killed状态。

🔥 核心应用程序   五个状态生命周期图  🔥

🔥 核心应用程序  activity的生命周期  🔥

oncreate()->onstart()->onResume()->onRestart()->onPouse()->onStop()->onDestory()

在这里插入图片描述

 🔥 核心应用程序 Activity横竖屏生命周期 🔥

横竖屏切换涉及到的是Activity的android:configChanges属性;
android:configChanges可以设置的属性值有:

1、orientation:消除横竖屏的影响

2、keyboardHidden:消除键盘的影响

3、screenSize:消除屏幕大小的影响

public class MainActivity extends AppCompatActivity {
public static final String TAG=“MainActivity”;@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.i(TAG,"onCreate");setContentView(R.layout.activity_main);
}@Override
protected void onStart() {super.onStart();Log.i(TAG,"onStart");
}@Override
protected void onResume() {super.onResume();Log.i(TAG,"onResume.........");
}@Override
protected void onPause() {super.onPause();Log.i(TAG,"onPause");
}@Override
protected void onStop() {super.onStop();Log.i(TAG,"onStop");
}@Override
protected void onDestroy() {super.onDestroy();Log.i(TAG,"onDestroy");
}

运行Activity,生命周期如下:

onCreate -> onStart -> onResume

切换横屏,生命周期如下:

onPause -> onStop -> onSaveInstanceState -> onDestroy  

onCreate -> onStart -> onRestoreInstanceState -> onResume

再切回竖屏,生命周期如下:

onPause -> onStop -> onSaveInstanceState -> onDestory 

onCreate -> onStart -> onRestoreInstanceState -> onResume

修改AndroidManifest.xml,添加android:configChanges="orientation"并切换横屏,生命周期如下:

onPause -> onStop -> onSaveInstanceState -> onDestory 

onCreate -> onStart -> onRestoreInstanceState -> onResume

再切换竖屏,生命周期如下:

onPause -> onStop -> onSaveInstanceState -> onDestory 

onCreate -> onStart -> onRestoreInstanceState -> onResume

修改AndroidManifest.xml,属性改为android:configChanges=“orientation|keyboardHidden|screenSize”,切换横屏,生命周期如下:

执行函数  onConfigurationChanged()

再切换回竖屏 , 生命周期如下:

执行函数  onConfigurationChanged()

结论 : 

设置Activity的android:configChanges属性为orientation或者orientation|keyboardHidden或者不设置这个属性的时候,横竖屏切换会重新调用各个生命周期方法,切横屏时会执行1次,切竖屏时会执行1次;
设置Activity的属性为android:configChanges="orientation|keyboardHidden|screenSize"时,横竖屏切换不会重新调用各个生命周期方法,只会执行onConfigurationChanged方法;

 🔥 Activity的管理方式 🔥

Android系统可以运行多个app , App也可以包含多个Activity , Activity 是用任务栈的方式进行管理的。

任务栈是与用户交互的页面顺序,但与 App 不是 一对一的关系,Android 根据 Activity 的 launchMode 和 taskAffinity 等属性,管理 Acticity 的栈归属

 任务栈内 Activity 的顺序不可更改,遵循入栈弹栈(先进后出)的管理原则

 🔥 启动模式 Standard 🔥

Standard 启动模式是 App 默认的启动模式。在 AndroidManifest.xml 文件中,不对 activity 的 launchMode 参数进行设置,这个 activity 就会以 standard 模式启动。

 在 Android 中,点击桌面图标,首次启动 App,创建应用的 LaunchActivity A,然后点击一个按钮,跳到另一个 Activity B,这时候,Activity A 的状态会被保留,且压入任务栈中,Activity B 会被创建,并且显示在任务栈顶。

如果 app 中有 activity A、B、C,且都是以 standard 模式启动,那么,多次页面跳转后,它的堆栈可能变成:

A - B - C - B - B - C - C - A - C

只要开启新 Activity 就会创建,进栈。然后触发页面返回操作时,按顺序一一回退页面。 

🔥 singleTop 启动模式  🔥 

在 App 中,详情页中可能存在其他物品的详情页链接,用户大概率会点击进入下一个详情链接,然后详情链接内又有详情链接,当浏览了十来个物品后,想回到最初的列表页,需要疯狂点击返回按钮。避免这种情况,则可以使用 singleTop 的启动模式 —— 当栈顶已经是详情页时,再次打开一个详情页,不会重新创建页面,只会回调当前页面的 onPause ->onNewIntent 传递新的 Intent -> onResume

class SingleTopActivity : AppCompatActivity() {private val idKey = "id"private lateinit var textView: TextViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_launch)initViews()updateIntent()}override fun onNewIntent(intent: Intent?) {super.onNewIntent(intent)// 更新 intentsetIntent(intent)// 重新设置页面内容updateIntent()}private fun initViews() {textView = findViewById(R.id.tv_title)textView.setOnClickListener {startActivity(Intent(this, SingleTopActivity::class.java).apply {putExtra(idKey, intent.getIntExtra(idKey, 0) + 1)})}}private fun updateIntent() {val id = intent.getIntExtra(idKey, 0)textView.setText("get id: $id")}
}

如果 app 中有 activity A、B、C,其中 B 的模式启动为 singleTop, A、C 为 standard,那么,页面跳转情况如下:

启动 A: A
启动 B: A - B
启动 C: A - B - C
启动 C: A - B - C - C
启动 B: A - B - C -C - B
启动 B: A - B - C -C - B*

表示没有重新创建页面实例,但调用了 newIntent 进行更新

只有当 singleTop 模式的 Activity 存在于栈顶时,Activity 的表现与 standard 不同

🔥 启动模式 singleInstancePerTask 🔥 

最适合作为 MainActivity 的启动模式

App 主页的需求为:

  1. App 的第一个页面

  2. 后续页面不需要启动新的主页,只需回退到主页

这意味着:

  1. 该 Activity 处于栈底

  2. 栈中只会存在一个该 Activity 实例

因而 singleInstancePerTask 最适合作为 MainActivity 的启动模式

在 standard 和 singleTop 的启动模式下,activity 可以存在多个实例,位于栈中的任何地方。但 singleInstancePerTask 在一个栈中,只会有一个实例。即:

如果 app 中有 activity A、B 、C,其中 B 的模式启动为 singleInstancePerTask,A、C 为 standard,那么,页面跳转情况如下:

启动 A: A
启动 B: A -> 切换栈 -> B
启动 C: A | B - C
启动 A: A | B - C- A
启动 B: A | B*
返回:A

在实际应用中,A - SplashActivity、LaunchActivity; B - MainActivity;C - 普通页面

如果 没有主动将 Activity A finish ,在 Activity B 中返回上一个页面,会切换回 Activity A;或者在处于 Activity B 任务栈时,回到桌面,系统会自动关闭 Activity A 所在的隐藏栈。

在 Android 12 以下版本,设置后无效,等同于 standard 模式

🔥 启动模式 SingleTask 🔥 

singleTask 与 singleInstancePerTask 的区别

在没有 singleInstancePerTask 模式之前,singleTask 是 MainActivity 启动模式首选,二者的区别在于:

  • singleTask 可以处在栈中的任意位置,而 singleInstancePerTask 只能处于栈底

  • singleTask 在 Android 中,只会有一个实例(startActivity 时,没有则创建,有则调起所在的栈,并回退到该 activity,同时回调 onNewIntent ),而 singleInstancePerTask 可以搭配 flag,在多个栈中有不同实例(可以有两个栈同时以该 Activity 为栈底)

  • singleInstancePerTask 会在系统已存在没有该实例的同 taskAffinity 任务栈时,重新开启一个栈,而 singleTask 则会直接在该栈顶创建 Activity

 如果 app 中有 activity A、B 、C,其中 B 的模式启动为 singleTask,A、C 为 standard,那么,页面跳转情况如下:

启动 A: A
启动 B: A - B
启动 C: A - B - C
启动 A: A - B - C- A
启动 B: A - B*

A - SplashActivity、LaunchActivity; B - MainActivity;C - 普通页面。在 Android 12 之前的实际应用中,需要通过代码来控制 Activity A 的自动跳转和关闭:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 判断是否处于栈底(首次启动)if (isTaskRoot) {// 跳转到真正的启动页面startActivity(Intent(this, MainActivity::class.java))}finish()
}

 从桌面点击 icon 唤醒后台 App 时,如果 MainActivity 为 singleTask 模式,会创建 SplashActivity,置于原本栈顶,然后回调到 onCreate 中,finish SplashActivity,显示原本任务栈。而如果 MainActivity 为 singleInstancePerTask 启动模式,则不会有创建 SplashActivity 的流程,直接将已存在的 MainActivity 为根的任务栈唤醒到前台。

当将 MainActivty 作为 LanchActivity ,且启动模式为 SingleTask 时,可能存在 bug

 🔥 singleInstance 启动模式 🔥

singleInstance 在 Android 中,也不会存在第二个实例,且 singleInstance 的栈中,有且只有这一个 Activity( 不会继续叠加新的 )

如果 app 中有 activity A、B 、C,其中 B 的模式启动为 singleTask,A、C 为 standard,那么,页面跳转情况如下:

启动 A: A
启动 B: A -> 切换栈 -> B(前台栈)
返回:B(onDestroy 销毁) -> 切换栈 -> A
启动 B: A -> 切换栈 -> B(前台栈)
启动 C: B (后台栈)| A - C(前台栈,显示 C)
返回 : B (后台栈) | A(前台栈,显示 A)
返回 : B(显示 B)

 当启动 singleInstance 的 Activity 时,会切换到新的任务栈,在 singleInstance 中启动 Activity 时,如果原本任务栈还存在,会回到原本任务栈,否则启动新任务栈;当回到原本任务栈时,原本的任务栈会变成前台任务栈,只有前台任务栈全部退出时,才会显示 singleInstance 所在的任务栈(即 singleInstance 的 Activity 会在最后显示)

如果此间插入 回桌面 的操作,那么两个任务栈的联系会被取消,singleInstance 的任务栈处于后台状态,不会再次返回前台,下次启动该 Activity 时,没有创建新的 Activity,只会回调 onNewIntent(如果后台任务栈没有被系统销毁)

启动 A: A
启动 B: A -> 切换栈 -> B(前台栈)
启动 C: B -> 切换栈 -> (A - )C(前台栈)
回桌面:B(后台栈);A - C (后台栈)
点击桌面图标:A - C
返回 : A
返回 : 桌面
点击桌面图标,启动 A:A
启动 B: A -> 切换栈 -> B*(前台栈,调用 onNewIntent)

🔥 App 间的相互跳转 🔥 

Android 的桌面(Launcher),实质上是一个 App。点击桌面图标启动 App,就是从一个 App 跳转到另一个 App 的过程

 当点击 launcher 中的图标时,会调用 startActivity 启动对应 APP 的 AndroidManifest 中注册的 LaunchActivity,且设置了 flag: Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS (api 28)。不同android 版本设置的 flag 不同,但本质上,都是启动对应 App 的 LaunchActivity,或者将 LaunchActivity 所在的任务栈调回前台。

 此时,如果 LaunchActivity 的 launchMode 被设置为 singleTask,在 LaunchActivity 所在任务栈回调到前台的同时,任务栈会回退到 LaunchActivity,并回调 onNewIntent。即,每次从桌面点击图标回到 App 页面,都相当于重启 App。因而,不建议将 MainActivity 作为 LaunchActivity 的同时,还将其 launchMode 设置为 singleTask(或者singleInstancePerTask) ,而是通过添加一个 SplashActivity 作为 LaunchActivity 的方式,区分 App 启动页和 App 主页。

处于启动优化考虑,让用户无感知跳转启动页面,可将 SplashActivity 和 MainActivity 的 window 背景设置为相同的启动页。 

 如果一定要将 MainActivity 设置为 LaunchActivity,请移除 singleTask 的 launchMode 设置,并通过跳转时设置 intentFlag,来实现回到首页功能。

val startMain = Intent(this, MainActivity::class.java)
startMain.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(startMain)

如果将 MainActivity 设置为 LaunchActivity,launchMode 为 standard,在正常系统调用时,没有问题。但如果其他应用,只是简单地 将暴露出去的 launchActivity 作为 intent 启动了,那么 App 的所有 Activity,都会被加载入调起此 App 的原始 App 的堆栈

 🔥 任务栈 和 App 对应关系 🔥

任务栈 和 App 不是一一对应的关系 , 一个 App,可以有多个任务栈

上文提到,只有 singleInstance 会启动独立任务栈。这个任务栈,在任务管理器中,是隐形状态,但在任务管理器中看不到 task,不意味着不存在。这个隐形栈内的 Activity,如果被用户切出之后(开启新的 Activity),除了完全退出当前任务栈,无法返回;且如果回到桌面在通过任务栈或者桌面图标调起应用,完全无法回到该 Activity。但在内存不吃紧时,这个隐形栈依旧存在于内存中,下次启用该 Activity 时,直接回调 onNewIntent。

 任务管理器显示 task,主要根据是 AndroidManifest 中一个重要属性 taskAffinity 。没有明确设置时,这个值默认为包名。所以当各种会开启新任务栈的 launchMode 被设置后,而 taskAffinity 又冲突了,那么,处于后台的 task 会被隐藏。

 设置了 taskAffinity 后,可以对 App 的不同 Activity 进行 task 分组。但是,只有会发生 任务栈切换的 task,此配置才有效,即,standard 模式的 activity,设置了 taskAffinity 之后,依旧只会在当前 task 叠加和删除。

 如果 singleInstance 启动模式的 taskAffinity 和其他 taskAffinity 设置为一样的,应用行为没有设置时的冲突状态一致。

 一个任务栈内,可以显示多 App 的 Activity

前面说到 standard 模式下,不会发生切栈效果,即便是开启了其他 app 的 Activity。

1、新建一个 APP A, manifest 中配置:

<!-- exported 必须为 true-->
<activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter><intent-filter><!-- 配置 action 为 view, category 为 DEFAULT, 将 activity 暴露给其他 APP 调用 --><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>

2、在设备中,运行 APP A。

3、新建 APP B,启用上方定义的 MainActivity:

findViewById<Button>(R.id.btn_action).setOnClickListener {// 会弹出设备中所有暴露出了 action 为 view 的 Activity 的 应用列表,选中之前的 APP A// 也可通过添加 scheme 的方式指定到当前 app,可自行了解val intent = Intent(Intent.ACTION_VIEW)startActivity(intent)// 也可通过 componentName 直接指定打开的 activity,但 activity 不存在的话会崩溃// val componentName = ComponentName("app.package.name", "app.package.name.ActivityA")// startActivity(Intent().apply { setComponent(componentName) })
}

4、打开任务管理器,会发现在 APP B 的任务堆栈中,存在 APP A 的 Activity:

 

 因而,暴露出去给其他 App 唤醒的 Activity,如可能被推广页调起的详情,或者启动页面,如果不想被加载进其他 App 的栈,需 将暴露出去,可能被其他 App 调起的 Activity 设置为 singleTask 启动模式(固定在自身 app 任务栈上显示)

如果需要 启动其他 App 暴露的 Activity,且不希望将 Activity 加载在自身栈内,在 intent 中添加 flag:FLAG_ACTIVITY_NEW_TASK

 🔥 Service简介 🔥

Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

    Service是一个可以在后台执行长时间操作而不使用用户界面的应用组件。那么问题来了,既然它不使用用户界面,那么它怎么知道应该什么时候开始执行什么操作呢?答案是——它可以与其他的引用组件形成一些联系,从而可以根据其传来的信息在合适的时候执行合适的操作。

🔥 Service的启动方式 🔥 

Service的启动方式主要有两种,分别是startService和bindService。其中,startService使用的是同一个Service,因此onStart()会执行多次,onCreate()只执行一次,onStartCommand()也会执行多次。使用bindService启动时,onCreate()与onBind()都只会调用一次。

使用startService启动时是单独开一个服务,与Activity没有任何关系,而bindService方式启动时,Service会和Activity进行绑定,当对应的activity销毁时,对应的Service也会销毁。

🔥 Service的生命周期 🔥 

 

startService 

onCreate():如果service没被创建过,调用startService()后会执行onCreate()回调;如果service已处于运行中,调用startService()不会执行onCreate()方法。

onStartCommand():多次执行了Context的startService()方法,那么Service的

onStartCommand()方法也会相应的多次调用。

onBind():Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。

onDestory():在销毁Service的时候该方法。

bindService 

启动的服务和调用者之间是典型的Client-Server模式。调用者是client,Service则是Server端。Service只有一个,但绑定到Service上面的Client可以有一个或很多个。bindService启动服务的生命周期与其绑定的client息息相关。

1)首先,在Service的onBind()方法中返回IBinder类型的实例。

2)onBInd()方法返回的IBinder的实例需要能够返回Service实例本身

3、.Service 的 onStartCommand 方法返回值所代表的含义

1)START_NOT_STICKY

在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。

2)START_STICKY

重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务 ,并且onStartCommand方法会执行,onStartCommand方法中的intent值为null。适用于媒体播放器或类似服务。

3)START_REDELIVER_INTEN

使用这个返回值时,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

 🔥 IntentService 🔥

1、IntentService 是 Service 的子类

默认开启了一个工作线程HandlerThread,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务。只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作。可以启动IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。

 2、IntentService原理

1)创建一个名叫 ServiceHandler 的内部 Handler

2)把内部Handler与HandlerThread所对应的子线程进行绑定

3)HandlerThread开启线程 创建自己的looper

4)通过 onStartCommand() intent,依次插入到工作队列中,并发送给 onHandleIntent()逐个处理可以用作后台下载任务 静默上传

3、与Service的区别

IntentService会创建独立的worker线程来处理所有的Intent请求 Service主线程不能处理耗时操作,IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常。为Service的onBind()提供默认实现,返回null;onStartCommand提供默认实现,将请求Intent添加到队列中。所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service。

 🔥 Service两种启动方式的区别 🔥

第一个不同:
        通过start()直接启动服务:
        服务一旦开启,就与调用者没有任何关系,调用者的activity即使退出,也不会影响后台服务的运行。

        通过bindService()绑定服务,启动的服务:
        通过绑定方式开启的服务,服务跟调用者不求同生但求同死。如果调用者的activity退出了,那她绑定的服务也会跟着退出。
        注意:如果一个程序的activity绑定了服务,那么这个activity退出时,会报异常,说是服务没有被释放。
        那么我们可以重写activity的onDestory方法,方法内调用unbindService(),去显示的解除与服务的绑定。

第二个不同点:
         Start直接启动服务的方法,调用者不能调用服务内部的方法。

         绑定服务启动服务的方法,调用者可以调用服务内部的方法;
         利用serviceConnection接口获取服务onbind返回的ibinder对象,
         这个对象同时实现了自定义的接口,这个接口内定义了服务中的方法。

 🔥 保证Service不被杀死 🔥

使用startForeground()方法,将Service设置为前台Service,这样系统就不会将其杀死;

使用AlarmManager定时唤醒Service,保证Service不会被杀死; 

使用JobScheduler来定时执行任务,保证Service不会被杀死; 

在AndroidManifest.xml中添加android:persistent="true"属性,让Service在系统重启后仍然存在;

在AndroidManifest.xml中添加android:stopWithTask="false"属性,让Service在用户退出应用时仍然存在;

在华为平板上使用华为的自启动管理器,让Service在系统重启后仍然存在; 

使用第三方推送服务,如果Service被杀死,可以通过推送服务来重新启动Service; 

 🔥 Service和IntentService 区别 🔥

Service服务是长期运行在后台;

它不是单独的进程,因为它和应用程序在同一个进程;

也不是单独的线程,它跟线程没有任何关系,所以不能进行耗时操作;

如果直接把耗时操作放在Service中的onStartCommand()中,可能发生ANR,如果有耗时操作,就必须开启一个单独的线程来处理;

启动方式和Service一样,都是startService();

继承于Service,包含Service所有特性,包括生命周期,是处理异步请求的一个类;

一般自定义一个InitializeService继承Service,然后复写onHandleIntent()方法,在这个方法中初始化这些第三方的,来执行耗时操作;

可以启动多次IntentService,每一个耗时操作以工作队列在onHandleIntent()方法中执行,执行完第一个再去执行第二个,以此类推;

所有的请求都在单线程中,不会阻塞主线程,同一个时间只处理同一个请求;

不需要像在Service中一样,手动开启线程,任务执行完成后不需要手动调用stopSelf()方法来停止服务,系统会自动关闭服务;

IntentService使用

一般用于App启动时在BaseApplication中初始化一些第三方的东西,比如腾讯Bugly、腾讯X5WebView、OkhttpUtils等,目的就是为了防止BaseApplication中加载东西过多,导致App启动速度过慢,所以就自定义一个 InitializeService,继承Service,然后重写 onHandleIntent()方法,在这个方法中初始化这些第三方的;

BaseApplication代码如下:

public class BaseApplication extends Application {@Overridepublic void onCreate() {super.onCreate();//initLeakCanary();                //Square公司内存泄漏检测工具//在子线程中初始化InitializeService.start(this);}
}

 InitializeService代码如下:

/*** Email: 2185134304@qq.com* Created by JackChen 2018/4/12 15:35* Version 1.0* Params:* Description:
*/
public class InitializeService extends IntentService {private static final String ACTION_INIT = "initApplication";public static void start(Context context) {Intent intent = new Intent(context, InitializeService.class);intent.setAction(ACTION_INIT);context.startService(intent);}public InitializeService(){super("InitializeService");}@Overrideprotected void onHandleIntent(Intent intent) {if (intent != null) {final String action = intent.getAction();if (ACTION_INIT.equals(action)) {initApplication();}}}private void initApplication() {initBugly();                                    //初始化腾讯bug管理平台
//        BaseConfig.INSTANCE.initConfig();               //初始化配置信息LogUtils.logDebug = true;                       //开启日志}/*** 初始化腾讯bug管理平台*/private void initBugly() {/* Bugly SDK初始化* 参数1:上下文对象* 参数2:APPID,平台注册时得到,注意替换成你的appId* 参数3:是否开启调试模式,调试模式下会输出'CrashReport'tag的日志* 注意:如果您之前使用过Bugly SDK,请将以下这句注释掉。*/CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(getApplicationContext());strategy.setAppVersion(AppUtils.getAppVersionName());strategy.setAppPackageName(AppUtils.getAppPackageName());strategy.setAppReportDelay(20000);                          //Bugly会在启动20s后联网同步数据/*  第三个参数为SDK调试模式开关,调试模式的行为特性如下:输出详细的Bugly SDK的Log;每一条Crash都会被立即上报;自定义日志将会在Logcat中输出。建议在测试阶段建议设置成true,发布时设置为false。*/CrashReport.initCrashReport(getApplicationContext(), "126dde5e58", true ,strategy);Log.e("TAG" , "初始化bugly111") ;}
}

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

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

相关文章

数据库架构演变过程

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

【JavaEE】博客系统前后端交互

目录 一、准备工作 二、数据库的表设计 三、封装JDBC数据库操作 1、创建数据表对应的实体类 2、封装增删改查操作 四、前后端交互逻辑的实现 1、博客列表页 1.1、展示博客列表 1.2、博客详情页 1.3、登录页面 1.4、强制要求用户登录&#xff0c;检查用户的登录状态 …

浏览器访问nginx转发打开oss上的html页面默认是下载,修改为预览

使用阿里云盒OSS上传了html页面&#xff0c;在nginx里配置跳转访问该页面时&#xff0c;在浏览器里直接默认下载了该页面&#xff0c;现在想实现预览功能&#xff0c;只需在nginx里的location里修改消息头的Content-Disposition为inline即可 注意要隐藏头信息proxy_hide_header…

万年历【小游戏】(Java课设)

系统类型 Java实现的小游戏 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Idea或eclipse 运行效果 更多Java课设系统源码地址&#xff1a;更多Java课设系统源码地址 更多Java小游戏运行效果展示&#xff1a;更多Java小游戏运行效果展…

pytest——断言后继续执行

前言 在编写测试用例的时候&#xff0c;一条用例可能会有多条断言结果&#xff0c;当然在自动化测试用例中也会遇到这种问题&#xff0c;我们普通的断言结果一旦失败后&#xff0c;就会出现报错&#xff0c;哪么如何进行多个断言呢&#xff1f;pytest-assume这个pytest的插件就…

TiProxy 原理和实现

说明 在上篇《TiProxy 尝鲜》 中做了一些实验&#xff0c;比如加减tidb节点后tiproxy可以做到自动负载均衡&#xff0c;如果遇到会话有未提交的事务则等待事务结束才迁移。 本次主要研究这样的功能在tiproxy中是如何实现的&#xff0c;本次分享内容主要为以下几部分&#xff…

【 Python 全栈开发 - 人工智能篇 - 45 】决策树与随机森林

文章目录 一、概念与原理1.1 决策树1.1.1 概念1.1.2 原理特征选择分割方法 1.1.3 优点与缺点1.1.4 Python常用决策树算法 1.2 随机森林1.2.1 概念1.2.2 原理1.2.3 优点与缺点1.2.4 Python常用随机森林算法 1.3 决策树与随机森林的比较1.3.1 相同之处1.3.2 不同之处 二、决策树算…

嵌入式开发:单片机嵌入式Linux学习路径

SOC&#xff08;System on a Chip&#xff09;的本质区别在于架构和功能。低端SOC如基于Cortex-M架构的芯片&#xff0c;如STM32和NXP LPC1xxx系列&#xff0c;不具备MMU&#xff08;Memory Management Unit&#xff09;&#xff0c;适用于轻量级实时操作系统如uCOS和FreeRTOS。…

Vue基础-综合案例(基于vue2)

一、目标 能够知道如何使用vue-cli创建vue项目能够知道如何在项目中安装与配置element-ui能够知道element-ui中常见组件的用法能够知道如何使用axios中的拦截器能够知道如何配置proxy接口代理 二、目录 vue-cli组件库axios拦截器proxy跨域代理用户列表案例 vue-cli 1.什么…

利用mysqldump实现分库分表备份的shell脚本

一、信息摘要 linux版本&#xff1a;CentOS 7.9 mysql版本&#xff1a;MySQL 5.7.36 脚本实现功能&#xff1a;利用mysqldump工具实现对mysql中的数据库分库备份&#xff0c;和对所备份数据库中的表分表备份 二、shell脚本 #!/bin/bash ######################### #File n…

[Linux]进程控制详解!!(创建、终止、等待、替换)

hello&#xff0c;大家好&#xff0c;这里是bang___bang_&#xff0c;在上两篇中我们讲解了进程的概念、状态和进程地址空间&#xff0c;本篇讲解进程的控制&#xff01;&#xff01;包含内容有进程创建、进程等待、进程替换、进程终止&#xff01;&#xff01; 附上前2篇文章…

使用Beego和MySQL实现帖子和评论的应用,并进行接口测试(附源码和代码深度剖析)

文章目录 小项目介绍源码分析main.gorouter.gomodels/user.gomodels/Post.gomodels/comment.gocontrollers/post.gocontrollers/comment.go 接口测试测试增加帖子测试查看帖子测试增加评论测试查看评论 小项目介绍 经过对需求的分析&#xff0c;我增加了一些额外的东西&#x…

Open3D (C++) ISS特征点提取

目录 一、算法原理1、原理概述2、参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。爬虫网站自重,把自己当个人 一、算法原理 1、原理概述 内部形状描述子(ISS)是一种表示立体几何形状的方法,该算法含有丰富的几何特征信息,可以完成高质量的点云配准。设…

禁用右键菜单AMD Software: Adrenalin Edition

本文参考链接&#xff1a; 删除win11右键一级菜单的AMD驱动栏 - 哔哩哔哩 windows11 求助删除右键菜单方法_windows11吧_百度贴吧 Windows安装最新的AMD显卡驱动后&#xff0c;右键菜单会多出AMD Software: Adrenalin Edition。使用一些右键菜单管理工具也没能屏蔽禁用掉该功…

mybatis-config.xml-配置文件详解

文章目录 mybatis-config.xml-配置文件详解说明文档地址:配置文件属性解析properties 属性应用实例 settings 全局参数定义应用实例 typeAliases 别名处理器举例说明 typeHandlers 类型处理器environments 环境environment 属性应用实例 mappers配置 mybatis-config.xml-配置文…

运维高级--shell脚本完成分库分表

为什么要进行分库分表 随着系统的运行&#xff0c;存储的数据量会越来越大&#xff0c;系统的访问的压力也会随之增大&#xff0c;如果一个库中的表数据超过了一定的数量&#xff0c;比如说MySQL中的表数据达到千万级别&#xff0c;就需要考虑进行分库分表&#xff1b; 其…

iOS-持久化

目的 1.快速展示&#xff0c;提升体验 已经加载过的数据&#xff0c;用户下次查看时&#xff0c;不需要再次从网络&#xff08;磁盘&#xff09;加载&#xff0c;直接展示给用户 2.节省用户流量&#xff08;节省服务器资源&#xff09; 对于较大的资源数据进行缓存&#xf…

ClickHouse(六):Clickhouse数据类型-1

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

从电容到晶体管的基本介绍

​随着科技的不断进步&#xff0c;元器件在现代电子学中扮演着至关重要的角色。从电容器到晶体管&#xff0c;各种元器件都具有不同的特性和用途。本文将从基础知识出发&#xff0c;介绍电子学中常见的元器件&#xff0c;以及它们在电路中的作用和应用。 电容器 电容器是一种…

Docker复杂命令便捷操作

启动所有状态为Created的容器 要启动所有状态为"created"的Docker容器&#xff0c;可以使用以下命令&#xff1a; docker container start $(docker container ls -aq --filter "statuscreated")上述命令执行了以下步骤&#xff1a; docker container l…