Android面试过程中遇到的一些问题2019

Android面试过程中遇到的一些问题2019

这里记录一下在Android中面试遇到的一些回答的不是很好的问题

1. 原生模块如何跳转到Flutter模块

  1. 集成sdk,并将sdk作为一个单独的module添加到项目中
  2. 跳转到Flutter模块

Flutter为Android提供了一个FlutterActivity,可以通过startActivity的方式跳转到Flutter界面

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}

Flutter的入口是main.dart这个文件。

2. 原生模块与Flutter之间的交互

通过写插件的方式可以实现与原生界面交互,主要是通过MethodChannel,在插件写完之后要注册插件才能正常使用插件的功能
可以参考一下这个地址 一个简单的插件

3.Dart线程

3.1 Dart线程的例子

可以参照一下dart的官方网站 dart官网对线程的描述

What’s the point?

  • Dart is single-threaded.
  • Synchronous code can make your program freeze.
  • Use Futures to perform asynchronous operations.
  • Use await in an async function to pause execution until a Future completes.
  • Or use Future’s then() method.
  • Use try-catch expressions in async functions to catch errors.
  • Or use Future’s catchError() method.
  • You can chain Futures to run asynchronous functions in order.

可以发现dart是单线程编程的语言,任何可以阻塞线程执行的代码都会导致程序卡死。
Dart使用Future对象表示异步操作,也可以使用async和await来表示异步

这里使用一个官方的例子来说明一下,可能会更加清楚

import 'dart:html';
import 'dart:async';

Future printDailyNewsDigest() async {
  String news = await gatherNewsReports();
  print(news);
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}

printWinningLotteryNumbers() {
  print('Winning lotto numbers: [23, 63, 87, 26, 2]');
}

printWeatherForecast() {
  print('Tomorrow\'s forecast: 70F, sunny.');
}

printBaseballScore() {
  print('Baseball score: Red Sox 10, Yankees 0');
}

Future gatherNewsReports() async {
  String path =
      'https://www.dartlang.org/f/dailyNewsDigest.txt';
  return (await HttpRequest.getString(path));
}

Android面试过程中遇到的一些问题2019_第1张图片

  1. The app begins executing.
  2. The main function calls printDailyNewsDigest(), which (because it’s marked async), immediately returns a Future, before any code is executed.
  3. The remaining print functions execute. Because they’re synchronous, each function executes fully before moving on to the next print function. For example, the winning lottery numbers are all printed before the weather forecast is printed.
  4. The body of the printDailyNewsDigest() function starts executing.
  5. After reaching the await expression (await gatherNewsReports()) and calling gatherNewsReports(), the program pauses, waiting for the Future returned by gatherNewsReports() to complete.
  6. Once that Future completes, execution of printDailyNewsDigest() continues, printing the news.
  7. When the printDailyNewsDigest() function body has completed executing, the Future that it originally returned completes, and the app exits.

第一步:app 开始执行
第二步: main方法调用了printDailyNewsDigest()方法,这个方法被标记为async,在其他代码执行之前,会返回一个Future对象
第三步:接着执行剩余的方法,由于他们是同步的,所以继续下一个打印功能之前,每个功能都会完全执行。举个例子,在打印天气预报之前打印中奖彩票的号码
第四步: printDailyNewsDigest()方法被执行
第五步: 接着会执行await的方法gatherNewsReports,程序会暂停,等待Future返回gatherNewsReports的执行结果
第六步: 一旦Future完成执行,printDailyNewsDigest()中的方法会继续执行,打印消息
第七步: 当printDailyNewsDigest()方法执行完毕,Future将返回完成状态,接着app就会退出

3.2 下面解释一下为什么Dart的单线程机制是如何设计的

Dart是Event-Looper以及Event-Queue的模型,所有的事件都是通过EventLooper的依次执行。
Dart的Loop就是

  1. 从EventQueue中获取Event
  2. 处理Event
  3. 直到EventQueue为空

这里便是一个简单的图
可以发现,用户的每一次操作都会产生一个Event
事件处理
这里是不是可以联想到Handler,Handler中也有一个Looper,通过Looper不断循环,去处理MessageQueue中的消息。这里就引出了一个问题,为什么Handler中的Looper是一个死循环,但是不会导致程序ANR呢?那现在又有一个问题,大家都写过Java程序,当main方法中的代码执行完毕之后,便会退出程序。但是Android中的代码执行完毕之后,并没有退出app,而是等待。这里就是Android中Handler的功劳,通过不断的Looper,来保证主线程中的代码不会执行完,如果执行完毕,那么这个线程就会退出。而UI的所有操作,比如屏幕刷新,各种点击事件,都会通过Handler来分发。所以ANR的原因是阻塞了Handler执行下一个Event,这个才是导致ANR的罪魁祸首。还有一点,在没有消息的时候回通过linux的epoll机制来阻塞,这个是另外一方面的知识,这里就不描述了。

回到Dart的线程机制。在Dart中,如果一个Dart方法开始执行,便无法打断,Dart通过
isolate起到类似线程的作用,在main方法执行完毕之后,Main isolate便开始处理一个个的Event。这一点是不是和Android的Handler机制很像呢?

3.2下面来说一下Dart中的EventQueue

在Dart中存在两个EventQueue,一个是EventQueue,还有一个是MicrotaskQueue
Microtask Queue存在的意义是:希望通过这个Queue来处理稍晚一些的事情,但是在下一个消息到来之前需要处理完的事情。

Event-Looper挑选Task的执行顺序为:

  1. 优先全部执行完Microtask Queue中的Event
  2. 直到Microtask Queue为空时,才会执行Event Queue中的Event

当Event Looper正在处理Microtask Queue中的Event时候,Event Queue中的Event就停止了处理了,此时App不能绘制任何图形,不能处理任何鼠标点击,不能处理文件IO等等

Android面试过程中遇到的一些问题2019_第2张图片
Dart中只能知道Event处理的先后顺序,不能知道某一个Event执行的具体时间点。

3.3 异步任务的调度

当有代码可以在后续任务执行的时候,有两种方式,通过dart:async这个Lib中的API即可:

  1. 使用Future类,可以将任务加入到Event Queue的队尾
  2. 使用scheduleMicrotask函数,将任务加入到Microtask Queue队尾

当使用EventQueue时,需要考虑清楚,尽量避免microtask queue过于庞大,否则会阻塞其他事件的处理

Android面试过程中遇到的一些问题2019_第3张图片

使用Future

new Future((){
    //  doing something
});

当然可以使用then的方式来拆解任务

void main(){
new Future(() => futureTask)  //  异步任务的函数
        .then((m) => "futueTask execute result:$m")  //   任务执行完后的子任务
        .then((m) => m.length)  //  其中m为上个任务执行完后的返回的结果
        .then((m) => printLength(m))
        .whenComplete(() => whenTaskCompelete);  //  当所有任务完成后的回调函数
}

int futureTask() {
    return 21; 
}

void printLength(int length) {
    print("Text Length:$length");
}

void whenTaskCompelete() {
    print("Task Complete");
}

使用scheduleMicrotask

async.scheduleMicrotask(() => microtask());

void microtask(){
  //  doing something
}

大家可以参考参考的地址1 参考地址2

4. dart如何创建一个对象

这个问题问的我有点蒙蔽,大半年没有用dart和Flutter了。

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

可以发现,dart创建对象的方式类似于kotlin,可以使用new关键字,也可以不使用new关键字

5.Greendao与Room的区别

个人的回答是,通过注解和apt的方式自动生成一些代码,包括sql等,这样可以节省简化sql的一些操作,不用自己手动写sql。但是面试官又问了一个问题?

5.1Greendao与Room的相对于原生的sqlite3 做了哪些优化操作?注解是两个框架的都用的东西,那么具体的区别在什么地方呢?

  1. 加密
    Room不支持SqlCipher加密,不过后续SqlCipher应该会提供针对Room的加密方式
    GreenDao集成了SqlCipher加密。

  2. 效率
    greendao的效率会高一些,但是Room是google的亲儿子,支持方面后期应该会好一些

  3. livedata
    Room支持livedata,greendao需要自己手写

sql的三种写入操作

  1. 慢速——最粗暴的方法
    直接通过sqllite3来执行操作
int sqlite3_exec(sqlite3*, const char *sql, int (*callback)(void*,int,char**,char**), void *, char **errmsg)  
  1. 中速——显式开启事务
    所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。通过事务的方式,这样可以提高效率

  2. 高速——写同步(synchronous)
    synchronous选项有三种可选状态,分别是full、normal、off
    full写入速度最慢,但保证数据是安全的,不受断电、系统崩溃等影响,而off可以加速数据库的一些操作,但如果系统崩溃或断电,则数据库可能会损毁

  3. 极速——执行准备

虽然写同步设为off后,速度又有小幅提升,但是仍然较慢。我又一次踏上了寻找提高SQLite插入效率方法的道路上。终于,我发现,SQLite执行SQL语句的时候,有两种方式:一种是使用前文提到的函数sqlite3_exec(),该函数直接调用包含SQL语句的字符串;另一种方法就是“执行准备”(类似于存储过程)操作,即先将SQL语句编译好,然后再一步一步(或一行一行)地执行。如果采用前者的话,就算开起了事务,SQLite仍然要对循环中每一句SQL语句进行“词法分析”和“语法分析”,这对于同时插入大量数据的操作来说,简直就是浪费时间。因此,要进一步提高插入效率的话,就应该使用后者。

综上所述啊,SQLite插入数据效率最快的方式就是:事务+关闭写同步+执行准备(存储过程),如果对数据库安全性有要求的话,就开启写同步。

详细的可以看一下这篇文章。我这里只是从面试的角度简单的收集了一下。

参考地址

6.多个behavior联动怎么做?你会重写Behavior中的那几个方法?

7.APP的启动流程

Android面试过程中遇到的一些问题2019_第4张图片

8.ActivityManagerService的启动

可以看一下

9.kotlin中几个操作符的区别

  1. let
  2. with
  3. run
  4. apply
  5. also

参考地址

10.kotlin中携程的原理

携程

11.常用的排序算法

这个自己看一下

12.常用的设计模式

这个自己看一下

你可能感兴趣的:(其他)