README
《Android开发艺术探索》
任玉刚共15章
第一章 Activity的生命周期和启动模式
四大组件:Activity/Service/Broadcast Receiver/Content Provider
六大布局:RelativeLayout/LinearLayout/FrameLayout/TableLayout/AbsoluteLayout/GridLayout
一个信使:Intent
可见界面:Activity/Toast/Snack/Dialog/Window/Notification
Activity的典型生命周期:onCreate() [onStart() [onResume() onPause() 前台可见] onStop() 后台可见] onDestroy() onRestart()
视图结构:View Parent->ViewGroup-View/ViewGroup树 Activity->Window(PhoneWindow)->DecorView->TitleView/ContentView(setContentView()/setSupportActionBar())
Activity启动:startActivity()->Instrumentation(Binder)->ActivityManagerService(ActivityStack)-ActivityThread
注:ActivityA启动ActivityB,ActivityA的onPause()先于ActivityB的onCreate()执行
Activity的异常生命周期:onSaveInstanceState()->onStop()-onStart()->onRestoreInstanceState() 委托思想
Activity优先级:前台可见 后台可见 不可见
Activity不重建生命周期:onConfigurationChanged() android:configChanges = "orientation|keyboardHidden" //使系统不重建Activity
Activity启动模式:android:launchMode="standard/singTop/singTask/singInstance" onNewIntent()
A task is a stack of activities
Context:Application/Activity/Service 只有Activity有TaskStack
taskAffinity配合singleTask/allowTaskReparenting使用 当allowTaskReparenting="true"时AppA启动AppB的ActivityX会转移到AppB的TaskStack中
Activity的Flags:FLAG_ACTIVITY_NEW_TASK/FLAG_ACTIVITY_SINGLE_TOP/FLAG_ACTIVITY_CLEAR_TOP/FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
IntentFilter的匹配规则:action/category/data
action的匹配规则(String):必须有 Intent只有一个 IntentFilter可多个 有一个匹配即可
category的匹配规则(String):可没有 Intent可多个 IntentFilter可多个 Intent完全匹配
data的匹配规则(mimeType+URI):必须有 Intent只有一个 IntentFilter可多个 有一个匹配即可
显示调用/隐示调用
隐示判断:PackageManager的resolveActivity()和queryIntentActivities() 或则 Intent的resolveActivity() MATCH_DEFAULT_ONLY
第二章 IPC机制
IPC(Inter-Process Communication):Bundle 文件共享 AIDL Messenger ContentProvider Socket
ANR Application Not Responding
Binder
Android中的进程:android:process 或则JNI Native folk
android:process=":remote" 私有进程
android:process="com.gh.light.remote" 全局进程 ShareUID
问题:静态成员和单例模式完全失效/线程同步机制完全失效/SharedPreferences的可靠性下降/Application会多次创建
Serializable接口:空接口 private static final long serialVersionUID = 1L; 序列化反序列化:ObjectOutputStream/ObjectInputStream 静态变量和transient不参与序列化
Parcelable接口:
public class User implements Parcelable {
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
/*内容描述*/
public int describeContents() {
return 0;
}
/*序列化*/
public void writeToParcel(Parcel out, int flags) {
out.writeInt(userId);
out.writeString(userName);
out.writeInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}
/*反序列化*/
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
public User createFromParcel(Parcel in) {
return new User(in);
}
public User[] newArray(int size) {
return new User[size];
}
};
private User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}
Serializable适用于序列化对象到存储设备和网络传输
Binder implements IBinder /dev/binder
public interface IBookManager extends android.os.IInterface
IBookManager文件详解:
DESCRIPTOR Binder的唯一标识,一般为Binder的类名
asInterface(android.os.IBinder obj) 转化Binder为AIDL
asBinder() 返回当前Binder
public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 运行中服务端 code可以标志请求方法 如果返回false则客户端请求失败
Proxy#getBookList() Proxy#addBook()
linkToDeath() unlinkToDeath()
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
public void binderDied() {
if(mBookManager == null) {
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManager = null;
}
};
mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
IPC方式:
Bundle/文件共享/SharedPreferences
Messenger信使:Message 服务端 客户端
服务端:
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_FROM_CLIENT:
System.out.println(msg.getData().getString("msg"));
Messenger client = msg.replyTo;
... ...
client.send(msg);
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客户端:
private Messenger mService;
// public void onServiceConnected(ComponentName className, IBinder service)
mService = new Messenger(service);
Message msg = Message.obtain(null, MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "Hello, this is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;//同服务端创建一个Messenger
mService.send(msg);
AIDL(Android Interface Defination Language)
步骤:
1 创建.aidl文件 byte short int long float double boolean char String CharSequence List(ArrayList) Map(HashMap) Parcelable AIDL
Book.java 同名Book.aidl parcelable Book;
in out inout 不支持静态常量
2 服务端实现 CopyOnWriteArrayList/ConcurrentHashMap
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private Binder mBinder = new IBookManager.Stub() {};
3 客户端实现
IBookManager bookManager = IBookManager.Stub.asInterface(service);
观察者模式 RemoteCallbackList
binderDied()肯onServiceDisconnected()区别 不可访问UI
ContentProvider
CRUD(insert() delete() update() query()) + onCreate() getType()
ContentResolver MediaStore SQLite
注册:
<provider
android:name".provider.BookProvider"
android:authorities="com.study.mm.book.provider"
android:permission="com.study.PROVIDER"
android:progress=":provider" />
访问:
Uri uri = Uri.parse("content://com.study.mm.book.provider");
getContentResolver().query(uri, null, null, null, null);
五大存储:SharedPreferences/File/SQLite/ContentProvider/Network
SQLiteOpenHelper Constructors() onCreate() onUpgrade()
UriMatcher
Socket TCP/UDP
Binder连接池
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
interface ICompute {
int add(int a, int b);
}
public class SecurityCenterImpl extends ISecurityCenter.Stub
public class ComputeImpl extends ICompute.Stub
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
Service里返回:new BinderPool.BinderPoolImpl();
第三章 View的事件体系
View是一个矩形区域
ViewParent->ViewGroup->(View/ViewGroup) View树
Activity->Window(PhoneWindow)->DecorView(TittleView+ContentView) setSupportActionBar() setContentView()
View坐标系:左上角为0点 右下为正方向 getLeft()/getRight()/getTop()/getBottom() left right top bottom x y translationX translationY
MotionEvent ACTION_DOWN ACTION_MOVE ACTION_UP getX() getY() (相对于当前View) getRawX() getRawY() (相对于手机屏幕)
TouchSlop ViewConfiguration.get(getContext()).getScaledTouchSlop()
VelocityTracker
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
int xVelocity = velocityTracker.getXVelocity();
velocityTracker.clear();velocityTracker.recycle();
GestureDetector
GestureDetector mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongPressEnable(false);
boolean consume = mGestureDetector.onTouchEvent(event); return consume;
实现:OnGesureListener OnDoubleTapListener的方法
Scroller弹性滑动
scrollTo() scrollBy()
Scroller scroller = new Scroller(mContext);
View的滑动:
使用scrollTo()/scrollBy() 操作简单 适合对View内容的滑动
使用动画 操作简单 适用于没有交互的View和实现复杂的动画效果
改变布局参数 操作复杂 适用于有交互的View
使用Scroller进行弹性滑动
View事件的分发处理(委托思想):先分发 再拦截 谁拦截 谁处理 不处理 就回调 处理完 就结束
public boolean dispatchTouchEvent(MotionEvent ev)
public boolean onInterceptTouchEvent(MotionEvent ev)
public boolean onTouchEvent(MotionEvent ev)
互动冲突:外部拦截法 内部拦截法
第十二章 Bitmap的加载和Cache
OOM(Out Of Memory)
16M Bitmap
LruCache 内存缓存
DisLruCache 磁盘缓存
LRU(Least Recently Used)最近最少使用
ListView GridView RecyclerView
Bitmap
BitmapFactory decodeFile/decodeResource/decodeStream/decodeByteArray
BitmapFactory.Options inJustDecodeBounds inSampleSize outWidth outHeight 2
public Bitmap compressBitmap(Resources res, int id, int requireWidth, int requireHeight) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResources(res, id, opts);
int outWidth = opts.outWidth;
int outHeight = opts.outHeight;
int inSampleSize = 1;
while(outWidth > requireWidth || outHeight >requireHeight) {
inSampleSize *= 2;
outWidth /= 2;
outHeight /= 2;
}
opts.inSampleSize = inSampleSize;
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeResources(res, id, opts);
}
LruCache Android 3.1 support-v4 Android 2.2 LinkedHashMap
强引用:不被GC
软引用:内存不足GC
弱引用:随时GC
int maxSize = (int)(Runtime.getRuntime().maxMemory() / 8);
LruCache<String, Bitmap> mLruCache = new LruCache<String, Bitmap>(maxSize) {
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
create()
entryRemoved()
};
mLruCache.get(key);
mLruCache.put(key, bitmap);
mLruCache.remove(key);
/sdcard/Android/data/package_name/cache
/data/data/package_name/cache
File directory = getCacheDirectory(context, "bitmap");
if (!directory.exists()) {
directory.mkdir();
}
int version = getAppVersion(context);
int maxSize = 10 * 1024 * 1024;
mDiskLruCache = DiskLruCache.open(directory, version, 1, maxSize);
private int getAppVersion(Context context) {
String packageName = context.getPackageName();
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
private File getCacheDirectory(Context context, String uniqueName) {
String cachePath;
String state = Environment.getExternalStorageState();
boolean isRemovable = Environment.isExternalStorageRemovable();
if (TextUtils.equals(state, Environment.MEDIA_MOUNTED) || !isRemovable) {
cachePath = context.getExternalCacheDir().toString();
} else {
cachePath = context.getCacheDir().toString();
}
return new File(cachePath + File.separator + uniqueName);
}
private String getKey(String src) {
String key;
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(src.getBytes());
byte[] bytes = digest.digest();
key = bytes2HexString(bytes);
} catch (NoSuchAlgorithmException e) {
key = String.valueOf(src.hashCode());
}
return key;
}
private String bytes2HexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String string = Integer.toHexString(0xFF & bytes[i]);
if (string.length() == 1) {
sb.append('0');
}
sb.append(string);
}
return sb.toString();
}
public void put(String key) {
try {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
OutputStream os = editor.newOutputStream(0);
} catch (IOException e) {
e.printStackTrace();
}
}
public void get(String key) {
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
InputStream is = snapshot.getInputStream(0);
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean remove(String key) {
try {
return mDiskLruCache.remove(key);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public void delete() {
try {
mDiskLruCache.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
public long size() {
return mDiskLruCache.size();
}
public void close() {
try {
mDiskLruCache.close();
} catch (IOException e) {
e.printStackTrace();
}
}
ImageLoader 先从内存缓存加载,再从磁盘缓存加载,最后从网络加载写入缓存
优化卡顿:
不在getView()执行耗时操作
控制异步任务的执行频率
开启硬件加速android:hardwareAccelerated="true"
第十四章 JNI和NDK编程
JNI Java Native Interface
NDK Native Development Kit
代码保护
提高效率
引用开源库
libc libm libz liblog
static {
System.loadLibrary("libHelloJNI");
}
public native static String print(String name);
public static native String print(String name);
native public static String print(String name);
jstring Java_com_study_hellojni_MainActivity_print(JNIEnv* env, jobject thiz, jstring name);
#include<jni.h>
env JVM指针 obj调用该方法的对象
Application.mk Android.mk jni目录
APP_ABI := armeabi
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := helloJNI
LOCAL_SRC_FILES := hello.cpp
include $(BUILD_SHARED_LIBRARY)
jbyte jshort jint jlong jfloat jdouble jboolean jchar
jclass jobject jsarray jsring void jobjectArray jbooleanArray ... jthrowable
Java call C/C++
C/C++ call Java
第十五章 Android性能优化
OOM SOF ANR
布局优化
减少层级
include merge ViewStub
16ms 60fps
内存优化
ListView Bitmap
MAT
Debug.startMethodTracing();
Debug.stopMethodTracing();