0%

Android开发知识要点汇总备忘

Activity四种启动模式(android:launchMode)

  1. standard
    默认启动方式,每次startActivity都会生成一个新的实例放入栈顶
  2. singleTop
    如果发现栈顶有该Activity的实例,则重新使用栈顶的Activity并调用onNewIntent方法传递新的Intent,如果栈顶没有,则重新创建一个Activity放入栈顶,简单的理解就是栈顶找得到Activity就不创建,如果没则创建,栈顶唯一
  3. singleTask
    在Activity栈中需找需要启动Activity实例,如果有,则将该Activity实例上面的所有Activity都出栈并且调用onNewIntent方法传递新的Intent数据,如果没有找到,则创建一个新的Activity实例。简单理解就是 栈中唯一
  4. singleInstance
    创建一个新的Activity task(可以理解为一个新的进程),该task中只有一个也是唯一一个Activity实例,且与其他task的Activity相互独立,拥有自己的Context上下文,因此如果使用静态变量会出现共享数据问题

Service相关知识点

  1. Context.startService()
    启动一个Service
  2. Context.bindService()
    作用是在Service和调用者之间建立一个桥梁,并不负责更多的工作(例如一个Service需要连接服务器的操作),一般使用bindService来绑定到一个现有的Service(即通过StartService启动的服务)。
  3. bind Service后不想随着Activity的退出而退出
    先startService,然后再bindService,不需要绑定的时候执行unbindService就可以了,unbindService会触发Service的onUnbind方法而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。
  4. 生命周期
    如果Service还未运行调用startService,则onCreate->onStartCommand->onDestroy
    如果Service已经运行调用startService,则onStartCommand->onDestroy
  5. 4种运行方式
    在Service的onStartCommand方法中返回START_STICKY、START_NOT_STICKY、START_REDELIVER_INTENT、START_STICKY_COMPATIBILITY即可设置Service的运行方式

START_STICKY
service进程被kill后,系统会尝试重新启动Service,Service将保留在开始状态,但是不保留那些传入的intent,然后调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent
START_NOT_STICKY
被kill后,系统不会尝试重新创建Service
START_REDELIVER_INTENT
被kill后,系统会尝试重新创建Service,并且在onStartCommand重新传入intent
START_STICKY_COMPATIBILITY
TART_STICKY的兼容版本,但不保证服务被kill后一定能重启

以上所说的kill的由系统kill的时候才有可能会被重启,如果是人为kill的话即使资源充足也不会重启

BroadcastReceiver相关知识点

  1. 注册广播有两种,一种是静态注册就是在AndroidManifest.xml文件中定义,另一种是动态代码注册
  2. 广播类型有5中,分别是 普通广播系统广播有序广播粘性广播应用内广播
  3. 10秒限制,如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。
  4. onReceive不要做太耗时的工作,否则ANR
  5. 不同方式注册的广播接收器中onReceive接收到的Context的类型不同

普通广播sendBroadcast
普通广播是无序的,如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

系统广播
android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。

有序广播sendOrderedBroadcast
发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播一样,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …)。
注意如果有多个BroadcastReceiver可以接受有序广播则按照priority值大小排序,相同priority则动态广播接收器优先接收,且优先接收的BroadcastReceiver可以截断、修改广播内容

粘性广播sendStickyBroadcast/sendStickyOrderedBroadcast(5.0后已过期)
广播Intent发送后会一直保留这个Intent,如果广播发送后还有注册这个广播内容的广播接收器也可以收到这个广播,且只会收到最后一个广播,使用这个API需要权限android.Manifest.permission.BROADCAST_STICKY,比如系统网络状态的改变发送的广播就是粘性广播

应用内广播LocalBroadcastManager

  1. 注册的时候将exported设置为false,这样使得非本App内部发出的此广播不被接收
  2. 使用LocalBroadcastManager发送和接收广播
  3. LocalBroadcastManager发送的广播只能有LocalBroadcastManager动态注册的广播接收器才能接收广播
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //注册应用内广播接收器
    localBroadcastManager = LocalBroadcastManager.getInstance(this);
    localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);

    //unregisterReceiver(mBroadcastReceiver);
    //取消注册应用内广播接收器
    localBroadcastManager.unregisterReceiver(mBroadcastReceiver);


    //发送应用内广播
    localBroadcastManager.sendBroadcast(intent);

onReceive接收到的Context的类型

  1. 静态注册的为ReceiverRestrictedContext
  2. 动态注册的为Activity Context
  3. LocalBroadcastManager注册的本地广播的为Application Context

Handler/Looper知识点

  1. 为某个线程启动消息循环,通过下面的代码就能将普通线程升级为Looper线程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class LooperThread extends Thread
    {
    @Override
    public void run()
    {
    // 将当前线程初始化为Looper线程
    Looper.prepare();

    // ...其他处理,如实例化handler

    // 开始循环处理消息队列
    Looper.loop();
    }
    }
  2. 每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal
  3. Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
  4. 一个线程可以有多个Handler,但是只能有一个Looper!
  5. 有了handler之后handler的post和sendMessage来发送消息了
  6. post(Runnable)方法发出的Runnable对象最后还是要被封装成message
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
    public final boolean post(Runnable r)
    {
    // 注意getPostMessage(r)将runnable封装成message
    return sendMessageDelayed(getPostMessage(r), 0);
    }

    private final Message getPostMessage(Runnable r)
    {
    Message m = Message.obtain(); //得到空的message
    m.callback = r; //将runnable设为message的callback,
    return m;
    }
  7. sendMessage和obtainMessage的区别,obtainMessage从消息池中获取一个可用消息,能够实现消息重用提升效率

自定义控件相关知识点

自定义控件分为三:种自绘控件、组合控件、以及继承控件

  1. 自绘控件,内容全部都是通过在onDraw方法中自己绘制出来
  2. 组合控件,通过现有控件组合成为一个新控件
  3. 继承控件,继承现有控件扩展功能
  4. 自定义ViewGroup需要重写onMeasure和onLayout方法,onLayout是抽象方法必须实现

onMeasure方法确定控件的宽高大小
onMeasure方法会传入widthMeasureSpec、heightMeasureSpec分别代表宽高参数,但是并不是真正的宽高,是由宽、高和各自方向上对应的模式来合成的一个值*(其中,在int类型的32位二进制位中,31-30这两位表示模式,0~29这三十位表示宽和高的实际值)* 可以通过下面的方法获得模式和大小的值,且onMesasure方法还需要负责调用子控件的onMeasure方法,来完成整个界面的大小定位,包括我们说的九宫图也是在这个方法中实现拉伸的

1
2
int specMode = MeasureSpec.getMode(measureSpec);//得到模式
int specSize = MeasureSpec.getSize(measureSpec);//得到大小

模式(MeasureSpe)有三种
UNSPECIFIED:表示默认值,父控件没有给子view任何限制,一般不会出现
EXACTLY:表示父控件给子view一个具体的值,子view要设置成这些值的大小,如matchparent/fillparent/200dp
AT_MOST:表示父控件个子view一个最大的特定值,而子view不能超过这个值的大小,对应wrapcontent

1
2
3
4
5
6
7
8
9
10
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = measureWidth(widthMeasureSpec);
int measureHeight = measureHeight(heightMeasureSpec);
// 计算自定义的ViewGroup中所有子控件的大小
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 设置自定义的控件MyViewGroup的大小
setMeasuredDimension(measureWidth, measureHeight);
}

onLayout方法完成界面的布局定位
onLayout方法是抽象的,子类必须实现这个方法,完成子控件在ViewGroup中的布局

跨进程通讯AIDL

Android并没有延续Linux的跨进程通讯机制,如:传统的管道(Pipe)、信号(Signal)和跟踪(Trace)等,而是引入了Binder机制,Binder分为四个组件Client、Server、Service Manage和Binder驱动,其中Client、Server、Service Manage是运行在用户进程空间中,Binder驱动是运行在内核空间中

  1. Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中
  2. Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server
  3. Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信
  4. Client和Server之间的进程间通信是同步的,且是通过Binder驱动程序间接实现
  5. Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力

体现在应用层面上采用AIDL来实现

  1. AIDL中能够传递java的基本类型、List、Map、CharSequence,和实现了Parcelable接口或者Serializable接口的对象作为参数
  2. 如果传递对象最为参数必须创建一个该对象的aidl文件,里面只有一句parcelable Book;
  3. 必须以Service为载体提供服务,所以新建一个类,继承Service,然后实现aidl接口中的方法提供服务,过程的绑定Service类似
  4. 配置Service权限到AndroidManifest.xml,且Service需提供一个Intent Action,在绑定的时候可以指定
  5. 将aidl文件及相应的包拷贝到客户端中,IDE会自动通过aidl文件生成相应的java接口
  6. 客户端通过intent action绑定服务,提供接口的调用

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.test;
import android.os.Parcel;
import android.os.Parcelable;
public class Test implements Parcelable
{
private String bookName;
private int bookPrice;

public Test()
{
}

public Test(Parcel parcel)
{
bookName = parcel.readString();
bookPrice = parcel.readInt();
}

public String getBookName()
{
return bookName;
}
public void setBookName(String bookName)
{
this.bookName = bookName;
}
public int getBookPrice()
{
return bookPrice;
}
public void setBookPrice(int bookPrice)
{
this.bookPrice = bookPrice;
}

public int describeContents()
{
return 0;
}
public void writeToParcel(Parcel parcel, int flags)
{
parcel.writeString(bookName);
parcel.writeInt(bookPrice);
}

public static final Parcelable.Creator<Book> CREATOR = new Creator<Test>()
{
public Test createFromParcel(Parcel source)
{
return new Test(source);
}
public Test[] newArray(int size)
{
return new Test[size];
}
};
}

Test.aidl

1
parcelable Test;

ITestAIDLService.aidl

1
2
3
4
5
6
7
package com.test;
import com.test.Book;
interface ITestAIDLService
{
String sayHello();
Test getTest();
}

ITestAIDLService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.test;
import com.test.ITestAIDLService.Stub;
import com.test.ITestAIDLService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class ITestAIDLService extends Service
{
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
/**
* 在AIDL文件中定义的接口实现。
*/
private IAIDLServerService.Stub mBinder = new Stub()
{

public String sayHello() throws RemoteException
{
return "Hello";
}

public Test getBook() throws RemoteException
{
Test mBook = new Test();
mBook.setBookName("Android应用开发");
mBook.setBookPrice(50);
return mBook;
}
};
}

声明Service

1
2
3
4
5
<service android:name="AidlServerService"  android:process=":remote">
<intent-filter>
<action android:name="com.chapter8.aidl.IAIDLServerService"></action>
</intent-filter>
</service>

客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private ServiceConnection mConnection = new ServiceConnection()
{
public void onServiceDisconnected(ComponentName name)
{
mIaidlServerService = null;
}
public void onServiceConnected(ComponentName name, IBinder service)
{
mIaidlServerService = IAIDLServerService.Stub.asInterface(service);
//aidl通信
try
{
String mText = "Say hello: " + mIaidlServerService.sayHello() + "/n";
mText += "书名: " + mIaidlServerService.getBook().getBookName()+"/n";
mText += "价格: " + mIaidlServerService.getBook().getBookPrice();
mTextView.setText(mText);
} catch (RemoteException e)
{
e.printStackTrace();
}
}
};


Intent service = new Intent("com.chapter8.aidl.IAIDLServerService");
bindService(service, mConnection,BIND_AUTO_CREATE);
谢谢您的打赏,我的大英雄 ^_^
Thank you for your generosity, my big hero ^_^

更多内容请关注公众号「西小玛」