先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
onPause,onstop, onDestroy 三种状态 下Activity都有可能被系统干掉。
-
为了保证程序的正确性,你 要在onPause()里写上持久层操作的代码,将用户编辑的内容都保存到存储介质上(一般是数据库 )。
-
实际工作中因为生命周期的变化而带来的问题也很多,比如你的应用程序起了新的线程在跑,这时候中断了,你还要去维护那个线程,是暂停还是杀掉还是数据回滚,是吧?因为Activity可能被杀掉,所以线程中使用的变量和一些界面元素就千万要注意了,一般我都是采用Android的消息机制 [Handler,Message]来处理多线程和界面交互的问题。
三、如何让Activity变成一个窗口
- 在AndroidManifest.xml 中设置Activity的主题
android :theme=“@android:style/Theme.Dialog”
android:theme=“@android:style/Theme.Dialog”
android:theme=“@android:style/Theme.Translucent”
android:theme=“@android:style/Theme.Translucent”
类似这种Activity的属性可以在android.R.styleable 类的AndroidManifestActivity 方法
中看到,AndroidManifest.xml中所有元素的属性的介绍都可以参考这个类 android.R.styleable.
四、Service使用场景和生命周期
1. 概念
Service不能与用户交互,不能自己启动,运行在后台。
如果我们退出应用时,Service进程并没有结束,它仍然在后台运行。
2. 使用场景
-
当我们想音乐在后台播放,就得Service,否则就听不到歌。
-
当应用的数据是通过网络获取的,不同时间段的数据是不同的
我们可以用Service在后台定时更新,而不用每打开应用的时候在去获取。
3. Service生命周期
Service的生命周期比Activity简单,它只继承了onCreate(),onStart(),onDestroy()三个方法。
-
第一次启动Service时,先后调用了onCreate() , onStart()
-
停止Service时,则执行onDestroy()
-
如果Service已经启动,当我们再次启动Service时,不会在执行onCreate(),而是直接执行onStart()
五、JNI 的书写步骤
-
声明 native 方法
-
编译该java类
-
使用 javah ? jni java 类名 生成扩展名为.h的头文件
-
使用 C/C++实现本地方法
-
将 C/C++编写的文件生成动态连接库
这里以HelloWorld为例:
class HelloWorld {
// 声明native方法,并且不能实现
public native void displayHelloWorld();
/* 加载动态库, 参数“hello”是动态库的名字
-
可以这样理解:我们的方法displayHelloWorld()没有实现,
-
但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化
-
这里一般是以static块进行加载
*/
static {
System.loadLibrary(“hello”);
}
public static void main(String[] args) {
new HelloWorld().displayHelloWorld();
}
}
六、JNI 调用中考虑的问题
1. java和c是如何互通的?
-
不能互通的原因主要是 数据类型 的问题。
-
但jni解决了这个问题,例如c文件中的
jstring
数据类型就是 java传入的String对象 ,经过jni函数的转化就能成为c的char*
。
对应数据类型关系如下表:
| Java 类型 | 本地c类型 | 说明 |
| — | — | — |
| boolean | jboolean | 无符号,8 位 |
| byte | jbyte | 无符号,8 位 |
| char | jchar | 无符号,16 位 |
| short | jshort | 有符号,16 位 |
| int | jint | 有符号,32 位 |
| long | jlong | 有符号,64 位 |
| float | jfloat | 32 位 |
| double | jdouble | 64 位 |
| void | void | N/A |
2. 如何将java传入的String参数转换为c的char*,然后使用?
-
java 传入的String参数,在c文件中被jni转换为jstring的数据类型
-
在c文件中声明
char* test
,然后
test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
- 注意:test使用完后,通知虚拟机平台相关代码无需再访问:
(*env)->ReleaseStringUTFChars(env, jstring, test);
3. 将c中获取的一个char*的buffer传递给java?
-
这个char*如果是一般的字符串的话,作为
string
传回去就可以了。 -
如果是含有’/0’的buffer,最好作为
bytearray
传出,
因为可以制定copy的length,如果copy到string,可能到’/0’就截断了。
有两种方式传递得到的数据:
-
在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
-
return 错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:
class RetObj
{
public byte[] bytearray;
}
这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下:
jclass cls;
jfieldID fid;
jbyteArray bytearray;
bytearray = (*env)->NewByteArray(env,len);
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);
cls = (*env)->GetObjectClass(env, retobj);
fid = (*env)->GetFieldID(env, cls, “retbytes”, “[B”]);
(*env)->SetObjectField(env, retobj, fid, bytearray);
4. 不知道占用多少空间的buffer,如何传递出去呢?
-
在 jni 的c文件中new出空间,传递出去。
-
java的数据不初始化,指向传递出去的空间即可。
七、Android中的动画有哪几类,它们的特点和区别是什么?
- Tween动画:使视图组件移动、放大、缩小以及产生透明度的变化
给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。
-
Frame动画:传统的动画方法,通过顺序的播放排列好的图片来实现。
-
属性动画:真正的改变属性。
八、Handler机制的原理
Andriod 提供了 Handler 和 Looper 来满足线程间的通信。
-
Looper : 一个线程可以产生一个Looper对象,管理此线程里的MessageQueue(消息队列)。
-
Handler : 构造Handler对象来与Looper沟通,
如push新消息到MessageQueue里,或者接收Looper从MessageQueue取出的消息。
-
MessageQueue : 消息队列,用来存放线程放入的消息。
-
线程 : UI线程通常就是主线程,程序启动时会替它建立一个MessageQueue。
九、mvc模式的原理以及在Android中的运用
松耦合,组件的重用。
-
view:视图层,一般采用xml文件进行界面的描述,也可以使用javascript+html等的方式作为view层。
-
controller:一般指Acitvity,所以不要在acitivity中数据操作等耗时操作的代码,要通过activity交割model业务逻辑层处理。
-
model:对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。
十、介绍下Android的数据存储方式
-
使用SharedPreferences存储数据;
-
文件存储数据;
-
SQLite数据库存储数据;
-
使用ContentProvider存储数据;
-
网络存储数据;
Android 中的数据存储都是私有的,其他应用程序都是无法访问的,除非通过ContentResolver获取其他程序共享的数据。
十一、介绍下ContentProvider是如何实现数据共享的
ContentProvider:为存储和获取数据提供统一的接口,可以在不同的应用程序之间共享数据。
无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格。
-
定义一个继承ContentProvider的类
-
实现ContentProvider的所有方法 (query、insert、update、delete、getType、onCreate)
-
在AndroidMinifest.xml中进行声明
十二、如何启用Service,如何停用Service
- 通过调用Context.startService()启动,调用Context.stopService()结束,
startService()可以传递参数给Service.
- 第二种方式是通过调用Context.bindService()启动,调用Context.unbindservice()结束
这种方式可以通过ServiceConnection访问Service。
-
在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),
-
其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
十三、注册广播接收器有几种方式,这些方式有何优缺点?
Android中,不同 进程之间 传递信息要用到广播。
方式1. 在Manifest.xml中注册广播
是一种比较推荐的方法,不需要手动注销广播。
(如果广播未注销,程序退出时可能会出错),范例代码:
<receiver android:name = “.MyBroadcastReceiver”
上面两个android:name分别是 广播名 和 广播动作名 。
如果要自己发送一个广播,在代码中为:
Intent i = new Intent(“com.zhuanghongji.broadcastreceiver.test”);
sendBroadcast(i);
广播就发出去了,然后是接收。
新建一个类,继承至BroadcastReceiver,也可以建一个BroadcastReceiver的实例,然后得写onReceive()方法,实现如下:
protected BroadcastReceiver mEvtReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(“com.zhuanghongji.broadcastreceiver.test”)) {
//Do something
}
}
};
方式2. 接在代码中实现,但需要手动注册注销
通过IntentFilter设置action,使用 registerReceiver() 方法进行注册。
IntentFilter filter = new IntentFilter();
filter.addAction(“com.zhuanghongji.broadcastreceiver.test”);
registerReceiver(mEvtReceiver, filter);
// 这时注册了一个recevier ,名为mEvtReceiver,
// 然后同样用上面的方法以重写onReceiver,
// 最后在程序的onDestroy中要注销广播,实现如下:
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mPlayerEvtReceiver);
}
十四、请谈谈Android引入广播机制的用意
1. 解答
- Android系统中的广播是广泛用于 应用程序之间通信 的一种手段。
它类似于事件处理机制,不同的地方就是广播的处理是系统级别的事件处理过程(一般事件处理是控件级别的)。在此过程中仍然是离不开Intent对象,理解广播事件的处理过程,灵活运用广播处理机制,在关键之处往往能实现特别的效果。
-
在Android 中如果要发送一个广播必须使用
sendBroadCast(Intent intent)
向系统发送对其感兴趣的广播接收器中。 -
使用广播必须要有一个intent 对象必设置其action动作对象使用广播必须在配置文件中显式的指明该广播对象。
-
每次接收广播 都会重新生成一个接收广播的对象,在 BroadCast中 尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理.
补充:
2. BroadcastReceiver的生命周期:
大意为:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。
3. 如何在一个广播接收器中处理多个动作呢?
-
同样的你必须在
Intent-filter
里面注册该动作,可以是系统的广播动作也可以是自己需要的广播。 -
之后你之需要在onReceive 方法中,通过
intent.getAction()
判断传进来的动作即可做出不同的处理,不同的动作。
小结:
-
在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
-
使用广播必须要有一个intent 对象必设置其action动作对象
-
使用广播必须在配置文件中显式的指明该广播对象
-
每次接收广播都会重新生成一个接收广播的对象
-
在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理
十五、单线程模型中Message,Handler,MessageQueue,Looper之间的关系
-
Message:Handler接收和处理的消息对象。
-
Handler:发送和处理消息。
-
MessageQueue:消息队列,以”先进先出”的方式管理Message。
-
Looper:读取MessageQueue的消息,然后把消息交给”发送该消息的Handler”进行处理。
详细内容可以看我的另一篇总结:Android 总结:Message,MessageQueue,Looper,Handler 消息机制
十六、AIDL 如何工作?能处理哪些类型的数据?
1. 概述
AIDL:Android Interface Definition Language(安卓接口描述语言)
-
编译器可以通过 aidl文件 生成一段代码,通过预先定义的接口达到 两个进程内部通信,进程间跨界对象访问的目的。
-
AIDL的IPC机制是基于接口的,是轻量级的。使用代理类在客户端和实现层间传递值.。
2. 使用AIDL, 需要完成2件事情
-
引入AIDL的相关类
-
调用aidl产生的class
理论上, 参数可以传递基本数据类型和String, 还有就是Bundle的派生类。
3. 具体实现步骤
- 创建AIDL文件,
在这个文件里面定义接口, 该接口定义了可供客户端访问的方法和属性。
- 编译AIDL文件
用Ant的话, 可能需要手动, 使用Eclipse plugin的话,可以根据adil文件自动生产java文件并编译, 不需要人为介入。
- 在Java文件中, 实现AIDL中定义的接口
编译器会根据AIDL接口, 产生一个JAVA接口。这个接口有一个名为Stub的内部抽象类
,它继承扩展了接口并实现了远程调用需要的几个方法。
- 向客户端提供接口 ITaskBinder
如果写的是service,扩展该Service并重载 onBind ()
方法来返回一个实现上述接口的类的实例。
- 在服务器端回调客户端的函数
前提是当客户端获取的IBinder接口的时候,要去注册回调函数, 只有这样, 服务器端才知道该调用那些函数。
4. AIDL语法和支持的数据类型
AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。
由于远程调用的需要, 这些参数和返回值并不是任何类型。
下面是些AIDL支持的数据类型:
-
不需要import声明的简单Java编程语言类型(int,boolean等)
-
String, CharSequence 不需要特殊声明
-
List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.
5. 实现接口时有几个原则
-
抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的.
-
IPC调用是同步的。
如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。
也就是说IPC调用会挂起应用程序导致界面失去响应。
这种情况应该考虑单起一个线程来处理.
- 不能在AIDL接口中声明静态属性。
6. IPC的调用步骤
-
声明一个接口类型的变量,该接口类型在.aidl文件中定义。
-
实现ServiceConnection。
-
调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
-
在ServiceConnection.onServiceConnected()实现中,
你会接收一个IBinder实例(被调用的Service).
调用YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
-
调用接口中定义的方法。 你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
-
断开连接,调用接口实例中的ApplicationContext.unbindService()
十七、什么是ANR?如何避免它?
1. 什么是ANR?
- ANR:Application Not Responding
-
在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。
-
Android应用程序完全运行在一个独立的线程中当出现下列情况时,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。
-
因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intent broadcast)。
- 当出现下列情况时,Android就会显示ANR对话框了:
-
对输入事件(如按键、触摸屏事件)的响应超过5秒
-
意向接受器(intentReceiver)超过10秒钟仍未执行完毕
2. 如何避免它?
-
潜在的比较耗时的操作,如访问网络和数据库;
-
或者是开销很大的计算,比如改变位图的大小;
-
需要在一个单独的子线程中完成 (或者是使用异步请求,如数据库操作)
主线程为子线程提供了 Handler,让子线程在即将结束的时候调用它与主线程通信。
十八、谈谈你对Intent的理解
一个intent是一个Intent对象,它保存了消息的内容。
最后
最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
一起互勉~
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
如何避免它?
-
潜在的比较耗时的操作,如访问网络和数据库;
-
或者是开销很大的计算,比如改变位图的大小;
-
需要在一个单独的子线程中完成 (或者是使用异步请求,如数据库操作)
主线程为子线程提供了 Handler,让子线程在即将结束的时候调用它与主线程通信。
十八、谈谈你对Intent的理解
一个intent是一个Intent对象,它保存了消息的内容。
最后
最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-lZPPo7xR-1713226909577)]
[外链图片转存中…(img-tYN6567p-1713226909577)]
一起互勉~
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-LfUtyJ6M-1713226909578)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!