Android 主要知识架构整理(面试终极总结190530)

1.字符串运行速度:StringBuilder > StringBuffer (线程安全)> String ,String为常量,其它为变量,所以运行慢。

String不可变原因:字符串常量池的需要;运行String对象缓存HashCode,提高效率;多线程安全。

Serializable和Parcelelable的区别

Serializable 序列化接口,开销大,建议使用,java方法; Parcelelable 使用麻烦,效率高,多用于内存,Android方法。

倒计时

adTimer = new CountDownTimer(3500, 1000) {
    @Override
    public void onTick(long millisUntilFinished) {
        TextView time = (TextView) findViewById(R.id.start_ad_time);
        time.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startTimer.cancel();
                toMainActivity();
            }
        });
        time.setText("跳过" + millisUntilFinished / 1000 + "秒");
    }
    @Override
    public void onFinish() {
        toMainActivity();
    }
};

2.Fragment生命周期

onAttach(): 完成Fragment和Activity的绑定,参数中的Activity即为要绑定的Activity,可以进行赋值等操作。

onCreate() : 完成Fragment的初始化

onCreateView() : 加载Fragment布局,绑定布局文件

onActivityCreated() : 表名与Fragment绑定的Activity已经执行完成了onCreate,可以与Activity进行交互操作。

onStart() : Fragment变为可见状态

onResume() : Fragment变为可交互状态

onPause(): Fragment变为不可交互状态(不代表不可见)

onSaveInstanceState():保存当前Fragment的状态。记录一些数据,比如EditText键入的文本,即使Fragment被回收又重新创建,一样能恢复EditText之前键入的文本。

onStop(): Fragment变为不可见状态

onDestroyView() : 销毁Fragment的有关视图,但并未和Activity解绑,可以通过onCreateView()重新创建视图。Fragment销毁时或者ViewPager+Fragment情况下会调用

onDestroy() : 销毁Fragment时调用

onDetach() : 解除和Activity的绑定。Fragmen销毁最后一步

/**
     * fragment切换回来加载数据
     * @param hidden
     */

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            showCarType(); //显示已选车型
        }
    }

3.四大引用类型

强引用有引用变量指向时永远不会被垃圾回收

软引用(SoftReference):如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。用来实现内存敏感的高速缓存

弱引用(WeakReference):弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。为了防止内存泄漏

虚引用(PhantomReference):如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

4.通讯方式

线程间:①共享变量;②管道;③handler;④runOnUiThread(Runnable);⑤view.post(Runnable)。

进程间:①管道;②FIFO;③消息队列;④信号量;⑤共享内存区;⑥套接字socket信号。

Activity间:①Intent;②借助类的静态变量;③借助全局变量/Application;④借助外部工具(SharedPreference、SQLite、File、剪贴板);⑤借助Service。

5.BitMap

图片优化:异步加载,压缩处理bitmapFactory.options,设置内存大小,缓存于内存、SD卡,没有内存再从网络取。

内存优化:缓存LRU、缩放、Config、Compress选择、内存管理、缓存方式等等方面入手。、内存管理、内存优化、缩放、config、compress

6.Handler机制

Handler绑定线程:当创建Handler时将通过ThreadLocal在当前线程绑定一个Looper对象,而Looper持有MessageQueue对象。执行Handler.sendMessage(Message)方法将一个待处理的Message插入到MessageQueue中,这时候通过Looper.loop()方法获取到队列中Message,然后再交由Handler.handleMessage(Message)来处理。

7.HashMap

HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

HashMap 的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。在Java编程语言中,加载因子默认值为0.75,默认哈希表元为101

hashMap的默认加载因子为0.75,加载因子表示Hsah表中元素的填满的程度。加载因子越大,填满的元素越多,空间利用率越高,但冲突的机会加大了。反之,加载因子越小,填满的元素越少,冲突的机会减小,但空间浪费多了。冲突的机会越大,则查找的成本越高。反之,查找的成本越小。当桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为加载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。

如何实现HashMap线程同步?

①使用 java.util.Hashtable 类,此类是线程安全的。

②使用 java.util.concurrent.ConcurrentHashMap,此类是线程安全的。

③使用 java.util.Collections.synchronizedMap() 方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。

8.UI卡顿原因

每16ms绘制一次Activity,如果由于一些原因导致了我们的逻辑、CPU耗时、GPU耗时大于16ms(应用卡顿的根源就在于16ms内不能完成绘制渲染合成过程,16ms需要完成视图树的所有测量、布局、绘制渲染及合成),UI就无法完成一次绘制,那么就会造成卡顿。①内存抖动问题(极短时间内分配给对象和回收对象的过程),②方法耗时,③view本身卡顿。

解决办法:修改方法,使其不耗时,放到子线程中,如网络访问,大文件操作等,防止ANR,避免GPU过度绘制。

造成ANR(Activity 5秒 broadcast10秒)的原因:主线程做耗时操作;主线程被其他线程锁;CPU被其他进程占用,该进程没有分配CPU资源;OnReceiver过多操作,IO操作,如数据库、文件、网络

9.MVP

由3部分组成:View负责显示,Presenter负责逻辑处理,Model提供数据。在MVP模式里通常包含3个要素(加上View interface是4个):

View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity)

Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)

Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试

GitUserView

public interface GitUserView extends View{
   void onSuccess(GitUser gitUser);
    void onError(String error);

}

GitUserPresenter

public class GitUserPresenter implements Presenter {

    private final static String TAG = "GitUserPresenter";

    private Context context;
    private DataManager manager;
    private CompositeDisposable compositeDisposable;
    private GitUserView gitUserView;
    private GitUser gitUser;

    public GitUserPresenter(Context context) {
        this.context = context;
    }

    @Override
    public void onCreate() {
        manager = new DataManager(context);
        compositeDisposable = ContantUtils.compositeDisposable;
        //RxJava1: mCompositeSubscription = new CompositeSubscription();
    }

    @Override
    public void onDestory() {
        if (compositeDisposable != null && !compositeDisposable.isDisposed()) {
            compositeDisposable.dispose(); //解除订阅
            //RxJava1:CompositeSubscription.unsubscribe();
        }
    }

    @Override
    public void attachView(View view) {
        gitUserView = (GitUserView) view;
    }

    public void getGitUser() {
        Observable observable = manager.getGitUser();
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        compositeDisposable.add(d);
                    }

                    @Override
                    public void onNext(GitUser value) {
                        gitUser = value;
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                        Log.i(TAG, "onError:" + e.toString());
                        gitUserView.onError("请求失败:\n" + e.toString());
                    }

                    @Override
                    public void onComplete() {
                        Log.i(TAG, "onComplete!");
                        if (gitUserView != null) {
                            gitUserView.onSuccess(gitUser);
                            Log.i(TAG, "gitUser:" + gitUser.toString());
                        }
                    }
                });
    }
}

DataManager.java

public class DataManager {

    private RetrofitService retrofitService;

    public DataManager(Context context) {
        this.retrofitService = RetrofitHelper.getInstance(context).getServer();
    }

    /**
     * 获取Weather
     * @return
     */
    public Observable getGitUser() {
        return retrofitService.getGitUser();
    }
}

RetrofitService.java

public interface RetrofitService {
    //get请求
    @GET("basil2style")
    Observable getGitUser();
}

RetrofitHelper .java

/**
 * Created by wuhuihui on 2019/5/17.
 * RetrofitHelper:初始化Retrofit,设置请求API的baseUrl、gson解析方式
 */
public class RetrofitHelper {
    private final String TAG = "RetrofitHelper";
    private Context context;

    private static RetrofitHelper instance = null;
    private Retrofit retrofit = null;

    private RetrofitHelper(Context context) {
        this.context = context;
        initGitUserRetrofit();
    }

    public static RetrofitHelper getInstance(Context context) {
        if (instance == null) instance = new RetrofitHelper(context);
        return instance;
    }

    private void initRetrofit() {
        Log.i(TAG, ContantUtils.APPVERSION_BASE_URL);
        retrofit = new Retrofit.Builder()
                .baseUrl(ContantUtils.APPVERSION_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(
                        new GsonBuilder().create()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //实现异步
                .client(MyApp.okHtpClient)
                .build();
    }

    /**
     * GitUser https请求
     */
    private void initGitUserRetrofit() {
        Log.i(TAG, ContantUtils.GITHUBSER_BASE_URL);
        retrofit = new Retrofit.Builder()
                .baseUrl(ContantUtils.GITHUBSER_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(
                        new GsonBuilder().create()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(MyApp.okHtpClient)
                .build();
    }

    public RetrofitService getServer() {
        return retrofit.create(RetrofitService.class);
    }
}

Activity->ShowGitUser

/**
 * 显示gitUser
 */
private void showGitUser() {
    findViewById(R.id.loadData).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (SystemUtils.isNetworkAvailable(activity)) {
                showData = (TextView) findViewById(R.id.showData);

                gitUserPresenter.onCreate(); //启动Presenter,订阅View
                gitUserPresenter.getGitUser(); //开始请求数据
                //刷新UI,显示数据
                gitUserPresenter.attachView(new GitUserView() {
                    @Override
                    public void onSuccess(GitUser gitUser) {
                        showData.setText(gitUser.toString());
                    }

                    @Override
                    public void onError(String error) {
                        showData.setText(error);
                    }
                });
            } else
                Toast.makeText(getApplicationContext(), "当前网络不可用", Toast.LENGTH_SHORT).show();
        }
    });
}

 //需要在activity的onDestory()方法中调用gitUserPresenter.onDestory();
 //取消view的引用,避免内存泄漏

MVP的优点

1、Model与View完全分离,修改互不影响

2、更高效地使用,因为所有的逻辑交互都发生在一个地方—Presenter内部

3、一个Preseter可用于多个View,而不需要改变Presenter的逻辑(因为View的变化总是比Model的变化频繁)。

4、更便于测试。把逻辑放在Presenter中,就可以脱离用户接口来测试逻辑(单元测试)

缺点:造成类数量爆炸,代码复杂度和学习成本高,在某些场景下presenter的复用会产生接口冗余。

MVC的优点:

(1)耦合性低。所谓耦合性就是模块代码之间的关联程度。利用MVC框架使得View(视图)层和Model(模型)层可以很好的分离,这样就达到了解耦的目的,所以耦合性低,减少模块代码之间的相互影响。

(2)可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率。

(3)模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护。

10.事件分发机制

事件分发过程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三个方法协助完成

onInterceptTouchEvent()默认返回false,不做截获。返回true之后,事件流的后端控件就没有机会处理touch事件。view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理,如果onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

onTouch() 方法优先级高于 onTouchEvent(event) 方法

1、Android 事件分发总是遵循 Activity(Window) -> ViewGroup -> View 的传递顺序;

2、onTouch() 执行总优先于 onClick()

每当控件被点击时:

如果在回调onTouch()里返回false,就会让dispatchTouchEvent方法返回false,那么就会执行onTouchEvent();如果回调了setOnClickListener()来给控件注册点击事件的话,最后会在performClick()方法里回调onClick()。

onTouch()返回false(该事件没被onTouch()消费掉) = dispatchTouchEvent()返回false(继续向下传递) = 执行onTouchEvent() = 执行OnClick()

如果在回调onTouch()里返回true,就会让dispatchTouchEvent方法返回true,那么将不会执行onTouchEvent(),即onClick()也不会执行;

onTouch()返回true(该事件被onTouch()消费掉) = dispatchTouchEvent()返回true(不会再继续向下传递) = 不会执行onTouchEvent() = 不会执行OnClick()

onTouch()返回true就认为该事件被onTouch()消费掉,因而不会再继续向下传递,即不会执行OnClick()。

onTouch()和onTouchEvent()的区别

这两个方法都是在View的dispatchTouchEvent中调用,但onTouch优先于onTouchEvent执行。

如果在onTouch方法中返回true将事件消费掉,onTouchEvent()将不会再执行。

解决ScrollView嵌套ListView手势冲突

原因:Scrollview拦截了触摸事件,需要重写ScrollView的dispatchTouchEvent在调用super.dispatchTouchEvent(ev)之前设置requestDisallowInterceptTouchEvent不允许拦截即可。

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }

@Override
    /**
     * 重写该方法,达到使ListView适应ScrollView的效果
     */
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

11.动画加载

image.png

image.png

12.Android 进程

一般大体分为前台进程,后台进程,可见进程,服务进程,空进程这五大进程。其中空进程优先级最低,调用startService()让service所在进程成为前台进程,service的onDestory()里重新启动自己可避免后台进程被杀死。

一个应用允许多个进程,在清单文件配置的service为一个进程,Android:process就可以配置;多进程会引起的异常:静态成员和单例模式会失效,线程同步机制完全失效,SharedPreferences可靠性下降,Application会多次创建。

binder机制:Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一。Android中的四大组件Activity,Service,Broadcast,ContentProvider,不同的App等都运行在不同的进程中,它是这些进程间通讯的桥梁。正如其名“粘合剂”一样,它把系统中各个组件粘合到了一起,是各个组件的桥梁。

Framework 工作原理:Android系统对Linux、kernel、lib库等封装,提供WMS、AMS、binder机制,handler-message机制等方式,供APP使用。Framework 就是提供APP生存环境。

6.0需要代码请求权限checkPermissions,7.0应用间文件共享限制,系统广播删除,8.0通知渠道、悬浮窗、透明窗口不允许屏幕旋转,9.0明文流量的网络请求(Https加密)

13.Activity的启动模式

①standard每一次都会创建新的实例;

②singleTop栈顶复用。和standard相似,但是如果栈顶已有实例,复用该实例,回调onNewIntent()方法;

③singleTask栈内复用。查找栈内有没有该实例,有则复用回调onNewIntent()方法,并且清空当前activity任务栈上面所有的activity;如果没有,新建Activity,并入栈;

④singleInstance单例模式,全局唯一。具备singleTask所有特性,独占一个任务栈。singleInstance不要用于中间页面,否则跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,再次启动,首先打开的是B。

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;

2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;

3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。

当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stoped 状态。

当** Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互,所以被覆盖的Activity并不会执行onStop()方法

显式意图和隐式意图的区别

显式意图 :必须指定要激活的组件的完整包名和类名 (应用程序之间耦合在一起)

一般激活自己应用的组件的时候,采用显示意图;

隐式意图: 只需要指定要动作和数据就可以 ( 好处应用程序之间没有耦合)

激活别人写的应用 隐式意图,不需要关心对方的包名和类名。

14.启动线程的方法

①继承thread 类②实现runnable 接口③实现callable 接口

实现Runnable接口优势:

1)适合多个相同的程序代码的线程去处理同一个资源

2)可以避免java中的单继承的限制

3)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

继承Thread类优势:

1)可以将线程类抽象出来,当需要使用抽象工厂模式设计时。

2)多线程同步

在函数体使用优势

1)无需继承thread或者实现Runnable,缩小作用域。

终止线程的三种方法

① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。

② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。

③ 使用interrupt方法中断线程。

线程同步的方法

①synchronized②同步代码块 ③使用特殊变量Volatile ④使用重入锁⑤使用局部变量⑥使用阻塞队列。

线程sleep()和wait()的区别

sleep()不释放同步锁,自动唤醒,需要try-catch,线程方法。

wait()释放同步锁,需要notify唤醒,是object方法。

15.线程安全的单例

public class SingleInstance{

        private static SingleInstance instance;

        public static SingleInstance getInstance(SingleInstance instance){

            if (instance == null) {
                synchronized (SingleInstance.class) {
                    if (instance == null) {
                        instance = new SingleInstance();
                    }
                }
            }
            return instance;
        }
    }

16.Service启动方式和生命周期

①startService():开启,调用者退出后Service仍在;

生命周期:onCreate()--onStartCommand()--onDestory()

通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。

②bindService():开启,调用者退出后Service随即退出。

生命周期:onCreate()--onBind()--onUnBind()--onDestory()

同时使用startService()启动服务与bindService()绑定服务:onCreate() -> onStartCommnad() -> onBind() -> onUnBind() -> onDestory

IntentService

Service不是独立进程,也不是独立线程,依赖于应用程序的主线程的,所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。

当我们编写的耗时逻辑,就需要引入IntentService,IntentService继承自Service,它包含了Service的全部特性,也包含生命周期,与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去你执行你的耗时操作。

protected abstract void onHandleIntent(Intent intent);

IntentService在执行onCreate的方法的时候,其实开了一个线程HandlerThread,并获得了当前线程队列管理的looper,并且在onStart的时候,把消息置入了消息队列,在消息被handler接受并且回调的时候,执行了onHandlerIntent方法,该方法的实现是子类去做的。

特点:

IntentService会创建独立的worker线程来处理所有的Intent请求;

会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程的问题;

所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;

为Service的onBind()提供默认实现,返回null;

为Service的onStartCommand提供默认实现,将请求Intent添加到队列中。

IntentService的使用

/**
 * IntentService的使用
 * Created by wuhuihui on 2019/6/10.
 */
public class NetworkReceiver extends BroadcastReceiver {

    private static final String TAG = NetworkReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (SystemUtils.isNetworkAvailable(context)) {
            // upload data by start intentservice
            Intent serviceIntent = new Intent(context, NetworkService.class);
            context.startService(serviceIntent);
        }
    }

    public static class NetworkService extends IntentService {
        public NetworkService() {
            super("NetworkService");
        }

        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            //执行耗时操作
        }
    }
}

结论:IntentService是通过Handler looper message的方式实现了一个多线程的操作,同时耗时操作也可以被这个线程管理和执行,同时不会产生ANR的情况。

17.网络请求

Http是服务层,借助HttpClient和HttpUrlConnection建立短连接,请求一次后断开,需要重连时才连。

①HttpClient:开源框架,无封装,原始,使用方便,开发快,实现比较稳定,Android废弃,Android 6.0删除;

②HttpUrlConnection:对网络请求没有HttpClient封装彻底,Android2.2之前存在bug,所以2.2之前用HttpClient,之后用容易优化的HttpUrlConnection,开源框架,封装了请求头、参数、内容体、响应在I/O流,接口中统一封成了HttpGet/HttpPost,减少了操作的繁琐性,访问速度快。

Https是以安全为目标的Http通道,简单讲就是Http的安全版,即Http下加入SSL层,安全基础是SSL,加密的详细内容是SSL,作用:建立一个信息安全通道,来保证数据传输的安全、确认网站的安全性。

Volley:适合处理数据量小,通信频繁的网络操作,内部封装了异步操作,可直接在线程执行并处理结果,同时可以取消,容易扩展,但是不适合大数据请求,比如下载表现糟糕,不支持https,android2.2及以下用HttpClient,android2.3及以上用HttpUrlConnection。

OkHttp:专注于提升网络连接效率的Http客户端,能够实现IP和端口的请求重用一个socket,大大降低了连接时间,也降低了服务器的压力,对Http和https都有良好的支持,不用担心app版本更换的困扰,但是okHttp请求是在线程里执行,不能直接刷新UI,需要手动处理。

总结:在项目实际运用中,视情况选择网络请求方式,也可以Volley+OkHttp搭配使用。异步回调用Volley,网络请求底层用OkHttp

RxJava + Retrofit3 + OkHttp3

①RxJava 主要用来实现线程切换,我们制定订阅在哪一个线程,观察在哪个线程,通过操作符进行数据变换,整个过程是键式的,简化逻辑。一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库),RxJava使异步操作变得非常简单。

②Retrofit 是网络请求的一个架子,用它设置一些参数和请求Url。

③OkHttp是网络请求的内核,实际的网络请求是它发出来的。工作流程:通过OkHttpClient将构建的Request转换为Call,然后在RealCall中进行异步或同步任务,最后通过一些的拦截器interceptor发出网络请求和得到返回的response。

18.Activity如何生成View?

Activity执行在attch()方法的时候,会创建一个PhoneWindow(Window的子类),在onCreate()方法的setContentView()方法中,创建DecorView,DecorView的addView()方法,把layout布局加载出来。通过onDraw()画出来,画View之前调用onMeasure()方法计算显示的大小。

View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

onMeasure()方法中主要负责测量,决定控件本身或其子控件所占的宽高;

onDraw()方法负责绘制,需要自己绘制我们的自定义控件的显示效果(Paint画笔对象,Canvas即画布);

onLayout()方法负责布局,大多数情况是在自定义ViewGroup中才会重写,主要用来确定子View在这个布局空间中的摆放位置。

19.为什么要使用多线程?

①更好地利用CPU资源;②进程间数据不能数据共享,线程可以;③系统创建进程需要为该进程重新分配系统资源,创建线程代价较小;④Java语言内置了多线程功能支持,简化了java多线程编程。

线程池是一种多线程处理形式,处理过程中将任务添加到队列,在创建线程后自动启动这些任务,线程池线程是后台线程,每个线程都使用默认的堆栈大小,以优先级执行。

20.内存溢出,内存泄漏

内存溢出(OOM):程序在申请内存时,没有足够的内存空间使用。

原因:加载对象过大,相对资源较多,来不及加载。

解决办法:内存引用上做处理,比如用软引用;图片加载时处理(压缩等);动态回收内存;优化内存分配,自定义堆内存大小,避免使用Enum,减少BitMap的内存占用,内存对象重复使用,避免对象的内存泄漏。

内存泄漏(memory leak): 程序在申请内存后,无法释放已申请的内存空间,一次泄漏危害可忽略,但推积严重最终会导致OOM;

内存泄漏原因

①非静态内部类造成的内存泄漏非静态类会持有外部类的引用,如果这个内部类比外部类的生命周期长,在外部类被销毁时,内部类无法回收,即造成内存泄漏;

②外部类中持有非静态内部类的静态对象 保持一致的生命周期,将内部类对象改成非静态;

③Handler 或 Runnable 作为非静态内部类 Handler 和 Runnable 作为匿名内部类,都会持有 Activity 的引用,由于 Handler 和 Runnable 的生命周期比 Activity 长,导致Activity 无法被回收,从而造成内存泄漏。 解决办法:将Handler 和 Runnable 定义为静态内部类,在Activity 的onDestory()方法中调用Handler 的 removeCallbacks 方法来移除 Message。

还有一种特殊情况,如果 Handler 或者 Runnable 中持有 Context 对象,那么即使使用静态内部类,还是会发生内存泄漏。解决办法:使用弱引用

④其他内存泄漏情况:比如BraodcastReceiver 未注销,InputStream 未关闭,再代码中多注意注销或关闭。

handler泄露:消息引用了handler对象,该对象又隐性地持有了Activity对象,当发生GC时以为message-handler-activity的引用链导致Activity无法被回收,即发生泄漏,简单来说就是handler对activity强引用导致的GC,无法及时回收Activity。(PS:GC垃圾回收,当堆内存里的对象没有引用指向时,GC回收。)

LeakCanary内存优化:LeakCanary.install() 会返回一个预定义的 RefWatcher,同时也会启用一个 ActivityRefWatcher,用于自动监控调用Activity.onDestroy() 之后泄露的 activity。

21.JNI和NDK
JNI是Java调用Native 语言的一种特性,属于Java,Java本地接口,使Java与本地其他类型语言交互(C++)。
实现步骤:在Java中声明Native方法,编译该文件得到.class文件,通过javah命令导出JNI头文件(.h文件),使用Java需要交互的本地代码实现子啊Java中声明的Native方法,编译so文件,通过Java执行Java程序,最终实现Java调用本地代码。
NDK(Native Develop Kit):Android开发工具包,属于Android。
作用:快速开发C、C++动态库,并自动将so文件和应用打包成APK,即可通过NDK在Android中使用JNI与本地代码(C、C++)交互(Android开发需要本地代码C、C++实现)
特点:运行效率高,代码安全性高,功能拓展性好,易于代码复用和移植。
使用步骤:①配置NDK环境;②创建Android项目,并于NDK进行关联;③在Android项目中声明所需调用的Native方法;④使用该Native方法;⑤通过NDK build命令编译产生so文件;⑥编译AS工程,实现调用本地代码。
JNI和NDK的关系:JNI实现目的,NDK是Android实现JNI的手段,即在AS开发环境中通过NDK从而实现JNI功能。

22.Glide缓存机制

内存存缓存的读存都在Engine类中完成。内存缓存使用弱引用和LruCache结合完成的,弱引用来缓存的是正在使用中的图片。图片封装类Resources内部有个计数器判断是该图片否正在使用。

Glide内存缓存的流程

读:是先从lruCache取,取不到再从弱引用中取;

存:内存缓存取不到,从网络拉取回来先放在弱引用里,渲染图片,图片对象Resources使用计数加一;

渲染完图片,图片对象Resources使用计数减一,如果计数为0,图片缓存从弱引用中删除,放入lruCache缓存。

1、Glide实现了内存缓存和磁盘缓存,且都设置了相应的大小,并根据lrus算法进行更新和删除。

2、Glide将加载了的图片添加到Map(Key, ResourceWeakReference)中实现内存缓存。

3、Glide的加载顺序是先从内存中获取,若没有则从磁盘中获取,在没有则通过网络获取。从网络上获取到了图片之后若设置了可缓存,则会缓存到磁盘中;然后在通过相应的加载参数进行解码压缩裁剪等操作之后得到图片设置给ImageView,同时缓存到内存中。

4、Glide通过为当前Activity添加一个fragment来监听相应的生命周期方法,从而实现加载与Activity生命周期绑定,onstart时进行加载、onstop时停止加载等。

5、Glide在开始加载图片时,会给当前ViewTarget设置对应的Request,同时也会ViewTarget中的View设置tag为Request。在listView复用时有了这个Tag值,从而解决错乱的问题

开源框架:ImageLoader、Glide(google)、Fresco(FaceBook)、Picasso(Square)

Picasso包体积小、清晰,但功能有局限不能加载gif、只能缓存全尺寸;

Glide功能全面,擅长大型图片流,提交较大;

Fresco内存优化,减少oom,体积更大

23.EventBus
EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发。
Gihub地址 :https://github.com/greenrobot/EventBus。
**EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment、Activity、Service线程之间传递数据,执行方法。 特点:代码简洁,是一种发布订阅设计模式(观察者设计模式)。 **

三大角色: Event:事件,可以是任意类型,EventBus会根据事件类型进行全局的通知。

Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING。

Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。

四个线程模型 ①POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。 ②MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。 ③BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。 ④ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。 参考链接:https://blog.csdn.net/ezconn/article/details/84034642

24. OKHttp LoggingInterceptor的使用

//      设置HttpLoggingInterceptor
        HttpLoggingInterceptor interceptor =
                new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                    @Override
                    public void log(String message) {
                        try {
                            Log.i(TAG, message);
                        } catch (Exception e) {
                            e.printStackTrace();
                            Log.e(TAG, message);
                        }
                    }
                });

        //包含header、body数据
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//        setlevel用来设置日志打印的级别,共包括了四个级别:NONE,BASIC,HEADER,BODY
//        BASEIC:请求/响应行
//        HEADER:请求/响应行 + 头
//        BODY:请求/响应行 + 头 + 体

//        初始化OkHttpClient
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                //FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
                .addNetworkInterceptor(new StethoInterceptor())
                .addInterceptor(interceptor)
                //持久化Cookie,OKHttp管理Cookie
                //用户登录时保存的cookie,在下次需要用到该用户的cookie放在请求头里
                .cookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(MyApp.getInstance())))
                .build();

25.XMPP

XMPP中定义了三个角色,客户端,服务器,网关。通信能够在这三者的任意两个之间双向发生。服务器同时承担了客户端信息记录,连接管理和信息的路由功能。网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在之上传输XML。(from 百度)

XMPP实现即时通讯通过Openfire + Spark搭建服务器。原理:userA和userB通过聊天服务器作为中介发送和接收消息,消息的以Xml格式传输

26. GreenDAO

GreenDAO是一个对象关系映射(ORM Object RelationShop Mapping)的框架,能够提供一个接口通过操作对象的方式去操作关系型数据库,它能够让你操作数据库时更简单、更方便。

①性能高,号称Android最快的关系型数据库,存取速度快

②内存占用小

③库文件比较小,小于100K,编译时间低,而且可以避免65K方法限制

④支持数据库加密 greendao支持SQLCipher进行数据库加密

⑤激活实体,支持缓存,代码自动生成。

27. JS交互

打开车必应//
image.png

28.Integer的自动装箱

//在-128~127 之外的数 Integer i1 =200; Integer i2 =200; System.out.println("i1==i2: "+(i1==i2)); // 在-128~127 之内的数 Integer i3 =100; Integer i4 =100; System.out.println("i3==i4: "+(i3==i4));

输出的结果是:

i1==i2: false i3==i4: true

29.RecyclerView简单/复杂item加载

1.简单加载:ListAdapter extents BaseQuickAdapter

构造方法、convert()

2.复杂加载:ListAdapter2 extents RecyclerView.Adapter

onCreateViewHolder()、onBindViewHolder()、getItemCount()、getItemViewType()

public class ListAdapter2 extends RecyclerView.Adapter {

    public static enum ITEM_TYPE {
        ITEM_TYPE_ONE,
        ITEM_TYPE_TWO
    }

    private Context context;
    private LayoutInflater layoutInflater;
    private List eventMessages = new ArrayList<>();

    public ListAdapter2(Context context) {
        this.context = context;
        layoutInflater = LayoutInflater.from(context);
    }

    public void addList(List list) {
        eventMessages.addAll(list);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if (viewType == ITEM_TYPE.ITEM_TYPE_ONE.ordinal()) {
            return new OneHolder(layoutInflater.inflate(R.layout.item_layout, parent, false));
        } else {
            return new TwoHolder(layoutInflater.inflate(R.layout.item_layout2, parent, false));
        }

    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        EventMessage eventMessage = eventMessages.get(position);
        if (holder instanceof OneHolder) {
            ((OneHolder) holder).msg.setText(eventMessage.getMessage());
        } else if (holder instanceof TwoHolder) {
            ((TwoHolder) holder).type.setText(eventMessage.getType() + "");
            ((TwoHolder) holder).msg.setText(eventMessage.getMessage());
        }
    }

    @Override
    public int getItemCount() {
        return eventMessages == null ? 0 : eventMessages.size();
    }

    @Override
    public int getItemViewType(int position) {
        return position % 2 == 0 ? ITEM_TYPE.ITEM_TYPE_ONE.ordinal() : ITEM_TYPE.ITEM_TYPE_TWO.ordinal();
    }

    public static class OneHolder extends RecyclerView.ViewHolder {
        public TextView msg;

        OneHolder(View view) {
            super(view);

            msg = (TextView) view.findViewById(R.id.msg);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d("OneHolder", "onClick--> position = " + getPosition());
                }
            });
        }
    }

    public static class TwoHolder extends RecyclerView.ViewHolder {
        public TextView type;
        public TextView msg;

        TwoHolder(View view) {
            super(view);

            type = (TextView) view.findViewById(R.id.type);
            msg = (TextView) view.findViewById(R.id.msg);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d("TwoHolder", "onClick--> position = " + getPosition());
                }
            });
        }
    }
}

30.匿名内部类

在实现时必须借助一个类或一个接口,若从这个层次上讲它是可以继承其他类也可以实现接口的,但若是通过extends 或implements 关键字那是不可能的.

31.数据库操作的两个方法

getWritableDatabase(); 
getReadableDatabase();

32.java获取泛型的真实类型

ParameterizedType parameterizedType =
        (ParameterizedType) this.getClass().getGenericSuperclass();//获取当前new对象的泛型的父类类型
Class clazz = (Class) parameterizedType.getActualTypeArguments()[0];
System.out.println("clazz ==>> "+clazz);

33.res/raw和assets的相同点:

两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。

res/raw和assets的不同点:

res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;

assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。

res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

读取文件资源;

读取res/raw下的文件资源:

InputStream is = context.getResources().openRawResource(R.id.filename);

读取assets下的文件资源:

/****** assets中读取图片/private Bitmap getImageFromAssetsFile(Context context, String fileName) { Bitmap image = null; AssetManager am = context.getResources().getAssets(); try { InputStream is = am.open(fileName); image = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return image; }

每天进步一点点。。。(2019-05-30)

你可能感兴趣的:(Android 主要知识架构整理(面试终极总结190530))