1、什么是ANR以及如何避免它?
答:在android中,由活动管理器(ActivityManager)和窗口管理器(WindowManager)来监听系统服务的响应,如果一个应用程序出现长时间无响应输入事件的状态,系统会弹出一个对话框提示应用程序无响应,提示用户是否继续等待还是退出,这种情况通常称为ANR(Application Not Responsing)。 一般情况下,应用程序无响应的时间超过5秒会出现ANR,而BroadcastReceiver超过10秒才会出现。
ANR一般是由于在主线程中执行过多的耗时操作比如数据库操作、网络服务,以及复杂的逻辑运算等等造成的。所以应开启子线程,利用Handler以及Message来处理这些操作,比如更新UI,也可以利用runOnUiThread来将子线程切换到主线程。
2、Android四大组件及生命周期?
答:四大组件分别是Activity、BroadcastReceiver、Service和Content Provider。
(1)Activity(活动):用于和用户进行交互的界面,在这个界面中可以通过控件来监听和处理一些用户输入事件(点击、触摸、滑动等)。当一个活动A第一次启动时,会依次调用onCreate()方法,onStart()方法和onResume()方法,当A利用Intent启动一个新的活动B时,如果B是一个窗体或透明型使得A仍然可见则A会调用onPause()方法,若A不可见则会调用onStop()方法,此时重新启动A会调用onRestart()方法,当A被销毁时会调用onDestroy()方法。
(2)BroadcastReceiver(广播接收器):用于接收来自系统或其他应用程序发送的广播,其注册有两种方式,一种是在代码中动态注册,一种是在manifest静态注册.
Activity中注册:
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android,intent.action.MY_RECEIVER");
registerReceiver(receiver,filter);
manifest中注册:
<Receiver android:name=".Myreceiver">
<Intent-Filter>
<action android:name="android,intent.action.MY_RECEIVER"/>
Intent-Filter>
Receiver>
BroadcastReceiver的生命周期很短,当接收到一个广播时会调用onReceive()方法,下面是API文档的解释,可以看到不能再这个方法中进行耗时操作,因为系统仅允许其在10秒之内接收广播,否则请求会被杀死。
When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed). You cannot launch a popup dialog in your implementation of onReceive().
(3)Service(服务):与Activity相同的级别,区别是Service用于长时间运行而不需要和用户直接交互或被其他应用程序使用,使用时必须在manifest中注册。Service也有两种启动方式startService()和bindService(),第一种启动后必须调用stopSelf()方法进行取消,或在启动之后可调用stopService();第二种用于将Activity或其他组件与Service绑定,可以使用IBinder接口与ServiceConnection来进行绑定。
Service的生命周期有两种形式,一种是通过context.startService()启动然后调用onCreate()方法和onStartCommand()方法,其中onCreate()只在第一次调用startService()时执行,最后调用stopSelf()或context.stopSevice()后Service的onDestroy()方法执行然后服务销毁。第二种是客户端可以通过context.bindService()来与服务建立一个永久的连接,当Service的onCreate()并未执行时,同样也可以创建Service,不过此时会执行onBind()方法,从而返回一个IBinder对象给客服端,允许客户端调用回服务,服务会一直运行只要建立连接并且没有断开,如果此时既调用了onStart()又调用了bindService(),Android中规定必须使得两种条件都不满足才能销毁服务,所以要同时调用stopService()和unbindService()再能使onDestroy()方法执行,服务才会销毁,完成最后的清理工作(停止线程、取消服务的注册等等)。
(4)ContentProvider(内容提供器):用于不同应用程序之间实现数据共享的功能,并且保证数据的安全性。这些数据可以使用文件、sharePreference或SQLite数据库进行存储。
使用ContentProvider会实现6个抽象方法,分别是onCreate(),query(),insert(),update(),delete(),getType().使用这些方法对数据进行增删改查操作。
3、Activity的四种启动模式?
答:standard、singleTop、singleTask、singleInstance。
(1)standard:默认的启动模式。在这种模式下,每当启动一个新的活动就会在返回栈中入栈,并处于栈顶位置,无论是否栈中已存在该活动,启动时都会创建一个实例。
(2)singleTop:当启动活动时发现栈顶已是该活动,则不再创建新的实例,若并未位于栈顶启动时仍会创建实例。
(3)singleTask:每次启动活动时系统会在返回栈中检查是否存在该活动的实例,如果有直接使用,并把这个活动上面所有活动出栈。
(4)singleInstance:在这种模式下,系统开始会检查是否存在目标Activity的实例,如果已存在,直接将其所在的Task转到前台,然后显示这个Activity;如果目标Activity不存在,则会创建一个新的Task,再创建一个目标Activity实例放入栈顶,无论是哪种情况,加载的活动都位于栈顶且Task中只有这一个活动。
4、Activity界面恢复与保存?
答:当手机的内存不足时,系统会销毁一些在后台运行的活动,此时如果重新启动这个活动,可能刚在正在进行的任务或处理的数据会消失,Android中提供了一种方法onSaveInstanceState()和RestoreInsatanceState()用来保存数据和重新获取数据,当系统要毁掉一个活动时,会调用onSaveInstanceState()方法,而这个方法会使用一个Bundle对象来保存数据,最后调用RestoreInstanceState()方法进行取数据,而且在Activity中的onCreate(Bundle savedInstanceState)方法的参数中也可以获取数据。
5、DDMS与traceView的区别?
答:DDMS是一个程序执行查看器,在其内可以看到一些线程和堆栈信息,可用于检测内存是否泄漏。
traceView是一个程序性能分析器,可用来跟踪代码执行的时间,在要跟踪的两个方法中分别添加Debug.startMethodTracing();和 Debug.stopMethodTracing();并在注册文件生命SD卡写权限,然后进行编译运行程序, 最后使用shell导出在SD卡中生成的trace文件,打开后可进行分析代码各处的耗时情况。
6、什么是AIDL以及如何使用?
答:AIDL( Android Interface definition language)服务是一种android内部进程通信接口的描述语言,通过它可以定义进程中的通信接口,从而实现跨进程访问。
AIDL服务建立的步骤如下:
(1)在Android Studio的工程下的app文件new一个aidl文件,该文件的语法类似于Java代码,不过不能加任何修饰符,AIDL不支持的数据类型(InputStream、OutputStream等等)。
在这里新建一个MyService.aidl文件,并且写入一个String方法。
// MyService.aidl
package com.example.asus.aidl;
// Declare any non-default types here with import statements
interface MyService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String getValue();
}
(2)如果aidl文件的内容是正确的,ADT会自动编译并且生成一个Java接口文件(*.java),位置如下图所示。
(3)建立一个TestService类(Service的子类),在该类中新建一个TestServiceImpl类继承Myservice.Stub类,然后在TestServiceImpl类中实现接口中的方法,并且在onBind()方法中返回TestServiceImpl的实例,否则客户端无法获得服务对象。
package com.example.asus.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
/**
* Created by asus on 2017/3/17.
*/
public class TestService extends Service {
public class TestServiceImpl extends MyService.Stub{
@Override
public String getValue() throws RemoteException {
return "Hello World";
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new TestServiceImpl();
}
}
(5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
<service android:name=".TestService">
<intent-filter>
<action android:name="com.example.wk.aidl.MyService"/>
intent-filter>
service>
其中”com.example.wk.aidl.MyService”是客户端访问AIDL服务的id。
(6)当服务端得到AIDL服务时,然后要在客户端绑定服务,首先新建一个工程用于客户端测试,把刚才的工程下的app/src/main目录下系统自动创建的aidl文件夹连同底下的包以及adil文件一起复制到新的工程的app/src/main目录下(一定要复制过去,如果新建即使命名相同可能也会编译失败)如图所示:
(7)新建一个类TestAdilClient用于绑定服务,定义两个Button,第二个先设为不可用,点击第一个Button会调用bindService()方法进行绑定,如果绑定成功会调用onServiceConnected()方法将服务端获取的IBinder对象传给客户端,第二个调用服务的Button会变为可用状态,然后点击调用服务端的getValue()方法获取一个字符串。
package com.example.asus.aidl;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class TestAidlClient extends AppCompatActivity implements View.OnClickListener {
private Button bind_service;
private Button send;
private TextView content;
private MyService myService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = MyService.Stub.asInterface(service); //将服务端的IBinder对象传给客户端
send.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind_service = (Button)findViewById(R.id.bind_Service);
send = (Button)findViewById(R.id.send);
send.setEnabled(false);
content = (TextView)findViewById(R.id.content);
bind_service.setOnClickListener(this);
send.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.bind_Service:
Intent intent = new Intent();
intent.setAction("com.example.wk.aidl.MyService");
intent.setPackage("com.example.asus.aidl");
bindService(intent,connection, Context.BIND_AUTO_CREATE);
break;
case R.id.send:
try{
content.setText(myService.getValue());
}catch (RemoteException e){
e.printStackTrace();
}
break;
}
}
}
在这里要注意的一点是在android5.0以上的版本在发送服务时不能使用隐式Intent,因此在创建一个Intent对象后不仅要添加action还要添加要访问app的包名。
intent.setAction("com.example.wk.aidl.MyService");
intent.setPackage("com.example.asus.aidl");