Android 基础面试题

Application生命周期 

       自定义的Application,然后在Application的生命周期中做一些操作

       生命周期方法有onCreate()  onTerminate()  onLowMemory onTerimMemory() 

       

public class App extends Application {

    @Override
    public void onCreate() {
        // 程序创建的时候执行
        Log.d(TAG, "onCreate");
        super.onCreate();
    }
    @Override
    public void onTerminate() {
        // 程序终止的时候执行
        Log.d(TAG, "onTerminate");
        super.onTerminate();
    }
    @Override
    public void onLowMemory() {
        // 低内存的时候执行
        Log.d(TAG, "onLowMemory");
        super.onLowMemory();
    }
    @Override
    public void onTrimMemory(int level) {
        // 程序在内存清理的时候执行
        Log.d(TAG, "onTrimMemory");
        super.onTrimMemory(level);
    }
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Log.d(TAG, "onConfigurationChanged");
        super.onConfigurationChanged(newConfig);
    }
    
}

 

Android Activity生命周期

转载于 https://blog.csdn.net/javazejian/article/details/51932554

 

  //Activity在被创建时候调用 生命周期中第一个调用的方法
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    //时表示Activity正在启动 说明Activity已经是可见状态
    @Override
    protected void onStart() {
        super.onStart();
    }

    //Activity已在前台可见,可与用户交互了
    @Override
    protected void onResume() {
        super.onResume();
    }

    //此方法被回调时则表示Activity正在停止
    @Override
    protected void onPause() {
        super.onPause();
    }

    //在onPause方法执行完成直接执行,表示Activity即将停止或者完全被覆盖
    @Override
    protected void onStop() {
        super.onStop();
    }

    //表示Activity正在重新启动,当Activity由不可见变为可见状态时
    @Override
    protected void onRestart() {
        super.onRestart();
    }

    //此时Activity正在被销毁,也是生命周期最后一个执行的方法
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

当Activity启动时,依次会调用onCreate(),onStart(),onResume(),而当Activity退居后台时(不可见,点击Home或者被新的Activity完全覆盖),onPause()和onStop()会依次被调用。当Activity重新回到前台(从桌面回到原Activity或者被覆盖后又回到原Activity)时,onRestart(),onStart(),onResume()会依次被调用。当Activity退出销毁时(点击back键),onPause(),onStop(),onDestroy()会依次被调用,到此Activity的整个生命周期方法回调完成。

Activity异常生命周期

异常的生命周期是指Activity被系统回收或者当前设备的Configuration发生变化(一般指横竖屏切换)从而导致Activity被销毁重建。

1 相关的系统配置发生改变导致Activity被杀死并重新创建(一般指横竖屏切换)

我们正常启动Activity时,onCreate,onStart,onResume方法都会依次被回调,而如果我们此时把竖屏的Activity人为的调整为横屏,我们可以发现onPause,onSaveInstanceState,onStop,onDestroy,onCreate,onStart,onRestoreInstanceState,onResume依次被调用,单从调用的方法我们就可以知道,Activity先被销毁后再重新创建,其异常生命周期如下: 

Snip20160716_8

 

Snip20160716_9 

现在异常生命周期的流程我们大概也就都明白,但是onSaveInstanceState和onRestoreInstanceState方法是干什么用的呢?实际上这两个方法是系统自动调用的,当系统配置发生变化后,Activity会被销毁,也就是onPause,onStop,onDestroy会被依次调用,同时因为Activity是在异常情况下销毁的,android系统会自动调用onSaveInstanceState方法来保存当前Activity的状态信息,因此我们可以在onSaveInstanceState方法中存储一些数据以便Activity重建之后可以恢复这些数据,当然这个方法的调用时机必须在onStop方法之前,也就是Activity停止之前。至跟onPause方法的调用时机可以随意。而通过前面的Log信息我们也可以知道当Activity被重新创建之后,系统还会去调用onRestoreInstanceState方法,并把Activity销毁时通过onSaveInstanceState方法保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法,因此我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重新创建,倘若被重建了,我们就可以对之前的数据进行恢复。从Log信息,我们可以看出onRestoreInstanceState方法的调用时机是在onStart之后的。这里有点需要特别注意,onSaveInstanceState和onRestoreInstanceState只有在Activity异常终止时才会被调用的,正常情况是不会调用这两个方法的。 

onRestoreInstanceState和onCreate方法都可以进行数据恢复,两者的区别在于,onRestoreInstanceState方法一旦被系统回调,其参数Bundle一定不为空,无需额外的判断,而onCreate的Bundle却不一定有值,因为如果Activity是正常启动的话,Bundle参数是不会有值的,因此我们需要额外的判断条件

2 内存不足导致低优先级的Activity被杀死

当系统内存不足的时候,系统就会按照一定的优先级去杀死目标Acitivity的进程来回收内存,并且此时Activity的onSaveInstanceState方法会被调用来存储数据,并在后续Activity恢复时调用onRestoreInstanceState方法来恢复数据,所以为了Activity所在进程尽量不被杀死,我们应该尽量让其保持高的优先级。那什么样的优先级较高呢?为了更深入了解这个问题,我们先来进入一个新的话题,Android 进程层次。

Android 进程层次

  在android系统中最重要的进程被称为前台进程,然后依次是任何可见进程、服务进程、后台进程,最后是空进程。接下来我们将进一步展开。 
  在开始之前我们先要明确一个问题,当我们谈论进程优先级的时候是以 activity、service 这样的组件来说的,但请注意这些优先级是在进程的级别上的,而非组件级别。只要有一个组件是前台进程,就会将整个进程变为前台进程。同时我们要知道绝大多数应用是单进程的,如果我们有生命周期差异很大的不同部分或者某个部分非常重量型,那么我们强烈建议把它们分为不同的进程,以便让重量级进程尽早被系统回收。 
明白这点后我们再看看下面一张图(图来自Who lives and who dies? Process priorities on Android): 
android_process
从图中我们可以看出共有5中优先级线程,Foreground Processes ,Visible Processes ,Service Processes , Background Processes , Empty Processes ;

  • 1.Foreground Processes(前台进程)

  系统中前台进程的数量很少(这点从图中也是可以看出来的), 前台进程几乎不会被杀死. 只有当内存低到无法保证所有的前台进程同时运行时才会选择杀死某个前台进程.以下几种都属于前台进程: 
a. 处于前台正与用户交互的activity 
b. 与前台activity绑定的service 
c. 调用了startForeground()方法的service 
d. 正在执行onCreate(), onStart(), 或onDestroy()方法的service 
e. 正在执行onReceive()方法的BroadcastReceiver. 
凡是包含以上任意一种情况的进程都是前台进程。当然一些 activity 在依靠他们成为前台进程的同事,也可能依赖 bound service 。任何进程,如果它持有一个绑定到前台 activity 的服务,那么它也被赋予了同样的前台优先级。

  • 2.Visible Processes(可视进程)

  此时如果一个Activity可见但并非处于前台时,如在Activity中弹出了一个对话框,从而导致Activity可见但位于后台无法与用户交互,这个进程就可以被视为可见进程,同时我们也必须明白可见 activity 的 bound service 和 content provider 也处于可见进程状态。这同样是为了保证使用中的 activity 所依赖的进程不会被过早地杀掉。但还是需要注意的是,只是可见并不意味着不能被杀掉。如果来自前台进程的内存压力过大,可见进程仍有可能被杀掉。从用户的角度看,这意味着当前 activity 背后的可见 activity 会被黑屏代替。当然,倘若我们正确地重建 activity ,在前台 activity 关闭之后,我们的进程和 activity 会立刻恢复而没有数据损失。

  • 3.Service Processes(服务进程)

  如果我们通过startService()启动一个service服务,那么它被看作是一个服务进程。对于许多在后台做处理(如异步加载数据,获取耗时资源等)而没有立即成为前台服务的app都属于这种情况。这是比较常见也是最合理的后台处理方式,这种进程只有在可见进程和前台进程内存不足时才有可能被杀掉。

  • 4.Background Processes(后台进程)

  假如我们的Activity目前是前台进程,但是这时候,我们点Home键,将导致onPause,onStop方法被调用,我们的进程也就变成了后台进程,当然我们的后台进程并不会被立马杀死,所以这些进程会保留一段时间,直到更高优先级进程需要内存的时候才被回收,并且是按照最近最少使用顺序(最少使用的会被优先回收)。很多时候我们会发现手机的内存大都是被后台App进程占用了大部分空间,而android系统这样做的好处是可以使用我们在下次重新打开此进程的app时无需重新分配和加载资源,从而拥有更好的用户体验。 
。然而内存不足的时候,他们仍然会被杀掉,所以我们应该和可见 activity 处理情况一样,应该尽量能够在不丢失用户状态的情况下重建这些 activity ,以便达到更佳的用户体验。

  • 5.Empty Processes(空进程)

  在任何层次中,空进程都是最低优先级的。如果我们的进程不属于以上类别,那它就是空进程。空进程是没有活跃的组件,只是出于缓存的目的而被保留(为了更加有效地使用内存而不是完全释放掉),只要 Android 系统内存需要可以随时杀掉它们。

嗯,现在我们应该对android进程有个比较明确的概念了,回到之前的的Activity内存不足被android系统杀死的话题,为了防止一些重要的Activity不被意外杀死,我们应该让当前所在进程的Activity保持较高的优先级,如使其变为前台进程,或者通过service去绑定,也可以让其成为单独的进程。当然如果真的不幸被杀死,我们也应该尽量通过onSaveInstanceState方法和onRestoreInstanceState方法来保存和恢复数据,以便获得更佳的用户体验。

解决Activity销毁重建问题

通过上面的分析我们知道当系统配置发生变化后,Activity会被重建,那有没有办法使其不重建呢?方法自然是有的,那就是我们可以给Activity指定configChange属性,当我们不想Activity在屏幕旋转后导致销毁重建时,可以设置configChange=“orientation”;当SDK版本大于13时,我们还需额外添加一个“screenSize”的值,对于这两个值含义如下: 



    
        
            
                
 
                
            
        
 
        
    


orientation:屏幕方向发生变化,配置该参数可以解决横竖屏切换时,Activity重建问题(API<13) 
screenSize:当设备旋转时,屏幕尺寸发生变化,API>13后必须配置该参数才可以保证横竖切换不会导致Activity重建。 
说白了就是设置了这两个参数后,当横竖屏切换时,Activity不会再重建并且也不会调用之前相关的方法,取而代之的是回调onConfigurationChanged方法。案例代码如下:

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LogUtils.e("onConfigurationChanged is invoke!!!");
    }

我们运行程序,然后执行横竖屏切换,看看打印的Log信息: 
 
从Log可以看到Activity确实没有重建,并且也没有回调onSaveIntanceState方法和RestoreInstanceState方法来存储或者恢复数据,相反的是onConfigurationChanged方法被回调了,因此我们在这种情况下,可以在此方法中做一些额外的工作

 

Android Service、IntentService,Service和组件间通信

Service 有2种启动方式,startService 启动服务,服务启动起来后,在后台无限期运行,直到通过stopService 或者 stopSelf 停止服务,服务与组件独立,通信比较困难(但还是有办法的,通过BroadcastReceiver )。另一种方式就是 bindService 即绑定服务,组件和服务绑定在一起,服务的生命后期受组件影响,如果绑定到服务的组件全部被销毁了,那么服务也就会停止了。绑定服务的方式通常用于组件和服务之间 需要相互通信。startService 这种 方式一般用于在后台执行任务,而不需要返回结果给组件。 这两种方式并非完全独立,也就是说,你可以绑定已经通过 startService 启动起来的服务,可以通过在Intent 中添加Action 来标示要执行的动作。比如:通过Intent Action 标记要播放的音乐,调用startService 来启动音乐服务播放音乐,在界面需要显示播放进度的时候,可以通过binderService 来绑定服务,从而获取歌曲信息。这种情况下,Service 需要实现两种方式的生命周期。这种情况下,除非所有客户端都已经取消绑定,否则通过stopService 或者 stopSelf 是不能停止服务的。

IntentService是Service 的子类,默认给我们开启了一个工作线程执行耗时任务,并且执行完任务后自 动停止服务。扩展IntentService比较简单,提供一个构造方法和实现onHandleIntent 方法就可了,不用重写父类的其他方法。但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder 的。使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。

 

Activity的onNewIntent

Android总Activity的启动模式分为四种:

1 Standard模式

Standard模式是Android的默认启动模式,你不在配置文件中做任何设置,那么这个Activity就是standard模式,这种模式下,Activity可以有多个实例,每次启动Activity,无论任务栈中是否已经有这个Activity的实例,系统都会创建一个新的Activity实例

2 SingleTop模式

SingleTop模式和standard模式非常相似,主要区别就是当一个singleTop模式的Activity已经位于任务栈的栈顶,再去启动它时,不会再创建新的实例,如果不位于栈顶,就会创建新的实例,现在把配置文件中FirstActivity的启动模式改为SingleTop,我们的应用只有一个Activity,FirstActivity自然处于任务栈的栈顶。

3 SingleTask模式

         SingleTask模式的Activity在同一个Task内只有一个实例,如果Activity已经位于栈顶,系统不会创建新的Activity实例,和singleTop模式一样。但Activity已经存在但不位于栈顶时,系统就会把该Activity移到栈顶,并把它上面的activity出栈。

4 SingleInstance模式

singleInstance模式也是单例的,但和singleTask不同,singleTask只是任务栈内单例,系统里是可以有多个singleTask Activity实例的,而singleInstance Activity在整个系统里只有一个实例,启动一singleInstanceActivity时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。

SingleInstance模式并不常用,如果我们把一个Activity设置为singleInstance模式,你会发现它启动时会慢一些,切换效果不好,影响用户体验。它往往用于多个应用之间,例如一个电视launcher里的Activity,通过遥控器某个键在任何情况可以启动,这个Activity就可以设置为singleInstance模式,当在某应用中按键启动这个Activity,处理完后按返回键,就会回到之前启动它的应用,不影响用户体验。

 

前提:ActivityA已经启动过,处于当前应用的Activity任务栈中; 

当ActivityA的LaunchMode为Standard时:

由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法

当ActivityA的LaunchMode为SingleTop时:

如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 ,生命周期顺序为:

onCreate--->onStart--->onResume---onPause>onNewIntent--->onResume

当ActivityA的LaunchMode为SingleInstance,SingleTask:

如果ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法,生命周期调用顺序为:

onCreate--->onStart--->onResume---按下Home键>onPause--->onstop--->onNewIntent--->onRestart--->onstart--->onResume
 

ContentProvider实例详解

ContentProvider通过uri来标识其它应用要访问的数据,通过ContentResolver的增、删、改、查方法实现对共享数据的操作。

要想让其它应用能使用自己的数据(例如通讯录)这个时候就用到了ContentProvider。

ContentProvider是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:
public boolean onCreate()
在创建ContentProvider时使用
public Cursor query()
用于查询指定uri的数据返回一个Cursor
public Uri insert()
用于向指定uri的ContentProvider中添加数据
public int delete()
用于删除指定uri的数据
public int update()
用户更新指定uri的数据
public String getType()
用于返回指定的Uri中的数据MIME类型
数据访问的方法insert,delete和update可能被多个线程同时调用,此时必须是线程安全的

public class StudentContentProvider extends ContentProvider {

    //这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities
    private static final String AUTHORITY = "com.jrmf360.studentProvider";

    //匹配成功后的匹配码
    private static final int MATCH_CODE = 100;

    private static UriMatcher uriMatcher;

    private StudentDao studentDao;

    //数据改变后指定通知的Uri
    private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");

    static {
        //匹配不成功返回NO_MATCH(-1)
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        //添加我们需要匹配的uri
        uriMatcher.addURI(AUTHORITY,"student", MATCH_CODE);
    }


    @Override
    public boolean onCreate() {
        studentDao = StudentDao.getInstance(getContext());
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int match = uriMatcher.match(uri);
        if (match == MATCH_CODE){
            Cursor cursor = studentDao.queryStudent();
            return cursor;
        }
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        if (uriMatcher.match(uri) == MATCH_CODE){
            studentDao.insertStudent(values);
            notifyChange();
        }
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        if (uriMatcher.match(uri) == MATCH_CODE){
            int delCount = studentDao.deleteStudent();
            notifyChange();
            return delCount;
        }
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
                      @Nullable String[] selectionArgs) {
        return 0;
    }

    private void notifyChange(){
        getContext().getContentResolver().notifyChange(NOTIFY_URI,null);
    }
}

xml文件中声明,声明如下:

  

authorities唯一标识该内容提供者,这样其它的应用才可以找到该内容提供者并操作它的数据;exported为true当前内容提供者可以被其它应用使用,默认为true。

通过其他的应用操作ContentProvider中的数据

获得ContentObserver

   contentResolver = getContentResolver();

通过ContentProvider来操作数据

@Override
public void onClick(View v) {
    int id = v.getId();
    if (id == R.id.btn_query) {
        Cursor cursor = contentResolver.query(STUDENT_URI, null, null, null, null, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                String name = cursor.getString(cursor.getColumnIndex("name"));
                int age = cursor.getInt(cursor.getColumnIndex("age"));
                String desc = cursor.getString(cursor.getColumnIndex("desc"));
                Log.e(getClass().getSimpleName(), "Student:" + "name = " + name + ",age = " + age + ",desc = " + desc);
            }
            cursor.close();
        }
    } else if (id == R.id.btn_insert) {
        ContentValues contentValues = new ContentValues();
        contentValues.put("name", "新插入");
        contentValues.put("age", "-100");
        contentValues.put("desc", "我是新插入的呦。。。");
        contentResolver.insert(STUDENT_URI, contentValues);
    } else if (id == R.id.btn_del) {
        contentResolver.delete(STUDENT_URI, null, null);
    }
}

 

 

BroadcastReceiver使用总结

用于不同组件和多线程之间的通信

静态注册和动态注册的区别

静态注册:在AndroidManifest.xml 中,通过标签来声明

  
    
        
    
  

优点:不受其他组件生命周期影响,即使应用程序被关闭,也能接收广播。
缺点:耗电,占内存。
适用场景:需要时刻监听的广播

 

动态注册:在代码中注册

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        receiver = new NetStateReceiver();
        registerReceiver(receiver, intentFilter);
        unregisterReceiver(receiver);//注销

优点:灵活,不耗电,易控,省内存
缺点:需要手动注销
适用场景:需要特定时候监听的广播

 

有序广播

一般广播都是所有接收者同时执行的,有序广播有所不同。
他根据广播的优先级,按顺序执行。只有当优先级高的广播接收者onReceive()方法执行完毕,才会执行优先级低的广播接受者的onReceive()方法。同时,当前正在执行的广播接收者能够终止广播的传播。

intentFilter.setPriority(100);//设置Priority来设置优先级 优先级越高的越早能接收到广播。(取 值范围:-1000~10000)


 

 

 

你可能感兴趣的:(android)