总结
我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题
等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~
Android 基础知识点
Java 基础知识点
Android 源码相关分析
常见的一些原理性问题
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
接下来我们进行服务的绑定:
private void bindKtvService() {
Intent intent = new Intent();
intent.setClassName("com.thunder.ktvaidlservice", "com.thunder.ktvaidlservice.KtvService");
getApplicationContext().bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("zxm", "onServiceConnected: ");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("zxm", "onServiceDisconnected: ");
}
}, Context.BIND_AUTO_CREATE);
}
然后我们获得 stub 的实例,记住此处千万不能 new 实例需要用
.Stub.asInterface(service);
方法。 随后我们在界面上添加两个按钮作为控制,client 完整代码如下:
public class MainActivity extends AppCompatActivity {
private IKtvController ktvController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
ktvController.setPause("sorry ~ pause");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
ktvController.setPause("hi ~ play");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bindKtvService();
}
private void bindKtvService() {
Intent intent = new Intent();
intent.setClassName("com.thunder.ktvaidlservice", "com.thunder.ktvaidlservice.KtvService");
getApplicationContext().bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("zxm", "onServiceConnected: ");
try {
ktvController = IKtvController.Stub.asInterface(service);
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("zxm", "onServiceDisconnected: ");
}
}, Context.BIND_AUTO_CREATE);
}
}
此时两端代码编写完毕,先后把服务和客户端的代码重新 run 一下,可以看到 pid 3919/3842 的两个进程
启动日志:
绑定服务的时候服务端调用了 onCreate 和 onBind 方法。客户端则成功的获取到了 ktv 的控制器的 bind 实例。我们用控制器实例才试着处理下夸进程的数据传输一个为暂停、一个为播放:
历经了一些小的挫折之后,成功的把客户端的数据通过 aidl 的跨进程传输的方式输出到了服务端,见上方日志。
双向通信
上方我们完成了两个独立进程的跨进程通信,但是不知道大家有没有发现只有一个客户端向服务端的单向通信。但是实际在跨进程通信中双向通信的使用场景特别多。下面我们就接着刚刚的例子,当在客户端点击暂停和播放以后服务端给客户端回复一个成功或者失败的状态。
1 修改 AIDL 文件,我们新增了一个状态的 aidl 文件我看看 AIDL 这块的变化
interface IConrtollerStatusListener {
void onPauseSuccess();
void onPauseFailed(int errorCode);
void onPlaySuccess();
void onPlayFailed(int errorCode);
}
import com.thunder.ktvaidlservice.IConrtollerStatusListener;
interface IKtvController {
void setOnControllerStatusListener(in IConrtollerStatusListener i);
void setPause(String pause);
void setPlay(String play);
}
分别重新添加到服务端以及客户端的工程中,然后 make 各自的项目。
2 客户端的工作很简单,把监听设置上即可代码如下,注意回来后操作UI需要自己切线程:
private void bindKtvService() {
Intent intent = new Intent();
intent.setClassName("com.thunder.ktvaidlservice", "com.thunder.ktvaidlservice.KtvService");
getApplicationContext().bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("zxm", "onServiceConnected: ");
try {
ktvController = IKtvController.Stub.asInterface(service);
ktvController.setOnControllerStatusListener(new IConrtollerStatusListener.Stub() {
@Override
public void onPauseSuccess() {
new Handler(Looper.getMainLooper()).post(() -> {
Toast.makeText(MainActivity.this, "onPauseSuccess", Toast.LENGTH_SHORT).show();
});
}
@Override
public void onPauseFailed(int errorCode) {
new Handler(Looper.getMainLooper()).post(() -> {
Toast.makeText(MainActivity.this, "onPauseFailed" + errorCode, Toast.LENGTH_SHORT).show();
});
}
@Override
public void onPlaySuccess() {
Toast.makeText(MainActivity.this, "onPlaySuccess", Toast.LENGTH_SHORT).show();
}
@Override
public void onPlayFailed(int errorCode) throws RemoteException {
Toast.makeText(MainActivity.this, "onPlayFailed" + errorCode, Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("zxm", "onServiceDisconnected: ");
}
}, Context.BIND_AUTO_CREATE);
}
3 服务端代码,我们来回调暂停和播放。各自模拟一个 1 秒的耗时操作,代码如下:
private static class KtvBinder extends IKtvController.Stub {
private IConrtollerStatusListener listener;
@Override
public void setOnControllerStatusListener(IConrtollerStatusListener i) throws RemoteException {
listener = i;
}
@Override
public void setPause(String pause) throws RemoteException {
Log.e("zxm", pause);
//模拟暂停耗时 1000 毫秒
if (listener != null) {
new Thread(() -> {
try {
Thread.sleep(1000);
if (System.currentTimeMillis() % 2 == 0) {
listener.onPauseSuccess();
} else {
listener.onPauseFailed(1002);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RemoteException remoteException) {
remoteException.printStackTrace();
}
}).start();
}
}
@Override
public void setPlay(String play) throws RemoteException {
Log.e("zxm", play);
//模拟播放耗时 1000 毫秒
if (listener != null) {
new Thread(() -> {
try {
Thread.sleep(1000);
if (System.currentTimeMillis() % 2 == 0) {
listener.onPlaySuccess();
} else {
listener.onPlayFailed(1001);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RemoteException remoteException) {
remoteException.printStackTrace();
}
}).start();
}
}
}
这样既完成了一个两个进程之间的双向通信,我们来看下 UI 效果:
失败和成功都是从另外一个进程传回来,包括一次错误码的数据输出。
in out inout
注意到我们上面修改的 AIDL 文件代码有的 in ,这和我们平时看到的 Java 代码不一样。
不妨我们把 AIDL 的这个 in 修改为 out 试试?看看是否还能进行服务端返回给客户端的通信。
out 直接报错
inout 从字面理解是支持双流向,但是编译也报错
我们来看下这篇文章的解释
(https://maimai.cn/article/detail?fid=1609989144&efid=hY89uIb6wFrJ4mChypxecQ)
注意事项
1 点击设置错误
2021-10-16 14:33:07.061 3919-3919/com.thunder.ktvaidlclient E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.thunder.ktvaidlclient, PID: 3919
java.lang.IllegalStateException: Could not execute method for android:onClick
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
at android.view.View.performClick(View.java:6256)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View$PerformClick.run(View.java:24697)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
at android.view.View.performClick(View.java:6256)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View$PerformClick.run(View.java:24697)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.SecurityException: Binder invocation to an incorrect interface
at android.os.Parcel.readException(Parcel.java:1942)
at android.os.Parcel.readException(Parcel.java:1888)
at com.thunder.ktvaidlclient.IKtvController$Stub$Proxy.setPause(IKtvController.java:110)
2 切记客户端要与服务端的 aidl 的包名一致否则报如下错误
Process: com.thunder.ktvaidlclient, PID: 4087
java.lang.SecurityException: Binder invocation to an incorrect interface
at android.os.Parcel.readException(Parcel.java:1942)
at android.os.Parcel.readException(Parcel.java:1888)
at com.thunder.ktvaidlclient.IKtvController$Stub$Proxy.setPause(IKtvController.java:110)
at com.thunder.ktvaidlclient.MainActivity$2.onClick(MainActivity.java:37)
at android.view.View.performClick(View.java:6256)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View$PerformClick.run(View.java:24697)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
3 在 aidl 方法中如果想要操作 UI 需要自己处理线程切换。否则报错如下:
java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
at android.widget.Toast$TN.<init>(Toast.java:390)
at android.widget.Toast.<init>(Toast.java:114)
at android.widget.Toast.makeText(Toast.java:277)
at android.widget.Toast.makeText(Toast.java:267)
at com.thunder.ktvaidlclient.MainActivity$3$1.onPauseFailed(MainActivity.java:67)
at com.thunder.ktvaidlservice.IConrtollerStatusListener$Stub.onTransact(IConrtollerStatusListener.java:77)
at android.os.Binder.execTransact(Binder.java:674)
Other
服务其实还有很多种,我们上文介绍到的是跨进程的 bind 服务,其实还有普通服务、以及前台服务,他们也都有各种的用途和场景。详见 Google 文档服务概览
参考资料
developer.android.google.cn/guide/topic…
www.jianshu.com/p/d1fac6cce…
github.com/FelixLee052…
www.bilibili.com/video/BV1oD…
www.bilibili.com/video/BV185…
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
最后送福利了,现在关注我可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
entBus 3.0源码解析
9.zxing源码分析
最后送福利了,现在关注我可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
[外链图片转存中…(img-KoAAjtEs-1715578758780)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!