面试题:

1. 泛型:  
	1. 什么是泛型,能解决什么问题  
	2. 说说java中的泛型的工作机制  
	3. 在泛型中extends和super关键字的区别     
2. Android 8.0适配?

1. 面试题解题

1.1 泛型

1.1.1 什么是泛型

泛型是Java SE1.5引入的,泛型本质是参数化类型,将所操作的数据类型指定为一个参数,该参数类型可以用在类、接口、方法中。分别叫做泛型类、泛型接口、泛型方法。

  1. 泛型类

泛型类用于类的定义中,比如List,Set,Map等。

/**
  * 此处的T是泛型标识,可以是任意的大写字母
  * 在调用时必须指定T的具体类型:new People<Integer>(60);
  */
 
 public class People<T> {
     private T t;
 
     public People(T t) {
         this.t = t;
     }
 }
  1. 泛型接口

同泛型类一样,用于接口的定义中,见下面的例子。

public interface Age<T> {
     T getAge();
 }

 public class People implements Age<Integer>{

     @Override
     public Integer getAge() {
         return new Random().nextInt(100);
     }
 }
  1. 泛型方法

需要在方法中使用的泛型如果没有在类和接口中定义过,那么必须在方法中定义后才可使用。

/**
  * public 和返回值E中间的类型申明是必需的,因为只有申请了该类型,才能在方法中使用
  */
 public <E> E getInfomation(Class<E> eClass) throws IllegalAccessException, InstantiationException {
     E e = eClass.newInstance();
     return e;
 }
1.1.2 泛型能解决什么问题
  1. 在编译时检查参数类型,避免异常发生
  2. 消除强制类型转换
  3. 提高代码复用率
1.1.3 说说java中的泛型的工作机制

Java泛型是编译时技术,在运行时不包含泛型信息,仅仅是Class的实例中包含类型参数的定义信息。它是通过Java编译器的擦除前端来实现的,可以认为是从源码到源码的转换,从泛型源码转为非泛型的代码。它有几个特点:

  1. 不能依靠泛型做类型转换
  2. 同一个泛型类,在被调用时类类型是一样的,无论传入的泛型类是否一样
1.1.4 在泛型中extends和super关键字的区别

使用底下的例子来说明,extends是上界通配符,指定类的上边界。它只能取,不能存,因为你不清楚具体存的是哪个实现类,所以无法存入。
super是下界通配符,指定类的下边界,它只能存,不能取,因为根本不清楚取出的数据是什么实现类,只能指定为Object。

class Food{}
class Fruit extends Food{}

class Apple extends Fruit{}

class Banana extends Fruit{}

public void testExtends(List<? extends Fruit> fruits){
    // fruits.add(new Apple());
    Fruit fruit = fruits.get(0);
}

public void testSuper(List<? super Fruit> list){
    // Object object = list.get(0);
    list.add(new Fruit());
}

1.2 Android 8.0适配?

1.2.1 Android8.0 通知适配

Android 8.0加入了ChannelId和ChannelGroup的概念,如果在创建通知的时候不加ChannelId,通知是不会被唤起的。

  1. 创建小组,这个步骤可有可无
  2. 创建ChannelId(为了简单,都简写写死,如果创建了小组,需要给ChannelId配置GroupId)
NotificationChannel notificationChannel = new NotificationChannel("track_id", "track_name", NotificationManager.IMPORTANCE_LOW);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(notificationChannel);
  1. 在构建通知时,加入ChannelId(比以往多加一个参数ChannelId)
Notification notification = new Notification.Builder(this, "track_id").build();
1.2.2 广播限制

Android 8.0的应用无法在其清单文件中注册隐式广播接收器。

两种方案:

  1. 动态注册广播
  2. 指定包名,作为显示广播发送
1.2.3 后台服务适配

Android 8.0 对应用在后台执行的操作增加了限制,当应用处于后台时,不允许再启动服务。Android8.0提供了方法,在后台可以创建前台服务,下面看下步骤

  1. 启动前台服务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
 	context.startForegroundService(service);
 }else{
 	context.startService(service);
 }
  1. 建立前台服务通知并调用startForeground(1,notification)

如果是IntentService,这部分逻辑需要加到onHandleIntent中,因为onCreate并不是每次调用startService都会调用。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
 	NotificationChannel notificationChannel = new NotificationChannel("track_id", "track_name", NotificationManager.IMPORTANCE_LOW);
 	NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 	notificationManager.createNotificationChannel(notificationChannel);
 	Notification notification = new Notification.Builder(this, "track_id").build();
 	startForeground(1,notification);
 }
  1. 关闭前台服务
service.stopForeground(true);
1.2.4 apk安装权限

Android 8.0 去除允许未知来源选项,需要用户手动确认,如果没有适配,覆盖安装时直接闪退。

  1. 最简单的解决方式就是,在Manifest文件中配置请求安装权限,这样在app调用安装界面时,系统会询问用户授权。
  2. 当然,你可以可以做的更复杂一些。
  1. 清单文件中配置REQUEST_INSTALL_PACKAGES权限
  2. 调用packageManager.canRequestPackageInstalls()获取是否已有该权限
PackageManager packageManager = getApplicationContext().getPackageManager();
 boolean canInstallApk = packageManager.canRequestPackageInstalls();
  1. 如果没有权限,则弹框提示,点击确定跳转用户授权页面(一般更多的是使用startActivityForResult,授权完成继续安装)
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
 startActivity(intent);