Android面试常见知识点【一】(不定期更新)

Android面试常见知识点(不定期更新)



activity生命周期:


  • HOME键的执行顺序:onPause->onStop->onRestart->onStart->onResume

  • BACK键的顺序: onPause->onStop->onDestroy->onCreate->onStart->onResume

情景:

   Activity A 跳转 Activity B ,再回到Activity A,再退出,activity执行的生命周期:

    1、启动A:
        A:onCreate->onStart->onResume
    2、在A中启动B:
        (A)onPause->(B)onCreate->(B)onStart->(B)onResume->(A)onStop
    3、在B中返回A(按物理硬件返回键):
        (B)onPause->(A)onRestart->(A)onStart->(A)onResume->(B)onStop->(B)onDestroy
    4、继续返回:
        (A)onPause->(A)onStop->(A)onDestroy


Activity的启动和关闭:

启动:

通过Intent意图开启一个Activity.

  • 显示意图
    • new Intent(Context,Class)
    • intent.setClass(Context,Class)
    • intent.setClassName(Context,String className)
    • intent.setClassName(String packageName,String className)
    • intent.setComponent(ComponentName component)
    • 应用场景:开启自己应用程序内部的界面,效率高
  • 隐身式意图
    • new Intent(String action)
    • new Intent(String action,Uri uri)
    • Intent intent = new Intent();
      • intent.setAction(String action)
      • intent.setData(Uri uri)
    • 意图过滤器:<Intent-filter>
      • action  action 代表该intent所要完成的一个抽象“动作”
      • category  为Activity增加额外的附加类别信息
      • data 声明数据的类型(mimeType)、约束(scheme)、主机名(host)、端口(port)、路径(path)等
    • 应用场景:开启别的应用程序中的界面,系统需要查询匹配的Activity,效率较低
  • 显示与隐式的区别:
    • 显示意图:必须制定要激活的组件的完整包名和类名(应用程序之间耦合在一起,一般是在激活自己应用中的组件时使用显示意图)
    • 隐式意图:只需要指定执行的动作和数据就可以(好处:应用程序之间没有耦合,一般在激活其他应用中的组件时使用隐式意图)

关闭:

Activity.finish();或点击返回键

Fragment生命周期:

与activity生命周期对比:

情景:

1、切换到该Fragment:
onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume
2、屏幕灭掉:
onPause->onSaveInstanceState->onStop
3、屏幕解锁:
onStart->onResume
4、切换到其他fragment:
onPause->onStop->onDestroyView
5、切回本身的fragment:
onCreateView->onActivityCreated->onStart->onResume
6、回到桌面:
onPause->onSaveInstanceState->onStop
7、回到应用:
onStart->onResume
8、退出应用:
onPause->onStop->onDestroy->onDestroy->onDetach


Service:

概述:

Android四大组件之一,可以理解为是一个长期在后台运行的没有界面的Activity,
运行在主线程中,不能在服务中执行耗时操作。

Service的两种启动方式:

1、StartService:访问者与Service之间没有关联,即使服务的开启者退出了,Service仍然运行
(适用于开启下载服务、播放音乐等)
启动服务:Context.startService(Intent intent)
关闭服务:Context.stopService(Intent intent)或Service.stopSelf()
2、BindService:基于IBinder方式将两个组件进行绑定,然后相互传值,访问者与Service绑定在一起,Service的
开启者一旦退出,Service也就随之终止。

使用步骤:

①定义类继承自Service
②在清单文件中进行注册
③创建Binder的子类
④在onBind()方法返回自定义的Binder子类的对象
⑤在Service的开启者(比如Activity)中创建类实现ServiceConnection接口
onServiceConnected(ComponentName,IBinder)
onServiceDisconnected(ComponentName)
⑥调用Context.bindService(Intent,ServiceConnection,int)方法绑定指定服务


Service的生命周期:



ANR(Application Not Responding)问题:

概述:应用程序无响应默认情况下:在android中activity的最长执行时间是5秒,BroadcastReceiver的最长执行时间则是10秒

什么会引发ANR:

在Android里,应用程序的响应是由Activity Manager和WindowManager系统服务监视的。当它检测到以下情况中

一个时,Android就会针对特定的应用程序显示ANR:

1.在5秒内没有响应输入的事件(如按键按下,屏幕触摸)

2.BroadcastReceiver在10秒内没有执行完毕

造成以上两点的原因有很多,比如在主线程中做了非常耗时的操作,如下载,io异常

如何避免ANR:

1.运行在主线程里的任何方法都尽可能少做事情。在activity的关键生命周期方法里尽可能少的去做创建操作。
可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等。
2.应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。
3.避免在Intent Receiver 里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。
如果你的应用程序在响应Intent广播时需要向用户展示什么,应该使用Notification Manager来实现。


OOM(Out Of Memory)问题:

内存占有量超过了VM所分配的最大。通常OOM都发生在需要用到大量内存的情况下(创建或解析Bitmap,分配特大的数组),

多数OOM都是因为Bitmap太大。

避免OOM方法:

1、主动释放Bitmap的内存:

思路:只加载可见区域的Bitmap;滑动时不加载;停止滑动后,开始重新加载可见区域的图片;释放滑出可见区域的Bitmap的内存。

2、设计Cache:

LruCache其实就是一个LinkedHashMap,任意时刻,当一个值被访问时,它就会被移动到队列的开始位置,所以这也是为什么要用LinkedHashMap的原因。

思路:

1.始终从cache中去取Bitmap,如果取到Bitmap,就直接把这个Bitmap设置到ImageView上面。
2.如果缓存中不存在,那么启动一个task去加载(可能从文件来,也可能从网络)
3.每一个ImageView上面都可能绑定一个task,所以这个ImageView必须提供一个方法能得到与之相关联的task
为什么要这样做?因为在给一个ImageView绑定task之前,必须要把原先的task取消。

常用布局:

  • 线性布局 LinearLayout

  • 相对布局 RelativeLayout

  • 帧布局 FrameLayout

  • 表格布局 TableLayout

  • 网格布局 GridLayout

  • 绝对布局AbsoluteLayout(Android 2.2后弃用)

Android中线程的概念:

  • MainThread 主线程(UI线程):应用启动时创建,处理与UI相关事情,如点击事件、数据更新。

  • WorkThread子线程(工作线程):Android4.0之后UI线程不能访问网络资源或执行耗时操作,必须开启子线程


AsyncTask异步任务:

  • 功能:在类中实现异步操作,并提供回调方法反馈当前异步执行的程度,最后将反馈的结果提供给UI主线程

  • 回调方法:

    • onPreExecute:线程任务开始执行前的准备工作(运行在主线程中)
    • doInBackground:在子线程中执行的代码
    • onProgressUpdate:在线程的执行中需要执行的界面操作(运行在主线程中)
    • onPostExecute:线程任务结束之后的界面更新
    • onCancelled()调用过cancel()方法并且线程结束之后调用
  • 常用方法:

    • cancel(boolean):取消任务
    • publishProgress(int progressValue):发布任务进度
  • 三个泛型参数:

    • 第一个泛型参数:是传递给doInBackground方法的参数的类型
    • 第二个泛型参数:是传递给onProgressUpdate方法的参数的类型
    • 第三个泛型参数:是传递给onPostExecute方法的参数的类型
  • 基本使用步骤:

    1. 定义一个类,继承自AsyncTask,并指定三个泛型
    2. 重写回调方法
    3. 在UI线程中实例化AsyncTask对象
    4. 在UI线程中,执行AsyncTask对象的execute(...)方法,开始执行异步任务
    5. 在执行过程中,可以通过调用cancel(true)方法,停止异步任务

ContentProvider内容提供者:

概述:

ContentProvider是不同应用程序之间进行数据交换的标准API,应用程序可以通过ContentProvider将私有的数据提供给其他应用程序。一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作被暴露的内部数据,包括增加数据、删除数据、修改数据、查询数据等

内容提供者使用步骤总结:

  1. 定义一个类继承自ContentProvider

  2. 在清单文件中进行注册

  3. 使用UriMatcher添加匹配规则

  4. 根据需求在类中定义增删改查的方法

  5. 使用ContentResolver访问其他应用中的ContentProvider所提供的接口

BroadcastReceiver:

概述:Android四大组件之一

定义:广播接收器,即接收通过某一频道(Action)发送的广播,发送者可以是Activity和Service
作用:
  1. 监听系统广播,并做相应的处理,比如电量过低时需要保存相关数据,或发出通知警告用户
  2. 后台运行的服务,如经过耗时操作后,获取了网络数据,通过广播的方式通知用户

基本使用步骤:

  1. 创建广播接收器类,继承自BroadcastReceiver
  2. 改写onReceive()方法
  3. 注册广播接收器(有两种方式)
    • 静态注册,在清单文件中注册
      • <receiver android:name="类名">
      • <action android:name="广播名称">
      • 特点:一旦注册了,就是系统级的、全局的,不能解除注册
    • 动态注册,使用代码注册
      • registerReceiver(BroadcastReceiver,IntentFilter)
      • 注意:动态注册的广播,需要调用unRegisterReceiver()方法取消注册,不然会抛出异常
      • 特点:比较灵活,可以动态注册绑定和动态解除注册,一般为本应用程序所使用较多

Android内存优化:

  1. Android的内存机制:

        Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象

在java堆内分配空间。然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化

了,给我们请了一个专门的清洁工(GC)。

     那么GC怎么能够确认某一个对象是不是已经被放弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一颗树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象(连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

2.Android的内存溢出:

Android的内存溢出是如何发生的?

Android的虚拟机是基于寄存器Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。(上面提到的OOM)

为什么会出现内存不够用的情况呢?我想原因主要有两个:

  • 由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。
  • 保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制

3.万恶的static:

static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多),这时就要谨慎对待了。

 public class ClassName {  
     private static Context mContext;  
     //省略  
} 

以上的代码是很危险的,如果将Activity赋值到该mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

如:

 private static Drawable sBackground;  
     
 @Override  
 protected void onCreate(Bundle state) {  
   super.onCreate(state);  
     
   TextView label = new TextView(this);  
   label.setText("Leaks are bad");  
     
   if (sBackground == null) {  
     sBackground = getDrawable(R.drawable.large_bitmap);  
   }  
   label.setBackgroundDrawable(sBackground);  
     
   setContentView(label);  
 } 
sBackground,是一个静态的变量,但是我们发现,我们并没有显式的保存Context的引用,但是,当Drawable与View连接之后,

Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。

这个引用链如下:

Drawable->TextView->Context

所以,最终该Context也没有得到释放,发生了内存泄露。

如何才能有效的避免这种引用的发生?

  • 应该尽量避免static成员变量引用资源耗费过多的实例,比如Cntext。
  • Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
  • 使用WeakReference代替引用。比如可以使用WeakReference mContextRef。

4.都是线程惹的祸:

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。

看一下下面一段代码:

 public class MyActivity extends Activity {  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        new MyThread().start();  
    }  
  
    private class MyThread extends Thread{  
        @Override  
        public void run() {  
            super.run();  
            //do somthing  
        }  
    }  
}

      这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。

      由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

     有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的, 是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

这种线程导致的内存泄露问题应该如何解决?

  • 将线程的内部类,改为静态内部类。
  • 在线程内部采用弱引用保存Context引用。

    解决的模型如下:

 public abstract class WeakAsyncTaskProgress, Result, WeakTarget> extends  
        AsyncTaskProgress, Result> {  
    protected WeakReference mTarget;  
  
    public WeakAsyncTask(WeakTarget target) {  
        mTarget = new WeakReference(target);  
    }  
  
      
    @Override  
    protected final void onPreExecute() {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            this.onPreExecute(target);  
        }  
    }  
  
      
    @Override  
    protected final Result doInBackground(Params... params) {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            return this.doInBackground(target, params);  
        } else {  
            return null;  
        }  
    }  
  
      
    @Override  
    protected final void onPostExecute(Result result) {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            this.onPostExecute(target, result);  
        }  
    }  
  
    protected void onPreExecute(WeakTarget target) {  
        // No default action  
    }  
  
    protected abstract Result doInBackground(WeakTarget target, Params... params);  
  
    protected void onPostExecute(WeakTarget target, Result result) {  
        // No default action  
    }  
} 

事实上,线程的问题并不仅仅在于内存泄露,还会带来一些灾难性的问题。

5.超级大胖子Bitmap

       可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。

如何解决Bitmap带给我们的内存问题?

  • 1、及时的销毁。

             虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于他占用的内存过多,所以很可能会超过java堆的限制,因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。

  •  2、设置一定的采样率。

              有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,  这时候可以设置

一定的采样率,那么就可以大大减少占用的内存。如下面的代码:

private ImageView preview;  
 BitmapFactory.Options options = new BitmapFactory.Options();  
 options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一  
 Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);  
preview.setImageBitmap(bitmap);

  •   3、巧妙的运用软引用(SoftRefrence)

                有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以

          使Bitmap在内存快不足时得到有效的释放。如下例:

 private class MyAdapter extends BaseAdapter {  
  
    private ArrayList> mBitmapRefs = new ArrayList>();  
    private ArrayList mValues;  
    private Context mContext;  
    private LayoutInflater mInflater;  
  
    MyAdapter(Context context, ArrayList values) {  
        mContext = context;  
        mValues = values;  
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
    }  
    public int getCount() {  
        return mValues.size();  
    }  
  
    public Object getItem(int i) {  
        return mValues.get(i);  
    }  
  
    public long getItemId(int i) {  
        return i;  
    }  
  
    public View getView(int i, View view, ViewGroup viewGroup) {  
        View newView = null;  
        if(view != null) {  
            newView = view;  
        } else {  
            newView =(View)mInflater.inflate(R.layout.image_view, false);  
        }  
  
        Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(i).fileName);  
        mBitmapRefs.add(new SoftReference(bitmap));     //此处加入ArrayList  
        ((ImageView)newView).setImageBitmap(bitmap);  
  
        return newView;  
    }  
}

6、行踪诡异的Cursor

      Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cursor最终会被释放掉。

然而如果Cursor的数据量特别大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC

来处理。并且Android明显是倾向于编程者手动的将Cursor掉,因为在源码中我们发现,如果等到垃圾回收器来回收时,会给用户

以错误提示。

所以我们使用Cursor的方式一般如下:

 Cursor cursor = null;  
try {  
    cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
    if(cursor != null) {  
        cursor.moveToFirst();  
        //do something  
    }  
} catch (Exception e) {  
    e.printStackTrace();    
} finally {  
    if (cursor != null) {  
       cursor.close();  
    }  
} 
有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Activity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。

 @Override  
protected void onDestroy() {        
    if (mAdapter != null && mAdapter.getCurosr() != null) {  
        mAdapter.getCursor().close();  
    }  
    super.onDestroy();   
}

       CursorAdapter中的changeCursor函数,会将原来的Cursor释放掉,并替换为新的Cursor,所以你不用担心原来的Cursor没有被关闭。

        你可能会想到使用Activity的managedQuery来生成Cursor,这样Cursor就会与Activity的生命周期一致了,多么完美的解决方法!然而事实上managedQuery也有很大的局限性。

         managedQuery生成的Cursor必须确保不会被替换,因为可能很多程序事实上查询条件都是不确定的,因此我们经常会用新查询的Cursor来替换掉原先的Cursor。因此这种方法适用范围也是很小。

7、其他补充:

        其实,要减小内存的使用,其实还有很多方法和要求。比如不要使用整张整张的图,尽量使用9path图片。Adapter要使用convertView等等,好多细节都可以节省内存。这些都需要我们去挖掘,谁叫Android的内存不给力来着。







推荐一个很不错的博文:

http://blog.csdn.net/awangyunke/article/details/44223951

(里面有很多小项目Demo)










                  






    

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值