索引:
- Binder机制,共享内存实现原理
- ActivityThread工作原理
- 嵌套滑动实现原理
- View的绘制原理,自定义View,自定义ViewGroup
- View、SurfaceView 与 TextureView
- 主线程Looper.loop为什么不会造成死循环
- ViewPager的原理
- BroadcastReceiver使用总结
- AndroidP新特性
- Asset目录与res目录的区别
11. Binder机制,共享内存实现原理
Binder是跨进程通信(IPC)的一种解决方案。Binder中文即粘合剂,意思是粘合两个不同的进程。从定义来讲Binder是一种Android中实现跨进程的方式;也是一种虚拟的物理设备驱动,连接Service进程、Client进程和ServiceManager进程;而对于Android代码来说,Binder是一个类,实现了IBinder接口,将Binder机制模型以代码的形式具体实现的Android中。
一个进程空间分为用户空间和内和空间,进程间用户空间数据不可共享而内核空间是可以共享的,因为所有进程共用一个内核空间。用户空间可以和内核空间通过系统调用交互,从而实现内存共享。
copy_from_user()
:将用户空间的数据拷贝到内核空间
copy_to_user()
:将内核空间的数据拷贝到用户空间
12. ActivityThread工作原理
ActivityThread是Android应用程序的主线程(UI线程)。理解ActivityThread类似理解Android线程管理的关键。
从源码我们可以看到ActivityThred在main函数中创建了Looper,这也是为什么我们再主线程使用Handler不需要自己构建Looper的原因。然后main()通过thread.attach(false)
绑定应用进程。
主线程的消息机制还是又Handler去执行的。Looper的loop()方法则是起点和和兴。首先通过myLooper()方法获取Looper对象,取出Looper持有的MessageQueue。然后从MessageQueue取出Message,如果为null,说明线程正在推出。Message不为空,则调用Message的target handler对该Meeage分发,处理完毕后调用recycle()方法进行回收。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
13. 嵌套滑动实现原理
嵌套滑动的实现与传统的事件分发不同,嵌套滑动式从子View传递给父View,从下到上的一个顺序。实现嵌套滑动,需要我们外层父布局实现NestedScrollingParent,内层子View实现NestedScrollingChild。
NestedScrollingParent接口需要涉及两个方法。首先是onStartNestedScroll(View child, View target, int nestedScrollAxes):
nestedChild想要进行嵌套滚动时,会调用nestedParent的这个方法。这个芳法用于指示是否支持嵌套滚动。第二个是onNestedPreScroll(View target, int dx, int dy, int[] consumed):
当我们滚动nestedChild时,nestedChild进行实际的滚动前,会先调用nestParent的这个方法。nestedParent在这个方法中可以把子View想要滚动的距离消耗掉一部分或是全部消耗。
关于原理,事实上,是nestedChild的onTouchEvent()方法中会对发生的Touch事件进行判断,若为DOWN事件则会调用startNestedScroll()方法;若为MOVE事件则会调用dispatchNestedPreScroll()方法。
参考文章:十分钟Android中的嵌套滚动机制
14. View的绘制原理,自定义View,自定义ViewGroup
View的绘制主要分为View的绘制和ViewGroup的绘制。对于单一View的绘制,在draw方法中,依次绘制背景、内容、装饰。而我们经常重写的onDraw方法其实就是绘制内容。而ViewGroup的绘制会扫尾复杂一些,首先还是绘制自身,依次是背景、内容、子View、装饰。绘制子View的时候ViewGroup会遍历子View,然后挨个绘制。整个绘制自上而下,树形结构循环。
15. View、SurfaceView 与 TextureView
SurfaceView与TextrueView是View的子类,特点是能够在独立线程中绘制和渲染,在专用的GPU线程中大大提高渲染的性能。
SurfaceView:可以通过SurfaceHolder.addCallBack在子线程中更新UI,由于SurfaceHolder的双缓冲功能,可以是画面更加流畅的运行,但是由于holder的存在导致画面更新存在间隔,并且同样因为holder导致SurfaceView不能进行像View一样setAlpha和setRotation。比较适用于类似坦克大战等需要不断告诉画布更新的游戏。
TextrueView:可以通过TextureView.setSurfaceTextureListener在子线程更新UI。适用于音视频播放器或相机应用的开发。
16. 主线程Looper.loop为什么不会造成死循环
首先,结论是主线程确实阻塞了,但是主线程在初始化过程中由ActivityThread的main()方法中会创建一套消息循环组件包括Looper,MessageQueue,Handler,然后由MessageQueue中的next()调用底层MessageQueue,通过epoll进行阻塞,有主线程消息的时候通过发送消息激活主线程。主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗,也就不会造成ANR。
17. ViewPager的原理
ViewPager实现视图左右滑动,原理在于创建了三个视图,屏幕中间展示的是中间的视图,而屏幕两侧隐藏着的则是预加载的视图,当左右滑动时,将预加载的视图显示出来,并且缓存当前视图。
18. BroadcastReceiver使用总结
首先自定义MyBroadcastReceiver继承BroadcastReceiver,作为接收者。并且注册需要接收的Intent意图,即广播。
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "MyBroadcastReceiver";
public static int m = 1;
@Override
public void onReceive(Context context, Intent intent) {
Log.w(TAG, "intent:" + intent);
String name = intent.getStringExtra("name");
Log.w(TAG, "name:" + name + " m=" + m);
m++;
Bundle bundle = intent.getExtras();
}
}
其次,广播分为静态注册和动态注册。当接受有序广播时,在权限值相同时,动态注册的接收者优先接收广播。
//静态注册
<receiver android:name=".MyBroadcastReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
//动态注册
mBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(mBroadcastReceiver, intentFilter);
最后通过
sendBoradcast(Inteng i)
方法发送广播的就可以了,如果要发送有序广播,则调用sendOrderedBroadcast(intent, receiverPermission, ...)
19. AndroidP新特性
1 室内WIFI定位 。该功能API在android.net.wifi.rtt下
2 刘海平的支持。能够通过windowInsets.getDisplayCutout()
获取一些不应该绘制的部分屏幕。
3 增加了多许多通知的支持,优化了通知渠道。
4 新的图片解码类ImageDecoder
5 Android P引入了一个新的AnimatedImageDrawable类来绘制和显示GIF和WebP动画图像。
20. Asset目录与res目录的区别
** 注意: **
1 assets目录下的资源文件不会在R.java自动生成ID,所以读取assets目录下的文件必须指定文件的路径。可以通过AssetManager类来访问这些文件。比如要读取assets目录下的background.png:
Bitmap bgImg = getImageFromAssetFile( "background.png" );
/**
* 从assets中读取图片
*/
private Bitmap getImageFromAssetsFile(String fileName)
{
Bitmap image = null;
AssetManager am = getResources().getAssets();
try
{
InputStream is = am.open(fileName);
image = BitmapFactory.decodeStream(is);
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
return image;
}
2 如果在res/drawable目录下建了一个名为ppt的子目录,则通过
R.drawable.ppt.xxx
是获取不到ppt目录下的xxx文件的,会报 "R.layout.ppt cannot be resolved
" 的错误。若在assets目录下建立一个名为ppt的子目录,并将background.png放入其中,则代码Bitmap bgImg = getImageFromAssetFile( "ppt/background.png" );
可正常运行。
End
笔者的Github Blog,希望各位大大提意见,点个star,谢谢
传送门:WusyBlog求互粉互赞,互赞所有文章可以私聊我。哈哈,希望我们的原创文章能让更多朋友看到,一起变强。
笔者新开通了微信公众号——饮水思源|wusy 计划持续运营,每日为您分享Android干货、原创文章。微信扫描下方的二维码关注我,开发学习路上不迷路。谢谢各位