Android 面试题 应用程序结构 九

news2024/11/19 2:28:14

🔥 核心应用程序  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: TextView
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_launch)

        initViews()
        updateIntent()
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        // 更新 intent
        setIntent(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-->
<activity
    android: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 {
    @Override
    public 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");
    }

    @Override
    protected 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.coloradmin.cn/o/813504.html

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

相关文章

大数据Flink(五十四):Flink用武之地

文章目录 Flink用武之地 一、Event-driven Applications【事件驱动】 二、Data Analytics Applications【数据分析】 三、​​​​​​​Data Pipeline Applications【数据管道】 Flink用武之地 应用场景 | Apache Flink 从很多公司的应用案例发现&#xff0c;其实Flink主…

tinkerCAD案例:25. 量角器 - 测量角度

tinkerCAD案例&#xff1a;25. 量角器 - 测量角度 原文 Now we’re going to make a protractor! A Protractor is one of the most basic, but essential, tools for making measurements. It is, then, surprising that the modern protractor is barely over 200 years ol…

简单实现jdk1.7HashMap

1.定义一个Map接口,Entry<K,V>对象为Map的元素 package test;public interface Map<K,V> {V put(K k,V v);V get(K k);int size();interface Entry<K,V>{K getKey();V getValue();}}2.主要实现了put,get以及size()方法 package test;public class HashMap&…

uniapp小程序,根据小程序的环境版本,控制的显页面功能按钮的示隐藏

需求&#xff1a;根据小程序环境控制控制页面某个功能按钮的显示隐藏&#xff1b; 下面是官方文档和功能实现的相关代码&#xff1a; 实现上面需要&#xff0c;用到了uni.getAccountInfoSync()&#xff1a; uni.getAccountInfoSync() 是一个 Uniapp 提供的同步方法&#xff0c…

零代码编程:用ChatGPT对Excel表格进行批量自动化处理

F盘的“北交所上市公司全部发明专利”文件夹里面有几百个这样的Excel表格&#xff0c;格式一致&#xff0c;需要合并所有表格内容到一个表格&#xff0c;方便查找内容&#xff0c;但是不要前面两行。 可以在ChatGPT中这样输入&#xff1a; 写一段Python程序&#xff1a; F盘的…

基于opencv与机器学习的摄像头实时识别数字!附带完整的代码、数据集和训练模型!!

前言 使用摄像头实时识别数字算是目标检测任务&#xff0c;总体上分为两步&#xff0c;第一步是检测到数字卡片的位置&#xff0c;第二步是对检测到的数字卡片进行分类以确定其是哪个数字。在第一步中主要涉及opencv的相关功能&#xff0c;第二步则使用机器学习的方式进行分类…

求三个球面交点的高效解法

文章目录 一、问题描述二、推导步骤代数法几何法 三、MATLAB代码 一、问题描述 如图&#xff0c;已知三个球面的球心坐标分别为 P 1 ( x 1 , y 1 , z 1 ) , P 2 ( x 2 , y 2 , z 2 ) , P 3 ( x 3 , y 3 , z 3 ) P_1(x_1,y_1,z_1),P_2(x_2,y_2,z_2),P_3(x_3,y_3,z_3) P1​(x1​,…

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

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

【机器学习】西瓜书习题3.3Python编程实现对数几率回归

参考代码 结合自己的理解&#xff0c;添加注释。 代码 导入相关的库 import numpy as np import pandas as pd import matplotlib from matplotlib import pyplot as plt from sklearn import linear_model导入数据&#xff0c;进行数据处理和特征工程 # 1.数据处理&#x…

ChatGPT炒股:爬取股票官方微信公众号的新闻资讯

上市公司的微信公众号&#xff0c;现在已经成为官网之外最重要的官方信息发布渠道。有些不会在股票公告中发布的消息&#xff0c;也会在微信公众号进行发布。所以&#xff0c;跟踪持仓股票的公众号信息&#xff0c;非常重要。 下面&#xff0c;以贝特瑞的官方公众号“贝特瑞新…

合并两个有序数组——力扣88

文章目录 题目描述法一 双指针法二 逆向双指针 题目描述 法一 双指针 使用双指针方法&#xff0c;将两个数组看作队列&#xff0c;每次从两个数组头部取出比较小的数字放到结果中。 void merge(vector<int>&nums1, int m,vector<int>&nums2, int n){int p1…

无涯教程-jQuery - Select menu组件函数

小部件选择菜单功能可与JqueryUI中的小部件一起使用&#xff0c;它提供了可替换样式的选择元素。一个简单的选择菜单如下所示。 Select menu - 语法 $( "#menu" ).selectmenu(); Select menu - 示例 以下是显示选择菜单用法的简单示例- <!doctype html> &…

关于Java的多线程实现

多线程介绍 进程&#xff1a;进程指正在运行的程序。确切的来说&#xff0c;当一个程序进入内存运行&#xff0c;即变成一个进程&#xff0c;进程是处于运行过程中的程序&#xff0c;并且具有一定独立功能。 线程&#xff1a;线程是进程中的一个执行单元&#xff0c;负责当前进…

大数据课程D11——hadoop的Ganglia

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Ganglia的概念&#xff1b; ⚪ 掌握Ganglia的安装操作&#xff1b; ⚪ 掌握Ganglia的监控Flume操作&#xff1b; 一、概述 1. Ganglia是UC Berkeley发起的一个开源…

JVM基础篇-程序计数器

程序计数器 定义 Program Counter Register 程序计数器&#xff08;寄存器&#xff09; 作用:记住下一条jvm指令的执行地址特点 是线程私有的:每个线程都有自己的程序计数器不会存在内存溢出(规定) 作用 左侧:jvm指令 右侧:java代码 0: getstatic #20 // PrintSt…

三维点云与深度图相互转换

点云转深度图 一、效果二、实现原理与代码2.1 获取点云边界2.2 确定图像大小2.3 稀疏点图像填充2.4 完整代码三、由深度图转换回点云信息丢失问题3.1 深度图转点云3.2 深度图转点云代码3.3 多视角的深度图融合一、效果 对点云进行转换,z向表示深度,转换效果如下 二、实现…

Docker安装配置启动Oracle11g容器解决ORA-12541:TNS: 无监听程序连接第三方客户端

Windows下安装可参考我这篇&#xff1a;win11&win7下安装oracle11g数据库全过程 一、下载与启动 前提&#xff1a;需要安装配置好docker(设置镜像源、配置阿里云加速)等&#xff0c;可参考我这篇 基于CentOS7安装配置docker与docker-compose 。 Docker容器相关操作可参考…

【自动化运维】playbook剧本

目录 一、Ansible 的脚本 playbook 剧本1.1playbooks的组成 二、剧本编写实验2.1定义、引用变量2.2使用远程主机sudo切换用户2.3whenn条件判断2.4迭代 三、Templates 模板四、Tags模板 一、Ansible 的脚本 playbook 剧本 1.1playbooks的组成 &#xff08;1&#xff09;Tasks&…

Diffusion扩散模型学习2——Stable Diffusion结构解析-以文本生成图像(文生图,txt2img)为例

Diffusion扩散模型学习2——Stable Diffusion结构解析-以文本生成图像&#xff08;文生图&#xff0c;txt2img&#xff09;为例 学习前言源码下载地址网络构建一、什么是Stable Diffusion&#xff08;SD&#xff09;二、Stable Diffusion的组成三、生成流程1、文本编码2、采样流…

Python自动化测试----生成测试报告

如何才能让用例自动运行完之后&#xff0c;生成一张直观可看易懂的测试报告呢&#xff1f; 对于自动化测试有兴趣的朋友可以观看这个视频&#xff1a; 【整整200集】超超超详细的Python接口自动化测试进阶教程&#xff0c;真实模拟企业项目实战&#xff01;&#xff01; 小编使…