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状态。

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

Android 面试题 应用程序结构 九_第1张图片

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

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

Android 面试题 应用程序结构 九_第2张图片

  核心应用程序 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 中配置:



    
        
        
    

    
        
        
        
    

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

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

findViewById

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

 Android 面试题 应用程序结构 九_第3张图片

 因而,暴露出去给其他 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的生命周期  

 Android 面试题 应用程序结构 九_第4张图片

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: [email protected]
 * 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") ;

    }
}

你可能感兴趣的:(Android,面试题,应用程序结构)