Android技术点小结(一)

一. 请描述下Activity的生命周期?

相信图1-1很多人都看过,我再总结下:

(1)、onCreate()——执行Activity的创建工作。每个Activity生命周期都会最先执行且执行一次的方法。

(2)、onRestart()——已经被初始化的Activity重新回到可见状态时执行的回调。

(3)、onStart()——当Activity进入可见状态但不可操控时执行的回调。

(4)、onResume()——当Activity进入可操控状态时执行的回调。

(5)、onPause()——当Activity离开可操控状态时执行的回调。在包括此状态之后的所有状态下,Activity都有可能以为内存不足被系统回收,若要做数据存储,推荐在这做。

(6)、onStop()——当Activity被其它Activity遮盖掉时执行的回调。特别注意,此时遮盖的Activity已执行完onCreate()。

(7)、onDestory()——当Activity被程序finish或者被系统kill掉后执行的回调。

图1-1:

Android技术点小结(一)_第1张图片


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

重写onSaveInstanceState(Bundle bundle)方法,把要保存的数据放入bundle中,在发生Activity“可能”被销毁的情况下(Home、电源键、跳转)系统回调用该回调,之后在重写执行onCreate时,从其参数中取出值。注意:用户主动销毁不会执行onSaveInstanceState,入BACK。


三、如何将一个Activity设置成窗口的样式?如何将Activity设置为半透明?

在Androidfest.xml中

窗口模式:android:theme="@android:style/Theme.Dialog"

透明:android:theme="@android:style/Theme.Translucent"

可调节透明度:

1、android:theme="@style/自定义样式",

2、自定义样式继承@android:style/Theme.Translucent,

3、设置bg为#00ffffff-#ffffffff,颜色代码为八位,前两位透明度00-FF,透明-不透明,后面六位RBG颜色代码。


四、如何退出Activity?如何安全退出已调用多个Activity的Application?

单个:finish();

多个:

1、记录打开的Activity:
每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。
2、发送特定广播:
在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。
3、递归退出
在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。


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

1、FrameLayout(框架布局):一层堆一层的遮盖,大小可控,位置固定左上角(和绝对布局的区别)

2、LinearLayout (线性布局):分为横向和纵向线性布局,布局内子元素成一行(列)依次排列。另外子元素宽度可以用layout_weight熟悉控制,值越小,元素所占宽度(高度)越大。

3、AbsoluteLayout(绝对布局):类似框架布局,一层堆一层,大小可控,位置可控。

4、RelativeLayout(相对布局):可以控制元素位置相对于另外一个元素的位置。

5、TableLayout(表格布局):事先设定布局子元素的行和列,子元素可以同时占多个相邻行(列)的格子。


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

1、SQLite: SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。

2、SharedPreference: 除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个xml文件,常用于存储较简单的参数设置。

3、File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。

4、ContentProvider: Android系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个Content Provider都会对外提供一个公共的URI(包装成Uri对象),如果应用程序有数据需要共享时,就需要使用Content Provider为这些数据定义一个URI,然后其他的应用程序就通过Content Provider传入这个URI来对数据进行操作。

5、网络:很多人容易忽视这项,但是实际中,网络是很多非游戏类APP的最主要数据存储方式之一。


七、请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系?

简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理. 
1. Message 
    Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
2. Handler 
    Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
3. Message Queue 
    Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
    每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4. Looper 
    Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:
    1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
    2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
    1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
    2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
    3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
    由此可见,我们实现的handleMessage方法是优先级最低的!
    3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
    在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行! 
    1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper; 
    2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。

八、AIDL的全称是什么?如何工作?能处理哪些类型的数据?

AIDL的英文全称是Android Interface Define Language

A进程要去调用B进程中的service时,并实现通信,我们通常都是通过AIDL来操作的

A工程:

首先我们在net.blogjava.mobile.aidlservice包中创建一个RemoteService.aidl文件,在里面我们自定义一个接口,含有方法getADT插件会在gen目录下自动生成一个RemoteService.java文件,该类中含有一个名为RemoteService.stub的内部类,该内部类中含有aidl文件接口的get方法。

说明一:aidl文件的位置不固定,可以任意

然后定义自己的MyService类,在MyService类中自定义一个内部类去继承RemoteService.stub这个内部类,实现get方法。在onBind方法中返回这个内部类的对象,系统会自动将这个对象封装成IBinder对象,传递给他的调用者。

其次需要在AndroidManifest.xml文件中配置MyService类,代码如下:

  

 

    

    

        

     

为什么要指定调用AIDL服务的ID,就是要告诉外界MyService这个类能够被别的进程访问,只要别的进程知道这个ID,正是有了这个ID,B工程才能找到A工程实现通信。

说明:AIDL并不需要权限

B工程:

      首先我们要将A工程中生成的RemoteService.java文件拷贝到B工程中,在bindService方法中绑定aidl服务

      绑定AIDL服务就是将RemoteServiceID作为intentaction参数。

      说明:如果我们单独将RemoteService.aidl文件放在一个包里,那个在我们将gen目录下的该包拷贝到B工程中。如果我们将RemoteService.aidl文件和我们的其他类存放在一起,那么我们在B工程中就要建立相应的包,以保证RmoteService.java文件的报名正确,我们不能修改RemoteService.java文件

           bindService(new Inten("net.blogjava.mobile.aidlservice.RemoteService"), serviceConnection, Context.BIND_AUTO_CREATE); 

       ServiceConnectiononServiceConnected(ComponentName name, IBinder service)方法中的service参数就是A工程中MyService类中继承了RemoteService.stub类的内部类的对象。


九、注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意?

在android下,要想接受广播信息,那么这个广播接收器就得我们自己来实现了,我们可以继承BroadcastReceiver,就可以有一个广播接受器了。有个接受器还不够,我们还得重写BroadcastReceiver里面的onReceiver方法,当来广播的时候我们要干什么,这就要我们自己来实现,不过我们可以搞一个信息防火墙。具体的代码:

   public class SmsBroadCastReceiverextends BroadcastReceiver

  {

  @Override

  public void onReceive(Context context, Intent intent)

  {

  Bundle bundle = intent.getExtras();

  Object[] object = (Object[])bundle.get("pdus");

  SmsMessage sms[]=new SmsMessage[object.length];

  for(int i=0;i

  {

  sms[0] =SmsMessage.createFromPdu((byte[])object);

  Toast.makeText(context, "来自"+sms.getDisplayOriginatingAddress()+"的消息是:"+sms.getDisplayMessageBody(),Toast.LENGTH_SHORT).show();

  }

  //终止广播,在这里我们可以稍微处理,根据用户输入的号码可以实现短信防火墙。

  abortBroadcast();

  }

  }

  当实现了广播接收器,还要设置广播接收器接收广播信息的类型,这里是信息:android.provider.Telephony.SMS_RECEIVED

  我们就可以把广播接收器注册到系统里面,可以让系统知道我们有个广播接收器。这里有两种,一种是代码动态注册:

  //生成广播处理

  smsBroadCastReceiver = newSmsBroadCastReceiver();

  //实例化过滤器并设置要过滤的广播

  IntentFilter intentFilter = newIntentFilter("android.provider.Telephony.SMS_RECEIVED");

  //注册广播

  BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver,intentFilter);

  一种是在AndroidManifest.xml中配置广播

  package="spl.broadCastReceiver"

  android:versionCode="1"

  android:versionName="1.0">

  android:label="@string/app_name">

  两种注册类型的区别是:

  1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。

  2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

十、什么是ANR?如何避免它?

ANR:应用程序没有响应(Application Not Responding),五秒
在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。当出现下列情况时,Android就会显示ANR对话框了:
1、对输入事件(如按键、触摸屏事件)的响应超过5秒
2、意向接受器(intentReceiver)超过10秒钟仍未执行完毕
3、Android应用程序完全运行在一个独立的线程中(例如main)。这就意味着,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intent broadcast)。

  因此,任何运行在主线程中的方法,都要尽可能的只做少量的工作。特别是活动生命周期中的重要方法如onCreate()和 onResume()等更应如此。潜在的比较耗时的操作,如访问网络和数据库;或者是开销很大的计算,比如改变位图的大小,需要在一个单独的子线程中完成(或者是使用异步请求,如数据库操作)。但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束 -- 也不需要调用Therad.wait()或者Thread.sleep()方法。取而代之的是,主线程为子线程提供一个句柄(Handler),让子线程在即将结束的时候调用它(xing:可以参看Snake的例子,这种方法与以前我们所接触的有所不同)。使用这种方法涉及你的应用程序,能够保证你的程序对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处理而产生的ANR。这种实践需要应用到所有显示用户界面的线程,因为他们都面临着同样的超时问题。

你可能感兴趣的:(android)