zoukankan      html  css  js  c++  java
  • 很全面的Android面试题

    这些有些来源于网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有,有可能有些问题讲的不对,能指正的希望大家不吝指教。

     

    Activity

    什么是Activity

    四大组件之一,一个和用户交的互界面就是一个activity,是所有 View 的容器

     

    Activity 生命周期

    生命周期描述的是一个类 从创建(new出来)到死亡(垃圾回收)的过程中会执行的方法.

    在这个过程中会针对不同的生命阶段会调用不同的方法

    Activity从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:

    • oncreate:Activity对象创建完毕,但此时不可见
    • onstart:Activity在屏幕可见,但是此时没有焦点
    • onResume:Activity在屏幕可见,并且获得焦点
    • onPause:Activity此时在屏幕依然可见,但是已经没有焦点
    • onStop:Activity已经不可见了,但此时Activity的对象还在内存中
    • onDestroy:Activity对象被销毁

    其实这些方法都是两两对应的,onCreate创建与onDestroy销毁;onStart可见与onStop不可见;onResume可编辑(即焦点)与onPause;

    还有一个onRestart方法了,在Activity被onStop后,但是没有被onDestroy,在再次启动此Activity时就调用onRestart(而不再调用onCreate)方法;如果被onDestroy了,则是调用onCreate方法。

      

    两个Activity之间跳转时必然会执行的是哪几个方法。

    一般情况比如说有两个activity,分别叫A,B ,当在A里面激活B组件的时候, A 会调用 onPause()方法,然后B 调用onCreate() ,onStart(), OnResume() ,这个时候B覆盖了窗体, A会调用onStop()方法.  如果B呢 是个透明的,或者是对话框的样式, 就不会调用onStop()方法。

    因此,我们在两个activities中传递数据,或者共享资源时(如数据库连接),需要在前一个activity的onPause()方法而不是onStop()方法中进行

     

    横竖屏切换时候Activity的生命周期。

    这个生命周期跟清单文件里的配置有关系

    1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,默认首先销毁当前activity,然后重新加载

    2、设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法,游戏开发中, 屏幕的朝向都是写死的

     

    如何将一个Activity设置成窗口的样式。

    可以自定义一个activity的样式

    android:theme="@android:style/Theme.Dialog"

     

    你后台的Activity被系统 回收怎么办?如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态?


     

     

     

     

     

     

    除了在栈顶的activity,其他的activity都有可能在内存不足的时候被系统回收,一个activity越处于栈底,被回收的可能性越大.如果有多个后台进程,在选择杀死的目标时,采用最近最少使用算法(LRU)。

    Activity中提供了一个 onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用, 可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。onSaveInstanceState()方法会携带一个 Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用 putString()方法保存字符串,使用 putInt()方法保存整型数据。每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle中取值,第二个参数是真正要保存的内容。在 MainActivity中添加如下代码就可以将临时数据进行保存:

    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        String tempData = "Something you just typed";
        outState.putString("data_key", tempData);
    }
    onCreate()方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null,但是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个参就会带有之前所保存的全部数据
    protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           if (savedInstanceState != null) {
         String tempData = savedInstanceState.getString("data_key");
         }
    

    也可以通过onRestoreInstanceState来存储和恢复数据,区别是不需要判断空了,onRestoreInstanceState调用一定是有值的

     Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。除非该activity是被用户主动销毁的,通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

    onSaveInstanceState()被执行的场景有哪些:

    系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

    1. 当用户按下HOME键时
    2. 长按HOME键,选择运行其他的程序时
    3. 锁屏时
    4. 从activity A中启动一个新的activity时
    5. 屏幕方向切换时

    如何退出Activity?

    退出activity 直接调用 finish () 方法 

    用户点击back键 就是退出一个activity ,退出activity 会执行 onDestroy()方法 。

    1、抛异常强制退出:

    该方法通过抛异常,使程序Force Close。不推荐使用

    验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。

    安全结束进程  android.os.Process.killProcess(android.os.Process.myPid());

    2、记录打开的Activity:

    每打开一个Activity,就用集合记录下来。在需要退出时,关闭每一个Activity即可。

    可以写在Application里,直接getApplication.list.add,在需要退出时遍历集合里的Activity,finish掉

    也可以定义一个baseactivity里面进行操作,记得一般不用的话最后都需要把list=null

    3、发送特定广播:

    //在baseactivity里注册广播

    registerReceiver(receiver, filter)

    //想退出的时候就在onRecriver方法里finish()。

    4、可以通过 intent的flag 来实现.. intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一个新的activity,然后在新的activity的oncreate方法里面就可以finish掉.

    讲一讲你对activity的理解 

    把上面的几点用自己的心得写出来

     

    两个Activity之间怎么传递数据?

     

    • 基本数据类型可以通过Intent 传递数据  

        //把数据封装至intent对象中

        //把数据封装至bundle对象中
        Bundle bundle = new Bundle();
        bundle.putString("malename", "李志");
        //把bundle对象封装至intent对象中
        intent.putExtras(bundle);

        startActivity(intent); 

    • 传递对象
    如果要传递对象,需要把对象类序列化,然后intent.putExtra("mp3Info", mp3Info)
    在另一个activity,或服务、广播中取出: Mp3Info mp3Info =(Mp3Info)intent.getSerializableExtra("mp3Info");
    实现Parcelable接口的可以用intent直接传 ,和Serializable区别就是Parcelable是直接在内存中读写,要比序列化要快,
    Serializabl会使用反射,序列化和反序列化过程需要大量 I/O 操作,但是要注意Parcelable传递对象的大小。

     

    怎么让在启动一个Activity是就启动一个service?

    在activity的onCreate()方法里面 startService();

     

    如何返回数据

    基本流程:

    • 使用 startActivityForResult(Intent intent, int requestCode) 方法打开 Activity;
    • 新 Activity 中调用 setResult(int resultCode, Intent data) 设置返回数据之后,关闭 Activity 就会调用onActivityResult 方法;
    • 在原来的activity里重写 onActivityResult(int requestCode, int resultCode, Intent data) 方法;
    • 注意:新的 Activity 的启动模式不能设置成 singleTask(如果已创建,会使用以前创建的)与 singleInstance(单

    独的任务栈) ,不能被摧毁(执行不到 finish 方法) ,父 Activity 中的 onActivityResult 方法将不会执行;

    请描述一下Intent 和 Intent Filter。

    Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。

    通过Intent 可以实现各种系统组件的调用与激活.

    Intent filter:是传递的信息,这些信息不是必须的,有:

    Action: 动作 view

    Data: 数据uri uri

    Category : 而外的附加信息

     

    隐式跳转
    • 隐式意图跳转至指定Activity

       

        <intent-filter >
            <action android:name="com.itheima.second"/>
            <data android:scheme="asd" android:mimeType="aa/bb"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
      
      Intent intent = new Intent("com.example.activitytest.ACTION_START");
    category 是默认的,不需要写,每个 Intent中只能指定一个 action,但却能指定多个 category
    intent.addCategory("com.example.activitytest.MY_CATEGORY");
    可以调用 Intent中的 addCategory()方法来添加一个 category
    intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可
    // 电话
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_CALL); // 设置动作
    intent.setData(Uri.parse("tel://123456")); // 设置数据
    // 网页
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://192.168.1.45:8080/androidWeb"));
    // 音频/视频,设置 type
    intent.setAction(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.parse("file:///mnt/sdcard/daqin.mp3"), "audio/*"); // 设置数据和数据类型,将启动音频播放器(vedio)
     
     

    PendingIntent和Intent区别

    它们都可以去指明某一个“意图”,都可以用于启动活动、启动服务以及发送广播等。不同的是,Intent更加倾向于去立即执行某个动作,而 PendingIntent更加倾向于在某个合适的时机去执行某个动作。所以,也可以把 PendingIntent简单地理解为延迟执行的 Intent。

    PendingIntent的用法同样很简单,它主要提供了几个静态方法用于获取 PendingIntent的实例,可以根据需求来选择是使用 getActivity()方法、getBroadcast()方法、还是 getService()方法。

    每8小时开启广播

    AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
    int anHour = 8 * 60 * 60 * 1000; // 这是8小时的毫秒数
    long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
    Intent i = new Intent(this, AutoUpdateReceiver.class);
    PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
    manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
    

      

    Intent传递数据和Bundle传递数据的区别

    Intent传递数据和Bundle传递数据是一回事,

    Intent传递时内部还是调用了Bundle。

    以下为源码:

    public Intent putExtra(String name, boolean value) {
       if (mExtras == null) {
          mExtras = new Bundle();
       }
       mExtras.putBoolean(name, value);
       return this;
    }
    

      

    启动一个Activity有哪几种方法

    startActivity

    startActivityforresult

    launcher的activity

    widget

     


    同一个程序,但不同的Activity是否可以放在不同的Task任务栈中?

    启动模式里有个Singleinstance,可以运行在另外的单独的任务栈里面。用这个模式启动的activity,在内存中只有一份,这样就不会重复的开启。

    也可以在激活一个新的activity时候, 给intent设置flag,Intent的flag添加FLAG_ACTIVITY_NEW_TASK,这个被激活的activity就会在新的task栈里面

     

    Android中Task任务栈的分配。

    1)任务栈的概念

    问:一个手机里面有多少个任务栈?

    答:一般情况下,有多少个应用正在运行,就对应开启多少个任务栈;每开启一个应用程序就会创建一个与之对应的任务栈;

    栈:后进先出,最先进栈,就会最后出栈。Activity的启动模式就是修改任务栈的排列情况

    2)任务栈的作用:

    它是存放 Activity 的引用的,Activity不同的启动模式,对应不同的任务栈的存放;

    可通过 getTaskId()来获取任务栈的 ID,如果前面的任务栈已经清空,新开的任务栈 ID+1,是自动增长的;

     首先来看下Task的定义,Google是这样定义Task的:Task实际上是一个Activity栈,通常用户感受的一个Application就是一个Task。从这个定义来看,Task跟Service或者其他Components是没有任何联系的,它只是针对Activity而言的。

     

     Activity的启动模式

    standard 标准启动模式(自己启动自己会按三次才能退出)
    singleTop 单一顶部模式
      • 如果任务栈的栈顶存在这个要开启的activity,不会重新的创建activity,而是复用已经存在的activity。如果栈顶没有或者不在栈顶,会重新创建
      • 会调用 onNewInstance(),复用已经存在的实例
      • 应用场景:singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很耗内存的。
    singeTask 单一任务栈,在当前任务栈里面只能有一个实例存在
    • 当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。保证整个任务栈里面只有一个实例存在
    • 会调用 onNewInstance(),复用已经存在的实例
    • 应用场景:
    singleTask适合作为程序入口点,例如应用中的主页(Home页)。假设用户在主页跳转到其他页面,运行多次操作后想返回到主页,假设不使用SingleTask模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。

    singleInstance:activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在
      • 如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
      • 应用场景: 电话拨打界面
    singleInstance适合需要与程序分离开的页面。例如我们有个需求,需要打开别的应用,这个时候如果不设置singleInstance的话,这个新打开的应用在我们程序的任务栈里,用户想要按任务键切换的话没法切换。

     

     

     

    Android下的进程

    进程是被系统创建的,当内存不足的时候,又会被系统回收

    内存管理:Android 系统在运行多个进程时,如果系统资源不足,会强制结束一些进程,优先选择哪个进程来结束是有优先级的。

    会按照以下顺序杀死(进程级别):

    ①、空: 进程中没有任何组件(无组件启动,做进程缓存使用,恢复速度快),任务栈清空,意味着程序退出了,但进程留着,这个就是空进程,容易被系统回收;

    ②、后台:进程中只有停止状态(onStop)的 Activity;

    ③、服务:进程中有正在运行的服务;

    ④、可见:进程中有一个暂停状态(onPause)的 Activity;

    ⑤、前台:进程中正在运行一个 Activity;

    Activity 在退出的时候进程不会销毁, 会保留一个空进程方便以后启动. 但在内存不足时进程会被销毁;

    Activity 中不要在 Activity 做耗时的操作, 因为 Activity 切换到后台之后(Activity 停止了), 内存不足时, 也容易被销毁;

     

     Service

    什么是Service以及描述下它的生命周期。Service有哪些启动方法,有什么区别,怎样停用Service?

    在Service的生命周期中,被回调的方法比Activity少一些,只有onCreate, onStartCommand, onDestroy,onBind和onUnbind。

    通常有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。

    1 通过startService

        Service会经历 onCreate 到onStartCommand,然后处于运行状态,stopService的时候调用onDestroy方法。

       如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。

    2 通过bindService   

        Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbind->onDestroyed方法。

     

    1.一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。
    2.服务启动了之后会一直保持运行状态,直到 stopService()或stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止下来了。

    3.当调用了startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行这样就把服务的生命周期完整地走了一遍。

    start –> bind -> unbind -> stop 经常使用服务长期后台运行,又可以调用服务中的方法

     

    service如何杀不死?

    1.onStartCommand方法,返回START_STICKY(粘性)当service因内存不足被kill,当内存又有的时候,service又被重新创建

    2.设置优先级,在服务里的ondestory里发送广播 在广播里再次开启这个服务,双进程守护

     

    service是否在main thread中执行, service里面是否能执行耗时的操作?

    默认情况,如果没有显示的指定service所运行的进程, Service和Activity是运行在当前app所在进程的main thread(UI主线程)里面

    service里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 ),需要在子线程中执行 new Thread(){}.start();

     

    service里面可以弹土司吗

    service里面弹toast需要添加到主线程里执行
      @Override  
        public void onCreate(){  
            handler = new Handler(Looper.getMainLooper());                          
            System.out.println("service started");  
           handler.post(new Runnable() {    
                 @Override    
                 public void run() {    
                    Toast.makeText(getApplicationContext(), "Test",Toast.LENGTH_SHORT).show();
                    });
         }

     

    Activity怎么和service绑定,怎么在activity中启动自己对应的service?

    startService() 一旦被创建 调用着无关,没法使用service里面的方法

    bindService () 把service 与调用者绑定 ,如果调用者被销毁, service会销毁,可以使用service 里面的方法

     Service与Activity怎么实现通信

    方法一:

    1. 添加一个继承Binder的内部类,并添加相应的逻辑方法
    2. 重写Service的onBind方法,返回我们刚刚定义的那个内部类实例
    3. Activity中创建一个ServiceConnection的匿名内部类,并且重写里面的onServiceConnected方法和onServiceDisconnected方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用,在onServiceConnected方法中,我们可以得到一个刚才那个service的binder对象,通过对这个binder对象进行向下转型,得到我们那个自定义的Binder实例,有了这个实例,做可以调用这个实例里面的具体方法进行需要的操作了

    方法二
    通过BroadCast(广播)的形式
    当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新视图

    什么是IntentService?有何优点?

    普通的service ,默认运行在ui main 主线程,Sdk给我们提供的方便的,带有异步处理的service类

    OnHandleIntent() 处理耗时的操作,不需要开启子线程,这个方法已经在子线程中运行了

    Intentservice若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopService()

     

    startForeground(idnotification)

    拥有service的进程具有较高的优先级。当内存不足时,拥有service的进程具有较高的优先级。

    1. 如果service正在调用onCreate,  onStartCommand或者onDestory方法,那么用于当前service的进程相当于前台进程以避免被killed。

    2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.

    3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。

    4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。

     

    什么时候使用Service?

    如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

    1.Service的特点可以让他在后台一直运行,可以在service里面创建线程去完成耗时的操作.

    2.Broadcast receiver捕获到一个事件之后,可以起一个service来完成一个耗时的操作.

     

    广播

    请描述一下Broadcast Receiver。

    Android中:系统在运行过程中,会产生会多事件,那么某些事件产生时,比如:电量改变、收发短信、拨打电话、屏幕解锁、开机,系统会发送广播,只要应用程序接收到这条广播,就知道系统发生了相应的事件,从而执行相应的代码。使用广播接收者,就可以收听广播

    广播分两种:有序广播、无序广播

    无序广播:无序广播不可中断,不能互相传递数据;

    有序广播:一个接一个的传递,广播可中断,通过调用 abortBroadcast()方法;接收者之间可以传递数据(intent);

    无序广播(标准广播)

    • 所有与广播中的action匹配的广播接收者都可以收到这条广播,并且是没有先后顺序,视为同时收到

    有序广播

    • 所有与广播中的action匹配的广播接收者都可以收到这条广播,但是是有先后顺序的,按照广播接收者的优先级排序
      • 优先级的定义:-1000~1000
      • <intent-filter android:priority="100" >
        <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
        </intent-filter> 
      • 最终接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收
      • abortBroadCast:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截
     

    如何注册广播

    广播的方式一般有两种,在代码中注册和在 AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。
    动态注册需要使用广播接收者时,执行注册的代码,不需要时,执行解除注册的代码
    • 安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的
    1. 屏幕锁屏和解锁
    2. 电量改变
    public class MainActivity extends Activity {
        private IntentFilter intentFilter;
        private NetworkChangeReceiver networkChangeReceiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.activity_main);
               intentFilter = new IntentFilter();
               intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
               networkChangeReceiver = new NetworkChangeReceiver();
               registerReceiver(networkChangeReceiver, intentFilter);
        }
        @Override
        protected void onDestroy() {
               super.onDestroy();
               unregisterReceiver(networkChangeReceiver);
        }
        class NetworkChangeReceiver extends BroadcastReceiver {
                @Override
                public void onReceive(Context context, Intent intent) {
                     Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show();
                }
        }
    }
    静态注册:
    可以使用清单文件注册
    • 广播一旦发出,系统就会去所有清单文件中寻找,哪个广播接收者的action和广播的action是匹配的,如果找到了,就把该广播接收者的进程启动起来

    四大组件其中比较特殊的是广播接收者,可以不在清单文件中配置,可以通过代码进行注册。其他组件全部在清单文件中注册

     

    避免使用隐式 Intent 广播(静态、动态注册)敏感信息,信息可能被其他注册了对应BroadcastReceiver 的 App 接收 
    如果广播仅限于应用内,则可以使用 LocalBroadcastManager#sendBroadcast()实现,避免敏感信息外泄和 Intent 拦截的风险。 
    Intent intent = new Intent("my-sensitive-event");
    intent.putExtra("event", "this is a test event");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 
    

      

     

    广播的生命周期

    a. 广播接收者的生命周期非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁;

    b. 广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response错误对话框;

    c. 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉;

    d. 耗时的较长的工作最好放在服务中完成;


     

    内容提供者

    请介绍下ContentProvider是如何实现数据共享的。

    是四大组件之一内容提供者的作用:把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用

    应用的数据库是不允许其他应用访问的,内容提供者的作用就是让别的应用访问到你的数据库。

    在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址

      <provider android:name="com.itheima.contentprovider.PersonProvider"
          android:authorities="com.itheima.person"
          android:exported="true"
       ></provider>

    把自己的数据通过uri的形式共享出去,android 系统下不同程序,数据默认是不能共享访问 

    需要去实现一个类去继承ContentProvider 

     

    为什么要用ContentProvider?它和sql的实现上有什么差别?

    屏蔽数据存储的细节,对用户透明,用户只需要关心操作数据的uri就可以了

    不同app之间共享,操作数据

    Sql也有增删改查的方法.

    但是contentprovider 还可以去增删改查本地文件. xml文件的读取,更改,网络数据读取更改

     

     

    请介绍下Android的数据存储方式

    1.文件储存,在内部文件和SD卡

    getCacheDir(),在data/data/包名/cache

    getFilesDir(),在data/data/包名/files

    SD卡:首先通File file = new File(Environment.getExternalStorageDirectory(), "info.txt"),然后通过io存储

    2.SharedPreference 

    3.SQLite数据库
    当应用程序需要处理的数据量比较大时,为了更加合理地存储、管理、查询数据,往往使用关系数据库来存储数据。Android系统的很多用户数据,如联系人信息,通话记录,短信息等,都是存储在SQLite数据库当中的,所以利用操作SQLite数据库的API可以同样方便的访问和修改这些数据。

    4.ContentProvider:
    主要用于在不同的应用程序之间实现数据共享的功能,不同于sharepreference和文件存储中的两种全局可读写操作模式,内容提供其可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险

     

     

    Fragment

    简单说说Fragment

    用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容

    生命周期方法跟Activity一致,可以理解把其为就是一个Activity,与Activity同存亡,Activity的XX方法调用,Fragment的XX方法就调用

     

    他们是怎么进行传递数据的?

    活动传递给Fragment:为了方便碎片和活动之间进行通信, FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例,前提是自己在布局文件中定义fragment这个标签,代码如下所示:

    RightFragment rightFragment = (RightFragment) getFragmentManager()
    .findFragmentById(R.id.right_fragment);
     

    调用 FragmentManager的 findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。还有findViewByTag,在replace 的时候设置tag

    或者在fragment里声明接口,然后activity获得fragment对象调用接口里的方法

     

    fragment数据传递给活动,直接getActivity就可以调用活动里的方法了

    activity给fragment传递数据一般不通过fragment的构造方法来传递,会通过setArguments来传递,因为当横竖屏会调用fragment的空参构造函数,数据丢失。

     

    fragmentfragment数据传递

    首先在一个fragment可以得到与它相关联的活动,然后再通过这个活动去获取另外一个fragment的实例,这样也就实现了不同fragment之间的通信功能

     

    FragmentManager , add 和 replace 有什么区别?

    • 使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace ,add 和 replace 影响的只是界面,而控制回退的,是事务。
    • add 是把一个fragment添加到一个容器 container 里。replace 是先remove掉相同id的所有fragment,然后在add当前的这个fragment。
    • 在大部分情况下,这两个的表现基本相同。因为,一般,咱们会使用一个FrameLayout来当容器,而每个Fragment被add 或者 replace 到这个FrameLayout的时候,都是显示在最上层的。所以你看到的界面都是一样的。但是, 使用add的情况下,这个FrameLayout其实有2层,多层肯定要比一层的来得浪费,所以还是推荐使用replace。 当然有时候还是需要使用add的。比如要实现轮播图的效果,每个轮播图都是一个独立的Fragment,而他的容器FrameLayout需要add多个Fragment,这样他就可以根据提供的逻辑进行轮播了。而至于返回键的时候,这个跟事务有关,跟使用add还是replace没有任何关系。
    • replace()方法会将被替换掉的那个Fragment彻底地移除掉,因此最好的解决方案就是使用hide()和show()方法来隐藏和显示Fragment,这就不会让Fragment的生命周期重走一遍了

     

    Fragment和view的区别

    Fragment可以有效的对 view进行管理(增删和替换)而且结构更加清晰,有模块化的实现思想。

    用view 很多逻辑代码可能都需要写在Activity里,如果view很多, 耦合度会很高。用Fragment则可以各自管理,起了解耦的作用。

    一般软件简单的话直接view,复杂的用Fragment

    viewpager是一个滑动切换的控件Fragment是一个轻量级的Activity,这个Fragment可以放到这个Viewpager里面去运行。

    例如QQ或微信那样,可以来回切换不同的选项卡,即切换了不同的Fragment通常Viewpager 会放fargment或者view

     

    Fragment和Activity的区别

    因为现在的手机屏幕都比较大了,Activity会把内容拉的很长,用Fragment的话可以左侧是列表,右侧是内容。在一个Activity里切换界面,切换界面时只切换Fragment里面的内容。Fragment通常用来作为一个activity界面的一部分。

     

    view

    请介绍下Android中常用的五种布局。

    FrameLayout(帧布局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局)

    • FrameLayout:从屏幕的左上角开始布局,叠加显示, 实际应用 播放器的暂停按钮. 
    • LinearLayout:线性布局,他首先是一个一个从上往下罗列在屏幕上。每一个LinearLayout里面又可分为垂直布局、水平布局。当垂直布局时,每一行就只有一个元素,多个元素依次垂直往下;水平布局时,只有一行,每一个元素依次向右排列。
    • AbsoluteLayout:用X,Y坐标来指定元素的位置android:layout_x="20px" ,android:layout_y="12px" ,指定平板机型的游戏开发、机顶盒开发中经常用到绝对布局
    • RelativeLayout:在相对的布局中主要就进行避免覆盖的问题,就是组件1可能会覆盖在组件2上(屏幕适配),在相对的布局中主要就进行避免覆盖的问题,就是组件1可能会覆盖在组件2上
    • TableLayout:有几行,就有几个<TableRow/>,有几列,那么在<TableRow>中就有几个<TestView>,TableRow的子节点的宽和高是包裹内容,不需要指定宽高
      

    TextView 、ImageView ,Button,ImageButton他们之间的联系和区别

    ImageView 控件负责显示图片,其图片来源既可以是资源文件的id,也可以是Drawable对象或 Bitmap 对象,还可以是 内容提供者(Content Provider)的Uri.

    ImageButton 控件 继承自 ImageView。与Button相同之处:都用于响应按钮的点击事件

    不同之处:ImageButton只能显示图片;Button用于显示文字

    屏幕适配

    • 开发时选取主流屏幕 1280*720,用相对布局和线性布局
    • 用dp sp不用px,dp单位动态匹配
    • 开发后期在不同的分辨率上测试,没有太大问题可以上线
    • 权重适配:weight 只有线性布局有
    • 代码适配:getWindowManager().getDefaultDisplay().getWidth();获得屏幕的宽高
    • 如果屏幕放不下了,可以使用 ScrollView(可以上下拖动)
    • 布局适配:layout-800x180 针对某一种屏幕 工作量大
    • 尺寸适配:dp=px/设备密度=getResource().getDisplayMetrice().dsnsity;
    • 根据不同分辨率的屏幕建立不同的valuse,比如valuse-1280x720,values里的dimens里算出dp,最后引用系统会自动匹配。
    (约等于)320*240(0.5) 480*320(1) 480*800(1.5) 1280*720(2)就不用布局适配了
    • 在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。大分辨率图片(单维度超过 1000)大分辨率图片建议统一放在 xxhdpi 目录下管理,否则将导致占用内存成倍数增加。
    说明:为了支持多种屏幕尺寸和密度,Android 为多种屏幕提供不同的资源目录进行适配。为不同屏幕密度提供不同的位图可绘制对象,可用于密度特定资源的配置限定符(在下面详述) 包括 ldpi(低)、 mdpi(中)、 hdpi(高)、 xhdpi(高)、 xxhdpi (超超高)和 xxxhdpi(超超超高)。例如,高密度屏幕的位图应使用 drawable-hdpi。根据当前的设备屏幕尺寸和密度,将会寻找最匹配的资源,如果将高分辨率图片放入低密度目录,将会造成低端机加载过大图片资源,又可能造成 OOM,同时也是资源浪费,没有必要在低端机使用大。

    RelativeLayout和FrameLayout的区别

    FrameLayout主要是在多层之间的布局,RelativeLayout则是在同层之间不同位置之间的布局,效果上没有什么大的区别,都可以实现,只是看哪种实现更容易。比如我们收到信息了,我们可以在信息图标上增加FrameLayout 用于显示气泡。

    Padding和Margin有什么区别?

    Padding 是控件内容的距离margin是控件和控件间的距离

    Listview如何显示不同条目?

    第一种:在getview方法里根据position 进行判断,比如,position等于0是,显示标题,否则显示数据,当然相应的setOnItemClickListener也要去判断

    第二种:根据listview里的getItemViewType,getViewTypeCount方法显示不同条目

        /** 根据位置 判断当前条目是什么类型 */
        @Override
        public int getItemViewType(int position) {  //20     
            if (position == datas.size()) { // 当前是最后一个条目
                return MORE_ITEM;
            }
            return getInnerItemViewType(position); // 如果不是最后一个条目 返回默认类型
        }
        private int getInnerItemViewType(int position) {
            return DEFAULT_ITEM;
        }
        /** 当前ListView 有几种不同的条目类型 */
        @Override
        public int getViewTypeCount() {
            return super.getViewTypeCount() + 1; // 2 有两种不同的类型
        }
    

      

    ScrowView 使用的注意

    在不同的屏幕上显示内容不同的情况,其实这个问题我们往往是用滚动视图来解决的,也就是 ScrowView,

    需要注意的是 ScrowView 中使用 layout_weight 是无效的,既然使用 ScrowView 了,就把它里面的控件的大小都设成固定的吧。

     

    Android UI中的View如何刷新

    在主线程中 拿到view调用Invalide()方法,在子线程里面可以通过postInvalide()方法;

    invalidate();//主线程,刷新当前视图 导致 执行onDraw执行

    postInvalidate();//子线程

     

    什么是ANR(异步加载) 如何避免它?

    android在主线程是不能加载网络数据或图片、数据库查询、复杂业务逻辑处理以及费时任务操作,因为Android的UI操作并不是线程安全的,并且所有涉及UI的操作必须在UI线程中完成。Android应用在5s内无响应的话会导致ANR(Application Not Response),这就要求开发者必须遵循两条法则:1、不能阻塞UI线程,2、确保只在UI线程中访问Android UI工具包。

    Activity 5秒 broadcast10秒,服务20秒,耗时的操作在主thread里面完成

    解决办法:Thread + Handler + Message ,Thread + Handler + post,AsyncTask,intentservice

    runOnUiThread(Runnable)在子线程中直接使用该方法,可以更新UI

    实现侧边栏、和指示器效果、页面滑动有几种方式

    侧边栏:自定义、slidingmenu、DrawerLayout 、SlidingDrawer

    指示器效果:自定义、viewpager里面的PagerTabStrip、ActionBar Tab标签、viewpagerindicate、FragmentTabHost、TabActivity、radiobutton

    页面滑动:自定义、viewpager、手势识别器,其实就是onTouchEvent提供的简单工具类,onTouchEvent将触摸事件委托给了手势识别器

    其他

    Gradle中buildToolsVersion和TargetSdkVersion的区别是什么

    compileSdkVersion, minSdkVersion 和 targetSdkVersion 的作用:他们分别控制可以使用哪些 API ,要求的 API 级别是什么,以及应用的兼容模式。TargetSdkVersion 设为23那么是按6.0设置的(运行时权限),小于23是按6.0以前的方式(安装时默认获得权限,且用户无法在安装App之后取消权限)

    进程间怎么通信

    binder是安卓中的一个类,它实现了IBinder接口,是安卓中跨进程通信的方式。当绑定服务的时候会返回一个binder对象,然后通过他进行多进程间的通信。

    其实进程间通信就是为了实现数据共享。一个程序不同组件在不同进程也叫多进程,和俩个应用没有本质区别。使用process属性可以实现多进程,但是会带来很多麻烦,主要原因是共享数据会失败,弊端有:静态和单利失效,同步失效,sharedprefer可靠性减低等问题

    Intent,Binder(AIDL),sharedpre、Messenger

    • 使用intent的附加信息extras来传递,通过bundle,传递的是bundle支持的类型,比如基本数据类型、实现pracellable或serializeable的对象
    • 使用文件共享,序列化或是sharedpre,不过不适用于读写并发的操作
    • 通过message进行传递,在远程服务里创建message对像,在onbind里返回(message.getbinder)。在客户端绑定服务,拿着message对象发消息(可以用bundle)。在远程服务的handlermessage方法就会收到。他是一个个处理的,如果大量并发请求用aidl,mrssage底层就是aidl
    • AIDL
    • socket可以实现俩个终端通信,也可以在一个设备的俩个进程通信。需要在服务里创建服务端
    • ContentProvider(进程间数据共享)和message一样,底层也是binder,除了oncreate方法其他方法(crud)都是运行在bindler线程里。所以在oncerate里不能做耗时操作。在其他应用访问通过uri(主机名),例如gercontentResolver.query(uri,null,null....)
    • 有以上6种

    使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。

    假设手机本地需要缓存数据,如何保证和服务器的数据统一?

    • 比如有个网络更新的功能,activity可以每隔半小时开启service去访问服务器,获取最新的数据。

    • 在缓存文件里面加入时间戳,根据实际情况在一定的时间差内再次访问网络数据、判断URL

    在缓存的第一行写一个上当前时间,读的时候判断是不是过期,根据需求看需要多久跟新

    分页怎么做的?

    分页根据服务器接口参数决定每次加载多少,getviewtype,getitemview

    分批处理 解决的是时间等待的问题,不能解决内存占用的问题。

    要想解决内存占用问题,可以采用分页方式

    什么Context

    Android工程环境中像Activity、Service、BroadcastReceiver等系统组件,而这些组件并不是像一个普通的Java对象new一下就能创建实例的了,而是要有它们各自的上下文环境,这就是Context。可以这样讲,Context是维持Android程序中各组件能够正常工作的一个核心功能类。

    Context功能很多,可以弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等等等都需要用到Context。

    Context一共有Application、Activity和Service三种类型:Context数量 = Activity数量 + Service数量 + 1 。1代表着Application的数量,因为一个应用程序中可以有多个Activity和多个Service,但是只能有一个Application。
    Application通常作为工具类来使用的,Application中在onCreate()方法里去初始化各种全局的变量数据是一种比较推荐的做法,但是如果你想把初始化的时间点提前到极致,也可以去重写attachBaseContext()方法。不能写在构造函数里。

    Android程序与Java程序的区别?

    Android程序用android sdk开发,java程序用javasdk开发.

    Android SDK引用了大部分的Java SDK,少数部分被Android SDK抛弃,比如说界面部分,java.awt swing package除了java.awt.font被引用外,其他都被抛弃,在Android平台开发中不能使用。 Android sdk 添加工具jar httpclient , pull opengl

    Android中的动画有哪几类,它们的特点和区别是什么?

    三种:补间动画、帧动画、属性动画。
    补间动画是放置到res/anim/下面
    帧动画是放置到res/drawable/下面,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长
    补间动画
    • 如果动画中的图像变换比较有规律时,例如图像的移动(TranslateAnimation)、旋转(RotateAnimation)、缩放(ScaleAnimation)、透明度渐变(AlphaAnimation),这些图像变化过程中的图像都可以根据一定的算法自动生成,我们只需要指定动画的第一帧和最后一帧图像即可,这种自动生成中间图像的动画就是补间动画。
    • 补间动画,只是一个动画效果,组件其实还在原来的位置上,xy没有改变
    帧动画:传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影,一张张图片不断的切换,形成动画效果,要自己指定每一帧

    属性动画:动画的对象除了传统的View对象,还可以是Object对象,动画结束后,Object对象的属性值被实实在在的改变了

    Android工程的目录结构 

    src:项目的java代码

    assets:资源文件夹,存放视频或者音乐等较大的资源文件

    bin:存放应用打包编译后的文件

    res:资源文件夹,在这个文件夹中的所有资源,都会有资源id,读取时通过资源id就可以读取

         资源id不能出现中文

    coverview原理:

    这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用,在 getView()方法中进行了判断,如果 convertView为空,则使用

    LayoutInflater去加载布局,如果不为空则直接对 convertView进行重用

    android的启动流程

    当程序启动Linux内核后,会加载各种驱动和数据结构,当有了驱动以后,开始启动Android系统同时会加载用户级别的第一个进程init(systemcoreinit.c),加载init.rc文件,会启动一个Zygote进程,此进程是Android系统的一个母进程,用来启动Android的其他服务进程,然后接着在里面启动各种硬件服务和activity。Android系统启动完成,打开了Luncher应用的Home界面。

    Logcat

    1. Log.v()

    这个方法用于打印那些最为琐碎的,意义最小的日志信息。对应级别 verbose,是Android日志里面级别最低的一种。

    2. Log.d()

    这个方法用于打印一些调试信息, 这些信息对你调试程序和分析问题应该是有帮助的。对应级别 debug,比 verbose高一级。

    3. Log.i()

    这个方法用于打印一些比较重要的数据,这些数据应该是你非常想看到的,可以帮你分析用户行为的那种。对应级别 info,比 debug高一级。

    4. Log.w()

    这个方法用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别 warn,比 info高一级。

    5. Log.e()

    这个方法用于打印程序中的错误信息,比如程序进入到了 catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别 error,比 warn高一级.

    推送

    所有需要客户端被动接收信息的功能模块,都可以用推送实现,比如A想给B发消息,A调服务器接口,服务器只是存数据,它调推送的接口,推送去去找B。推送用的是xmpp协议, 是一种基于TCP/IP的协议, 这种协议更适合消息发送

    - socket 套接字, 发送和接收网络请求
    - 长连接 keep-alive, 服务器基于长连接找到设备,发送消息
    - 心跳包 , 客户端会定时(30秒一次)向服务器发送一段极短的数据,作为心跳包, 服务器定时收到心跳,证明客户端或者,才会发消息.否则将消息保存起来,等客户端活了之后(重新连接),重新发送.

     由于篇幅原因,附俩篇优化和自定义控件常见的面试题,希望对大家有所帮助:

    Android优化指南

    Android自定义控件概念总结

     

  • 相关阅读:
    Good Bye 2014 B. New Year Permutation(floyd )
    hdu 5147 Sequence II (树状数组 求逆序数)
    POJ 1696 Space Ant (极角排序)
    POJ 2398 Toy Storage (叉积判断点和线段的关系)
    hdu 2897 邂逅明下 (简单巴什博弈)
    poj 1410 Intersection (判断线段与矩形相交 判线段相交)
    HDU 3400 Line belt (三分嵌套)
    Codeforces Round #279 (Div. 2) C. Hacking Cypher (大数取余)
    Codeforces Round #179 (Div. 2) B. Yaroslav and Two Strings (容斥原理)
    hdu 1576 A/B (求逆元)
  • 原文地址:https://www.cnblogs.com/sixrain/p/7520957.html
Copyright © 2011-2022 走看看