Android开发
基础知识点
Java基础
略 略 略
Activity
Android最基本的界面容器,用于显示所有APP的内容
生命周期
Activity的创建到销毁,会执行onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()、onRestart()等生命周期方法
整条生命周期方法可分为四种形态,分别为活跃、失去焦点、停止、销毁
活跃 -- Activity处于栈顶,是可见状态,可以与用户进行交互
失去焦点 -- 被一个透明Activity或其他View拦住了,此时Activity失去与用户交互的能力,但其所有状态信息与成员变量都还存在
停止 -- 当一个Activity被另一个Activity完全覆盖后,被覆盖的Activity进入Stopped状态,此时Activity不可见,但其所有状态信息与成员变量都还存在
销毁 -- Activity被系统回收掉,Activity处于销毁
- onCreate 在Activity 第一次被创建时调用,可以在该方法中进行一些初始化操作
- onStart Activity正在启动时调用,Activity已处于可见状态,但并没有在前台显示,用户不可见,无法进行交互
- onResume 当Activity处于可见状态并且在前台显示。此时的Activity用户可见,可以进行交互。当Activity重新回到前台时,也会调用该方法
- onPause 当Activity正在停止时调用,但Activity仍可见。可以在这个方法里面做一些数据存储等等
- onStop 在Activity即将停止或完全覆盖时调用,此时Activity不可见,在后台运行,在这个方法不可有太耗时操作
- onDestroy 在Activity销毁时调用,因此可以在该方法中进行一些回收和资源释放的操作
- onRestart 在Activity重新启动时调用,Activity由不可见变为可见状态时调用。当打开一个新的Activity,当前Activity被暂停(onPause 和 onStop 被执行),又回到当前Activity,onRestart被回调
Activity生命周期不同时期的执行顺序
第一次启动Activity
onCreate() --> onStart() --> onResume()打开新的Activity,老Activity生命周期
onPause() --> onStop()重新回到原Activity,原Activity生命周期
onRestart() --> onStart() --> onResume()按back键退出Activity生命周期
onPause() --> onStop() --> onDestroy()按Home键切换到桌面后又回到该Activity
onPause() --> onStop() --> onRestart() --> onStart() --> onResume()调用finish()方法,Activity生命周期
onDestory()横竖屏切换
onPause() -> onSaveInstanceState() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()内存不足Activity销毁
当系统内存不足时,优先级低的Activity会被杀死,onSaveInstanceState()方法会被调用,后续Activity恢复时调用onRestoreInstanceState()方法,具体情况类似于横竖屏切换
onSaveInstanceState() 方法和 onRestoreInstanceState() 方法在 Activity 异常终止并重新恢复时调用。
Fragment
Android碎片化界面容器,用于嵌入显示APP指定内容,可以将多个Fragment组合在一个Activity中,Activity可以重复使用某个Fragment,并可以在运行时添加或移除Fragment。
生命周期
Fragment的生命周期和Activity生命周期很相似,只是多了几个方法:
onAttach()
在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)onCreateView()
当Fragment 创建视图时调用onActivityCreated()
在相关联的 Activity 的 onCreate() 方法已返回时调用。onDestroyView()
当Fragment中的视图被移除时调用onDetach()
当Fragment 和 Activity 取消关联时调用。
Fragment生命周期不同时期的执行顺序
打开页面
onCreate() --> onCreateView() --> onActivityCreated() --> onResume()按Home键
onPause() --> onStop()重新回到页面
onStart() --> onResume()按back键
onPause() --> onStop() --> onDestroyView() --> onDestroy() --> onDetach()
Fragment生命周期与Activity生命周期变化的先后过程
打开页面
Fragment.onCreate()-->Fragment.onCreateView()-->Activity.onCreate()-->Fragment.onActivityCreated()-->Activity.onStart()-->Fragment.onStart()-->Activity.onResume()-->Fragment.onResume()按Home键
Fragment.onPause()-->-->Activity.onPause()-->Fragment.onStop()-->Activity.onStop()重新回到页面
Activity.onRestart()-->Activity.onStart()-->Fragment.onStart()-->Activity.onResume()-->Fragment.onResume()按back键
Fragment.onPause()-->Activity.onPause()-->Fragment.onStop()-->Activity.onStop()-->Fragment.onDestroyView()-->Fragment.onDestroy()-->Fragment.onDetach()-->Activity.onDestroy()
界面绑定
- 静态绑定
静态绑定在Activity的布局xml当中完成
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
- 动态绑定
通过编程方式将Fragment添加某个Activity现有的ViewGroup的里面,要想在Activity中执行Fragment事务,必须使用FragmentTransaction中的API
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment exampleFragment = new ExampleFragment();
fragmentTransaction.add(R.id.frame_layout,exampleFragment);
fragmentTransaction.commit();
Service
后台服务,用于执行一些后台操作
生命周期
onCreate():首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。
onStartCommand():当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。
onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。
onBind():当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。
onUnbind():当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。
onRebind():当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。
Service生命周期执行顺序
启动Service服务
单次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()停止Service服务
stopService() —> onDestroy()绑定Service服务
bindService() —> onCreate() —> onBind()解绑Service服务
unbindService() —> onUnbind() —> onDestroy()启动绑定Service服务
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()解绑停止Service服务
unbindService() —> onUnbind() —> stopService() —> onDestroy()解绑绑定Service服务
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
BroadcastReceiver
广播接收器,通过广播的方式进行消息传递,本质上就是一个全局的监听器,可以监听到各种广播,可实现不同组件之间的通信。广播的特点就是发送方并不关心接收方是否接到数据,也不是关心接收方是如何处理数据的,通过这样的形式来达到接、收双方的完全解耦。
广播按照类型可分为全局广播和本地广播
全局广播(系统广播)就是发出的广播可以被其他任意的应用程序接收,或者可以接收来自任意应用程序的广播。例如:开机启动、网络状态改变、拍照、屏幕关闭与开启等等。每一个系统广播都具有特定的intent-filter,包括具体的action,系统广播发出后,将会被相应BroadcastReceiver接收。
本地广播是只能在应用程序的内部进行传递的广播,广播接收器也只能接收内部的广播,不能接受其他应用程序的广播。
使用方法:
1、注册本地广播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("test");
if (mReceiver == null) {
mReceiver = new MyReceiver();
LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,intentFilter);
}
2、发送本地广播
Intent intent = new Intent();
intent.setAction("test");
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
3、注销本地广播
@Override
public void onDestroy() {
if (mReceiver != null) {
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
}
super.onDestroy();
}
广播按照机制可分为标准广播和有序广播
标准广播(无序广播)是指所有的接收者都会接收事件,不可以被拦截,不可以被修改,这种广播是异步的,所有广播Intent匹配的BroadcastReceiver,都可以收到这条广播并且不分先后顺序,视为同时收到,能过Context.sendBroadcast()方法发送。这种效率比较高,但缺点是接收器不能将处理结果传递给下一下接收器,并且无法中途终止广播
Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction("test");
//发送广播
sendBroadcast(intent);
有序广播指广播会按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。这种广播发出后,可以通过Receiver的Intent-filter中的android:priority属性来设置优先级,优先级从-1000~1000,数越大,优先级超高。使用setResult()方法把结果传递给下一下接收者,通过getResult()方法获取上一个接收者传递过来的结果,并可以通过AbortBroadcast()方法丢弃该广播。
静态注册
静态注册就是在AndroidManifest中注册BroadcastReceiver,并指定它所接收的广播种类,动态注册会长驻
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
动态注册
动态注册就是使用Java代码注册广播接收器
@Override
protected void onResume() {
super.onResume();
receiver = new Receiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("test");
registerReceiver(mReceiver, intentFilter); // 注册广播接收器
}
@Override
protected void onPause() {
unregisterReceiver(receiver); // 注销广播接收器
super.onPause();
}
ContentProvider
内容共享,专门用于不同应用间数据交互和共享的组件。实质上ContentProvider就是对SQLiteOpenHelper进一步的封装,以一个或多个表的形式将数据呈现给外部应用使用。通过Uri映射来选择需要操作数据库中的哪个表,并对表中的数据进行增删改查处理。ContentProvider其底层使用了Binder来完成APP进程之间的通信,同时使用匿名共享内存来作为共享数据的载体。ContentProvider支持访问权限管理机制,以控制数据的访问者及访问方式,保证数据访问的安全性。
- Url
- Schema
- Authority
- Path
- Id
Intent
传递数据容器,用于Activity、Fragment界面跳转,广播发送和接收数据传递等。Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android系统根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。Intent可以分为隐式和显示
- 隐式
没有明确指出目标组件,不会用组件名称定义需要激活的目标组件,它更加广泛地用于在不同应用程序之间传递消息,所以必须由Android系统帮助应用程序寻找与Intent请求意图最匹配的组件。
<activity
android:name=".personal.MyOrdersActivity"
android:launchMode="singleTop"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateUnspecified|adjustPan">
<intent-filter>
<action android:name="com.adyl.activity.order.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.adyl.activity.order.ACTION_START" />
</intent-filter>
</activity>
Intent intent = new Intent();
intent = new Intent("com.adyl.activity.order.ACTION_START");
intent.addCategory("com.adyl.activity.order.ACTION_START");
startActivity(intent);
- 显示
明确的指出了目标组件,由于开发人员往往并不清楚别的应用程序的组件名称,因此,显式Intent更多用于在应用程序内部传递消息。比如在某应用程序内,一个Activity启动一个Service或都启动另一个Activity。在显式Intent消息中,决定目标组件的唯一要素就是组件名称,因此,如果你的Intent中已经明确定义了目标组件的名称,那么你就完全不用再定义其他Intent内容。
startActivity(new Intent(this, MainActivity.class));
Application
应用对象,全局应用对象
每个Android应用运行时,会首先创建Application类并实例化。一个应用里面只有一个Application对象,所以Application为单例模式。Application 对象的生命周期是整个App的生命周期。
<application xmlns:tools="http://schemas.android.com/tools"
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:theme">
/**
* @author
*/
public class App extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
Manifest
Android项目配置菜单文件,用于注册和配置Activity、Service、权限等。
Manifest的作用:
- 为应用包命名,应用包可以作用程序程序唯一标识
- 描述应用的各个组件,包括构成应用的 Activity、Service、BroadcastReceiver和ContentProvider。它还为实现每个组件的类命名并发布其功能,例如它们可以处理的 Intent 消息。这些声明向 Android 系统告知有关组件以及可以启动这些组件的条件的信息。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tengbo.drivertransport">
<application xmlns:tools="http://schemas.android.com/tools"
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:theme">
<activity
android:screenOrientation="portrait"
android:name="com.tengbo.module_main.ui.login.SplashActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="design_width_in_dp"
android:value="360"/>
<meta-data
android:name="design_height_in_dp"
android:value="640"/>
</application>
</manifest>
UI
Layout
- RelativeLayout
- LinearLayout
- ConstraintLayout
- FrameLayout
View
- TextView
- Button
- ImageView
- SeekBar
- WebView
- RecycleView
- TimePicker
- DatePicker
- 自定义控件
Resource
- Anim
-
Tweened animation(渐变动画)
通过对场景里的对象不断做图像变换产生动画效果,Animation是以XML格式定义的,分为以下几种形式:Alpha:渐变透明度动画效果
Scale:渐变尺寸伸缩动画效果
Translate:画面转换位置移动动画效果
Rotate:画面转换位置移动动画效果<scale xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fromXScale="0.0" android:fromYScale="0.0" android:pivotX="50%" android:pivotY="50%" android:toXScale="1.0" android:toYScale="1.0"/>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" android:shareInterpolator=["true" | "false"] > <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float" /> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float" /> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float" /> </set>
-
Frame by frame(补间动画)
顺序播放事先做好的图像,就是一帧一帧的画面,排列好顺序按照时间进行播放,产生画面流动,类似于老电影播放<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@mipmap/image1" android:duration="350" /> <item android:drawable="@mipmap/image2" android:duration="350" /> <item android:drawable="@mipmap/image3" android:duration="350" /> <item android:drawable="@mipmap/image4" android:duration="350" /> <item android:drawable="@mipmap/image5" android:duration="350" /> <item android:drawable="@mipmap/image6" android:duration="350" /> </animation-list>
-
menu
- Option Menu(选项菜单)
- Context Menu(上下文菜单)
- Sub Menu(子菜单)
assets
静态资源,例如html页面、配置文件等
values
- colors -- 全局颜色值
- string -- 存放所有字符串资源
- styles -- 页面统一样式处理
- arrarys -- 数组资源
OpenGL
OpenGL图像处理,手机上做图像处理有很多方式,但是目前为止最高效的方法是有效地使用图形处理单元,或者叫 GPU;GPU 可以集中来处理好一件事情,就是并行地做浮点运算。事实上,图像处理和渲染就是在将要渲染到窗口上的像素上做许许多多的浮点运算。也就是说用GPU来分担CPU的工作,提高工作效率。
- GLSurfaceView
手势
网络通信
Http
-
HttpUrlConnection
Http请求是客户端与服务端之间,发送请求与返回应答的标准TCP,客户端发送一个HTTP请求后,就与服务端建立起了TCP连接,服务端接收到请求后,就返回客户端响应数据。
HttpUrlConnection是标准的发送GET请求与POST请求类。HttpURLConnection继承自URLConnection,使用相对简单,易于扩展。
使用HttpUrlConnection的步骤:- 创建URL对象
- 通过URL对象调用openConnection()方法获得HttpURLConnection对象
- HttpURLConnection对象设置其他连接属性
- HttpURLConnection对象调用getInputStream()方法向服务器发送http请求
并获取到服务器返回的输入流 - 读取输入流,转换成String字符串
new Thread(new Runnable() { @Override public void run() { initBaidu(); } }).start();
private void initBaidu() { try { //1,创建URL URL url = new URL("https://www.baidu.com/");//指定网站 //2,openConnection HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); //3,InputStream InputStream inputStream = httpURLConnection.getInputStream(); //4,InputStreamReader InputStreamReader reader = new InputStreamReader(inputStream, "UTF-8"); //5,水桶盛水--BufferedReader BufferedReader bufferedReader = new BufferedReader(reader); StringBuffer buffer = new StringBuffer(); String temp = null; while ((temp = bufferedReader.readLine()) != null) { //如果不为空就一直取 buffer.append(temp); } bufferedReader.close();//记得关闭 reader.close(); inputStream.close(); Log.e("MAIN",buffer.toString());//打印结果 } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
Socket
Socket(套接字)是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。Socket是用于描述IP地址和端口,是一个通信链的句柄,用来实现不同虚拟机或物理机之间的通信。应用程序通过Socket向网络发出请求或应答请求。网络中两个进程通过一个双向的通信连接实现数据的交换,建立网络通信连接至少需要一对Socket,连接的一端称为一个Socket。
- 应用层 协议:TFTP、HTTP、SNMP、FTP、SMTP、DNS、Telnet...
- 传输层 协议:TCP、UDP
- 网路层 协议:IP、ICMP、OSPF、EIGRP、IGMP
- 数据链路层 协议:SLIP、CSLIP、PPP、MTU
- TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
- UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
Apache-MINA是个异步通信框架,一般使用场景是服务端开发,长连接、异步通信。Mina是一个基于NIO的网络框架,下面是一个客户端Demo。
private static Socket socket;
public MinaClient() {}
public static Socket getMinaClient() throws IOException {
socket = new Socket();
socket.connect(new InetSocketAddress("218.201.73.169", 9123));
return socket;
}
/**
* 向服务器写入数据
*
* @param socket
* @param str
* @return
*/
public static Boolean sendContent(Socket socket, String str) {
try {
OutputStream output = new BufferedOutputStream(socket.getOutputStream());
output.write(str.getBytes("utf-8"));
output.flush();
LogUtils.i(Consts.zwy, "发送消息:" + str);
} catch (IOException e) {
e.printStackTrace();
CloseSocket(socket);
return false;
}
return true;
}
/**
* 读取服务器数据
*
* @param socket
* @return
* @throws IOException
*/
public static String receiveContent(Socket socket) throws IOException {
String str = null;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
str = br.readLine();
LogUtils.i(Consts.zwy, "收到消息:" + str + "--->" + DateUtil.getDateTime());
return str;
}
/**
* 关闭连接
*
* @param socket
* @throws IOException
*/
public static void CloseSocket(Socket socket) {
try {
socket.close();
socket = null;
} catch (Exception e) {
socket = null;
e.printStackTrace();
}
}
NFC
- Reader/writer mode(读卡)
- Card Emulation Mode(仿真卡)
- P2P mode(点对点)
USB
- usb 附件
- usb 主机
Headset Bluetooth(蓝牙通信)
数据存储
Sqlite
嵌入式轻型、关联式数据库 轻量级、不需要安装、单一文件、跨平台/可移植性、弱类型字段、开源
File
文件保存
SharedPreferences
轻量级的存储类,主要保存一些小的数据,使用key-value方式保存
测试工具
第三方框架
okHttp、Retrofit -- 网络请求
ButterKnife -- View注解
EventBus3.0 -- 通信组件
Dagger2 -- 依赖注入
Glide、PhotoView -- 网络图片处理
LeakCanary -- 内存监控
FastJson、GJson -- Json解析
第三方调用
地图
语音识别
支付
广告、统计分析
安全管理
网络通信安全
1、使用https,https是一种加密流量协议,因些窃听者无法听易拦截
2、证书与公钥固定
3、消毒与验证,定期编程验证
4、与其他应用程序通信,最好使用Intent,保护好IPC
5、端与端加密,A发送的内容只有B可以收到解密
代码混淆
将源代码转换成一种功能上等价,但是难于阅读和理解的形式的行为。
加密方式 MD5、DES、RSA、
MD5 一种被广泛使用的密码散列函数,可以产生出一个128位的散列值,用于确保信息传输完整一致,为了便于展示和读写一般将128位的二进制数转换成32位16进制数(如:655A6E9A375DF4F82B730833C807AADD)
DES 一种对称加密算法,即加密和解密使用相同密钥的算法,因为DES使用56位密钥以现代计算能力,24小时内即可被破解
RSA 最流行的公钥密码算法,使用长度可以变化的密钥,RSA是第一个既能用于数据加密也能用于数字签名的算法,RSA的安全性依赖于大数分解,小于1024位的N已经被证明是不安全的,而且由于RSA算法进行的都大数计算,使得RSA最快也比DES慢,这是RSA最大的缺陷,因此通常只能用于加密少量数据或者加密密钥,但RSA仍然不失为一种高强度的算法
大致原理如下:
1、随机选择两个大质数p和q,p不等于q,计算N=pq;
2、选择一个大于1小于N的自然数e,e必须与(p-1)(q-1)互素。
3、用公式计算出d: d*e=1(mod(p-1)(q-1)).
4、销毁p和q.
最终得到的N和e就是“公钥”,d就是“私钥”,发送方使用N去加密数据,接收方只有使用d才能解开数据内容
性能优化
UI优化
UI的绘制,主要原因是绘制的层级深,页面复杂,刷新不合理,由于这些原因导致卡顿的场景更多出现在UI和启动后的初始界面以及跳转到页面的绘制上。UI的过度绘制,绘制的页面有几层View,底层View都是隐藏的,这种的还绘制的话就会造成过度绘制
解决方案:
1、绘制UI时,尽量减少绘制UI层次;减少不必要的view嵌套,可以用Hierarche Viewer工具来检测。
2、当使用FrameLayout的时候,我们可以使用merge,这样可以避免自己的帧布局和系统的ContentFrameLayout帧布局重叠造成重复计算(measure和layout)
3、提高显示速度,使用ViewStub;当加载的时候才会占用,不加载的时候就是隐藏,仅仅占用位置
4、在view层级相同的情况下,尽量使用LinerLayout而不是RelativeLayout;因为RelativeLayout在测量的时候测量二次,而LinerLayout只测量一次
6、布局复用,比如ListView、RecyclerView。删除控件中无用的属性
7、尽量避免过度绘制,例如:layout设置了背影,同时主题默认也给一个背景,这时只需要取一个背景。自定义View时,使用canvas.clipRect()来帮助系统识别哪些可见的区域,只有在这个区域才会被绘制。
内存优化
数据处理,数据处理量太大,一般分为三种情况
1、数据在主线程处理,长时间得不到反馈
2、数据处理占用CPU高,导致主线程拿不到时间片
3、内存增加导致GC频繁,从而引起卡顿
解决方案:
1、不要在主线程进行网络访问/大文件的IO操作
2、合理的刷新机制,尽量减少刷新次数,尽量避免后台有高的CPU线程运行,缩小刷新荡然区域
3、对象引用。强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型
4、减少不必要的内存开销。注意自动装箱,增加内存复用,例如:有效复用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
5、使用合适的数据类型,例如:数据类容器结构,可以用使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等
6、图片内存优化,使用一些图片缓存方式对图片进行压缩、保存、加载等等(工具:libjpeg)
启动速度优化
APP启动流程:
- 点击 APP图标,Launcher进程采用Binder IPC向System_server进程发起startActivity请求;
- System_server进程收到请求后,向zygote进程发送创建进程的请求;
- Zygote进程fork出新子进程,既APP进程;
- APP进程,通过Binder IPC向System_server进程发起attachApplication请求;
- System_server进程在收到请求后,进行一系列准备工作后,现通过Binder IPC向APP进程发送scheduleLaunchActivity请求;
- APP进程的Binder线程(ApplicationThread)在收到请求后,通过Handler向主线程发送Launch_Activity消息;
- 主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。
- APP便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到APP的主界面。
Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>測量布局绘制显示在界面上
启动方式:
冷启动
当启动应用时,后台没有该应用的进程,这时系统会又一次创建一个新里程分配给该应用,这个启动方式就是冷启动。热启动
当启动应用,后台已有该应用的进程,所以在已有进程的情况下,这样的启动会从已有的进程来启动应用,这个方式叫热启动。特点:
1、冷启动由于系统会又一次创建一个新的进程分配给它,所以先创建和初始化Application类,再创建和初始化MainActivity类,最后显示在界面上
2、热启动由于会从已有进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity。所以热启的过程仅仅须要创建和初始化一个MainActivity即可了,而不必创建和初始化Application,由于一个应用从新进程的创建到进程的销毁。Application仅仅会初始化一次。
- 启动时间计算
点击应用图标开始到创建一个新进程,最后出现界面的第一帧,这段时间的总合就算是启动时间,可以使用adb shell命令的方式进行测量:
adb shell am start -W [packageName]/[packageName.MainActivity]
命令运行成功以后,会出现以下三个值:
1、ThisTime:一般和TotalTime时间一样,如果有透明Activity预先处理其它操作,ThisTime会比TotalTime小
2、TotalTime:应用启动时间,包含主界面显示之前所有操作的时间
3、WaitTime:一般比TotalTime大点,包含系统影响的耗时
解决方案:
1、启动速度的监控,提高应用启动速度。例如闪屏页面,优化布局,加载逻辑优化,数据准备等
2、在Application初始化中,不要进行耗时操作,数据读取可以放在异步线程当中
3、降低MainActivity布局层次,避免在生命周期方法中做过多的耗时操作
稳定性优化
Android应用的稳定性定义比较宽泛,影响其原因也有很,例如:内存使用不合理、网络状态不理想、业务场景不周全等。其中最常的就是Crash和ANR,它们直接导致应用无法使用。一般解决方案:
1、提高代码质量,业务场景合理性
2、Crash监控,记录崩溃信息,方便分析原因(工具:Bugly)
安装包优化
解决方案:
1、尽量只使用一套图片,使用高分辨率真的图片
2、移除无用的依赖库
3、动态下载资源
4、对特殊字体文件压缩,使用FontCreator工具提取自己需要的文字。
5、代码混淆,使用代码混淆工具压缩代码,减少体积(工具:AndResGuard)
NDK开发
Native Development Kit,Android的一种开发工具包,可以快速开C、C++的动态库,并自动将so和应用一起打包成APK,即可通过NDK在Android中使用JNI与本地代码交互。
优点:
- 运行效率高
- 代码安全性高
- 跨平台
调用.so包的方法System.loadLibrary,在静态初始化块内调用System.loadLibrary加载本地库,是加载库的最简单方法,这种块中的代码是在虚拟机加载类时执行的,静态初始化块可能会显著增加开销。
public class JniUtils {
static {
System.loadLibrary("JniUtils");
}
public native String getCLanguageString();
}