Android面试总结ing.

2019-08-06  本文已影响0人  飞的鱼33

由于近期在准备面试,所以就把面试题作为简书的开始之旅吧。

一.自定义Handler的时候,如何有效避免内存泄漏的问题

问题原因:一般非静态内部类持有外部类引用的情况下,当外部类使用完成时无法被系统回收内存,从而造成内存泄漏。这里非静态的Handler持有Activity等组件的引用,当Activity被销毁后,该Handler依然持有Activity的引用,从而造成内存泄漏。

Handler:是一个消息发送和处理机制的核心组件之一,用来处理异步消息,通常与Message、MessageQueue、Looper配合使用。

Handler工作机制:在主线程创建一个Handler对象,并重写handleMessage()方法,然后在子线程需要发送消息的地方创建Message对象(一般推荐Message.obtain()这个静态的方法获取,因为这个方法会从消息池中获取Message对象,如果消息池是空的,才会构建一个新的Message对象),通过handler把消息发送出去。之后这条消息进入MessageQueue中等待被处理,通过Looper对象会一直从MessageQueue中取出待处理的消息,它会开启无限循环(Loper.loop())并不停的从 MessageQueue 中查看是否有新消息,如果有就拿出来处理,如果没有呢,就阻塞(其实真正的阻塞在 MessageQueue 的 next 里),最后分发返回Handler的handleMessage()方法中。

Message和Runnable类是消息的载体,通过sendMessage发送消息;

MessageQueue是一个消息等待的队列,存放着等待处理的消息;

Looper则负责从队列中取出消息。

为什么使用Handler:

主线程无法进行时间比较繁长的任务,所以需要子线程进行处理,然而子线程无法进行UI的界面更新,所以我们需要使用handler来传递消息给主线程,让其完成UI的更新。由于主线程和子线程进行不同的时间工作,所要需要用MessageQueue来存放子线程的消息,Looper取出消息交给主线程响应。

在主线程创建Handler和子线程创建的区别:

1.在主线程中创建handler,并不需要经历初始化looper等操作,因为在主线程刚创建的时候系统就自动为该主线程创建好了并启动了循环(系统在应用启动的入口ActivityThread类的main()方法中帮我们调用了),所以我们只需要在主线程中new一个handler并且重写handleMessage方法,当其他的子线程需要更新UI的时候,只需要获取该handle对象,并将信息发送即可。

2.在子线程创建handler,需要先初始化Looper,调用Looper.prepare();然后 创建looper对象,looper=Looper.myLooper();最后启动looper循环,looper.loop();

Handler的2个主要作用:

1.安排调度(scheule)消息和可执行的runnable,可以立即执行,也可以安排在某个将来的时间点执行。

2.让某一个行为(action)在其他线程中执行。

解决方法:

1.保证Activity被finish()时该线程的消息队列没有这个Activity的handler内部类的引用。这个场景是及其常见的,因为handler经常被用来发延时消息。一个补救的办法就是在该类需要回收的时候,手动地把消息队列中的消息清空:mHandler.removeCallbacksAndMessages(null);

2.要么让这个handler不持有Activity等外部组件实例,让该Handler成为静态内部类。(静态内部类是不持有外部类的实例的,因而也就调用不了外部的实例方法了)

3.在2方法的基础上,为了能调用外部的实例方法,传递一个外部的弱引用进来)

4.将Handler放到抽取出来放入一个单独的顶层类文件中。

引用类型 内容
强引用(Strong Reference) 默认引用,如果一个对象具有强引用,垃圾回收器绝不会回收它。在内存空 间不足时,Java虚拟机宁愿抛出OutOfMemory的错误,使程序异常终止,也不会强引用的对象来解决内存不足问题
软引用(SoftReference) 如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
弱引用(WeakReference) 在垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference) 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

弱引用示例:

public class TestWeakActivity extends AppCompatActivity {

    private MyHandler myHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myHandler=new MyHandler(TestWeakActivity.this);
    }


    public static class MyHandler extends Handler{
        private WeakReference<TestWeakActivity> wActivity;
        private MyHandler(TestWeakActivity activity){
            wActivity=new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            TestWeakActivity tActivity=wActivity.get();
            if(null != tActivity){
                //处理逻辑信息
            }
        }
    }
}

二、Activity与Fragment的通信方式总结
一般我们可以通过以下几种方式实现Activity和Fragment的通信:

       //Activity
        Bundle bundle=new Bundle();
        bundle.putString("name","张三");
        TestFragment testFragment=new TestFragment();
        testFragment.setArguments(bundle);
@Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_main, null);
        Bundle bundle = getArguments();
        //这里就拿到了之前传递的参数
        String name = bundle.getString("name");
        return view;
    }

Handler

public class MainActivity extends AppCompatActivity { 
      public Handler mHandler = new Handler(){       
          @Override
           public void handleMessage(Message msg) { 
                super.handleMessage(msg);
                 //相应的逻辑处理代码
           }
     }
     
   } 
    public class MainFragment extends Fragment{ 
          //保存Activity传递的handler
           private Handler mHandler;
           @Override
           public void onAttach(Activity activity) { 
                super.onAttach(activity);
                if(activity instance MainActivity){ 
                      mHandler =  ((MainActivity)activity).mHandler; 
                }
           }
           
     }

这种方式的缺点:

EventBus

// Activity  注册订阅者
EventBus.getDefault().register(this);
//定义接收信息的方法
@Subscribe
    public void onEventMainThread(String msg) {
        Log.d("接收到消息: ",msg);
    }
//发送消息
EventBus.getDefault().post("Hello");

广播

//接收广播
public  static  class JumpReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            KLog.d("aaa_收到广播: "+intent.getAction());
        }
    }
//注册和发送广播
private JumpReceiver receiver;

IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("jumpOrder");
receiver=new JumpReceiver();
registerReceiver(receiver,intentFilter);

//触发广播
Intent mIntent = new Intent("jumpOrder");
sendBroadcast(mIntent);

接口回调

另外Fragment与Fragment之间通讯可以参考

三、内存泄漏的原因以及解决方法

内存泄漏:当一个对象使用完成后,本该被系统回收内存时,而有另外一个正在使用的对象持有它的引用从而导致对象不能回收。这种本该被回收的对象不能被系统回收而停留在堆内存中,就产生了内存泄漏。
内存溢出(OOM):程序在申请内存时,没有足够的内存空间供其使用,就会出现OOM。也就是内存不够用。

内存泄漏的根本原因:生命周期长的对象持有生命周期短的对象的引用,持有引用者的生命周期>被引用者的生命周期;
内存溢出的原因
1.当发生过多的内存泄漏就会导致程序内存空间不够,所需的内存空间会超出系统分配的空间,从而内存溢出(关键原因);
2.还有一种情况就是手机本身的内存不足;
内存溢出场景:1.往内存中加载超大文件;2.循环创建大量对象;

java的GC内存回收机制:当对象不再持有任何引用的时候才会进行回收。

常见的内存泄漏场景以及解决方法:
1.资源对象使用完后没有关闭

2.Static修饰的成员变量以及单例模式
原因:static关键字修饰的成员变量(生命周期) = 应用程序(生命周期)
例如: 当Activity需销毁时,由于mContext的生命周期 = 应用程序的生命周期,则 Activity无法被回收,从而出现内存泄露
解决方法:如果使用Context ,尽可能使用ApplicationContext
单例模式就是静态成员变量,所以在创建单例时,传入的Context最好是ApplicationContext

3.非静态内部类持有外部类的引用
非静态内部类创建静态实例引起的内存泄漏;
Handler持有Activity的引用
解决办法:将Handler声明为静态内部类,就不会持有外部类的引用,其生命周期就和外部类无关
如果Handler里面需要context的话,可以通过弱引用方式引用外部类

4.线程操作不当
原因:线程持有对象的引用在后台执行,与对象的生命周期不一致
解决办法:静态实例+弱引用(WeakReference)方式,使其生命周期一致

如何避免内存泄漏:
(1)编码规范上:
①资源对象用完一定要关闭,最好加finally
②静态集合对象用完要清理
③接收器、监听器使用时候注册和取消注册成对出现
④context使用注意生命周期,如果是静态类引用直接用ApplicationContext
⑤使用静态内部类
⑥结合业务场景,设置软引用,弱引用,确保对象可以在合适的时机回收
(2)建设内存监控体系
线下监控:
①使用ArtHook检测图片尺寸是否超出imageview自身宽高的2倍
②编码阶段Memery Profile看app的内存使用情况,是否存在内存抖动,内存泄漏,结合Mat分析内存泄漏
线上监控:
①上报app使用期间待机内存、重点模块内存、OOM率
②上报整体及重点模块的GC次数,GC时间
③使用LeakCannery自动化内存泄漏分析

如何确定是否存在内存泄漏:

内存优化:
(1)为应用申请更大内存,把manifest上的largeheap设置为true
(2)减少内存的使用
①使用优化后的集合对象,比如SpaseArray;
②使用微信的mmkv替代sharedpreference;
③对于经常打log的地方使用StringBuilder来组拼,替代String拼接
④统一带有缓存的基础库,特别是图片库,如果用了两套不一样的图片加载库就会出现2个图片各自维护一套图片缓存
⑤给ImageView设置合适尺寸的图片,列表页显示缩略图,查看大图显示原图
⑥优化业务架构设计,比如省市区数据分批加载,需要加载省就加载省,需要加载市就加载失去,避免一下子加载所有数据
⑦界面优化(层级减少、界面尽量不刷新或者局部刷新)也会优化内存
(3)避免内存泄漏

四、Android的启动模式LanuchMode

1.standard:标准模式、默认模式,每次创建一个Activity都会创建一个新的实例;
应用场景:大多数的普通Activity

2.singleTop:栈顶复用模式
如果该Activity处于栈顶,则不会创建新的Activity实例,直接复用栈顶的Acticity,会调用onNewIntent();
如果该Activity不存在或者不处于栈顶,则会跟standard模式一样;
应用场景:假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存(一般常见于社交应用中的通知栏行为功能,例如:App 用户收到几条好友请求的推送消息,需要用户点击推送通知进入到请求者个人信息页,将信息页设置为 SingleTop 模式就可以增强复用性)

3.singleTask:栈内复用模式,不会存在多个实例
如果该Activity不存在栈中,则会新建一个Activity实例;
如果该Activity处于栈中,则会移除该Activity上面的所有Activity,使之成为栈顶;
应用场景:一般用作应用的首页,例如浏览器主页,用户可能从多个应用启动浏览器,但主界面仅仅启动一次,其余情况都会走onNewIntent(),并且会清空主界面上面的其他页面

4.singleInstance:全局单例模式
是全局单例模式,是一种加强的SingleTask模式。它除了具有它所有特性外,还加强了一点:具有此模式的Activity仅仅能单独位于一个任务栈中。
应用场景:这个经常使用于系统中的应用,比如Launch、锁屏键的应用等等,整个系统中仅仅有一个!所以在我们的应用中一般不会用到(模式常应用于独立栈操作的应用,如闹钟的提醒页面,当你在A应用中看视频时,闹钟响了,你点击闹钟提醒通知后进入提醒详情页面,然后点击返回就再次回到A的视频页面,这样就不会过多干扰到用户先前的操作了)

五、如何实现多线程
多线程在应用中是很常见,通常有以下几种实现方式:

 public class MyThread extends Thread{
        @Override
        public void run() {
            //处理耗时逻辑
        }
    }
new MyThread().start(); //启动线程

⑵实现Runnable接口

public class MyThread implements Runnable{
        @Override
        public void run() {
            //处理逻辑
        }
    }
new Thread(new MyThread()).start();//启动线程

也可以使用匿名类来实现 Runnable 接口:

new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        }).start();

⑶Handler
3.1Handler的POST方式

// 声明一个Handler对象
private static Handler handler=new Handler();
// 新启动一个子线程
new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //进行逻辑处理  
                    }
                });
            }
        }).start();
new Thread(new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //延迟2秒处理逻辑
                    }
                },2000);
            }
        }).start();

3.2Handler的Message方式

private  Handler  handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //根据消息码判断处理
            switch (msg.what){
                case 1:
                    break;
                    default:
            }
        }
    };

发送消息

        // 获取一个Message对象,设置what为1
        Message msg=Message.obtain();
        msg.what=1;
        msg.obj="内容";
        // 发送这个消息到消息队列中
        handler.sendMessage(msg);

⑷AsyncTask
为了更方便地在子线程中进行更新 UI 操作,Android 基于异步处理消息机制封装了一个工具类 AsyncTask。
AsyncTask 是个抽象类,所以要使用它必须创建一个子类 。 在继承时可以指定 3 个泛型参数:

参数 说明
Params 开始异步任务执行时传入的参数类型,对应excute()中传递的参数
Progress 异步任务执行过程中,返回下载进度值的类型
Result 返回的结果类型,与doInBackground()的返回值类型保持一致

使用步骤:

public class MyAsyncTask extends AsyncTask<Params, Progress, Result>{

        // 作用:执行线程任务前的操作
        @Override
        protected void onPreExecute() {

        }

        // 作用:接收输入参数、执行任务中的耗时操作、返回线程任务执行的结果
        @Override
        protected Object doInBackground(Object[] objects) {
            // 自定义的线程任务
            return null;
        }

        // 作用:在主线程显示线程任务执行的进度
        @Override
        protected void onProgressUpdate(Object[] values) {

        }

        // 作用:接收线程任务执行结果、将执行结果显示到UI组件
        @Override
        protected void onPostExecute(Object o) {
        }


        // 作用:将异步任务设置为:取消状态
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }
MyAsyncTask myAsyncTask=new MyAsyncTask();
myAsyncTask.execute();

比如模拟一个加载进度条的demo:

public class MyAsyncTask extends AsyncTask<String, Integer, String>{

        // 作用:执行线程任务前的操作
        @Override
        protected void onPreExecute() {
            progress_Tv.setText("加载中...");
        }

        @Override
        protected String doInBackground(String... result) {
            // 自定义的线程任务
            try {
                int count = 0;
                int length = 1;
                while (count<99) {
                    count += length;
                    // 可调用publishProgress显示进度, 之后将执行onProgressUpdate
                    publishProgress(count);
                    // 模拟耗时任务
                    Thread.sleep(50);
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        // 作用:在主线程显示线程任务执行的进度
        @Override
        protected void onProgressUpdate(Integer... progress) {
            progressBar.setProgress(progress[0]);
            progress_Tv.setText("加载..." + progress[0] + "%");
        }

        // 作用:接收线程任务执行结果、将执行结果显示到UI组件
        @Override
        protected void onPostExecute(String result) {
            // 执行完成后,则更新UI
            progress_Tv.setText("加载完成");
        }


        // 作用:将异步任务设置为:取消状态
        @Override
        protected void onCancelled() {
            progress_Tv.setText("已取消");
            progressBar.setProgress(0);
        }
    }

注意事项

如何终止线程
Thread的一些主要函数

函数名 作用
start 启动线程
run 线程运行时所执行的代码
sleep 线程休眠,进入阻塞状态,sleep方法不会释放锁(其它线程不会进入synchronized方法体或方法块)
wait 进入阻塞状态,wait 方法释放锁,只有调用对象的 notify() ,线程才会继续运行
interrupt 中断线程,注意只能中断阻塞状态的线程
 public volatile boolean isNext= false;
    @Override
    public void run() {
        super.run();
        while (!isNext){
            // thread runing
        }
    }
public class SleepThread extends Thread{
        @Override
        public void run() {
            super.run();
            int i = 0;
            while(!isInterrupted()){  // 判断线程是否被打断
                try {
                    i++;
                    Thread.sleep(1000);
                    Log.i("thread",Thread.currentThread().getName()+":Running()_Count:"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Log.i("thread",Thread.currentThread().getName()+"异常抛出,停止线程");
                    break;// 抛出异常跳出循环
                }
            }

        }
    }

关于线程池的优化以及管理可以参考这篇文章

多线程的同步:
1.同步机制
在多线程中,有可能多个线程同时访问一个有限资源,必须防止这种情况的发生,所以引入了同步机制。在线程使用一个资源时为其加锁,这样其他线程便不能访问该资源,必须等待解锁后才能访问。
实现:
1.1使用synchronized创建方法,被synchronized修饰的方法叫同步方法;
1.2使用synchronized创建同步代码块,通过使用synchronized同步代码块,锁定一个对象,该对象作为可执行的标志从而达到同步的效果;
如果想要使用synchronized同步代码块达到和使用synchronized方法同样的效果,可以锁定this引用:

synchronized(this){

}
区别:

2.共享成员变量
成员变量与局部变量
成员变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作,这多个线程是共享一个成员变量的;

局部变量:如果一个变量是局部变量,那么多个线程对同一个对象进行操作,每个线程都会有一个该局部变量的拷贝。他们之间的局部变量互不影响。
eg:
实现了Runnable的线程类:


//两个线程操作同一个对象,共享成员变量
//int i;
@Override
public void run() {
    //两个线程操作同一个对象,各自保存局部变量的拷贝
    int i = 0;
    while(i<100){
        System.out.println(i);
        i++;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

在main方法中用两个线程操作同一个对象:

public static void main(String[] args) {

MyThread3 myThread = new MyThread3();
//下面两个线程对同一个对象(Runnable的实现类对象)进行操作
Thread thread = new Thread(myThread);
Thread thread2 = new Thread(myThread);
//各自保存局部变量的拷贝,互不影响,输出200个数字
thread.start();
thread2.start();
}

这里如果把i变成成员变量,则输出100个数字。

3.使用volatile关键字修饰
public volatile int inc = 0;
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

4.reentrantLock加锁结合Condition条件设置,在线程调度上保障数据同步
5.cas=compare and swap(set), 在保证操作原子性上,确保数据同步
六、抽象类和接口的区别
1.抽象类可以有构造方法,接口没有;
2.抽象类是继承的,接口是实现的;
3.抽象类的访问速度比接口快,因为接口需要时间去寻找类中实现的方法;
4.抽象类可以做方法的声明,也可以做方法的实现,接口只能做方法的声明;
5.抽象类里面的变量可以是普通变量,接口里面的变量只能是公共的静态变量;
6.一个类只能继承一个抽象类,但能实现多个接口;
应用场景
抽象类:

接口:

七、ANR出现的场景以及解决方法
ANR:application not responding,应用程序无响应。
在Android系统中,应用的响应能力是由活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务来监控的,通常在以下三种情况出现:

造成的原因主要是在主线程进行了耗时操作,阻塞主线程;
解决方法:
对于耗时的操作,比如网络访问、文件读写、数据库读写等,需要开辟子线程完成操作,避免死锁的出现,主线程主要实现UI更新。

八、谈下Broadcast Receiver
Broadcast(广播)是一种针对应用程序间传输信息,进行通信的机制;
Broadcast Receiver(广播接收器)则是接收来自系统和应用程序间的广播并对其做出响应的组件。
广播的类型:

1.普通广播
普通广播是一种完全异步执行的广播,当一条广播发送出去后,所有广播接收器几乎同一时刻都会接收到该广播,因此它们的接收先后顺序是随机的。普通广播不能被接收器截断。
发送一个普通广播很简单,通过Intent把要发送的广播数值传递进来,再调用sendBroadcast()方法把广播发送出去,这样所有监听该广播的接收器都会收到信息。

Intent castIntent=new Intent("testBroadCast");//指明广播值
sendBroadcast(castIntent);

2.有序广播
有序广播是一种同步执行的广播,当一条广播发送出去后,同一时刻只有一个广播接收器会接收到该消息,当这个广播接收器处理完逻辑后,广播才会继续传递,所以这个接收有先后顺序,优先级(priority)高的接收器会优先接收到广播消息。有序广播可以被广播接收器截断广播消息,使之无法继续传递。
2.1 调用sendOrderedBroadcast()发送
2.2 广播接收者会按照priority优先级从大到小进行排序
2.3 优先级相同的广播,动态注册的广播优先处理(推荐使用动态注册,因为8.0后限制绝大部分广播只能动态注册)

Intent castIntent=new Intent("testBroadCast");//指明广播值
sendOrderedBroadcast(castIntent,null);

3.本地广播
以上两种广播都是属于全局广播,即发送出去的广播可以被其他应用所接收,我们也能接收其他应用发送的广播。为了能够简单解决全局广播带来的安全性问题,Android引入了一套本地广播机制(仅支持动态注册),使用这个机制发送的广播消息,只能够在应用程序内部传递,广播接收器也只能接收本应用程序发送的广播消息。
优点:

3.1 通过LocalBroadcastManager.getInstance(this)方法获取一个LocalBroadcastManager实例
3.2 用LocalBroadcastManager提供的registerReceiver()和unregisterReceiver()方法来动态注册和取消接收器
3.3 sendBroadcast()方法发送本地广播

private LocalBroadcastManager localBroadcastManager;
private MyReceiver myReceiver;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager=LocalBroadcastManager.getInstance(this);
        //动态注册本地广播
        IntentFilter intentFilter=new IntentFilter();
        intentFilter.addAction("testLocalBroadCast");
        myReceiver=new MyReceiver();
        localBroadcastManager.registerReceiver(myReceiver,intentFilter);

        findViewById(R.id.all).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent("testLocalBroadCast");
                localBroadcastManager.sendBroadcast(intent);//发送本地广播
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(myReceiver);
    }

    public class MyReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //接收广播处理逻辑
        }
    }

九、简述app启动的流程
APP启动是通过Binder机制的,在此先简单介绍下Binder机制相关知识:
1.Linux内核:
①用户空间和内核空间:用户空间是指用户程序所运行的空间,内核空间指的是Linux内核运行的空间。它们是相互隔离的,也就预防了即使用户程序奔溃了,也不影响到内核;

②系统调用:用户空间访问内核空间的唯一方式就是系统调用(文件、网络等);通过这个统一入口接口,所有的资源访问都在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,内核统一维护,从而保障了系统的安全和稳定;

③Binder驱动:用户空间可以通过系统指定的入口进行调用访问内核,但是用户空间想要访问另外一个用户空间,就需要内核添加一个模块(Linux 的动态可加载内核模块(Loadable Kernel Module,LKM)机制)作为内核的一部分运行在内核中,用户之间通过这个模块作为桥梁,就能相互进行通信。在Android系统中,这个内核模块就是binder驱动。

2.Binder通信机制:
Linux内核本身也提供了很多进程通信方式,比如socket、管道之类的,但是为什么就专门为Android定制了一套binder机制呢?

通信机制分为三大步:
⑴ServiceManager进程在内部维护一张表,表里存储了向其注册过的进程信息
⑵服务器端进程向ServiceManager注册其信息
⑶客户端进程向ServiceManager访问获取相关信息,通过binder驱动和服务器端进程通讯

通常意义上来说,Binder就是指Andriod的通信机制。

下面正式进入APP启动流程介绍:
首先,APP启动涉及到很多进程和类,先简单介绍下它们:

所以,APP启动步骤如下:
①点击APP图标后,Launcher进程通过Binder向AMS发送打开Activity的请求,在SystemServer进程中的服务端AMS收到 START_ACTIVITY_TRANSACTION 命令后进行处理,调用 startActivity() 方法

②如果应用没有启动过,则需要创建一个新的进程去运行APP,通过ActivityStarter处理Intent和Flag等信息,通过Socket通知zygote进程fork新进程

③zygote进程会进行孵化(虚拟机和资源的复制),然后fork出新进程,在创建新进程的时候,AMS会保存一个 ProcessRecord 信息,Activity 应用程序中的AndroidManifest.xml 配置文件中,我们没有指定 Application 标签的 process 属性,系统就会默认使用 package 的名称。每一个应用程序都有自己的 uid,因此,这里 uid + process 的组合就可以为每一个应用程序创建一个ProcessRecord。每次在新建新进程前的时候会先判断这个 ProcessRecord 是否已存在,如果已经存在就不会新建进程了,这就属于应用内打开 Activity 的过程了

④进程创建成功切换至 App 进程,进入 app 进程后将 ActivityThread 类加载到新进程,zygote进程会调用ActivityThread的main()方法,这是应用程序的入口,主线程Looper和Handler的创建、开启主线程Looper的消息循环以及创建了ActivityThread执行 attach(false) 方法

public static void main(String[] args) {
    // 1. mainLooper
    Looper.prepareMainLooper();
    // 2. 创建ActivityThread,执行attach
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    // 3. Handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    // 4. loop()
    Looper.loop();
}

attach函数最终调用了AMS的远程接口ActivityManagerProxy的attachApplication函数,传入的参数是mAppThread,这是一个ApplicationThread类型的Binder对象,这个工程其实是将本地的 ApplicationThread 传递到 AMS。然后 AMS 就可以通过 ApplicationThread 的代理 ApplicationThreadProxy 来调用应用程序 ApplicationThread.bindApplication来创建Application

private void attach(boolean system) {
        if (!system) {
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(this.mAppThread);
            } catch (RemoteException var5) {
                throw var5.rethrowFromSystemServer();
            }
        } 
    }

attachApplication()方法内部调用了attachApplicationLocked()方法

@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

在attachApplicationLocked()方法中,会调用thread对象的bindApplication()方法,thread对象就是我们传递过来的ApplicationThread对象。顾名思义,bindApplication()方法应该就是创建Android应用Application的方法

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
 
            ......       
 
            if (app.instr != null) {
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            } else {
                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            }
 
            ......
 
     if (normalMode) {
         try {
            /*
             * 调用ActivityStackSupervisor的attachApplicationLocked()方法
             * 在该方法中调用了realStartActivityLocked()方法来启动一个Activity   
             */
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
         } catch (Exception e) {
           ......
         }
    }
 
    return true;
}

⑤在执行完bindApplication()之后,会调用ActivityStackSupervisor的attachApplicationLocked()方法来,在该方法中会调用realStartActivityLocked()方法来启动一个Activity,ActivityThread利用ClassLoader去加载Activity,创建Activity实例,回调oncreate()方法,Activity执行完onResume()方法之后,调用makeVisible()方法,才会真正的把视图展示出来

ApplicationThread.scheduleLaunchActivity()
//发送消息 H.LAUNCH_ACTIVITY。

sendMessage(H.LAUNCH_ACTIVITY, r);

ActivityThread.handleLaunchActivity()
//最终回调目标 Activity 的 onConfigurationChanged(),初始化 WindowManagerService。
//调用 ActivityThread.performLaunchActivity()

ActivityThread.performLaunchActivity() {
    //类似 Application 的创建过程,通过 classLoader 加载到 activity.
    activity = mInstrumentation.newActivity(classLoader, 
               component.getClassName(), r.intent);
    //因为 Activity 有界面,所以其 Context 是 ContextThemeWrapper 类型,但实现类仍是ContextImpl.
    Context appContext = createBaseContextForActivity(r, activity);
    activity.attach(context,mInstrumentation,application,...);
    //与 Window 进行关联
    
    //attach 后调用 activity 的 onCreate()方法。
    mInstrumentation.callActivityOnCreate(activity,...)
    
}
//在ActivityThread.handleLaunchActivity里,接着调用

Activity.performCreate() -> onCreate()
//最终回调目标 Activity 的 onCreate()。

Activity.setContentView()
//设置 layout 布局

ActivityThread.performResumeActivity()
//最终回调目标 Activity 的 onResume()。

十、Android的事件分发机制
事件传递的对象顺序流程: Activity > ViewGroup > View

事件分发的的核心方法:

方法 作用 调用时刻
dispatchTouchEvent() 分发点击事件 当点击事件能够传递给当前view时,此方法就会被调用
onInterceptTouchEvent() 判断是否拦截某个事件(该方法只存在ViewGroup中) 在ViewGroup()的dispathchTouchEvent内部调用
onTouchEvent() 处理点击事件 在dispathchTouchEvent内部调用

View点击事件的分发流程

view事件分发.png

View的 onTouch() 先于onClick() 执行

事件分发总结流程

流程图.png

onTouch() 和 onTouchEvent() 的区别

详情可以参考这篇文章

上一篇下一篇

猜你喜欢

热点阅读