标签归档:Android

android-Service和Thread的区别

1.服务不是单一的进程。服务没有自己的进程,应用程序可以不同,服务运行在相同的进程中。

2.服务不是线程。可以在线程中工作。

一.在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。

同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程。

二.如果任务占用CPU时间多,资源大的情况下,要使用线程。

 

servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而thread是由本应用程序托管。

 

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用Thread 来执行一些异步的操作。

 

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的Service 是运行在主进程的main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的main 线程上运行的。如果是Remote Service,那么对应的Service 则是运行在独立进程的main 线程上。

 

既然这样,那么我们为什么要用Service 呢?其实这跟android 的系统机制有关,我们先拿Thread 来说。Thread 的运行是独立于Activity 的,也就是说当一个Activity 被finish 之后,如果你没有主动停止Thread 或者Thread 里的run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当Activity 被finish 之后,你不再持有该Thread 的引用。另一方面,你没有办法在不同的Activity 中对同一Thread 进行控制。

 

举个例子:如果你的Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该Thread 需要在Activity 没有start的时候也在运行。这个时候当你start 一个Activity 就没有办法在该Activity 里面控制之前创建的Thread。因此你便需要创建并启动一个Service ,在Service 里面创建、运行并控制该Thread,这样便解决了该问题(因为任何Activity 都可以控制同一Service,而系统也只会创建一个对应Service 的实例)。

 

因此你可以把Service 想象成一种消息服务,而你可以在任何有Context 的地方调用Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在Service 里注册BroadcastReceiver,在其他地方通过发送broadcast 来控制它,当然这些都是Thread 做不到的。

———————————————————————————

 

广播接收者(BroadcastReceiver)用于接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收,这个特性跟JMS中的Topic消息接收者类似。要实现一个广播接收者方法如下:
第一步:继承BroadcastReceiver,并重写onReceive()方法。
public class IncomingSMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
}
}
第二步:订阅感兴趣的广播Intent,订阅方法有两种:
第一种:使用代码进行订阅
IntentFilter filter = new IntentFilter(“android.provider.Telephony.SMS_RECEIVED”);
IncomingSMSReceiver receiver = new IncomingSMSReceiver();
registerReceiver(receiver, filter);
第二种:在AndroidManifest.xml文件中的<application>节点里进行订阅:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.provider.Telephony.SMS_RECEIVED”/>
</intent-filter>
</receiver>

如果你想别人接收到的短信,达到你不可告人的目的,那么使用BroadcastReceiver
当系统收到短信时,会发出一个广播Intent,Intent的action名称为、

如果要短信终止广播就要配置上你的广播接收者的级别

<intent-filter android:priority=”100″ >
<action android:name=”android.provider.Telephony.SMS_RECEIVED” />
</intent-filter>

android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,我们使用名称“pdus”即可从Intent中获取到短信内容。

在AndroidManifest.xml文件中的<application>节点里对接收到短信的广播Intent进行订阅:
<receiver android:name=”.你的receiver名称”>
<intent-filter><action android:name=”android.provider.Telephony.SMS_RECEIVED”/></intent-filter></receiver>
在AndroidManifest.xml文件中添加以下权限:
<uses-permission android:name=”android.permission.RECEIVE_SMS”/><!– 接收短信权限 –>
<uses-permission android:name=”android.permission.SEND_SMS”/><!– 发送短信权限 –>

广播接收者的响应性

在Android中,每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法, onReceive() 方法执行完后,BroadcastReceiver 的实例就会被销毁。当onReceive() 方法在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)错误对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成。这里不能使用子线程来解决,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束BroadcastReceiver就先结束了。BroadcastReceiver一旦结束,此时BroadcastReceiver所在的进程很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。如果它的所在进程被杀死,那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的。

public class IncomingSMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
//发送Intent启动服务,由服务来完成比较耗时的操作
Intent service = new Intent(context, XxxService.class);
context.startService(service);
}

除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。
接收电池电量变化广播Intent ,在AndroidManifest.xml文件中的<application>节点里订阅此Intent:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.intent.action.BATTERY_CHANGED”/>
</intent-filter>
</receiver>

接收开机启动广播Intent,在AndroidManifest.xml文件中的<application>节点里订阅此Intent:
<receiver android:name=”.IncomingSMSReceiver”>
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED”/>
</intent-filter>
</receiver>
并且要进行权限声明:
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>

广播接收者补充

广播分两种
有序广播 按照广播的优先级 发给相对应的广播接收者-1000-1000 激活广播通过onrecve方法处理
无序广播

有序广播有一个特例
sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);
resultReceiver 广播接受者 如果我们显示的指定了广播接收者
无论如何 都会接受广播 无法通过abortBroadcast();的方法终止广播
比如拨打电话有个out_goingcall 的广播是指定广播接收者的无法通过abortBroadcast()方法终止的,但是是可以将拨打的电话号码数据清空置为null,setResultData(null)就无法拨打电话

 

另外一种特殊的广播sendStickyBroadcast(intent) // 阴魂不散的广播
一般广播事件发送完毕被广播接受者接收到onReceive执行完毕后广播接收者的生命周期就结束了 这个会保持长时间的停留直到广播事件结束完毕
例如系统的Wifi,网卡状态的改变要一定的时间,保证网络状态更新完毕后才结束

代码中注册,如果代码没有执行,就接受不到广播事件

什么时候使用广播,例如sdcard新增图片的时候是无法显示到图库的当sdcard被挂载状态发生改变才会重新加载sdcard的数据

这时可以发送一个sd挂载的通知,通知系统的gallery去获取到新的图片.

Intent intent = newIntent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse(“file://”+Environment.getExternalStorageDirectory()));

sendBroadcast(intent);

Anroid https代码示例

https与http的通信,在我看来主要的区别在于https多了一个安全验证机制,而Android采用的是X509验证,首先我们需要这重写X509类,建立我们的验证规则、、不过对于特定的项目,我们一般都是无条件信任服务端的,因此我们可以对任何证书都无条件信任(其实本质上我们只是信任了特定url的证书,为了偷懒,才那么选择的)/**

 * 信任所有主机-对于任何证书都不做检查
 */
class MytmArray implements X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() {
        // return null;
        return new X509Certificate[] {};
    }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
    }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
        // System.out.println("cert: " + chain[0].toString() + ", authType: "
        // + authType);
    }
};

好了,我们写好了信任规则,接下载就要创建一个主机的信任列表

static TrustManager[] xtmArray = new MytmArray[] { new MytmArray() };
    /**
     * 信任所有主机-对于任何证书都不做检查
     */
    private static void trustAllHosts() {
        // Create a trust manager that does not validate certificate chains
        // Android 采用X509的证书信息机制
        // Install the all-trusting trust manager
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, xtmArray, new java.security.SecureRandom());
            HttpsURLConnection
                    .setDefaultSSLSocketFactory(sc.getSocketFactory());
            // HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);//
            // 不进行主机名确认
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            // TODO Auto-generated method stub
            // System.out.println("Warning: URL Host: " + hostname + " vs. "
            // + session.getPeerHost());
            return true;
        }
    };

上面的都是https通信需要做的几个基本要求,接下载我们要做的就是https的使用啦下面就以get和post为例进行说明,中间还涉及到cookie的使用

String httpUrl="XXXXX"
String result = "";
        HttpURLConnection http = null;
        URL url;
        try {
            url = new URL(httpUrl);
            // 判断是http请求还是https请求
            if (url.getProtocol().toLowerCase().equals("https")) {
                trustAllHosts();
                http = (HttpsURLConnection) url.openConnection();
                ((HttpsURLConnection) http).setHostnameVerifier(DO_NOT_VERIFY);// 不进行主机名确认
            } else {
                http = (HttpURLConnection) url.openConnection();
            }
            http.setConnectTimeout(10000);// 设置超时时间
            http.setReadTimeout(50000);
            http.setRequestMethod("GET");// 设置请求类型为
            http.setDoInput(true);
            http.setRequestProperty("Content-Type", "text/xml");
//http.getResponseCode());http或https返回状态200还是403
BufferedReader in = null;
            if (obj.getHttpStatus() == 200) {
                getCookie(http);
                in = new BufferedReader(new InputStreamReader(
                        http.getInputStream()));
            } else
                in = new BufferedReader(new InputStreamReader(
                        http.getErrorStream()));
            result = in.readLine();
            Log.i("result", result);
            in.close();
            http.disconnect();
 https或http的get请求写好了,哦中间涉及到了一个getCookie的方法,如下:
Java代码  收藏代码
/** 得到cookie */
    private static void getCookie(HttpURLConnection http) {
        String cookieVal = null;
        String key = null;
        DataDefine.mCookieStore = "";
        for (int i = 1; (key = http.getHeaderFieldKey(i)) != null; i++) {
            if (key.equalsIgnoreCase("set-cookie")) {
                cookieVal = http.getHeaderField(i);
                cookieVal = cookieVal.substring(0, cookieVal.indexOf(";"));
                DataDefine.mCookieStore = DataDefine.mCookieStore + cookieVal
                        + ";";
            }
        }
    }
public static Query HttpQueryReturnClass(String httpUrl, String base64) {

Java代码  收藏代码
        String result = "";
        Log.i("控制", httpUrl);
        Query obj = new Query();
        HttpURLConnection http = null;
        URL url;
        try {
            url = new URL(httpUrl);
            // 判断是http请求还是https请求
            if (url.getProtocol().toLowerCase().equals("https")) {
                trustAllHosts();
                http = (HttpsURLConnection) url.openConnection();
                ((HttpsURLConnection) http).setHostnameVerifier(DO_NOT_VERIFY);// 不进行主机名确认
            } else {
                http = (HttpURLConnection) url.openConnection();
            }
            http.setConnectTimeout(10000);// 设置超时时间
            http.setReadTimeout(50000);
            http.setRequestMethod("POST");// 设置请求类型为post
            http.setDoInput(true);
            http.setDoOutput(true);
            http.setRequestProperty("Content-Type", "text/xml");
            http.setRequestProperty("Cookie", DataDefine.mCookieStore);
            DataOutputStream out = new DataOutputStream(http.getOutputStream());
            out.writeBytes(base64);
            out.flush();
            out.close();
            obj.setHttpStatus(http.getResponseCode());// 设置http返回状态200还是403
            BufferedReader in = null;
            if (obj.getHttpStatus() == 200) {
                getCookie(http);
                in = new BufferedReader(new InputStreamReader(
                        http.getInputStream()));
            } else
                in = new BufferedReader(new InputStreamReader(
                        http.getErrorStream()));
            result = in.readLine();// 得到返回结果
            in.close();
            http.disconnect();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

这里面的base64是我经过base64加密过以后的数据

Android获取网络状态及类型

代码示例


package com.example.vdisktest;  
  
import android.app.Activity;  
import android.content.Context;  
import android.database.Cursor;  
import android.net.ConnectivityManager;  
import android.net.NetworkInfo;  
import android.net.Uri;  
import android.os.Bundle;  
import android.telephony.TelephonyManager;  
import android.text.TextUtils;  
import android.util.Log;  
  
public class NetTypeActivity extends Activity {  
  
    /** Called when the activity is first created. */  
    public static final String CTWAP = "ctwap";  
    public static final String CTNET = "ctnet";  
    public static final String CMWAP = "cmwap";  
    public static final String CMNET = "cmnet";  
    public static final String NET_3G = "3gnet";  
    public static final String WAP_3G = "3gwap";  
    public static final String UNIWAP = "uniwap";  
    public static final String UNINET = "uninet";  
  
    public static final int TYPE_CT_WAP = 5;  
    public static final int TYPE_CT_NET = 6;  
    public static final int TYPE_CT_WAP_2G = 7;  
    public static final int TYPE_CT_NET_2G = 8;  
  
    public static final int TYPE_CM_WAP = 9;  
    public static final int TYPE_CM_NET = 10;  
    public static final int TYPE_CM_WAP_2G = 11;  
    public static final int TYPE_CM_NET_2G = 12;  
  
    public static final int TYPE_CU_WAP = 13;  
    public static final int TYPE_CU_NET = 14;  
    public static final int TYPE_CU_WAP_2G = 15;  
    public static final int TYPE_CU_NET_2G = 16;  
  
    public static final int TYPE_OTHER = 17;  
  
    public static Uri PREFERRED_APN_URI = Uri  
            .parse("content://telephony/carriers/preferapn");  
  
    /** 没有网络 */  
    public static final int TYPE_NET_WORK_DISABLED = 0;  
  
    /** wifi网络 */  
    public static final int TYPE_WIFI = 4;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        long start = System.currentTimeMillis();  
        int checkNetworkType = checkNetworkType(this);  
        Log.i("NetType""===========elpase:"  
                + (System.currentTimeMillis() - start));  
  
        switch (checkNetworkType) {  
        case TYPE_WIFI:  
            Log.i("NetType""================wifi");  
            break;  
        case TYPE_NET_WORK_DISABLED:  
            Log.i("NetType""================no network");  
            break;  
        case TYPE_CT_WAP:  
            Log.i("NetType""================ctwap");  
            break;  
        case TYPE_CT_WAP_2G:  
            Log.i("NetType""================ctwap_2g");  
            break;  
        case TYPE_CT_NET:  
            Log.i("NetType""================ctnet");  
            break;  
        case TYPE_CT_NET_2G:  
            Log.i("NetType""================ctnet_2g");  
            break;  
        case TYPE_CM_WAP:  
            Log.i("NetType""================cmwap");  
            break;  
        case TYPE_CM_WAP_2G:  
            Log.i("NetType""================cmwap_2g");  
            break;  
        case TYPE_CM_NET:  
            Log.i("NetType""================cmnet");  
            break;  
        case TYPE_CM_NET_2G:  
            Log.i("NetType""================cmnet_2g");  
            break;  
        case TYPE_CU_NET:  
            Log.i("NetType""================cunet");  
            break;  
        case TYPE_CU_NET_2G:  
            Log.i("NetType""================cunet_2g");  
            break;  
        case TYPE_CU_WAP:  
            Log.i("NetType""================cuwap");  
            break;  
        case TYPE_CU_WAP_2G:  
            Log.i("NetType""================cuwap_2g");  
            break;  
        case TYPE_OTHER:  
            Log.i("NetType""================other");  
            break;  
        default:  
            break;  
        }  
    }  
  
    /*** 
     * 判断Network具体类型(联通移动wap,电信wap,其他net) 
     *  
     * */  
    public static int checkNetworkType(Context mContext) {  
        try {  
            final ConnectivityManager connectivityManager = (ConnectivityManager) mContext  
                    .getSystemService(Context.CONNECTIVITY_SERVICE);  
            final NetworkInfo mobNetInfoActivity = connectivityManager  
                    .getActiveNetworkInfo();  
            if (mobNetInfoActivity == null || !mobNetInfoActivity.isAvailable()) {  
                // 注意一:  
                // NetworkInfo 为空或者不可以用的时候正常情况应该是当前没有可用网络,  
                // 但是有些电信机器,仍可以正常联网,  
                // 所以当成net网络处理依然尝试连接网络。  
                // (然后在socket中捕捉异常,进行二次判断与用户提示)。  
                return TYPE_NET_WORK_DISABLED;  
            } else {  
                // NetworkInfo不为null开始判断是网络类型  
                int netType = mobNetInfoActivity.getType();  
                if (netType == ConnectivityManager.TYPE_WIFI) {  
                    // wifi net处理  
                    return TYPE_WIFI;  
                } else if (netType == ConnectivityManager.TYPE_MOBILE) {  
                    // 注意二:  
                    // 判断是否电信wap:  
                    // 不要通过getExtraInfo获取接入点名称来判断类型,  
                    // 因为通过目前电信多种机型测试发现接入点名称大都为#777或者null,  
                    // 电信机器wap接入点中要比移动联通wap接入点多设置一个用户名和密码,  
                    // 所以可以通过这个进行判断!  
  
                    boolean is3G = isFastMobileNetwork(mContext);  
  
                    final Cursor c = mContext.getContentResolver().query(  
                            PREFERRED_APN_URI, nullnullnullnull);  
                    if (c != null) {  
                        c.moveToFirst();  
                        final String user = c.getString(c  
                                .getColumnIndex("user"));  
                        if (!TextUtils.isEmpty(user)) {  
                            if (user.startsWith(CTWAP)) {  
                                return is3G ? TYPE_CT_WAP : TYPE_CT_WAP_2G;  
                            } else if (user.startsWith(CTNET)) {  
                                return is3G ? TYPE_CT_NET : TYPE_CT_NET_2G;  
                            }  
                        }  
                    }  
                    c.close();  
  
                    // 注意三:  
                    // 判断是移动联通wap:  
                    // 其实还有一种方法通过getString(c.getColumnIndex("proxy")获取代理ip  
                    // 来判断接入点,10.0.0.172就是移动联通wap,10.0.0.200就是电信wap,但在  
                    // 实际开发中并不是所有机器都能获取到接入点代理信息,例如魅族M9 (2.2)等...  
                    // 所以采用getExtraInfo获取接入点名字进行判断  
  
                    String netMode = mobNetInfoActivity.getExtraInfo();  
                    Log.i("""==================netmode:" + netMode);  
                    if (netMode != null) {  
                        // 通过apn名称判断是否是联通和移动wap  
                        netMode = netMode.toLowerCase();  
  
                        if (netMode.equals(CMWAP)) {  
                            return is3G ? TYPE_CM_WAP : TYPE_CM_WAP_2G;  
                        } else if (netMode.equals(CMNET)) {  
                            return is3G ? TYPE_CM_NET : TYPE_CM_NET_2G;  
                        } else if (netMode.equals(NET_3G)  
                                || netMode.equals(UNINET)) {  
                            return is3G ? TYPE_CU_NET : TYPE_CU_NET_2G;  
                        } else if (netMode.equals(WAP_3G)  
                                || netMode.equals(UNIWAP)) {  
                            return is3G ? TYPE_CU_WAP : TYPE_CU_WAP_2G;  
                        }  
                    }  
                }  
            }  
  
        } catch (Exception ex) {  
            ex.printStackTrace();  
            return TYPE_OTHER;  
        }  
  
        return TYPE_OTHER;  
  
    }  
  
    private static boolean isFastMobileNetwork(Context context) {  
        TelephonyManager telephonyManager = (TelephonyManager) context  
                .getSystemService(Context.TELEPHONY_SERVICE);  
  
        switch (telephonyManager.getNetworkType()) {  
        case TelephonyManager.NETWORK_TYPE_1xRTT:  
            return false// ~ 50-100 kbps  
        case TelephonyManager.NETWORK_TYPE_CDMA:  
            return false// ~ 14-64 kbps  
        case TelephonyManager.NETWORK_TYPE_EDGE:  
            return false// ~ 50-100 kbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_0:  
            return true// ~ 400-1000 kbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_A:  
            return true// ~ 600-1400 kbps  
        case TelephonyManager.NETWORK_TYPE_GPRS:  
            return false// ~ 100 kbps  
        case TelephonyManager.NETWORK_TYPE_HSDPA:  
            return true// ~ 2-14 Mbps  
        case TelephonyManager.NETWORK_TYPE_HSPA:  
            return true// ~ 700-1700 kbps  
        case TelephonyManager.NETWORK_TYPE_HSUPA:  
            return true// ~ 1-23 Mbps  
        case TelephonyManager.NETWORK_TYPE_UMTS:  
            return true// ~ 400-7000 kbps  
        case TelephonyManager.NETWORK_TYPE_EHRPD:  
            return true// ~ 1-2 Mbps  
        case TelephonyManager.NETWORK_TYPE_EVDO_B:  
            return true// ~ 5 Mbps  
        case TelephonyManager.NETWORK_TYPE_HSPAP:  
            return true// ~ 10-20 Mbps  
        case TelephonyManager.NETWORK_TYPE_IDEN:  
            return false// ~25 kbps  
        case TelephonyManager.NETWORK_TYPE_LTE:  
            return true// ~ 10+ Mbps  
        case TelephonyManager.NETWORK_TYPE_UNKNOWN:  
            return false;  
        default:  
            return false;  
  
        }  
    }  
  
}  

 

android如何让service不被杀死-提高进程优先级

1.在service中重写下面的方法,这个方法有三个返回值, START_STICKY是service被kill掉后自动重写创建

[代码]java代码:

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

return START_STICKY;

}—————-

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// TODO Auto-generated method stub

Log.v(“TrafficService”,”startCommand”);

flags = START_STICKY;

return super.onStartCommand(intent, flags, startId);

// return START_REDELIVER_INTENT;

}

2.在Service的onDestroy()中重启Service.

public void onDestroy() {

Intent localIntent = new Intent();

localIntent.setClass(this, MyService.class); //销毁时重新启动Service

this.startService(localIntent);

}

用qq管家杀掉进程的时候,调用的是系统自带的强制kill功能(即settings里的),在kill时,会将应用的整个进程停掉,当然包括service在内,如果在running里将service强制kill掉,显示进程还在。不管是kill整个进程还是只kill掉进应用的 service,都不会重新启动service。不知道你是怎么实现重启的,实在是不解。 在eclipse中,用stop按钮kill掉进程的时候,倒是会重启service

KILL问题:

1. settings 中stop service

onDestroy方法中,调用startService进行Service的重启。

2.settings中force stop 应用

捕捉系统进行广播(action为android.intent.action.PACKAGE_RESTARTED)

3. 借助第三方应用kill掉running task

提升service的优先级

service开机启动

今天我们主要来探讨android怎么让一个service开机自动启动功能的实现。Android手机在启动的过程中会触发一个Standard Broadcast Action,名字叫android.intent.action.BOOT_COMPLETED(记得只会触发一次呀),在这里我们可以通过构建一个广播接收者来接收这个这个action.下面我就来简单写以下实现的步骤:

第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(Context context, Intent intent),在其中启动你想要启动的Service或app。

[代码]java代码:

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.util.Log;

public class BootBroadcastReceiver extends BroadcastReceiver {

//重写onReceive方法

@Override

public void onReceive(Context context, Intent intent) {

//后边的XXX.class就是要启动的服务

Intent service = new Intent(context,XXXclass);

context.startService(service);

Log.v(“TAG”, “开机自动服务自动启动…..”);

//启动应用,参数为需要自动启动的应用的包名

Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);

context.startActivity(intent );

}

}

第二步:配置xml文件,在re

ceiver接收这种添加intent-filter配置

[代码]java代码:

<receiver android:name=”BootBroadcastReceiver”>

<intent-filter>

<action android:name=”android.intent.action.BOOT_COMPLETED”></action>

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</receiver>

第三步:添加权限 <uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED” />

如何实现一个不会被杀死的进程

看Android的文档知道,当进程长期不活动,或系统需要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。

但是如果某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),应该怎么做,才能使进程不被杀死。

add
android:persistent=”true” into the <application> section in your AndroidManifest.xml

切记,这个
不可滥用,系统中用这个的service,app一多,整个系统就完蛋了。

目前系统中有phone等非常有限的,必须一直活着的应用在试用。

提升service优先级的方法

Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保 证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

从Android 1.5开始,一个已启动的service可以调用startForeground(int, Notification)将service置为foreground状态,调用stopForeground(boolean)将service置为 background状态。

我们会在调用startForeground(int, Notification)传入参数notification,它会在状态栏里显示正在进行的foreground service。background service不会在状态栏里显示。

在Android 1.0中,将一个service置为foreground状态:

setForeground(true);

mNM.notify(id, notification);

将一个service置为background状态:

mNM.cancel(id);

setForeground(false);

对比看出,在1.0 API中调用setForeground(boolean)只是简单的改变service的状态,用户不会有任何觉察。新API中强制将 notification和改变service状态的动作绑定起来,foreground service会在状态栏显示,而background service不会。

Remote service controller & binding

跨进程调用Service。暂时不研究。

如何防止Android应用中的Service被系统回收? 很多朋友都在问,如何防止Android应用中的Service被系统回收?下面简单解答一下。

对于Service被系统回收,一般做法是通过提高优先级可以解决,在AndroidManifest.xml文件中对于intent-filter可以通过
android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播,推荐大家如果你的应用很重要,可以考虑通过系统常用intent action来触发。

 

 

 

下面这是从另外的网页上看到的,一并转过来了:http://goo.gl/eEfBup

为了提高 我们的Activity中的线程的
线程优先级(Thread-Priority),我们需要在AndroidManifest.xml 中使用 ‘uses-permission’ 这样做:
XML: 
          <uses-permission id=”android.permission.RAISED_THREAD_PRIORITY”/>

  现在你可以在你的Activity中使用以下代码改变或提高任何线程的优先级:
Java: 
          import android.os.Process;
// …

// ———————————–
// Set the priority of the calling thread, based on Linux priorities:
// ———————————–

// Changes the Priority of the calling Thread!
Process.setThreadPriority(12);
// Changes the Priority of passed Thread (first param)
Process.setThreadPriority(Process.myTid(), 12);

  这里 range 的范围是 -20 (高) 到 +19 (低). 不要选得 太高  

  最好使用预先定义在 android.os.Process 的constants :
Java: 
          // Lower is ‘more impotant’
Process.THREAD_PRIORITY_LOWEST = 19
Process.THREAD_PRIORITY_BACKGROUND = 5
Process.THREAD_PRIORITY_DEFAULT = 0
Process.THREAD_PRIORITY_FOREGROUND = -5
Process.THREAD_PRIORITY_DISPLAY = -10
Process.THREAD_PRIORITY_URGENT_DISPLAY = -15

Android四大基本组件介绍与生命周期

Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。

一:了解四大基本组件

Activity :

应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。

Activity之间通过Intent进行通信。在Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。

典型的动作类型有:M AIN(activity的门户)、VIEW、PICK、EDIT 等。而动作对应的数据则以URI 的形式进行表示。例如:要查看一个人的联系方式,你需要创建一个动作类型为VIEW 的intent,以及一个表示这个人的URI。

与之有关系的一个类叫IntentFilter。相对于intent 是一个有效的做某事的请求,一个intentfilter 则用于描述一个activity(或者IntentReceiver)能够操作哪些intent。一个activity 如果要显示一个人的联系方式时,需要声明一个IntentFilter,这个IntentFilter 要知道怎么去处理VIEW 动作和表示一个人的URI。IntentFilter 需要在AndroidManifest.xml 中定义。通过解析各种intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,activity 将会调用startActivity(Intent myIntent)方法。然后,系统会在所有安装的应用程序中定义的IntentFilter 中查找,找到最匹配myIntent 的Intent 对应的activity。新的activity 接收到myIntent 的通知后,开始运行。当startActivity 方法被调用将触发解析myIntent 的动作,这个机制提供了两个关键好处:

A、Activities 能够重复利用从其它组件中以Intent 的形式产生的一个请求;

B、Activities 可以在任何时候被一个具有相同IntentFilter 的新的Activity 取代。

AndroidManifest文件中含有如下过滤器的Activity组件为默认启动类当程序启动时系统自动调用它

<intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

BroadcastReceive广播接收器:

你的应用可以使用它对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice 来响应它们收到的信息,或者用NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

广播类型:

普通广播通过Context.sendBroadcast(Intent myIntent)发送的

有序广播通过Context.sendOrderedBroadcast(intent, receiverPermission)发送的,该方法第2个参数决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 发送的优先级越高;广播接收者接收广播时的级别级别(可通过intentfilter中的priority进行设置设为2147483647时优先级最高),同级别接收的先后是随机的, 再到级别低的收到广播,高级别的或同级别先接收到广播的可以通过abortBroadcast()方法截断广播使其他的接收者无法收到该广播,还有其他构造函数

异步广播通过Context.sendStickyBroadcast(Intent myIntent)发送的,还有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,  initialCode, initialData, initialExtras)方法,该方法具有有序广播的特性也有异步广播的特性;发送异步广播要: <uses-permission android:name=“android.permission.BROADCAST_STICKY” />权限,接收并处理完Intent后,广播依然存在,直到你调用removeStickyBroadcast(intent)主动把它去掉

注意:发送广播时的intent参数与Contex.startActivity()启动起来的Intent不同,前者可以被多个订阅它的广播接收器调用,后者只能被一个(Activity或service)调用

监听广播Intent步骤:

1>             写一个继承BroadCastReceiver的类,重写onReceive()方法,广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态,注意:为了保证用户交互过程的流畅,一些费时的操作要放到线程里,如类名SMSBroadcastReceiver

2>            注册该广播接收者,注册有两种方法程序动态注册和AndroidManifest文件中进行静态注册(可理解为系统中注册)如下:

        静态注册,注册的广播,下面的priority表示接收广播的级别”2147483647″为最高优先级

<receiver android:name=".SMSBroadcastReceiver" >
  <intent-filter android:priority = "2147483647" >
    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  </intent-filter>
</receiver >

动态注册,一般在Activity可交互时onResume()内注册BroadcastReceiver

IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(mBatteryInfoReceiver ,intentFilter);

//反注册
unregisterReceiver(receiver);

注意:

1.生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的

2. 动态注册广播接收器还有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用

系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播

Service 服务:

一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。

比较好的一个例子就是一个正在从播放列表中播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的activity,因为使用者当然会认为在导航到其它屏幕时音乐应该还在播放的。在这个例子中,媒体播放器这个activity 会使用Context.startService()来启动一个service,从而可以在后台保持音乐的播放。同时,系统也将保持这个service 一直执行,直到这个service 运行结束。另外,我们还可以通过使用Context.bindService()方法,连接到一个service 上(如果这个service 还没有运行将启动它)。当连接到一个service 之后,我们还可以service 提供的接口与它进行通讯。拿媒体播放器这个例子来说,我们还可以进行暂停、重播等操作。

Service使用步骤如下

       1>继承service类

       2>AndroidManifast.xml配置清单文件中<application>节点里对服务进行配置

              <service name=”.SMSService”/>

服务不能自己运行,需要通过Contex.startService()或Contex.bindService()启动服务

通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService(),此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onStart(),如果服务已经启动再次调用只会触发onStart()方法

使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onBind(),如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()–>onDestory(),想主动解除绑定可使用Contex.unbindService(),系统依次调用onUnbind()–>onDestory();

Content Provider内容提供者 :

android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式,

其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳),

只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中

它的好处:统一数据访问方式。

android系统自带的内容提供者(顶级的表示数据库名,非顶级的都是表名)这些内容提供者在SDK文档的android.provider Java包中都有介绍。见:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser

├────CallLog

├────Contacts

│                ├────Groups

│                ├────People

│                ├────Phones

│                └────Photos

├────Images

│                └────Thumbnails

├────MediaStore

│                ├────Albums

│                ├────Artists

│                ├────Audio

│                ├────Genres

│                └────Playlists

├────Settings

└────Video

 CallLog:地址和接收到的电话信息

 Contact.People.Phones:存储电话号码

 Setting.System:系统设置和偏好设置

使用Content Provider对外共享数据的步骤

1>继承ContentProvider类并根据需求重写以下方法:

复制代码
    public boolean onCreate();//处理初始化操作

       /**
        * 插入数据到内容提供者(允许其他应用向你的应用中插入数据时重写)
        * @param uri
        * @param initialValues 插入的数据
        * @return
        */
       public Uri insert(Uri uri, ContentValues initialValues);

       /**
        * 从内容提供者中删除数据(允许其他应用删除你应用的数据时重写)
        * @param uri
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @return
        */
       public int delete(Uri uri, String selection, String[] selectionArgs);

       /**
        * 更新内容提供者已存在的数据(允许其他应用更新你应用的数据时重写)
        * @param uri
        * @param values 更新的数据
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @return
        */
       public int update(Uri uri, ContentValues values, String selection,
                     String[] selectionArgs);

       /**
        * 返回数据给调用者(允许其他应用从你的应用中获取数据时重写)
        * @param uri
        * @param projection 列名
        * @param selection 条件语句
        * @param selectionArgs 参数
        * @param sortOrder 排序
        * @return
        */
       public Cursor query(Uri uri, String[] projection, String selection,
                     String[] selectionArgs, String sortOrder) ;         

       /**
        * 用于返回当前Uri所代表数据的MIME类型
        * 如果操作的数据为集合类型(多条数据),那么返回的类型字符串应该为vnd.android.cursor.dir/开头
        * 例如要得到所有person记录的Uri为content://com.bravestarr.provider.personprovider/person,
     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.dir/person"
        * 如果操作的数据为单一数据,那么返回的类型字符串应该为vnd.android.cursor.item/开头
        * 例如要得到id为10的person记录的Uri为content://com.bravestarr.provider.personprovider/person/10,
     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.item/person"
        * @param uri
        */
       public String getType(Uri uri)
复制代码

这些方法中的Uri参数,得到后需要进行解析然后做对应处理,Uri表示要操作的数据,包含两部分信息:

       1.需要操作的contentprovider

       2.对contentprovider中的什么数据进行操作,一个Uri格式:结构头://authorities(域名)/路径(要操作的数据,根据业务而定)

              content://com.bravestarr.provider.personprovider/person/10

说明:contentprovider的结构头已经由android规定为content://

authorities用于唯一标识这个contentprovider程序,外部调用者可以根据这个找到他

路径表示我们要操作的数据,路径的构建根据业务而定.路径格式如下:

       要操作person表行号为10的记录,可以这样构建/person/10

       要操作person表的所有记录,可以这样构建/person

2>在AndroidManifest.xml中使用<provider>对ContentProvider进行配置注册(内容提供者注册它自己就像网站注册域名),ContentProvider采用authoritie(原意授权,可理解为域名)作为唯一标识,方便其他应用能找到

复制代码
<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <!-- authorities属性命名建议:公司名.provider.SomeProvider-->
        <provider android:name=".PersonProvider" android:authorities="com.bravestarr.provider.personprovider"/>
         ...
</application>
复制代码

关于四大基本组件的一个总结:

1>    4大组件的注册

4大基本组件都需要注册才能使用,每个Activity、service、Content Provider内容提供者都需要在AndroidManifest文件中进行配置AndroidManifest文件中未进行声明的activity、服务以及内容提供者将不为系统所见,从而也就不可用,而BroadcastReceive广播接收者的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)

AndroidManifest文件中进行注册格式如下:

<activity>元素的name 属性指定了实现了这个activity 的Activity 的子类。icon 和label 属性指向了包含展示给用户的此activity 的图标和标签的资源文件。

<service> 元素用于声明服务

<receiver> 元素用于声明广播接收器

<provider> 元素用于声明内容提供者

2>   4大组件的激活

• 容提供者的激活:当接收到ContentResolver 发出的请求后,内容提供者被激活。而其它三种组件──activity、服务和广播接收器被一种叫做intent 的异步消息所激活

• Activity的激活通过传递一个Intent 对象至Context.startActivity()或Activity.startActivityForResult()以载入(或指定新工作给)一个activity。相应的activity 可以通过调用getIntent() 方法来查看激活它的intent。如果它期望它所启动的那个activity 返回一个结果,它会以调用startActivityForResult()来取代startActivity()。比如说,如果它启动了另外一个Activity 以使用户挑选一张照片,它也许想知道哪张照片被选中了。结果将会被封装在一个Intent 对象中,并传递给发出调用的activity 的onActivityResult() 方法。

• 服务的激活可以通过传递一个Intent 对象至Context.startService()或Context.bindService()前者Android 调用服务的onStart()方法并将Intent 对象传递给它,后者Android 调用服务的onBind()方法将这个Intent 对象传递给它

• 发送广播可以通过传递一个Intent 对象至给Context.sendBroadcast() 、

Context.sendOrderedBroadcast()或Context.sendStickyBroadcast()Android 会调用所有对此广播有兴趣的广播接收器的onReceive()方法,将intent 传递给它们

3>   四大组件的关闭

内容提供者仅在响应ContentResolver 提出请求的时候激活。而一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。

Activity关闭:可以通过调用它的finish()方法来关闭一个activity

服务关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService ()方法关闭服务

二:四大组件的生命周期

     介绍生命周期之前,先提一下任务的概念

任务其实就是activity 的栈它由一个或多个Activity组成的共同完成一个完整的用户体验, 换句话说任务就是” 应用程序” (可以是一个也可以是多个,比如假设你想让用户看到某个地方的街道地图。而已经存在一个具有此功能的activity 了,那么你的activity 所需要做的工作就是把请求信息放到一个Intent 对象里面,并把它传递给startActivity()。于是地图浏览器就会显示那个地图。而当用户按下BACK 键的时候,你的activity 又会再一次的显示在屏幕上,此时任务是由2个应用程序中的相关activity组成的)栈底的是启动整个任务的Activity,栈顶的是当前运行的用户可以交互的Activity,当一个activity 启动另外一个的时候,新的activity 就被压入栈,并成为当前运行的activity。而前一个activity 仍保持在栈之中。当用户按下BACK 键的时候,当前activity 出栈,而前一个恢复为当前运行的activity。栈中保存的其实是对象,栈中的Activity 永远不会重排,只会压入或弹出,所以如果发生了诸如需要多个地图浏览器的情况,就会使得一个任务中出现多个同一Activity 子类的实例同时存在。

任务中的所有activity 是作为一个整体进行移动的。整个的任务(即activity 栈)可以移到前台,或退至后台。举个例子说,比如当前任务在栈中存有四个activity──三个在当前activity 之下。当用户按下HOME 键的时候,回到了应用程序加载器,然后选择了一个新的应用程序(也就是一个新任务)。则当前任务遁入后台,而新任务的根activity 显示出来。然后,过了一小会儿,用户再次回到了应用程序加载器而又选择了前一个应用程序(上一个任务)。于是那个任务,带着它栈中所有的四个activity,再一次的到了前台。当用户按下BACK 键的时候,屏幕不会显示出用户刚才离开的activity(上一个任务的根

activity)。取而代之,当前任务的栈中最上面的activity 被弹出,而同一任务中的上一个activity 显示了出来。

Activity栈:先进后出规则

                                                   

Android系统是一个多任务(Multi-Task)的操作系统,可以在用手机听音乐的同时,也执行其他多个程序。每多执行一个应用程序,就会多耗费一些系统内存,当同时执行的程序过多,或是关闭的程序没有正确释放掉内存,系统就会觉得越来越慢,甚至不稳定。

为了解决这个问题, Android 引入了一个新的机制– 生命周期(Life Cycle)。

Android 应用程序的生命周期是由Android 框架进行管理,而不是由应用程序直接控

制。通常,每一个应用程序(入口一般会是一个Activity 的onCreate 方法),都会产生

一个进程(Process)。当系统内存即将不足的时候,会依照优先级自动进行进程(process)的回收。不管是使用者或开发者, 都无法确定的应用程序何时会被回收。所以为了很好的防止数据丢失和其他问题,了解生命周期很重要。

Activity生命周期

                                                          

图3.1activity生命周期图

Activity整个生命周期的4种状态、7个重要方法和3个嵌套循环

1>   四种状态

  1.       活动(Active/Running)状态

当Activity运行在屏幕前台(处于当前任务活动栈的最上面),此时它获取了焦点能响应用户的操作,属于运行状态,同一个时刻只会有一个Activity 处于活动(Active)或运行

(Running)状态

  1.     暂停(Paused)状态

当Activity失去焦点但仍对用户可见(如在它之上有另一个透明的Activity或Toast、AlertDialog等弹出窗口时)它处于暂停状态。暂停的Activity仍然是存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接),但是当系统内存极小时可以被系统杀掉

3.      停止(Stopped)状态

完全被另一个Activity遮挡时处于停止状态,它仍然保留着所有的状态和成员信息。只是对用户不可见,当其他地方需要内存时它往往被系统杀掉

4.      非活动(Dead)状态

Activity 尚未被启动、已经被手动终止,或已经被系统回收时处于非活动的状态,要手动终止Activity,可以在程序中调用”finish”方法。

如果是(按根据内存不足时的回收规则)被系统回收,可能是因为内存不足了

内存不足时,Dalvak 虚拟机会根据其内存回收规则来回收内存:

      1. 先回收与其他Activity 或Service/Intent Receiver 无关的进程(即优先回收独

立的Activity)因此建议,我们的一些(耗时)后台操作,最好是作成Service的形式

      2.不可见(处于Stopped状态的)Activity

      3.Service进程(除非真的没有内存可用时会被销毁)

      4.非活动的可见的(Paused状态的)Activity

      5.当前正在运行(Active/Running状态的)Activity

 

2>  7个重要方法,当Activity从一种状态进入另一状态时系统会自动调用下面相应的方

法来通知用户这种变化

当Activity第一次被实例化的时候系统会调用,

整个生命周期只调用1次这个方法

通常用于初始化设置: 1、为Activity设置所要使用的布局文件2、为按钮绑定监听器等静态的设置操作

onCreate(Bundle savedInstanceState);

 

当Activity可见未获得用户焦点不能交互时系统会调用

onStart();

 

当Activity已经停止然后重新被启动时系统会调用

onRestart();

当Activity可见且获得用户焦点能交互时系统会调用

      onResume();

当系统启动另外一个新的Activity时,在新Activity启动之前被系统调用保存现有的Activity中的持久数据、停止动画等,这个实现方法必须非常快。当系统而不是用户自己出于回收内存时,关闭了activity 之后。用户会期望当他再次回到这个activity 的时候,它仍保持着上次离开时的样子。此时用到了onSaveInstanceState(),方法onSaveInstanceState()用来保存Activity被杀之前的状态,在onPause()之前被触发,当系统为了节省内存销毁了Activity(用户本不想销毁)时就需要重写这个方法了,当此Activity再次被实例化时会通过onCreate(Bundle savedInstanceState)将已经保存的临时状态数据传入因为onSaveInstanceState()方法不总是被调用,触发条件为(按下HOME键,按下电源按键关闭屏幕,横竖屏切换情况下),你应该仅重写onSaveInstanceState()来记录activity的临时状态,而不是持久的数据。应该使用onPause()来存储持久数据。

      onPause();

当Activity被新的Activity完全覆盖不可见时被系统调用

      onStop();

当Activity(用户调用finish()或系统由于内存不足)被系统销毁杀掉时系统调用,(整个生命周期只调用1次)用来释放onCreate ()方法中创建的资源,如结束线程等

      onDestroy();

3>  3个嵌套循环

             1.Activity完整的生命周期:从第一次调用onCreate()开始直到调用onDestroy()结束

             2.Activity的可视生命周期:从调用onStart()到相应的调用onStop()

                    在这两个方法之间,可以保持显示Activity所需要的资源。如在onStart()中注册一个广播接收者监听影响你的UI的改变,在onStop() 中注销。

             3.Activity的前台生命周期:从调用onResume()到相应的调用onPause()。

      举例说明:

例1:有3个Acitivity,分别用One,Two(透明的),Three表示,One是应用启动时的主Activity

      启动第一个界面Activity One时,它的次序是

             onCreate (ONE) – onStart (ONE) – onResume(ONE)

      点”打开透明Activity”按钮时,这时走的次序是

             onPause(ONE) – onCreate(TWO) – onStart(TWO) – onResume(TWO)

      再点back回到第一个界面,Two会被杀这时走的次序是

             onPause(TWO) – onActivityResult(ONE) – onResume(ONE) – onStop(TWO) – onDestroy(TWO)

      点”打开全屏Activity”按钮时,这时走的次序是

             onPause(ONE) – onCreate(Three) – onStart(Three) – onResume(Three) – onStop(ONE)

      再点back回到第一个界面,Three会被杀这时走的次序是

             onPause(Three) – onActivityResult(ONE) – onRestart(ONE) – onStart(ONE)- onResume(ONE) – onStop(Three) – onDestroy(Three)

      再点back退出应用时,它的次序是

             onPause(ONE) – onStop(ONE) – onDestroy(ONE)

例2:横竖屏切换时候Activity的生命周期

他切换时具体的生命周期是怎么样的:

1、新建一个Activity,并把各个生命周期打印出来

2、运行Activity,得到如下信息

onCreate–>
onStart–>
onResume–>

3、按crtl+f12切换成横屏时

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>
onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

5、修改AndroidManifest.xml,把该Activity添加android:configChanges=”orientation”,执行步骤3

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>

6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged

onSaveInstanceState–>
onPause–>
onStop–>
onDestroy–>
onCreate–>
onStart–>
onRestoreInstanceState–>
onResume–>
onConfigurationChanged–>

7、把步骤5的android:configChanges=”orientation” 改成 android:configChanges=”orientation|keyboardHidden”,执行步骤3,就只打印onConfigChanged

onConfigurationChanged–>

8、执行步骤4

onConfigurationChanged–>
onConfigurationChanged–>

总结:

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
总结一下整个Activity的生命周期

补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变

Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState –> onPause –> onStop,再次进入激活状态时: onRestart –>onStart—>onResume

BroadcastReceive广播接收器生命周期

生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息

它的生命周期为从回调onReceive()方法开始到该方法返回结果后结束

Service服务生命周期

                                                            

图3.2service生命周期图

Service完整的生命周期:从调用onCreate()开始直到调用onDestroy()结束

Service有两种使用方法:

1>以调用Context.startService()启动,而以调用Context.stopService()结束

2>以调用Context.bindService()方法建立,以调用Context.unbindService()关闭

service重要的生命周期方法

当用户调用startService ()或bindService()时,Service第一次被实例化的时候系统会调用,整个生命周期只调用1次这个方法,通常用于初始化设置。注意:多次调用startService()或bindService()方法不会多次触发onCreate()方法

void onCreate()

当用户调用stopService()或unbindService()来停止服务时被系统调用,(整个生命周期只调用1次)用来释放onCreate()方法中创建的资源

void onDestroy()

通过startService()方法启动的服务

      初始化结束后系统会调用该方法,用于处理传递给startService()的Intent对象。如音乐服务会打开Intent 来探明将要播放哪首音乐,并开始播放。注意:多次调用startService()方法会多次触发onStart()方法

void onStart(Intent intent)

通过bindService ()方法启动的服务

      初始化结束后系统会调用该方法,用来绑定传递给bindService 的Intent 的对象。注意:多次调用bindService()时,如果该服务已启动则不会再触发此方法

IBinder onBind(Intent intent)

用户调用unbindService()时系统调用此方法,Intent 对象同样传递给该方法

boolean onUnbind(Intent intent)

如果有新的客户端连接至该服务,只有当旧的调用onUnbind()后,新的才会调用该方法

void onRebind(Intent intent)

补充:onCreate(Bundle savedInstanceState)与onSaveInstanceState(Bundle savedInstanceState)配合使用,见如下代码,达到显示activity被系统杀死前的状态

复制代码
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != savedInstanceState) {
            String _userid = savedInstanceState.getString("StrUserId");
            String _uid = savedInstanceState.getString("StrUid");
            String _serverid = savedInstanceState.getString("StrServerId");
            String _servername = savedInstanceState.getString("StrServerName");
            int _rate = savedInstanceState.getInt("StrRate");
            //updateUserId(_userid);
            //updateUId(_uid);
            //updateServerId(_serverid);
            //updateUserServer(_servername);
            //updateRate(_rate);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putString("StrUserId", getUserId());
        savedInstanceState.putString("StrUid", getUId());
        savedInstanceState.putString("StrServerId", getServerId());
        savedInstanceState.putString("StrServerName", getServerName());
        savedInstanceState.putInt("StrRate", getRate());
    }
复制代码

 

引发activity摧毁和重建的其他情形

除了系统处于内存不足的原因会摧毁activity之外, 某些系统设置的改变也会导致activity的摧毁和重建. 例如改变屏幕方向(见上例), 改变设备语言设定, 键盘弹出等.

 

Android应用程序窗口设计框架介绍

 

Android系统中,一个Activity对应一个应用程序窗口,任何一个Activity的启动都是由AMS服务和应用程序进程相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属进程来完成。AMS服务通过realStartActivityLocked函数来通知应用程序进程启动某个Activity:

frameworksbaseservicesjavacomandroidserveram ActivityStack.java

  1. final boolean realStartActivityLocked(ActivityRecord r,
  2.         ProcessRecord app, boolean andResume, boolean checkConfig)
  3.         throws RemoteException {
  4.     …
  5.     //系统参数发送变化,通知Activity
  6.     if (checkConfig) {
  7.         ①Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(mService.mConfiguration,
  8.                 r.mayFreezeScreenLocked(app) ? r.appToken : null);
  9.         mService.updateConfigurationLocked(config, r, falsefalse);
  10.     }
  11.     //将进程描述符设置到启动的Activity描述符中
  12.     r.app = app;
  13.     app.waitingToKill = null;
  14.     //将启动的Activity添加到进程启动的Activity列表中
  15.     int idx = app.activities.indexOf(r);
  16.     if (idx < 0) {
  17.         app.activities.add(r);
  18.     }
  19.     mService.updateLruProcessLocked(app, truetrue);
  20.     try {
  21.         …
  22.         //通知应用程序进程加载Activity
  23.         ②app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
  24.                 System.identityHashCode(r), r.info,
  25.                 new Configuration(mService.mConfiguration),
  26.                 r.compat, r.icicle, results, newIntents, !andResume,
  27.                 mService.isNextTransitionForward(), profileFile, profileFd,
  28.                 profileAutoStop);
  29.         …
  30.     } catch (RemoteException e) {
  31.         …
  32.     }
  33.     if (mMainStack) {
  34.         mService.startSetupActivityLocked();
  35.     }
  36.     return true;
  37. }

AMS通过realStartActivityLocked函数来调度应用程序进程启动一个Activity,参数r为即将启动的Activity在AMS服务中的描述符,参数app为Activity运行所在的应用程序进程在AMS服务中的描述符。函数通过IApplicationThread代理对象ApplicationThreadProxy通知应用程序进程启动r对应的Activity,应用程序进程完成Activity的加载等准备工作后,AMS最后启动该Activity。启动Activity的创建等工作是在应用程序进程中完成的,AMS是通过IApplicationThread接口和应用程序进程通信的。r.appToken
在AMS服务端的类型为Token,是IApplicationToken的Binder本地对象。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
  2.         ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
  3.         Bundle state, List<ResultInfo> pendingResults,
  4.         List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
  5.         String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
  6.     //将AMS服务传过来的参数封装为ActivityClientRecord对象
  7.     ActivityClientRecord r = new ActivityClientRecord();
  8.     r.token = token;
  9.     r.ident = ident;
  10.     r.intent = intent;
  11.     r.activityInfo = info;
  12.     r.compatInfo = compatInfo;
  13.     r.state = state;
  14.     r.pendingResults = pendingResults;
  15.     r.pendingIntents = pendingNewIntents;
  16.     r.startsNotResumed = notResumed;
  17.     r.isForward = isForward;
  18.     r.profileFile = profileName;
  19.     r.profileFd = profileFd;
  20.     r.autoStopProfiler = autoStopProfiler;
  21.     updatePendingConfiguration(curConfig);
  22.     //使用异步消息方式实现Activity的启动
  23.     queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
  24. }

参数token从AMS服务端经过Binder传输到应用程序进程后,变为IApplicationToken的Binder代理对象,类型为IApplicationToken.Proxy,这是因为AMS和应用程序运行在不同的进程中。

通过queueOrSendMessage函数将Binder跨进程调用转换为应用程序进程中的异步消息处理

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private class H extends Handler {
  2.  public void handleMessage(Message msg) {
  3.     switch (msg.what) {
  4.             case LAUNCH_ACTIVITY: {
  5.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);
  6.                 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
  7.                 r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
  8.                 handleLaunchActivity(r, null);
  9.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  10.             } break;
  11.         }
  12.     }
  13. }

LAUNCH_ACTIVITY消息在应用程序主线程消息循环中得到处理,应用程序通过handleLaunchActivity函数来启动Activity。到此AMS服务就完成了Activity的调度任务,将Activity的启动过程完全交给了应用程序进程来完成。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2.     //主线程空闲时会定时执行垃圾回收,主线程当前要完成启动Activity的任务,因此这里先暂停GC
  3.     unscheduleGcIdler();
  4.     if (r.profileFd != null) {
  5.         mProfiler.setProfiler(r.profileFile, r.profileFd);
  6.         mProfiler.startProfiling();
  7.         mProfiler.autoStopProfiler = r.autoStopProfiler;
  8.     }
  9.     // Make sure we are running with the most recent config.
  10.     ①handleConfigurationChanged(nullnull);
  11.     //创建Activity
  12.     ②Activity a = performLaunchActivity(r, customIntent);
  13.     if (a != null) {
  14.         r.createdConfig = new Configuration(mConfiguration);
  15.         Bundle oldState = r.state;
  16.         //启动Activity
  17.         ③handleResumeActivity(r.token, false, r.isForward);
  18.         …
  19.     }else{
  20.         …
  21.     }
  22. }

performLaunchActivity

应用程序进程通过performLaunchActivity函数将即将要启动的Activity加载到当前进程空间来,同时为启动Activity做准备。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2.     ActivityInfo aInfo = r.activityInfo;
  3.     if (r.packageInfo == null) {
  4.         //通过Activity所在的应用程序信息及该Activity对应的CompatibilityInfo信息从PMS服务中查询当前Activity的包信息
  5.         r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);
  6.     }
  7.     //获取当前Activity的组件信息
  8.     ComponentName component = r.intent.getComponent();
  9.     if (component == null) {
  10.         component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
  11.         r.intent.setComponent(component);
  12.     }
  13.     if (r.activityInfo.targetActivity != null) {
  14.         //packageName为启动Activity的包名,targetActivity为Activity的类名
  15.         component = new ComponentName(r.activityInfo.packageName,
  16.                 r.activityInfo.targetActivity);
  17.     }
  18.     //通过类反射方式加载即将启动的Activity
  19.     Activity activity = null;
  20.     try {
  21.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
  22.         ①activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
  23.         StrictMode.incrementExpectedActivityCount(activity.getClass());
  24.         r.intent.setExtrasClassLoader(cl);
  25.         if (r.state != null) {
  26.             r.state.setClassLoader(cl);
  27.         }
  28.     } catch (Exception e) {
  29.         …
  30.     }
  31.     try {
  32.         //通过单例模式为应用程序进程创建Application对象
  33.         ②Application app = r.packageInfo.makeApplication(false, mInstrumentation);
  34.         if (activity != null) {
  35.             //为当前Activity创建上下文对象ContextImpl
  36.             ContextImpl appContext = new ContextImpl();
  37.             //上下文初始化
  38.             ③appContext.init(r.packageInfo, r.token, this);
  39.             appContext.setOuterContext(activity);
  40.             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
  41.             …
  42.             Configuration config = new Configuration(mCompatConfiguration);
  43.             //将当前启动的Activity和上下文ContextImpl、Application绑定
  44.             ④activity.attach(appContext, this, getInstrumentation(), r.token,
  45.                     r.ident, app, r.intent, r.activityInfo, title, r.parent,
  46.                     r.embeddedID, r.lastNonConfigurationInstances, config);
  47.             …
  48.             //调用Activity的OnCreate函数
  49.             ⑤mInstrumentation.callActivityOnCreate(activity, r.state);
  50.             …
  51.             //将Activity保存到ActivityClientRecord中,ActivityClientRecord为Activity在应用程序进程中的描述符
  52.             r.activity = activity;
  53.             …
  54.         }
  55.         r.paused = true;
  56.         //ActivityThread的成员变量mActivities保存了当前应用程序进程中的所有Activity的描述符
  57.         mActivities.put(r.token, r);
  58.     } catch (SuperNotCalledException e) {
  59.         …
  60.     }
  61.     return activity;
  62. }

在该函数中,首先通过PMS服务查找到即将启动的Activity的包名信息,然后通过类反射方式创建一个该Activity实例,同时为应用程序启动的每一个Activity创建一个LoadedApk实例对象,应用程序进程中创建的所有LoadedApk对象保存在ActivityThread的成员变量mPackages中。接着通过LoadedApk对象的makeApplication函数,使用单例模式创建Application对象,因此在android应用程序进程中有且只有一个Application实例。然后为当前启动的Activity创建一个ContextImpl上下文对象,并初始化该上下文,到此我们可以知道,启动一个Activity需要以下对象:

1)      XXActivity对象,需要启动的Activity;

2)      LoadedApk对象,每个启动的Activity都拥有属于自身的LoadedApk对象;

3)      ContextImpl对象,每个启动的Activity都拥有属于自身的ContextImpl对象;

4)      Application对象,应用程序进程中有且只有一个实例,和Activity是一对多的关系;

加载Activity类

  1. public Activity newActivity(ClassLoader cl, String className,
  2.         Intent intent)
  3.         throws InstantiationException, IllegalAccessException,
  4.         ClassNotFoundException {
  5.     return (Activity)cl.loadClass(className).newInstance();
  6. }

这里通过类反射的方式来加载要启动的Activity实例对象。

LoadedApk构造过程

首先介绍一下LoadedApk对象的构造过程:

frameworksbasecorejavaandroidapp ActivityThread.java

  1. public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
  2.         int flags) {
  3.     synchronized (mPackages) {
  4.         //通过Activity的包名从对应的成员变量中查找LoadedApk对象
  5.         WeakReference<LoadedApk> ref;
  6.         if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
  7.             ref = mPackages.get(packageName);
  8.         } else {
  9.             ref = mResourcePackages.get(packageName);
  10.         }
  11.         LoadedApk packageInfo = ref != null ? ref.get() : null;
  12.         if (packageInfo != null && (packageInfo.mResources == null
  13.                 || packageInfo.mResources.getAssets().isUpToDate())) {
  14.             …
  15.             return packageInfo;
  16.         }
  17.     }
  18.     //如果没有,则为当前Activity创建对应的LoadedApk对象
  19.     ApplicationInfo ai = null;
  20.     try {
  21.         //通过包名在PMS服务中查找应用程序信息
  22.         ai = getPackageManager().getApplicationInfo(packageName,
  23.                 PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
  24.     } catch (RemoteException e) {
  25.         // Ignore
  26.     }
  27.     //使用另一个重载函数创建LoadedApk对象
  28.     if (ai != null) {
  29.         return getPackageInfo(ai, compatInfo, flags);
  30.     }
  31.     return null;
  32. }

 

  1. public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
  2.         int flags) {
  3.     boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
  4.     boolean securityViolation = includeCode && ai.uid != 0
  5.             && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
  6.                     ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
  7.                     : true);
  8.     if ((flags&(Context.CONTEXT_INCLUDE_CODE|Context.CONTEXT_IGNORE_SECURITY))
  9.             == Context.CONTEXT_INCLUDE_CODE) {
  10.         …
  11.     }
  12.     return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
  13. }

 

  1. private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
  2.         ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
  3.     //再次从对应的成员变量中查找LoadedApk实例
  4.     synchronized (mPackages) {
  5.         WeakReference<LoadedApk> ref;
  6.         if (includeCode) {
  7.             ref = mPackages.get(aInfo.packageName);
  8.         } else {
  9.             ref = mResourcePackages.get(aInfo.packageName);
  10.         }
  11.         LoadedApk packageInfo = ref != null ? ref.get() : null;
  12.         if (packageInfo == null || (packageInfo.mResources != null
  13.                 && !packageInfo.mResources.getAssets().isUpToDate())) {
  14.             …
  15.             //构造一个LoadedApk对象
  16.             packageInfo =new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
  17.                         securityViolation, includeCode &&
  18.                         (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
  19.             //保存LoadedApk实例到ActivityThread的相应成员变量中
  20.             if (includeCode) {
  21.                 mPackages.put(aInfo.packageName,
  22.                         new WeakReference<LoadedApk>(packageInfo));
  23.             } else {
  24.                 mResourcePackages.put(aInfo.packageName,
  25.                         new WeakReference<LoadedApk>(packageInfo));
  26.             }
  27.         }
  28.         return packageInfo;
  29.     }
  30. }

 

frameworksbasecorejavaandroidappLoadedApk.java

  1. public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
  2.         CompatibilityInfo compatInfo,
  3.         ActivityThread mainThread, ClassLoader baseLoader,
  4.         boolean securityViolation, boolean includeCode) {
  5.     mActivityThread = activityThread;
  6.     mApplicationInfo = aInfo;
  7.     mPackageName = aInfo.packageName;
  8.     mAppDir = aInfo.sourceDir;
  9.     final int myUid = Process.myUid();
  10.     mResDir = aInfo.uid == myUid ? aInfo.sourceDir
  11.             : aInfo.publicSourceDir;
  12.     if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
  13.         aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
  14.                 mPackageName);
  15.     }
  16.     mSharedLibraries = aInfo.sharedLibraryFiles;
  17.     mDataDir = aInfo.dataDir;
  18.     mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
  19.     mLibDir = aInfo.nativeLibraryDir;
  20.     mBaseClassLoader = baseLoader;
  21.     mSecurityViolation = securityViolation;
  22.     mIncludeCode = includeCode;
  23.     mCompatibilityInfo.set(compatInfo);
  24.     if (mAppDir == null) {
  25.         //为应用程序进程创建一个ContextImpl上下文
  26.         if (ActivityThread.mSystemContext == null) {
  27.             ActivityThread.mSystemContext =
  28.                 ContextImpl.createSystemContext(mainThread);
  29.             ActivityThread.mSystemContext.getResources().updateConfiguration(
  30.                      mainThread.getConfiguration(),
  31.                      mainThread.getDisplayMetricsLocked(compatInfo, false),
  32.                      compatInfo);
  33.         }
  34.         mClassLoader = ActivityThread.mSystemContext.getClassLoader();
  35.         mResources = ActivityThread.mSystemContext.getResources();
  36.     }
  37. }

从以上LoadedApk的构造函数可以看出,LoadedApk类记录了Activity运行所在的ActivityThread、Activity所在的应用程序信息、Activity的包名、Activity的资源路径、Activity的库路径、Activity的数据存储路径、类加载器和应用程序所使用的资源等信息。

Application构造过程

当Activity为应用程序进程启动的第一个Activity,因此需要构造一个Application对象

frameworksbasecorejavaandroidappLoadedApk.java

  1. public Application makeApplication(boolean forceDefaultAppClass,
  2.         Instrumentation instrumentation) {
  3.     //在应用程序进程空间以单例模式创建Application对象
  4.     if (mApplication != null) {
  5.         return mApplication;
  6.     }
  7.     Application app = null;
  8.     //得到应用程序的Application类名
  9.     String appClass = mApplicationInfo.className;
  10.     //如果应用程序没用重写Application,则使用Android默认的Application类
  11.     if (forceDefaultAppClass || (appClass == null)) {
  12.         appClass = “android.app.Application”;
  13.     }
  14.     try {
  15.         java.lang.ClassLoader cl = getClassLoader();
  16.         //为Application实例创建一个上下文对象ContextImpl
  17.         ①ContextImpl appContext = new ContextImpl();
  18.         //初始化上下文
  19.         ②appContext.init(thisnull, mActivityThread);
  20.         //创建Application实例对象
  21.         ③app = mActivityThread.mInstrumentation.newApplication(
  22.                 cl, appClass, appContext);
  23.         appContext.setOuterContext(app);
  24.     } catch (Exception e) {
  25.         …
  26.     }
  27.     mActivityThread.mAllApplications.add(app);
  28.     mApplication = app;
  29.     if (instrumentation != null) {
  30.         try {
  31.             //调用Application的OnCreate函数
  32.             ④instrumentation.callApplicationOnCreate(app);
  33.         } catch (Exception e) {
  34.             …
  35.         }
  36.     }
  37.     return app;
  38. }

在应用程序开发过程中,当我们重写了Application类后,应用程序加载运行的是我们定义的Application类,否则就加载运行默认的Application类。从Application对象的构造过程就可以解释为什么应用程序启动后首先执行的是Application的OnCreate函数。在实例化Application对象时,同样创建并初始化了一个ContextImpl上下文对象。

ContextImpl构造过程

前面我们介绍了,每一个Activity拥有一个上下文对象ContextImpl,每一个Application对象也拥有一个ContextImpl上下文对象,那么ContextImpl对象又是如何构造的呢?

frameworksbasecorejavaandroidapp ContextImpl.java

  1. ContextImpl() {
  2.     mOuterContext = this;
  3. }

ContextImpl的构造过程什么也没干,通过调用ContextImpl的init函数进行初始化

  1. final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread) {
  2.     init(packageInfo, activityToken, mainThread, nullnull);
  3. }

 

  1. final void init(LoadedApk packageInfo,IBinder activityToken, ActivityThread mainThread,
  2.             Resources container, String basePackageName) {
  3.     mPackageInfo = packageInfo;
  4.     mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
  5.     mResources = mPackageInfo.getResources(mainThread);
  6.     if (mResources != null && container != null
  7.             && container.getCompatibilityInfo().applicationScale !=
  8.                     mResources.getCompatibilityInfo().applicationScale) {
  9.         mResources = mainThread.getTopLevelResources(
  10.                 mPackageInfo.getResDir(), container.getCompatibilityInfo());
  11.     }
  12.     mMainThread = mainThread;
  13.     mContentResolver = new ApplicationContentResolver(this, mainThread);
  14.     setActivityToken(activityToken);
  15. }

从ContextImpl的初始化函数中可以知道,ContextImpl记录了应用程序的包名信息、应用程序的资源信息、应用程序的主线程、ContentResolver及Activity对应的IApplicationToken.Proxy,当然对应Application对象所拥有的ContextImpl上下文就没有对应的Token了。通过前面的分析我们可以知道各个对象之间的关系:

对象Attach过程

Activity所需要的对象都创建好了,就需要将Activity和Application对象、ContextImpl对象绑定在一起。

frameworksbasecorejavaandroidapp Activity.java

  1. final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
  2.         Application application, Intent intent, ActivityInfo info, CharSequence title,
  3.         Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
  4.         Configuration config) {
  5.     attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
  6.         lastNonConfigurationInstances, config);
  7. }

context:Activity的上下文对象,就是前面创建的ContextImpl对象;

aThread:Activity运行所在的主线程描述符ActivityThread;

instr:用于监控Activity运行状态的Instrumentation对象;

token:用于和AMS服务通信的IApplicationToken.Proxy代理对象;

application:Activity运行所在进程的Application对象;

parent:启动当前Activity的Activity;

  1. final void attach(Context context, ActivityThread aThread,
  2.         Instrumentation instr, IBinder token, int ident,
  3.         Application application, Intent intent, ActivityInfo info,
  4.         CharSequence title, Activity parent, String id,
  5.         NonConfigurationInstances lastNonConfigurationInstances,
  6.         Configuration config) {
  7.     //将上下文对象ContextImpl保存到Activity的成员变量中
  8.     attachBaseContext(context);
  9.     //每个Activity都拥有一个FragmentManager,这里就是将当前Activity设置到FragmentManager中管理
  10.     mFragments.attachActivity(this);
  11.     //创建窗口对象
  12.     ①mWindow = PolicyManager.makeNewWindow(this);
  13.     mWindow.setCallback(this);
  14.     mWindow.getLayoutInflater().setPrivateFactory(this);
  15.     if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
  16.         mWindow.setSoftInputMode(info.softInputMode);
  17.     }
  18.     if (info.uiOptions != 0) {
  19.         mWindow.setUiOptions(info.uiOptions);
  20.     }
  21.     //记录应用程序的UI线程
  22.     mUiThread = Thread.currentThread();
  23.     //记录应用程序的ActivityThread对象
  24.     mMainThread = aThread;
  25.     mInstrumentation = instr;
  26.     mToken = token;
  27.     mIdent = ident;
  28.     mApplication = application;
  29.     mIntent = intent;
  30.     mComponent = intent.getComponent();
  31.     mActivityInfo = info;
  32.     mTitle = title;
  33.     mParent = parent;
  34.     mEmbeddedID = id;
  35.     mLastNonConfigurationInstances = lastNonConfigurationInstances;
  36.     //为Activity所在的窗口创建窗口管理器
  37.     ②mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),
  38.             (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  39.     if (mParent != null) {
  40.         mWindow.setContainer(mParent.getWindow());
  41.     }
  42.     mWindowManager = mWindow.getWindowManager();
  43.     mCurrentConfig = config;
  44. }

在该attach函数中主要做了以下几件事:

1)        将Activity设置到FragmentManager中;

2)        根据参数初始化Activity的成员变量;

3)        为Activity创建窗口Window对象;

4)        为Window创建窗口管理器;

到此为止应用程序进程为启动的Activity对象创建了以下不同的实例对象,它们之间的关系如下:

应用程序窗口创建过程

frameworksbasecorejavacomandroidinternalpolicy PolicyManager.java

  1. public static Window makeNewWindow(Context context) {
  2.     return sPolicy.makeNewWindow(context);
  3. }

通过Policy类的makeNewWindow函数来创建一个应用程序窗口

  1. private static final String POLICY_IMPL_CLASS_NAME =
  2.         “com.android.internal.policy.impl.Policy”;
  3. private static final IPolicy sPolicy;
  4. static {
  5.     try {
  6.         Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
  7.         sPolicy = (IPolicy)policyClass.newInstance();
  8.     } catch (ClassNotFoundException ex) {
  9.         …
  10.     }
  11. }

frameworksbasepolicysrccomandroidinternalpolicyimpl Policy.java

  1. public Window makeNewWindow(Context context) {
  2.     return new PhoneWindow(context);
  3. }

应用程序窗口的创建过程其实就是构造一个PhoneWindow对象。PhoneWindow类是通过静态方式加载到应用程序进程空间的。

  1. private static final String[] preload_classes = {
  2.     “com.android.internal.policy.impl.PhoneLayoutInflater”,
  3.     “com.android.internal.policy.impl.PhoneWindow”,
  4.     “com.android.internal.policy.impl.PhoneWindow$1”,
  5.     “com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback”,
  6.     “com.android.internal.policy.impl.PhoneWindow$DecorView”,
  7.     “com.android.internal.policy.impl.PhoneWindow$PanelFeatureState”,
  8.     “com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState”,
  9. };
  1. static {
  2.     for (String s : preload_classes) {
  3.         try {
  4.             Class.forName(s);
  5.         } catch (ClassNotFoundException ex) {
  6.             Log.e(TAG, “Could not preload class for phone policy: “ + s);
  7.         }
  8.     }
  9. }

PhoneWindow的构造过程

  1. public PhoneWindow(Context context) {
  2.     super(context);
  3.     mAlternativePanelStyle=getContext().getResources().getBoolean(com.android.internal.R.bool.config_alternativePanelStyle);
  4.     mLayoutInflater = LayoutInflater.from(context);
  5. }

构造过程比较简单,只是得到布局加载服务对象。

窗口管理器创建过程

通过前面的分析我们可以知道,在Activity启动过程中,会为Activity创建一个窗口对象PhoneWindow,应用程序有了窗口那就需要有一个窗口管理器来管理这些窗口,因此在Activity启动过程中还会创建一个WindowManager对象。

frameworksbasecorejavaandroidview Window.java

  1. public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
  2.         boolean hardwareAccelerated) {
  3.     mAppToken = appToken;// IApplicationToken.Proxy代理对象
  4.     mAppName = appName;
  5.     //得到WindowManagerImpl实例,
  6.     if (wm == null) {
  7.         wm = WindowManagerImpl.getDefault();
  8.     }
  9.     //为每个启动的Activity创建一个轻量级的窗口管理器LocalWindowManager
  10.     mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
  11. }

WindowManagerImpl为重量级的窗口管理器,应用程序进程中有且只有一个WindowManagerImpl实例,它管理了应用程序进程中创建的所有PhoneWindow窗口。Activity并没有直接引用WindowManagerImpl实例,Android系统为每一个启动的Activity创建了一个轻量级的窗口管理器LocalWindowManager,每个Activity通过LocalWindowManager来访问WindowManagerImpl,它们三者之间的关系如下图所示:

WindowManagerImpl以单例模式创建,应用程序进程中有且只有一个WindowManagerImpl实例

frameworksbasecorejavaandroidview WindowManagerImpl.java

  1. private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
  2. public static WindowManagerImpl getDefault() {
  3.     return sWindowManager;
  4. }

应用程序进程会为每一个Activity创建一个LocalWindowManager实例对象

frameworksbasecorejavaandroidview Window.java

  1. LocalWindowManager(WindowManager wm, boolean hardwareAccelerated) {
  2.     super(wm, getCompatInfo(mContext));
  3.     mHardwareAccelerated = hardwareAccelerated ||
  4.             SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
  5. }

frameworksbasecorejavaandroidview WindowManagerImpl.java

  1. CompatModeWrapper(WindowManager wm, CompatibilityInfoHolder ci) {
  2.     mWindowManager = wm instanceof CompatModeWrapper
  3.             ? ((CompatModeWrapper)wm).mWindowManager : (WindowManagerImpl)wm;
  4.     if (ci == null) {
  5.         mDefaultDisplay = mWindowManager.getDefaultDisplay();
  6.     } else {
  7.         mDefaultDisplay = Display.createCompatibleDisplay(
  8.                 mWindowManager.getDefaultDisplay().getDisplayId(), ci);
  9.     }
  10.     mCompatibilityInfo = ci;
  11. }

  1. public Display getDefaultDisplay() {
  2.     return new Display(Display.DEFAULT_DISPLAY, null);
  3. }

frameworksbasecorejavaandroidviewDisplay.java

  1. Display(int display, CompatibilityInfoHolder compatInfo) {
  2.     synchronized (sStaticInit) {
  3.         if (!sInitialized) {
  4.             nativeClassInit();
  5.             sInitialized = true;
  6.         }
  7.     }
  8.     mCompatibilityInfo = compatInfo != null ? compatInfo : new CompatibilityInfoHolder();
  9.     mDisplay = display;
  10.     init(display);
  11. }

构造Display对象时需要初始化该对象。

frameworksbasecorejniandroid_view_Display.cpp

  1. static void android_view_Display_init(
  2.         JNIEnv* env, jobject clazz, jint dpy)
  3. {
  4.     DisplayInfo info;
  5.     if (headless) {
  6.         // initialize dummy display with reasonable values
  7.         info.pixelFormatInfo.format = 1// RGB_8888
  8.         info.fps = 60;
  9.         info.density = 160;
  10.         info.xdpi = 160;
  11.         info.ydpi = 160;
  12.     } else {
  13.         status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info);
  14.         if (err < 0) {
  15.             jniThrowException(env, “java/lang/IllegalArgumentException”, NULL);
  16.             return;
  17.         }
  18.     }
  19.     env->SetIntField(clazz, offsets.pixelFormat,info.pixelFormatInfo.format);
  20.     env->SetFloatField(clazz, offsets.fps,      info.fps);
  21.     env->SetFloatField(clazz, offsets.density,  info.density);
  22.     env->SetFloatField(clazz, offsets.xdpi,     info.xdpi);
  23.     env->SetFloatField(clazz, offsets.ydpi,     info.ydpi);
  24. }

Display的初始化过程很简单,就是通过SurfaceComposerClient请求SurfaceFlinger得到显示屏的基本信息。

frameworksnativelibsgui SurfaceComposerClient.cpp

  1. status_t SurfaceComposerClient::getDisplayInfo(
  2.         DisplayID dpy, DisplayInfo* info)
  3. {
  4.     if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
  5.         return BAD_VALUE;
  6.     volatile surface_flinger_cblk_t const * cblk = get_cblk();
  7.     volatile display_cblk_t const * dcblk = cblk->displays + dpy;
  8.     info->w              = dcblk->w;
  9.     info->h              = dcblk->h;
  10.     info->orientation      = dcblk->orientation;
  11.     info->xdpi           = dcblk->xdpi;
  12.     info->ydpi           = dcblk->ydpi;
  13.     info->fps            = dcblk->fps;
  14.     info->density        = dcblk->density;
  15.     return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo));
  16. }

我们知道在SurfaceFlinger启动过程中,创建了一块匿名共享内存来保存显示屏的基本信息,这里就是通过访问这块匿名共享内存来读取显示屏信息。到此一个Activity所需要的窗口对象就创建完成了,在应用程序窗口的创建过程中一共创建了以下几个对象:

Activity视图对象的创建过程

在Activity的attach函数中完成应用程序窗口的创建后,通过Instrumentation回调Activity的OnCreate函数来为当前Activity加载布局文件,进一步创建视图对象。

frameworksbasecorejavaandroidappInstrumentation.java

  1. public void callActivityOnCreate(Activity activity, Bundle icicle) {
  2.     …
  3.     activity.performCreate(icicle);
  4.     …
  5. }

frameworksbasecorejavaandroidappActivity.java

  1. final void performCreate(Bundle icicle) {
  2.     onCreate(icicle);
  3.     mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
  4.             com.android.internal.R.styleable.Window_windowNoDisplay, false);
  5.     mFragments.dispatchActivityCreated();
  6. }

我们知道在应用程序开发中,需要重写Activity的OnCreate函数:

Packagesappsxxxsrccomxxx xxxActivity.java

  1. public void onCreate(Bundle savedInstanceState) {
  2.     super.onCreate(savedInstanceState);
  3.     setContentView(R.layout.main_activity);
  4.     …
  5. }

在OnCreate函数中通过setContentView来设置Activity的布局文件,就是生成该Activity的所有视图对象。

frameworksbasecorejavaandroidappActivity.java

  1. public void setContentView(View view, ViewGroup.LayoutParams params) {
  2.     getWindow().setContentView(view, params);
  3.     //初始化动作条
  4.     initActionBar();
  5. }

getWindow()函数得到前面创建的窗口对象PhoneWindow,通过PhoneWindow来设置Activity的视图。

frameworksbasepolicysrccomandroidinternalpolicyimplPhoneWindow.java

  1. public void setContentView(int layoutResID) {
  2.     //如果窗口顶级视图对象为空,则创建窗口视图对象
  3.     if (mContentParent == null) {
  4.         installDecor();
  5.     } else {//否则只是移除该视图对象中的其他视图
  6.         mContentParent.removeAllViews();
  7.     }
  8.     //加载布局文件,并将布局文件中的所有视图对象添加到mContentParent容器中
  9.     mLayoutInflater.inflate(layoutResID, mContentParent);
  10.     final Callback cb = getCallback();
  11.     if (cb != null && !isDestroyed()) {
  12.         cb.onContentChanged();
  13.     }
  14. }

PhoneWindow的成员变量mContentParent的类型为ViewGroup,是窗口内容存放的地方

frameworksbasepolicysrccomandroidinternalpolicyimplPhoneWindow.java

  1. private void installDecor() {
  2.     if (mDecor == null) {
  3.         ①mDecor = generateDecor();
  4.         mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5.         mDecor.setIsRootNamespace(true);
  6.         if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
  7.             mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  8.         }
  9.     }
  10.     if (mContentParent == null) {
  11.         ②mContentParent = generateLayout(mDecor);
  12.         mDecor.makeOptionalFitsSystemWindows();
  13.         //应用程序窗口标题栏
  14.         mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
  15.         if (mTitleView != null) {
  16.             …
  17.         } else {
  18.             //应用程序窗口动作条
  19.             mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
  20.             if (mActionBar != null) {
  21.                 …
  22.             }
  23.         }
  24.     }
  25. }

通过函数generateDecor()来创建一个DecorView对象

  1. protected DecorView generateDecor() {
  2.     return new DecorView(getContext(), –1);
  3. }

接着通过generateLayout(mDecor)来创建视图对象容器mContentParent

  1. protected ViewGroup generateLayout(DecorView decor) {
  2.     //通过读取属性配置文件设置窗口风格
  3.     if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
  4.         requestFeature(FEATURE_ACTION_BAR_OVERLAY);
  5.     }
  6.     …
  7.     //通过读取属性配置文件设置窗口标志
  8.     if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
  9.     setFlags(FLAG_FULLSCREEN,FLAG_FULLSCREEN&(~getForcedWindowFlags()));
  10.     }
  11.     …
  12.     WindowManager.LayoutParams params = getAttributes();
  13.     …
  14.     mDecor.startChanging();
  15.     //根据窗口主题风格选择不同的布局文件layoutResource
  16.     …
  17.     //加载布局文件
  18.     ①View in = mLayoutInflater.inflate(layoutResource, null);
  19.     //添加到DecorView中
  20.     ②decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  21.     //从窗口视图中找出窗口内容视图对象
  22.     ③ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  23.     …
  24.     mDecor.finishChanging();
  25.     return contentParent;
  26. }

到此Activity的所有视图对象都已经创建完毕,DecorView是Activity的顶级视图,由窗口PhoneWindow对象持有,在DecorView视图对象中添加了一个ViewGroup容器组件contentParent,所有用户定义视图组件将被添加到该容器中。

handleResumeActivity

performLaunchActivity函数完成了两件事:

1)        Activity窗口对象的创建,通过attach函数来完成;

2)        Activity视图对象的创建,通过setContentView函数来完成;

这些准备工作完成后,就可以显示该Activity了,应用程序进程通过调用handleResumeActivity函数来启动Activity的显示过程。

frameworksbasecorejavaandroidapp ActivityThread.java

  1. final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
  2.     unscheduleGcIdler();
  3.     ActivityClientRecord r;
  4.     try {
  5.         ①r = performResumeActivity(token, clearHide);
  6.     } catch (Exception e) {
  7.         …
  8.     }
  9.     if (r != null) {
  10.         final Activity a = r.activity;
  11.         …
  12.         if (r.window == null && !a.mFinished && willBeVisible) {
  13.             //获得为当前Activity创建的窗口PhoneWindow对象
  14.             r.window = r.activity.getWindow();
  15.             //获取为窗口创建的视图DecorView对象
  16.             View decor = r.window.getDecorView();
  17.             decor.setVisibility(View.INVISIBLE);
  18.             //在attach函数中就为当前Activity创建了WindowManager对象
  19.             ViewManager wm = a.getWindowManager();
  20.             //得到该视图对象的布局参数
  21.             ②WindowManager.LayoutParams l = r.window.getAttributes();
  22.             //将视图对象保存到Activity的成员变量mDecor中
  23.             a.mDecor = decor;
  24.             l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  25.             if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
  26.                 l.idleScreenAvailable = true;
  27.             } else {
  28.                 l.idleScreenAvailable = false;
  29.             }
  30.             l.softInputMode |= forwardBit;
  31.             if (a.mVisibleFromClient) {
  32.                 a.mWindowAdded = true;
  33.                 //将创建的视图对象DecorView添加到Activity的窗口管理器中
  34.                 ③wm.addView(decor, l);
  35.             }
  36.         } else if (!willBeVisible) {
  37.             …
  38.         }
  39.         …
  40.         if (!r.onlyLocalRequest) {
  41.             r.nextIdle = mNewActivities;
  42.             mNewActivities = r;
  43.             Looper.myQueue().addIdleHandler(new Idler());
  44.         }
  45.         …
  46.     } else {
  47.         …
  48.     }
  49. }

我们知道,在前面的performLaunchActivity函数中完成Activity的创建后,会将当前当前创建的Activity在应用程序进程端的描述符ActivityClientRecord以键值对的形式保存到ActivityThread的成员变量mActivities中:mActivities.put(r.token, r),r.token就是Activity的身份证,即是IApplicationToken.Proxy代理对象,也用于与AMS通信。上面的函数首先通过performResumeActivity从mActivities变量中取出Activity的应用程序端描述符ActivityClientRecord,然后取出前面为Activity创建的视图对象DecorView和窗口管理器WindowManager,最后将视图对象添加到窗口管理器中。

我们知道Activity引用的其实是轻量级的窗口管理器LocalWindowManager

frameworksbasecorejavaandroidview Window.java

  1. public final void addView(View view, ViewGroup.LayoutParams params) {
  2.     WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
  3.     CharSequence curTitle = wp.getTitle();
  4.     //应用程序窗口
  5.     if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
  6.         wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
  7.         if (wp.token == null) {
  8.             View decor = peekDecorView();
  9.             if (decor != null) {
  10.                 // LayoutParams 的token设置为W本地Binder对象
  11.                 wp.token = decor.getWindowToken();
  12.             }
  13.         }
  14.         if (curTitle == null || curTitle.length() == 0) {
  15.             //根据窗口类型设置不同的标题
  16.             …
  17.             if (mAppName != null) {
  18.                 title += “:” + mAppName;
  19.             }
  20.             wp.setTitle(title);
  21.         }
  22.     } else {//系统窗口
  23.         if (wp.token == null) {
  24.             wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
  25.         }
  26.         if ((curTitle == null || curTitle.length() == 0)
  27.                 && mAppName != null) {
  28.             wp.setTitle(mAppName);
  29.         }
  30.     }
  31.     if (wp.packageName == null) {
  32.         wp.packageName = mContext.getPackageName();
  33.     }
  34.     if (mHardwareAccelerated) {
  35.         wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
  36.     }
  37.     super.addView(view, params);
  38. }

LocalWindowManager的addView函数对不同类型窗口的布局参数进行相应的设置,比如布局参数中的token设置,如果是应用程序窗口,则设置token为W本地Binder对象。如果不是应用程序窗口,同时当前窗口没有父窗口,则设置token为当前窗口的IApplicationToken.Proxy代理对象,否则设置为父窗口的IApplicationToken.Proxy代理对象。最后视图组件的添加工作交给其父类来完成。LocalWindowManager继承于CompatModeWrapper,是WindowManagerImpl的内部类。

frameworksbasecorejavaandroidviewWindowManagerImpl.java

  1. public void addView(View view, android.view.ViewGroup.LayoutParams params) {
  2.     mWindowManager.addView(view, params, mCompatibilityInfo);
  3. }

前面我们介绍了,每一个Activity拥有一个轻量级窗口管理器,通过轻量级窗口管理器LocalWindowManager来访问重量级窗口管理器WindowManagerImpl,因此视图组件的添加过程又转交给了WindowManagerImpl来实现。

  1. public void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih) {
  2.     addView(view, params, cih, false);
  3. }

该函数又调用WindowManagerImpl的另一个重载函数来添加视图组件

  1. private void addView(View view, ViewGroup.LayoutParams params,
  2.         CompatibilityInfoHolder cih, boolean nest) {
  3.     …
  4.     final WindowManager.LayoutParams wparams= (WindowManager.LayoutParams)params;
  5.     ViewRootImpl root;
  6.     View panelParentView = null;
  7.     synchronized (this) {
  8.         …
  9.         //从mViews中查找当前添加的View
  10.         int index = findViewLocked(view, false);
  11.         //如果已经存在,直接返回
  12.         if (index >= 0) {
  13.             …
  14.             return;
  15.         }
  16.         //尚未添加当前View
  17.         if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
  18.                 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
  19.             final int count = mViews != null ? mViews.length : 0;
  20.             for (int i=0; i<count; i++) {
  21.                 if (mRoots[i].mWindow.asBinder() == wparams.token) {
  22.                     panelParentView = mViews[i];
  23.                 }
  24.             }
  25.         }
  26.         //为Activity创建一个ViewRootImpl对象
  27.         ①root = new ViewRootImpl(view.getContext());
  28.         …
  29.         //设置视图组件的布局参数
  30.         view.setLayoutParams(wparams);
  31.         if (mViews == null) {
  32.             index = 1;
  33.             mViews = new View[1];
  34.             mRoots = new ViewRootImpl[1];
  35.             mParams = new WindowManager.LayoutParams[1];
  36.         } else {
  37.             //动态增加mViews数组长度
  38.             index = mViews.length + 1;
  39.             Object[] old = mViews;
  40.             mViews = new View[index];
  41.             System.arraycopy(old, 0, mViews, 0, index-1);
  42.             //动态增加mRoots数组长度
  43.             old = mRoots;
  44.             mRoots = new ViewRootImpl[index];
  45.             System.arraycopy(old, 0, mRoots, 0, index-1);
  46.             //动态增加mParams数组长度
  47.             old = mParams;
  48.             mParams = new WindowManager.LayoutParams[index];
  49.             System.arraycopy(old, 0, mParams, 0, index-1);
  50.         }
  51.         index–;
  52.         ②mViews[index] = view;
  53.         mRoots[index] = root;
  54.         mParams[index] = wparams;
  55.     }
  56.     try {
  57.         ③root.setView(view, wparams, panelParentView);
  58.     } catch (RuntimeException e) {
  59.         …
  60.     }
  61. }

到此我们知道,当应用程序向窗口管理器中添加一个视图对象时,首先会为该视图对象创建一个ViewRootImpl对象,并且将视图对象、ViewRootImpl对象、视图布局参数分别保存到窗口管理器WindowManagerImpl得mViews、mRoots、mParams数组中,如下图所示:

最后通过ViewRootImpl对象来完成视图的显示过程。

ViewRootImpl构造过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public ViewRootImpl(Context context) {
  2.     …
  3.     ①getWindowSession(context.getMainLooper());
  4.     mThread = Thread.currentThread();
  5.     mLocation = new WindowLeaked(null);
  6.     mLocation.fillInStackTrace();
  7.     mWidth = –1;
  8.     mHeight = –1;
  9.     mDirty = new Rect();
  10.     mTempRect = new Rect();
  11.     mVisRect = new Rect();
  12.     mWinFrame = new Rect();
  13.     ②mWindow = new W(this);
  14.     mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
  15.     mInputMethodCallback = new InputMethodCallback(this);
  16.     mViewVisibility = View.GONE;
  17.     mTransparentRegion = new Region();
  18.     mPreviousTransparentRegion = new Region();
  19.     mFirst = true// true for the first time the view is added
  20.     mAdded = false;
  21.     mAccessibilityManager = AccessibilityManager.getInstance(context);
  22.     mAccessibilityInteractionConnectionManager =
  23.         new AccessibilityInteractionConnectionManager();
  24.     mAccessibilityManager.addAccessibilityStateChangeListener(
  25.             mAccessibilityInteractionConnectionManager);
  26.     ③mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, mHandler, this);
  27.     mViewConfiguration = ViewConfiguration.get(context);
  28.     mDensity = context.getResources().getDisplayMetrics().densityDpi;
  29.     mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
  30.     mProfileRendering = Boolean.parseBoolean(
  31.             SystemProperties.get(PROPERTY_PROFILE_RENDERING, “false”));
  32.     ④mChoreographer = Choreographer.getInstance();
  33.     PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
  34.     mAttachInfo.mScreenOn = powerManager.isScreenOn();
  35.     loadSystemProperties();
  36. }

在ViewRootImpl的构造函数中初始化了一些成员变量,ViewRootImpl创建了以下几个主要对象:

1)        通过getWindowSession(context.getMainLooper())得到IWindowSession的代理对象,该对象用于和WMS通信。

2)        创建了一个W本地Binder对象,用于WMS通知应用程序进程。

3)        采用单例模式创建了一个Choreographer对象,用于统一调度窗口绘图。

4)        创建ViewRootHandler对象,用于处理当前视图消息。

5)        构造一个AttachInfo对象;

6)        创建Surface对象,用于绘制当前视图,当然该Surface对象的真正创建是由WMS来完成的,只不过是WMS传递给应用程序进程的。

  1. private final Surface mSurface = new Surface();
  2. final ViewRootHandler mHandler = new ViewRootHandler();

IWindowSession代理获取过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public static IWindowSession getWindowSession(Looper mainLooper) {
  2.     synchronized (mStaticInit) {
  3.         if (!mInitialized) {
  4.             try {
  5.                 //获取输入法管理器
  6.                 InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
  7.                 //获取窗口管理器
  8.                 IWindowManager windowManager = Display.getWindowManager();
  9.                 //得到IWindowSession代理对象
  10.                 sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());
  11.                 float animatorScale = windowManager.getAnimationScale(2);
  12.                 ValueAnimator.setDurationScale(animatorScale);
  13.                 mInitialized = true;
  14.             } catch (RemoteException e) {
  15.             }
  16.         }
  17.         return sWindowSession;
  18.     }
  19. }

以上函数通过WMS的openSession函数创建应用程序与WMS之间的连接通道,即获取IWindowSession代理对象,并将该代理对象保存到ViewRootImpl的静态成员变量sWindowSession中

  1. static IWindowSession sWindowSession;

因此在应用程序进程中有且只有一个IWindowSession代理对象。

frameworksbaseservicesjavacomandroidserverwmWindowManagerService.java

  1. public IWindowSession openSession(IInputMethodClient client,
  2.         IInputContext inputContext) {
  3.     if (client == nullthrow new IllegalArgumentException(“null client”);
  4.     if (inputContext == nullthrow new IllegalArgumentException(“null inputContext”);
  5.     Session session = new Session(this, client, inputContext);
  6.     return session;
  7. }

在WMS服务端构造了一个Session实例对象。

AttachInfo构造过程

frameworksbasecorejavaandroidview View.java

  1. AttachInfo(IWindowSession session, IWindow window,
  2.         ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
  3.     mSession = session;//IWindowSession代理对象,用于与WMS通信
  4.     mWindow = window;//W对象
  5.     mWindowToken = window.asBinder();//W本地Binder对象
  6.     mViewRootImpl = viewRootImpl;//ViewRootImpl实例
  7.     mHandler = handler;//ViewRootHandler对象
  8.     mRootCallbacks = effectPlayer;//ViewRootImpl实例
  9. }

 

创建Choreographer对象

Android Project Butter分析中介绍了Android4.1引入VSYNC、Triple Buffer和Choreographer来改善Android先天存在的UI流畅性差问题,有关Choreographer的实现过程请参看Android系统Choreographer机制实现过程

视图View添加过程

窗口管理器WindowManagerImpl为当前添加的窗口创建好各种对象后,调用ViewRootImpl的setView函数向WMS服务添加一个窗口对象。

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  2.     synchronized (this) {
  3.         if (mView == null) {
  4.             //将DecorView保存到ViewRootImpl的成员变量mView中
  5.             mView = view;
  6.             mFallbackEventHandler.setView(view);
  7.             mWindowAttributes.copyFrom(attrs);
  8.             attrs = mWindowAttributes;
  9.             mClientWindowLayoutFlags = attrs.flags;
  10.             setAccessibilityFocus(nullnull);
  11.             //DecorView实现了RootViewSurfaceTaker接口
  12.             if (view instanceof RootViewSurfaceTaker) {
  13.                 mSurfaceHolderCallback =
  14.                         ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
  15.                 if (mSurfaceHolderCallback != null) {
  16.                     mSurfaceHolder = new TakenSurfaceHolder();
  17.                     mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
  18.                 }
  19.             }
  20.             …
  21.             //同时将DecorView保存到mAttachInfo中
  22.             mAttachInfo.mRootView = view;
  23.             mAttachInfo.mScalingRequired = mTranslator != null;
  24.             mAttachInfo.mApplicationScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
  25.             if (panelParentView != null) {
  26.                 mAttachInfo.mPanelParentWindowToken
  27.                         = panelParentView.getApplicationWindowToken();
  28.             }
  29.             …
  30.             //在添加窗口前进行UI布局
  31.             ①requestLayout();
  32.             if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  33.                 mInputChannel = new InputChannel();
  34.             }
  35.             try {
  36.                 mOrigWindowType = mWindowAttributes.type;
  37.                 mAttachInfo.mRecomputeGlobalAttributes = true;
  38.                 collectViewAttributes();
  39.                 //将窗口添加到WMS服务中,mWindow为W本地Binder对象,通过Binder传输到WMS服务端后,变为IWindow代理对象
  40.                 ②res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
  41.                         getHostVisibility(), mAttachInfo.mContentInsets,
  42.                         mInputChannel);
  43.             } catch (RemoteException e) {
  44.                 …
  45.             }
  46.             …
  47.             //建立窗口消息通道
  48.             if (view instanceof RootViewSurfaceTaker) {
  49.                 mInputQueueCallback =
  50.                     ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
  51.             }
  52.             if (mInputChannel != null) {
  53.                 if (mInputQueueCallback != null) {
  54.                     mInputQueue = new InputQueue(mInputChannel);
  55.                     mInputQueueCallback.onInputQueueCreated(mInputQueue);
  56.                 } else {
  57.                     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
  58.                 }
  59.             }
  60.             …
  61.         }
  62.     }
  63. }

通过前面的分析可以知道,用户自定义的UI作为一个子View被添加到DecorView中,然后将顶级视图DecorView添加到应用程序进程的窗口管理器中,窗口管理器首先为当前添加的View创建一个ViewRootImpl对象、一个布局参数对象ViewGroup.LayoutParams,然后将这三个对象分别保存到当前应用程序进程的窗口管理器WindowManagerImpl中,最后通过ViewRootImpl对象将当前视图对象注册到WMS服务中。

ViewRootImpl的setView函数向WMS服务添加一个窗口对象过程:

1)         requestLayout()在应用程序进程中进行窗口UI布局;

2)         WindowSession.add()向WMS服务注册一个窗口对象;

3)         注册应用程序进程端的消息接收通道;

窗口UI布局过程

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. public void requestLayout() {
  2.     //检查当前线程是否是UI线程
  3.     checkThread();
  4.     //标识当前正在请求UI布局
  5.     mLayoutRequested = true;
  6.     scheduleTraversals();
  7. }

窗口布局过程必须在UI线程中进行,因此该函数首先检查调用requestLayout()函数的线程是否为创建ViewRootImpl对象的线程。然后调用scheduleTraversals()函数启动Choreographer的Callback遍历过程。

  1. void scheduleTraversals() {
  2.     if (!mTraversalScheduled) {
  3.         mTraversalScheduled = true;
  4.         //暂停UI线程消息队列对同步消息的处理
  5.         mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
  6.         //向Choreographer注册一个类型为CALLBACK_TRAVERSAL的回调,用于处理UI绘制
  7.         mChoreographer.postCallback(
  8.                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  9.         //向Choreographer注册一个类型为CALLBACK_INPUT的回调,用于处理输入事件
  10.         scheduleConsumeBatchedInput();
  11.     }
  12. }

关于Choreographer的postCallback()用法在前面进行了详细的介绍,当Vsync事件到来时,mTraversalRunnable对象的run()函数将被调用。

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
  2. final class TraversalRunnable implements Runnable {
  3.         @Override
  4.         public void run() {
  5.             doTraversal();
  6.         }
  7. }

mTraversalRunnable对象的类型为TraversalRunnable,该类实现了Runnable接口,在其run()函数中调用了doTraversal()函数来完成窗口布局。

  1. void doTraversal() {
  2.     if (mTraversalScheduled) {
  3.         mTraversalScheduled = false;
  4.         mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
  5.         if (mProfile) {
  6.             Debug.startMethodTracing(“ViewAncestor”);
  7.         }
  8.         Trace.traceBegin(Trace.TRACE_TAG_VIEW, “performTraversals”);
  9.         try {
  10.             performTraversals();
  11.         } finally {
  12.             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  13.         }
  14.         if (mProfile) {
  15.             Debug.stopMethodTracing();
  16.             mProfile = false;
  17.         }
  18.     }
  19. }

performTraversals函数相当复杂,其主要实现以下几个重要步骤:

1.执行窗口测量;

2.执行窗口注册;

3.执行窗口布局;

4.执行窗口绘图;

  1. private void performTraversals() {
  2.     // cache mView since it is used so much below…
  3.     final View host = mView;
  4.     if (host == null || !mAdded)
  5.         return;
  6.     mWillDrawSoon = true;
  7.     boolean windowSizeMayChange = false;
  8.     boolean newSurface = false;
  9.     boolean surfaceChanged = false;
  10.     WindowManager.LayoutParams lp = mWindowAttributes;
  11.     int desiredWindowWidth;
  12.     int desiredWindowHeight;
  13.     final View.AttachInfo attachInfo = mAttachInfo;
  14.     final int viewVisibility = getHostVisibility();
  15.     boolean viewVisibilityChanged = mViewVisibility != viewVisibility
  16.             || mNewSurfaceNeeded;
  17.     WindowManager.LayoutParams params = null;
  18.     if (mWindowAttributesChanged) {
  19.         mWindowAttributesChanged = false;
  20.         surfaceChanged = true;
  21.         params = lp;
  22.     }
  23.     …
  24.     /****************执行窗口测量******************/
  25.     boolean layoutRequested = mLayoutRequested && !mStopped;
  26.     if (layoutRequested) {
  27.         …
  28.         // Ask host how big it wants to be
  29.         windowSizeMayChange |= measureHierarchy(host, lp, res,
  30.                 desiredWindowWidth, desiredWindowHeight);
  31.     }
  32.     …
  33.     /****************向WMS服务添加窗口******************/
  34.     if (mFirst || windowShouldResize || insetsChanged ||
  35.             viewVisibilityChanged || params != null) {
  36.         …
  37.         try {
  38.             final int surfaceGenerationId = mSurface.getGenerationId();
  39.             relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
  40.             …
  41.         } catch (RemoteException e) {
  42.         }
  43.         …
  44.         if (!mStopped) {
  45.             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
  46.                     (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
  47.             if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
  48.                     || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
  49.                 …
  50.                  // Ask host how big it wants to be
  51.                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  52.                 …
  53.             }
  54.         }
  55.     }
  56.     /****************执行窗口布局******************/
  57.     final boolean didLayout = layoutRequested && !mStopped;
  58.     boolean triggerGlobalLayoutListener = didLayout
  59.             || attachInfo.mRecomputeGlobalAttributes;
  60.     if (didLayout) {
  61.         performLayout();
  62.         …
  63.     }
  64.     …
  65.     /****************查找窗口焦点******************/
  66.     boolean skipDraw = false;
  67.     if (mFirst) {
  68.         // handle first focus request
  69.         if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: mView.hasFocus()=”
  70.                 + mView.hasFocus());
  71.         if (mView != null) {
  72.             if (!mView.hasFocus()) {
  73.                 mView.requestFocus(View.FOCUS_FORWARD);
  74.                 mFocusedView = mRealFocusedView = mView.findFocus();
  75.                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: requested focused view=”
  76.                         + mFocusedView);
  77.             } else {
  78.                 mRealFocusedView = mView.findFocus();
  79.                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, “First: existing focused view=”
  80.                         + mRealFocusedView);
  81.             }
  82.         }
  83.         if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_ANIMATING) != 0) {
  84.             // The first time we relayout the window, if the system is
  85.             // doing window animations, we want to hold of on any future
  86.             // draws until the animation is done.
  87.             mWindowsAnimating = true;
  88.         }
  89.     } else if (mWindowsAnimating) {
  90.         skipDraw = true;
  91.     }
  92.     /****************执行窗口绘制******************/
  93.     mFirst = false;
  94.     mWillDrawSoon = false;
  95.     mNewSurfaceNeeded = false;
  96.     mViewVisibility = viewVisibility;
  97.     …
  98.     boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
  99.             viewVisibility != View.VISIBLE;
  100.     if (!cancelDraw && !newSurface) {
  101.         if (!skipDraw || mReportNextDraw) {
  102.             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  103.                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
  104.                     mPendingTransitions.get(i).startChangingAnimations();
  105.                 }
  106.                 mPendingTransitions.clear();
  107.             }
  108.             performDraw();
  109.         }
  110.     } else {
  111.         if (viewVisibility == View.VISIBLE) {
  112.             // Try again
  113.             scheduleTraversals();
  114.         } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  115.             for (int i = 0; i < mPendingTransitions.size(); ++i) {
  116.                 mPendingTransitions.get(i).endChangingAnimations();
  117.             }
  118.             mPendingTransitions.clear();
  119.         }
  120.     }
  121. }
performMeasure

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
  2.  Trace.traceBegin(Trace.TRACE_TAG_VIEW, “measure”);
  3.  try {
  4.   mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  5.  } finally {
  6.   Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  7.  }
  8. }
relayoutWindow

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
  2.         boolean insetsPending) throws RemoteException {
  3.     …
  4.     int relayoutResult = sWindowSession.relayout(
  5.             mWindow, mSeq, params,
  6.             (int) (mView.getMeasuredWidth() * appScale + 0.5f),
  7.             (int) (mView.getMeasuredHeight() * appScale + 0.5f),
  8.             viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
  9.             mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
  10.             mPendingConfiguration, mSurface);
  11.     …
  12.     return relayoutResult;
  13. }

这里通过前面获取的IWindowSession代理对象请求WMS服务执行窗口布局,mSurface是ViewRootImpl的成员变量

  1. private final Surface mSurface = new Surface();

frameworksbasecorejavaandroidview Surface.java

  1. public Surface() {
  2.     checkHeadless();
  3.     if (DEBUG_RELEASE) {
  4.         mCreationStack = new Exception();
  5.     }
  6.     mCanvas = new CompatibleCanvas();
  7. }

该Surface构造函数仅仅创建了一个CompatibleCanvas对象,并没有对该Surface进程native层的初始化,到此我们知道应用程序进程为每个窗口对象都创建了一个Surface对象。并且将该Surface通过跨进程方式传输给WMS服务进程,我们知道,在Android系统中,如果一个对象需要在不同进程间传输,必须实现Parcelable接口,Surface类正好实现了Parcelable接口。ViewRootImpl通过IWindowSession接口请求WMS的完整过程如下:

frameworksbasecorejavaandroidviewIWindowSession.java$ Proxy

  1. public int relayout(android.view.IWindow window, int seq,
  2.         android.view.WindowManager.LayoutParams attrs, int requestedWidth,
  3.         int requestedHeight, int viewVisibility, int flags,
  4.         android.graphics.Rect outFrame,
  5.         android.graphics.Rect outOverscanInsets,
  6.         android.graphics.Rect outContentInsets,
  7.         android.graphics.Rect outVisibleInsets,
  8.         android.content.res.Configuration outConfig,
  9.         android.view.Surface outSurface) throws android.os.RemoteException {
  10.     android.os.Parcel _data = android.os.Parcel.obtain();
  11.     android.os.Parcel _reply = android.os.Parcel.obtain();
  12.     int _result;
  13.     try {
  14.         _data.writeInterfaceToken(DESCRIPTOR);
  15.         _data.writeStrongBinder((((window != null)) ? (window.asBinder()): (null)));
  16.         _data.writeInt(seq);
  17.         if ((attrs != null)) {
  18.             _data.writeInt(1);
  19.             attrs.writeToParcel(_data, 0);
  20.         } else {
  21.             _data.writeInt(0);
  22.         }
  23.         _data.writeInt(requestedWidth);
  24.         _data.writeInt(requestedHeight);
  25.         _data.writeInt(viewVisibility);
  26.         _data.writeInt(flags);
  27.         mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
  28.         _reply.readException();
  29.         _result = _reply.readInt();
  30.         if ((0 != _reply.readInt())) {
  31.             outFrame.readFromParcel(_reply);
  32.         }
  33.         if ((0 != _reply.readInt())) {
  34.             outOverscanInsets.readFromParcel(_reply);
  35.         }
  36.         if ((0 != _reply.readInt())) {
  37.             outContentInsets.readFromParcel(_reply);
  38.         }
  39.         if ((0 != _reply.readInt())) {
  40.             outVisibleInsets.readFromParcel(_reply);
  41.         }
  42.         if ((0 != _reply.readInt())) {
  43.             outConfig.readFromParcel(_reply);
  44.         }
  45.         if ((0 != _reply.readInt())) {
  46.             outSurface.readFromParcel(_reply);
  47.         }
  48.     } finally {
  49.         _reply.recycle();
  50.         _data.recycle();
  51.     }
  52.     return _result;
  53. }

从该函数的实现可以看出,应用程序进程中创建的Surface对象并没有传递到WMS服务进程,只是读取WMS服务进程返回来的Surface。那么WMS服务进程是如何响应应用程序进程布局请求的呢?
frameworksbasecorejavaandroidviewIWindowSession.java$ Stub

  1. public boolean onTransact(int code, android.os.Parcel data,
  2.         android.os.Parcel reply, int flags)throws android.os.RemoteException {
  3.     switch (code) {
  4.     case TRANSACTION_relayout: {
  5.         data.enforceInterface(DESCRIPTOR);
  6.         android.view.IWindow _arg0;
  7.         _arg0 = android.view.IWindow.Stub.asInterface(data.readStrongBinder());
  8.         int _arg1;
  9.         _arg1 = data.readInt();
  10.         android.view.WindowManager.LayoutParams _arg2;
  11.         if ((0 != data.readInt())) {
  12.             _arg2 = android.view.WindowManager.LayoutParams.CREATOR
  13.                     .createFromParcel(data);
  14.         } else {
  15.             _arg2 = null;
  16.         }
  17.         int _arg3;
  18.         _arg3 = data.readInt();
  19.         int _arg4;
  20.         _arg4 = data.readInt();
  21.         int _arg5;
  22.         _arg5 = data.readInt();
  23.         int _arg6;
  24.         _arg6 = data.readInt();
  25.         android.graphics.Rect _arg7;
  26.         _arg7 = new android.graphics.Rect();
  27.         android.graphics.Rect _arg8;
  28.         _arg8 = new android.graphics.Rect();
  29.         android.graphics.Rect _arg9;
  30.         _arg9 = new android.graphics.Rect();
  31.         android.graphics.Rect _arg10;
  32.         _arg10 = new android.graphics.Rect();
  33.         android.content.res.Configuration _arg11;
  34.         _arg11 = new android.content.res.Configuration();
  35.         android.view.Surface _arg12;
  36.         _arg12 = new android.view.Surface();
  37.         int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4,
  38.                 _arg5, _arg6, _arg7, _arg8, _arg9, _arg10, _arg11, _arg12);
  39.         reply.writeNoException();
  40.         reply.writeInt(_result);
  41.         if ((_arg7 != null)) {
  42.             reply.writeInt(1);
  43.             _arg7.writeToParcel(reply,
  44.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  45.         } else {
  46.             reply.writeInt(0);
  47.         }
  48.         if ((_arg8 != null)) {
  49.             reply.writeInt(1);
  50.             _arg8.writeToParcel(reply,
  51.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  52.         } else {
  53.             reply.writeInt(0);
  54.         }
  55.         if ((_arg9 != null)) {
  56.             reply.writeInt(1);
  57.             _arg9.writeToParcel(reply,
  58.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  59.         } else {
  60.             reply.writeInt(0);
  61.         }
  62.         if ((_arg10 != null)) {
  63.             reply.writeInt(1);
  64.             _arg10.writeToParcel(reply,
  65.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  66.         } else {
  67.             reply.writeInt(0);
  68.         }
  69.         if ((_arg11 != null)) {
  70.             reply.writeInt(1);
  71.             _arg11.writeToParcel(reply,
  72.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  73.         } else {
  74.             reply.writeInt(0);
  75.         }
  76.         if ((_arg12 != null)) {
  77.             reply.writeInt(1);
  78.             _arg12.writeToParcel(reply,
  79.                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  80.         } else {
  81.             reply.writeInt(0);
  82.         }
  83.         return true;
  84.     }
  85.     }
  86. }

该函数可以看出,WMS服务在响应应用程序进程请求添加窗口时,首先在当前进程空间创建一个Surface对象,然后调用Session的relayout()函数进一步完成窗口添加过程,最后将WMS服务中创建的Surface返回给应用程序进程。

到目前为止,在应用程序进程和WMS服务进程分别创建了一个Surface对象,但是他们调用的都是Surface的无参构造函数,在该构造函数中并未真正初始化native层的Surface,那native层的Surface是在那里创建的呢?
frameworksbaseservicesjavacomandroidserverwm Session.java

  1. public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2.         int requestedWidth, int requestedHeight, int viewFlags,
  3.         int flags, Rect outFrame, Rect outContentInsets,
  4.         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
  5.     int res = mService.relayoutWindow(this, window, seq, attrs,
  6.             requestedWidth, requestedHeight, viewFlags, flags,
  7.             outFrame, outContentInsets, outVisibleInsets,
  8.             outConfig, outSurface);
  9.     return res;
  10. }

frameworksbaseservicesjavacomandroidserverwm WindowManagerService.java

  1. public int relayoutWindow(Session session, IWindow client, int seq,
  2.         WindowManager.LayoutParams attrs, int requestedWidth,
  3.         int requestedHeight, int viewVisibility, int flags,
  4.         Rect outFrame, Rect outContentInsets,
  5.         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
  6.     …
  7.     synchronized(mWindowMap) {
  8.         // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
  9.         WindowState win = windowForClientLocked(session, client, false);
  10.         if (win == null) {
  11.             return 0;
  12.         }
  13.         …
  14.         if (viewVisibility == View.VISIBLE &&
  15.                 (win.mAppToken == null || !win.mAppToken.clientHidden)) {
  16.             …
  17.             try {
  18.                 if (!win.mHasSurface) {
  19.                     surfaceChanged = true;
  20.                 }
  21.                 //创建Surface
  22.                 Surface surface = winAnimator.createSurfaceLocked();
  23.                 if (surface != null) {
  24.                     outSurface.copyFrom(surface);
  25.                 } else {
  26.                     outSurface.release();
  27.                 }
  28.             } catch (Exception e) {
  29.                 …
  30.             }
  31.             …
  32.         }
  33.         …
  34.     }
  35.     …
  36. }

frameworksbaseservicesjavacomandroidserverwmWindowStateAnimator.java

  1. Surface createSurfaceLocked() {
  2.     if (mSurface == null) {
  3.         …
  4.         try {
  5.             …
  6.             if (DEBUG_SURFACE_TRACE) {
  7.                 mSurface = new SurfaceTrace(
  8.                         mSession.mSurfaceSession, mSession.mPid,
  9.                         attrs.getTitle().toString(),
  10.                         0, w, h, format, flags);
  11.             } else {
  12.                 mSurface = new Surface(
  13.                     mSession.mSurfaceSession, mSession.mPid,
  14.                     attrs.getTitle().toString(),
  15.                     0, w, h, format, flags);
  16.             }
  17.             mWin.mHasSurface = true;
  18.         } catch (Surface.OutOfResourcesException e) {
  19.             …
  20.         }
  21.         Surface.openTransaction();
  22.         …
  23.     }
  24.     return mSurface;
  25. }

Surface创建过程
frameworksbasecorejavaandroidviewSurface.java

  1. public Surface(SurfaceSession s,int pid, String name, int display, int w, int h, int format, int flags)
  2.     throws OutOfResourcesException {
  3.     checkHeadless();
  4.     if (DEBUG_RELEASE) {
  5.         mCreationStack = new Exception();
  6.     }
  7.     mCanvas = new CompatibleCanvas();
  8.     init(s,pid,name,display,w,h,format,flags);
  9.     mName = name;
  10. }

frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_init(
  2.         JNIEnv* env, jobject clazz,
  3.         jobject session,
  4.         jint, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
  5. {
  6.     if (session == NULL) {
  7.         doThrowNPE(env);
  8.         return;
  9.     }
  10.     SurfaceComposerClient* client =
  11.             (SurfaceComposerClient*)env->GetIntField(session, sso.client);
  12.     sp<SurfaceControl> surface;
  13.     if (jname == NULL) {
  14.         surface = client->createSurface(dpy, w, h, format, flags);
  15.     } else {
  16.         const jchar* str = env->GetStringCritical(jname, 0);
  17.         const String8 name(str, env->GetStringLength(jname));
  18.         env->ReleaseStringCritical(jname, str);
  19.         surface = client->createSurface(name, dpy, w, h, format, flags);
  20.     }
  21.     if (surface == 0) {
  22.         jniThrowException(env, OutOfResourcesException, NULL);
  23.         return;
  24.     }
  25.     setSurfaceControl(env, clazz, surface);
  26. }

到此才算真正创建了一个可用于绘图的Surface,从上面的分析我们可以看出,在WMS服务进程端,其实创建了两个Java层的Surface对象,第一个Surface使用了无参构造函数,仅仅构造一个Surface对象而已,而第二个Surface却使用了有参构造函数,参数指定了图象宽高等信息,这个Java层Surface对象还会在native层请求SurfaceFlinger创建一个真正能用于绘制图象的native层Surface。最后通过浅拷贝的方式将第二个Surface复制到第一个Surface中,最后通过writeToParcel方式写回到应用程序进程。

到目前为止,应用程序和WMS一共创建了3个Java层Surface对象,如上图所示,而真正能用于绘图的Surface只有3号,那么3号Surface与2号Surface之间是什么关系呢?outSurface.copyFrom(surface)
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_copyFrom(JNIEnv* env, jobject clazz, jobject other)
  2. {
  3.     if (clazz == other)
  4.         return;
  5.     if (other == NULL) {
  6.         doThrowNPE(env);
  7.         return;
  8.     }
  9.     //得到当前Surface所引用的SurfaceControl对象
  10.     const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
  11.     //得到源Surface所引用的SurfaceControl对象
  12.     const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
  13.     //如果它们引用的不是同一个SurfaceControl对象
  14.     if (!SurfaceControl::isSameSurface(surface, rhs)) {
  15.         setSurfaceControl(env, clazz, rhs);
  16.     }
  17. }

2号Surface引用到了3号Surface的SurfaceControl对象后,通过writeToParcel()函数写会到应用程序进程。
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_writeToParcel(
  2.         JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
  3. {
  4.     Parcel* parcel = (Parcel*)env->GetIntField(
  5.             argParcel, no.native_parcel);
  6.     if (parcel == NULL) {
  7.         doThrowNPE(env);
  8.         return;
  9.     }
  10.     const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
  11.     if (control != NULL) {
  12.         SurfaceControl::writeSurfaceToParcel(control, parcel);
  13.     } else {
  14.         sp<Surface> surface(Surface_getSurface(env, clazz));
  15.         if (surface != NULL) {
  16.             Surface::writeToParcel(surface, parcel);
  17.         } else {
  18.             SurfaceControl::writeSurfaceToParcel(NULL, parcel);
  19.         }
  20.     }
  21.     if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
  22.         setSurfaceControl(env, clazz, NULL);
  23.         setSurface(env, clazz, NULL);
  24.     }
  25. }

由于2号Surface引用的SurfaceControl对象不为空,因此这里就将SurfaceControl对象写会给应用程序进程
frameworksnativelibsgui Surface.cpp

  1. status_t SurfaceControl::writeSurfaceToParcel(
  2.         const sp<SurfaceControl>& control, Parcel* parcel)
  3. {
  4.     sp<ISurface> sur;
  5.     uint32_t identity = 0;
  6.     if (SurfaceControl::isValid(control)) {
  7.         sur = control->mSurface;
  8.         identity = control->mIdentity;
  9.     }
  10.     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
  11.     parcel->writeStrongBinder(NULL);  // NULL ISurfaceTexture in this case.
  12.     parcel->writeInt32(identity);
  13.     return NO_ERROR;
  14. }

写入Parcel包裹的对象顺序如下:

应用程序进程中的1号Surface通过readFromParcel()函数读取从WMS服务进程写回的Binder对象。
frameworksbasecorejni android_view_Surface.cpp

  1. static void Surface_readFromParcel(
  2.         JNIEnv* env, jobject clazz, jobject argParcel)
  3. {
  4.     Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
  5.     if (parcel == NULL) {
  6.         doThrowNPE(env);
  7.         return;
  8.     }
  9.     sp<Surface> sur(Surface::readFromParcel(*parcel));
  10.     setSurface(env, clazz, sur);
  11. }

frameworksnativelibsgui Surface.cpp

  1. sp<Surface> Surface::readFromParcel(const Parcel& data) {
  2.     Mutex::Autolock _l(sCachedSurfacesLock);
  3.     sp<IBinder> binder(data.readStrongBinder());
  4.     sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
  5.     if (surface == 0) {
  6.        surface = new Surface(data, binder);
  7.        sCachedSurfaces.add(binder, surface);
  8.     } else {
  9.         // The Surface was found in the cache, but we still should clear any
  10.         // remaining data from the parcel.
  11.         data.readStrongBinder();  // ISurfaceTexture
  12.         data.readInt32();         // identity
  13.     }
  14.     if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
  15.         surface = 0;
  16.     }
  17.     cleanCachedSurfacesLocked();
  18.     return surface;
  19. }

应用程序进程中的1号Surface按相反顺序读取WMS服务端返回过来的Binder对象等数据,并构造一个native层的Surface对象。

  1. Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
  2.     : SurfaceTextureClient()
  3. {
  4.     mSurface = interface_cast<ISurface>(ref);
  5.     sp<IBinder> st_binder(parcel.readStrongBinder());
  6.     sp<ISurfaceTexture> st;
  7.     if (st_binder != NULL) {
  8.         st = interface_cast<ISurfaceTexture>(st_binder);
  9.     } else if (mSurface != NULL) {
  10.         st = mSurface->getSurfaceTexture();
  11.     }
  12.     mIdentity   = parcel.readInt32();
  13.     init(st);
  14. }

每个Activity可以有一个或多个Surface,默认情况下一个Activity只有一个Surface,当Activity中使用SurfaceView时,就存在多个Surface。Activity默认surface是在relayoutWindow过程中由WMS服务创建的,然后回传给应用程序进程,我们知道一个Surface其实就是应用程序端的本地窗口,关于Surface的初始化过程这里就不在介绍。

performLayout

frameworksbasecorejavaandroidviewViewRootImpl.java

  1. private void performLayout() {
  2.     mLayoutRequested = false;
  3.     mScrollMayChange = true;
  4.     final View host = mView;
  5.     if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
  6.         Log.v(TAG, “Laying out “ + host + ” to (“ +
  7.                 host.getMeasuredWidth() + “, “ + host.getMeasuredHeight() + “)”);
  8.     }
  9.     Trace.traceBegin(Trace.TRACE_TAG_VIEW, “layout”);
  10.     try {
  11.         host.layout(00, host.getMeasuredWidth(), host.getMeasuredHeight());
  12.     } finally {
  13.         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  14.     }
  15. }

performDraw

frameworksbasecorejavaandroidview ViewRootImpl.java

  1. private void performDraw() {
  2.     if (!mAttachInfo.mScreenOn && !mReportNextDraw) {
  3.         return;
  4.     }
  5.     final boolean fullRedrawNeeded = mFullRedrawNeeded;
  6.     mFullRedrawNeeded = false;
  7.     mIsDrawing = true;
  8.     Trace.traceBegin(Trace.TRACE_TAG_VIEW, “draw”);
  9.     try {
  10.         draw(fullRedrawNeeded);
  11.     } finally {
  12.         mIsDrawing = false;
  13.         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  14.     }
  15.     …
  16. }

 

  1. private void draw(boolean fullRedrawNeeded) {
  2.     Surface surface = mSurface;
  3.     if (surface == null || !surface.isValid()) {
  4.         return;
  5.     }
  6.     …
  7.     if (!dirty.isEmpty() || mIsAnimating) {
  8.         //使用硬件渲染
  9.         if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
  10.             // Draw with hardware renderer.
  11.             mIsAnimating = false;
  12.             mHardwareYOffset = yoff;
  13.             mResizeAlpha = resizeAlpha;
  14.             mCurrentDirty.set(dirty);
  15.             mCurrentDirty.union(mPreviousDirty);
  16.             mPreviousDirty.set(dirty);
  17.             dirty.setEmpty();
  18.             if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
  19.                     animating ? null : mCurrentDirty)) {
  20.                 mPreviousDirty.set(00, mWidth, mHeight);
  21.             }
  22.         //使用软件渲染
  23.         } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
  24.             return;
  25.         }
  26.     }
  27.     …
  28. }

窗口添加过程

frameworksbaseservicesjavacomandroidserverwmSession.java

  1. public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2.         int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
  3.     return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets,
  4.             outInputChannel);
  5. }

frameworksbaseservicesjavacomandroidserverwmWindowManagerService.java

  1. public int addWindow(Session session, IWindow client, int seq,
  2.         WindowManager.LayoutParams attrs, int viewVisibility,
  3.         Rect outContentInsets, InputChannel outInputChannel) {
  4.     //client为IWindow的代理对象,是Activity在WMS服务中的唯一标示
  5.     int res = mPolicy.checkAddPermission(attrs);
  6.     if (res != WindowManagerImpl.ADD_OKAY) {
  7.         return res;
  8.     }
  9.     boolean reportNewConfig = false;
  10.     WindowState attachedWindow = null;
  11.     WindowState win = null;
  12.     long origId;
  13.     synchronized(mWindowMap) {
  14.         if (mDisplay == null) {
  15.             throw new IllegalStateException(“Display has not been initialialized”);
  16.         }
  17.         //判断窗口是否已经存在
  18.         if (mWindowMap.containsKey(client.asBinder())) {
  19.             Slog.w(TAG, “Window “ + client + ” is already added”);
  20.             return WindowManagerImpl.ADD_DUPLICATE_ADD;
  21.         }
  22.         //如果添加的是应用程序窗口
  23.         if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
  24.             //根据attrs.token从mWindowMap中取出应用程序窗口在WMS服务中的描述符WindowState
  25.             attachedWindow = windowForClientLocked(null, attrs.token, false);
  26.             if (attachedWindow == null) {
  27.                 Slog.w(TAG, “Attempted to add window with token that is not a window: “
  28.                       + attrs.token + “.  Aborting.”);
  29.                 return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
  30.             }
  31.             if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
  32.                     && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
  33.                 Slog.w(TAG, “Attempted to add window with token that is a sub-window: “
  34.                         + attrs.token + “.  Aborting.”);
  35.                 return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
  36.             }
  37.         }
  38.         boolean addToken = false;
  39.         //根据attrs.token从mWindowMap中取出应用程序窗口在WMS服务中的描述符WindowState
  40.         WindowToken token = mTokenMap.get(attrs.token);
  41.         if (token == null) {
  42.             …
  43.             ①token = new WindowToken(this, attrs.token, –1false);
  44.             addToken = true;
  45.         }
  46.         //应用程序窗口
  47.         else if (attrs.type >= FIRST_APPLICATION_WINDOW
  48.                 && attrs.type <= LAST_APPLICATION_WINDOW) {
  49.             AppWindowToken atoken = token.appWindowToken;
  50.             …
  51.         }
  52.         //输入法窗口
  53.         else if (attrs.type == TYPE_INPUT_METHOD) {
  54.             …
  55.         }
  56.         //墙纸窗口
  57.         else if (attrs.type == TYPE_WALLPAPER) {
  58.             …
  59.         }
  60.         //Dream窗口
  61.         else if (attrs.type == TYPE_DREAM) {
  62.             …
  63.         }
  64.         //为Activity窗口创建WindowState对象
  65.         ②win = new WindowState(this, session, client, token,
  66.                 attachedWindow, seq, attrs, viewVisibility);
  67.         …
  68.         if (outInputChannel != null && (attrs.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  69.             String name = win.makeInputChannelName();
  70.             InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
  71.             win.setInputChannel(inputChannels[0]);
  72.             inputChannels[1].transferTo(outInputChannel);
  73.             mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
  74.         }
  75.         …
  76.         //以键值对<IWindow.Proxy/Token,WindowToken>形式保存到mTokenMap表中
  77.         if (addToken) {
  78.             ③mTokenMap.put(attrs.token, token);
  79.         }
  80.         ④win.attach();
  81.         //以键值对<IWindow的代理对象,WindowState>形式保存到mWindowMap表中
  82.         ⑤mWindowMap.put(client.asBinder(), win);
  83.         …
  84.     }
  85.     …
  86.     return res;
  87. }

我们知道当应用程序进程添加一个DecorView到窗口管理器时,会为当前添加的窗口创建ViewRootImpl对象,同时构造了一个W本地Binder对象,无论是窗口视图对象DecorView还是ViewRootImpl对象,都只是存在于应用程序进程中,在添加窗口过程中仅仅将该窗口的W对象传递给WMS服务,经过Binder传输后,到达WMS服务端进程后变为IWindow.Proxy代理对象,因此该函数的参数client的类型为IWindow.Proxy。参数attrs的类型为WindowManager.LayoutParams,在应用程序进程启动Activity时,handleResumeActivity()函数通过WindowManager.LayoutParams
l = r.window.getAttributes();来得到应用程序窗口布局参数,由于WindowManager.LayoutParams实现了Parcelable接口,因此WindowManager.LayoutParams对象可以跨进程传输,WMS服务的addWindow函数中的attrs参数就是应用程序进程发送过来的窗口布局参数。在LocalWindowManager的addView函数中为窗口布局参数设置了相应的token,如果是应用程序窗口,则布局参数的token设为W本地Binder对象。如果不是应用程序窗口,同时当前窗口没有父窗口,则设置token为当前窗口的IApplicationToken.Proxy代理对象,否则设置为父窗口的IApplicationToken.Proxy代理对象,由于应用程序和WMS分属于两个不同的进程空间,因此经过Binder传输后,布局参数的令牌attrs.token就转变为IWindow.Proxy或者Token。以上函数首先根据布局参数的token等信息构造一个WindowToken对象,然后在构造一个WindowState对象,并将添加的窗口信息记录到mTokenMap和mWindowMap哈希表中。


在WMS服务端创建了所需对象后,接着调用了WindowState的attach()来进一步完成窗口添加。
frameworksbaseservicesjavacomandroidserverwmWindowState.java

  1. void attach() {
  2.     if (WindowManagerService.localLOGV) Slog.v(
  3.         TAG, “Attaching “ + this + ” token=” + mToken
  4.         + “, list=” + mToken.windows);
  5.     mSession.windowAddedLocked();
  6. }

frameworksbaseservicesjavacomandroidserverwmSession.java

  1. void windowAddedLocked() {
  2.     if (mSurfaceSession == null) {
  3.         mSurfaceSession = new SurfaceSession();
  4.         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
  5.                 WindowManagerService.TAG, ”  NEW SURFACE SESSION “ + mSurfaceSession);
  6.         mService.mSessions.add(this);
  7.     }
  8.     mNumWindow++;//记录对应的某个应用程序添加的窗口数量
  9. }

到此一个新的应用程序窗口就添加完成了。总结一下:

应用程序通过IWindowSession接口请求WMS服务添加一个应用程序窗口,WMS服务首先在自己服务进程为应用程序创建创建一个对应的WindowState描述符,然后保存到成员变量mWindowMap中。如果还没有为应用程序进程创建连接SurfaceFlinger的会话,就接着创建该会话通道SurfaceSession,我们知道,Activity中的视图所使用的画布Surface是在WMS服务进程中创建的,但是该画布所使用的图形buffer确实在SurfaceFlinger进程中分配管理的,而图形的绘制确是在应用程序进程中完成的,所以Activity的显示过程需要三个进程的配合才能完成。应用程序进程只与WMS服务进程交互,并不直接和SurfaceFlinger进程交互,而是由WMS服务进程同SurfaceFlinger进程配合。前面我们介绍了应用程序进程是通过IWindowSession接口与WMS服务进程通信的,那WMS服务是如何与SurfaceFlinger进程通信的呢,这就是windowAddedLocked函数要完成的工作。

在windowAddedLocked函数中使用单例模式创建一个SurfaceSession对象,在构造该对象时,通过JNI在native层创建一个与SurfaceFlinger进程的连接。

frameworksbasecorejavaandroidviewSurfaceSession.java

  1. public SurfaceSession() {
  2.     init();
  3. }

该init()函数是一个native函数,其JNI实现如下:

frameworksbasecorejni android_view_Surface.cpp

  1. static void SurfaceSession_init(JNIEnv* env, jobject clazz)
  2. {
  3.     sp<SurfaceComposerClient> client = new SurfaceComposerClient;
  4.     client->incStrong(clazz);
  5.     env->SetIntField(clazz, sso.client, (int)client.get());
  6. }

该函数构造了一个SurfaceComposerClient对象,在第一次强引用该对象时,会请求SurfaceFlinger创建一个专门处理当前应用程序进程请求的Client会话。

每个应用程序进程都持有一个与WMS服务会话通道IWindowSession,而服务端的Session有且只有一个SurfaceSession对象。系统中创建的所有IWindowSession都被记录到WMS服务的mSessions成员变量中,这样WMS就可以知道自己正在处理那些应用程序的请求。到此我们来梳理一下在WMS服务端都创建了那些对象:

1)        WindowState对象,是应用程序窗口在WMS服务端的描述符;

2)        Session对象,应用程序进程与WMS服务会话通道;

3)        SurfaceSession对象,应用程序进程与SurfaceFlinger的会话通道;

 

Android IntentFilter 匹配原则浅析

1
Intent
分为两大类,显式和隐式。


显式事件,就是指通过
component
Name

属性,明确指定了目标组件的事件。


比如我们新建一个
Intent,指名道姓的说,此事件用于启动名为“com.silenceburn.XXXX”的Activity,那么这就是一个显式事件。


隐式事件,就是指没有
component
Name

属性,没有明确指定目标组件的事件。


比如系统向所有监控通话情况的程序发送的“来电话了!”的事件,由于系统不确定谁会处理这个事件,因此系统不会明确指定目标组件,也就是说没有目标组件,那么这就是个隐式的事件。


此处只是简介显式和隐式事件,更精确详细的描述请查阅
SDK文档,我们只需要记住一点,两种事件的最大区别是
component Name

属性是否为空。

 

2

事件过滤策略

IntentFilter


系统在传送显式事件时非常方便,因为如果把
Intent比作一封信,那么component Name就是一个详细的收件人地址,系统可以精确的把显式事件送达目标组件。

 


而传送隐式事件时,就比较麻烦了。因为这封信的信封上,没有写收信地址!


那怎么办呢?系统做了一个艰难的决定,就是把信拆开看看。通过信件内容里面的线索,去寻找合适的收件人。


比如信中的线索描述到:“收信人是男性,快
30岁了,未婚,喜欢玩游戏”,那么系统就在小区里面去找这样的人。

 


非常值得庆幸的事情是,这个小区的人素质非常高,每户人家都写了点自我介绍在门口,


比如张三写道:“我是男性,
90后,未婚,喜欢玩游戏”,李四写道:“我是女性,快30岁了,未婚,喜欢逛街”等等等等。


有了每户人家的自我介绍,系统就能很快的定位真正的收件人了!

 


上面是一个类比的例子,不过
android系统处理隐式事件的策略,基本上就是上述这种模式了。

 


首先系统会通过观察
Intent的内容(打开信件看内容),取得匹配线索,系统所需的线索是如下三种

 action

 data (both URI and
data type)

 category

 


其次,系统中每个组件,如果想收取隐式事件,则必须声明自己的
IntentFilter(自我介绍,我对什么样的信件感兴趣)。


至于怎么写
IntentFilter,已经相当明了了,那就是应该是这样写:

我是组件XXXX,我想要接收这样的隐式事件:它的ACTION必须是
XXX,它的
category
必须是
YYYY

,它包含的
data必须是ZZZZ “


如果组件不声明
IntentFilter,那么所有的隐式事件都不会发送给该组件。(注意,这并不影响向该组件发送显式事件。)

 


对于系统中发生的每个隐式事件,系统都会尝试将
action, data , category
和系统中各个组件声明的
IntentFilter
去进行匹配,以找到合适的接收者。

3.IntentFilter匹配原则


对于显式事件,系统可以精确送达。对于隐式事件,系统分析事件的
action, data , category
内容,并和各个组件声明的
IntentFilter进行匹配,找出匹配的组件进行送达。actioncategory没什么好说的,再此我将最复杂的data匹配展开来进行描述一下:


首先务必认识到,
data是一个相对复杂的要素。

dataURI来描述和定位,URI由三部分组成,

scheme://host:port/path     

模式
://
主机:端口/路径


此外在事件中,还可以设置
dataMIME类型,作为事件的datatype属性。为了描述方便,下文将IntentFilter简写为filter,请大家注意。


首先明确一个匹配原则,就是对于
URI的匹配,只比较filter中声明的部分。


部分匹配原则:只要
filter中声明的部分匹配成功,就认为整个URI匹配成功。


举例来说,
    content://com.silenceburn.SdCardTester:1000/mydata/private/


filter定义为 
content://com.silenceburn.SdCardTester:1000/    


是可以匹配的。


注意
filter中并没有定义path部分,但是依然可以匹配成功,因为filter不声明的部分不进行比较。


换句话讲,任何符合
content://com.silenceburn.SdCardTester:1000/的事件,无论path是什么,都可以匹配成功。


接下来是真正的
data部分的,也就是URI的匹配规则如下:

1.

如果
dataURIdatatype为空,则
filter
URItype也必须为空,才能匹配成功

2.

如果
dataURI不为空,但是datatype为空,则
filter
必须定义
URI并匹配成功,且type为空,才能匹配成功

3.

如果
dataURI为空,但是datatype不为空,则
filter
必须
URI为空,定义type并匹配成功,才能匹配成功

4.

如果
dataURIdata都不为空,则
filter
URItype都必须定义并匹配成功,才能匹配成功。对于URI部分,有一个特殊处理,就是即使filter没有定义URIcontentfile两种URI也作为既存的URI存在。

 


通过上文的描述,大家就可以明白为什么在注册
SD卡插拔接收器时,不但需要

IntentFilter intentFilter = new
IntentFilter(Intent.ACTION_MEDIA_MOUNTED);

             
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);

             
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);

             
intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);

             
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);

             
intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);


而且需要添加

      
intentFilter.addDataScheme(“file”);


注册应用安装卸载事件时不但需要

      
IntentFilter intentFilter = new IntentFilter();

             
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);

             
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

             
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);


而且需要

      
intentFilter.addDataScheme(“package”);


原因就在
Data的匹配。


最后,该文章是经过我在互联网中,某个文章中提取和加工而来,首先感谢原作者的精心设计,原文地址如下:
http://blog.csdn.net/silenceburn/article/details/6083375

Android BroadcastReceiver 简介

Android BroadcastReceiver 简介

在 Android 中使用 Activity, Service, Broadcast, BroadcastReceiver

活动(Activity) – 用于表现功能

服务(Service) – 相当于后台运行的 Activity

广播(Broadcast) – 用于发送广播

广播接收器(BroadcastReceiver) – 用于接收广播

Intent – 用于连接以上各个组件,并在其间传递消息

BroadcastReceiver

在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件。下面将详细的阐述如何发送Broadcast和使用BroadcastReceiver过

滤接收的过程:

首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。

当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若 匹配则就会调用BroadcastReceiver的onReceive()方法。所以当我们定义一个BroadcastReceiver的时候,都需要 实现onReceive()方法。

注册BroadcastReceiver有两种方式:

一种方式是,静态的在AndroidManifest.xml中用<receiver>标签生命注册,并在标签内用<intent- filter>标签设置过滤器。

另一种方式是,动态的在代码中先定义并设置好一个 IntentFilter对象,然后在需要注册的地方调 Context.registerReceiver()方法,如果取消时就调用Context.unregisterReceiver()方法。如果用动 态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。

另外,若在使用sendBroadcast()的方法是指定了接收权限,则只有在AndroidManifest.xml中用<uses- permission>标签声明了拥有此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。

同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中 用<uses-permission>标签声明了,拥有此权限的Context对象所发送的Broadcast才能被这个 BroadcastReceiver所接收。

动态注册:

IntentFilter intentFilter = new IntentFilter();

intentFilter.addAction(String);–为 BroadcastReceiver指定action,使之用于接收同action的广播 registerReceiver(BroadcastReceiver,intentFilter);

一般:在onStart中注册,onStop中取消unregisterReceiver

发送广播消息:extends Service

指定广播目标Action:Intent Intent = new Intent(action-String)

–指定了此action的receiver会接收此广播

需传递参数(可选) putExtra();

发送:sendBroadcast(Intent);

android wifi讲解 wifi列表显示

最近项目中用到了wifi模块,今天做一个简单的总结。

1.怎样获取wifi对象并进行操作

要操作WIFI设备,需要先获取Context.getSystemService(Context.WIFI_SERVICE)来获取WifiManager对象,并通过这个对象来管理WIFI设备。

 

addNetwork(WifiConfiguration config) 添加一个config描述的WIFI网络,默认情况下,这个WIFI网络是DISABLE状态的。

calculateSignalLevel(int rssi , int numLevels) 计算信号的等级

compareSignalLevel(int rssiA, int rssiB) 对比网络A和网络B的信号强度

createWifiLock(int lockType, String tag) 创建一个WIFI 锁,锁定当前的WIFI连接

disableNetwork(int netId) 让一个网络连接失效

disconnect() 断开当前的WIFI连接

enableNetwork(int netId, Boolean disableOthers) 连接netId所指的WIFI网络,并是其他的网络都被禁用

getConfiguredNetworks() 获取网络连接的状态

getConnectionInfo() 获取当前连接的信息

getDhcpInfo() 获取DHCP 的信息

getScanResulats() 获取扫描测试的结果

getWifiState() 获取当前WIFI设备的状态

isWifiEnabled() 判断WIFI设备是否打开

pingSupplicant() ping操作,和PC的ping操作相同作用

ressociate() 重新连接WIFI网络,即使该网络是已经被连接上的

reconnect() 重新连接一个未连接上的WIFI网络

removeNetwork() 移除某一个网络

saveConfiguration() 保留一个配置信息

setWifiEnabled() 让一个连接有效

startScan() 开始扫描

updateNetwork(WifiConfiguration config) 更新一个网络连接

2.常用的wifi状态

WIFI_STATE_DISABLED WIFI网卡不可用

WIFI_STATE_DISABLING WIFI网卡正在关闭

WIFI_STATE_ENABLED WIFI网卡可用

WIFI_STATE_ENABLING WIFI网卡正在打开

WIFI_STATE_UNKNOWN WIFI网卡状态不可知

3.列表查看有连接信号的wifi热点

ScanResult对象就是用来表示附近wifi热点的属性的,可以通过WifiManager.getScanResults()返回一个ScanResult列表,后面我附上查看附近wifi热点的demo,ScanResult的重要属性有一下几个:

BSSID 接入点的地址

SSID 网络的名字,唯一区别WIFI网络的名字

Capabilities 网络接入的性能

Frequency 当前WIFI设备附近热点的频率(MHz)

Level 所发现的WIFI网络信号强度

4.连接wifi热点

通过WifiManager.getConfiguredNetworks()方法会返回WifiConfiguration对象的列表,然后再调用WifiManager.enableNetwork();方法就可以连接上指定的热点。

5.查看已经连接上的wifi信息

WifiInfo是专门用来表示连接的对象,这个对象可以通过WifiManager.getConnectionInfo()来获取。WifiInfo中包含了当前连接中的相关信息。

 

getBSSID() 获取BSSID属性

getDetailedStateOf() 获取客户端的连通性

getHiddenSSID() 获取SSID 是否被隐藏

getIpAddress() 获取IP 地址

getLinkSpeed() 获取连接的速度

getMacAddress() 获取Mac 地址

getRssi() 获取802.11n 网络的信号

getSSID() 获取SSID

getSupplicanState() 获取具体客户端状态的信息

在wifi操作中常用的类和方法就这些,下面给出一个列表显示wifi热点的demo。

 

1.activity的布局很简单就是一个ListView,activity_wifi_list.xml内容如下:

[html]

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”

    xmlns:tools=”http://schemas.android.com/tools”

    android:layout_width=”match_parent”

    android:layout_height=”match_parent”

    android:paddingBottom=”@dimen/activity_vertical_margin”

    android:paddingLeft=”@dimen/activity_horizontal_margin”

    android:paddingRight=”@dimen/activity_horizontal_margin”

    android:paddingTop=”@dimen/activity_vertical_margin”

    tools:context=”.WifiListActivity” >

 

    <ListView

        android:id=”@+id/listView”

        android:layout_width=”match_parent”

        android:layout_height=”wrap_content” >

    </ListView>

 

</RelativeLayout>

2.ListView的item布局文件item_wifi_list.xml内容如下:

[html]

<?xml version=”1.0″ encoding=”utf-8″?>

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”

    android:layout_width=”match_parent”

    android:layout_height=”match_parent” >

 

    <ImageView

        android:id=”@+id/imageView”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:layout_alignParentLeft=”true”

        android:layout_alignParentTop=”true”

        android:src=”@drawable/ic_launcher” />

 

    <TextView

        android:id=”@+id/textView”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:layout_alignBottom=”@+id/imageView”

        android:layout_marginBottom=”14dp”

        android:layout_toRightOf=”@+id/imageView”

        android:text=”TextView” />

 

    <TextView

        android:id=”@+id/signal_strenth”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:layout_alignBaseline=”@+id/textView”

        android:layout_alignBottom=”@+id/textView”

        android:layout_alignParentRight=”true”

        android:text=”TextView” />

 

</RelativeLayout>

3.下面就activity的代码了,这里需要自己自定义列表。

[java]

public class WifiListActivity extends Activity {

 

    private WifiManager wifiManager;

    List<ScanResult> list;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_wifi_list);

        init();

    }

 

    private void init() {

        wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);

        openWifi();

        list = wifiManager.getScanResults();

        ListView listView = (ListView) findViewById(R.id.listView);

        if (list == null) {

            Toast.makeText(this, “wifi未打开!”, Toast.LENGTH_LONG).show();

        }else {

            listView.setAdapter(new MyAdapter(this,list));

        }

 

    }

 

    /**

     *  打开WIFI

     */

    private void openWifi() {

       if (!wifiManager.isWifiEnabled()) {

        wifiManager.setWifiEnabled(true);

       }

 

    }

 

    public class MyAdapter extends BaseAdapter {

 

        LayoutInflater inflater;

        List<ScanResult> list;

        public MyAdapter(Context context, List<ScanResult> list) {

            // TODO Auto-generated constructor stub

            this.inflater = LayoutInflater.from(context);

            this.list = list;

        }

 

        @Override

        public int getCount() {

            // TODO Auto-generated method stub

            return list.size();

        }

 

        @Override

        public Object getItem(int position) {

            // TODO Auto-generated method stub

            return position;

        }

 

        @Override

        public long getItemId(int position) {

            // TODO Auto-generated method stub

            return position;

        }

 

        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            // TODO Auto-generated method stub

            View view = null;

            view = inflater.inflate(R.layout.item_wifi_list, null);

            ScanResult scanResult = list.get(position);

            TextView textView = (TextView) view.findViewById(R.id.textView);

            textView.setText(scanResult.SSID);

            TextView signalStrenth = (TextView) view.findViewById(R.id.signal_strenth);

            signalStrenth.setText(String.valueOf(Math.abs(scanResult.level)));

            ImageView imageView = (ImageView) view.findViewById(R.id.imageView);

            //判断信号强度,显示对应的指示图标

            if (Math.abs(scanResult.level) > 100) {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_0));

            } else if (Math.abs(scanResult.level) > 80) {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_1));

            } else if (Math.abs(scanResult.level) > 70) {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_1));

            } else if (Math.abs(scanResult.level) > 60) {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_2));

            } else if (Math.abs(scanResult.level) > 50) {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_3));

            } else {

                imageView.setImageDrawable(getResources().getDrawable(R.drawable.stat_sys_wifi_signal_4));

            }

            return view;

        }

 

    }

 

}

程序运行效果图如下:

Android之Wifi学习(2)——连接Wifi

  1. package org.sunchao;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.Button;
  8. import android.widget.ScrollView;
  9. import android.widget.TextView;
  10. public class Main extends Activity implements OnClickListener {
  11.     // 右侧滚动条按钮
  12.     private ScrollView sView;
  13.     private Button openNetCard;
  14.     private Button closeNetCard;
  15.     private Button checkNetCardState;
  16.     private Button scan;
  17.     private Button getScanResult;
  18.     private Button connect;
  19.     private Button disconnect;
  20.     private Button checkNetWorkState;
  21.     private TextView scanResult;
  22.     private String mScanResult;
  23.     private WifiAdmin mWifiAdmin;
  24.     /** Called when the activity is first created. */
  25.     @Override
  26.     public void onCreate(Bundle savedInstanceState) {
  27.         super.onCreate(savedInstanceState);
  28.         setContentView(R.layout.main);
  29.         mWifiAdmin = new WifiAdmin(Main.this);
  30.         init();
  31.     }
  32.     /**
  33.      * 按钮等控件的初始化
  34.      */
  35.     public void init() {
  36.         sView = (ScrollView) findViewById(R.id.mScrollView);
  37.         openNetCard = (Button) findViewById(R.id.openNetCard);
  38.         closeNetCard = (Button) findViewById(R.id.closeNetCard);
  39.         checkNetCardState = (Button) findViewById(R.id.checkNetCardState);
  40.         scan = (Button) findViewById(R.id.scan);
  41.         getScanResult = (Button) findViewById(R.id.getScanResult);
  42.         scanResult = (TextView) findViewById(R.id.scanResult);
  43.         connect = (Button) findViewById(R.id.connect);
  44.         disconnect = (Button) findViewById(R.id.disconnect);
  45.         checkNetWorkState = (Button) findViewById(R.id.checkNetWorkState);
  46.         openNetCard.setOnClickListener(Main.this);
  47.         closeNetCard.setOnClickListener(Main.this);
  48.         checkNetCardState.setOnClickListener(Main.this);
  49.         scan.setOnClickListener(Main.this);
  50.         getScanResult.setOnClickListener(Main.this);
  51.         connect.setOnClickListener(Main.this);
  52.         disconnect.setOnClickListener(Main.this);
  53.         checkNetWorkState.setOnClickListener(Main.this);
  54.     }
  55.     /**
  56.      * WIFI_STATE_DISABLING 0 WIFI_STATE_DISABLED 1 WIFI_STATE_ENABLING 2
  57.      * WIFI_STATE_ENABLED 3
  58.      */
  59.     public void openNetCard() {
  60.         mWifiAdmin.openNetCard();
  61.     }
  62.     public void closeNetCard() {
  63.         mWifiAdmin.closeNetCard();
  64.     }
  65.     public void checkNetCardState() {
  66.         mWifiAdmin.checkNetCardState();
  67.     }
  68.     public void scan() {
  69.         mWifiAdmin.scan();
  70.     }
  71.     public void getScanResult() {
  72.         mScanResult = mWifiAdmin.getScanResult();
  73.         scanResult.setText(mScanResult);
  74.     }
  75.     public void connect() {
  76.         mWifiAdmin.connect();
  77. //      startActivityForResult(new Intent(
  78. //              android.provider.Settings.ACTION_WIFI_SETTINGS), 0);
  79.         startActivity(new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS));
  80.     }
  81.     public void disconnect() {
  82.         mWifiAdmin.disconnectWifi();
  83.     }
  84.     public void checkNetWorkState() {
  85.         mWifiAdmin.checkNetWorkState();
  86.     }
  87.     @Override
  88.     public void onClick(View v) {
  89.         switch (v.getId()) {
  90.         case R.id.openNetCard:
  91.             openNetCard();
  92.             break;
  93.         case R.id.closeNetCard:
  94.             closeNetCard();
  95.             break;
  96.         case R.id.checkNetCardState:
  97.             checkNetCardState();
  98.             break;
  99.         case R.id.scan:
  100.             scan();
  101.             break;
  102.         case R.id.getScanResult:
  103.             getScanResult();
  104.             break;
  105.         case R.id.connect:
  106.             connect();
  107.             break;
  108.         case R.id.disconnect:
  109.             disconnect();
  110.             break;
  111.         case R.id.checkNetWorkState:
  112.             checkNetWorkState();
  113.             break;
  114.         default:
  115.             break;
  116.         }
  117.     }
  118. }

Wifi工具类:

  1. package org.sunchao;
  2. import java.util.List;
  3. import android.content.Context;
  4. import android.net.wifi.ScanResult;
  5. import android.net.wifi.WifiConfiguration;
  6. import android.net.wifi.WifiInfo;
  7. import android.net.wifi.WifiManager;
  8. import android.net.wifi.WifiManager.WifiLock;
  9. import android.util.Log;
  10. public class WifiAdmin {
  11.     private final static String TAG = “WifiAdmin”;
  12.     private StringBuffer mStringBuffer = new StringBuffer();
  13.     private List<ScanResult> listResult;
  14.     private ScanResult mScanResult;
  15.     // 定义WifiManager对象
  16.     private WifiManager mWifiManager;
  17.     // 定义WifiInfo对象
  18.     private WifiInfo mWifiInfo;
  19.     // 网络连接列表
  20.     private List<WifiConfiguration> mWifiConfiguration;
  21.     // 定义一个WifiLock
  22.     WifiLock mWifiLock;
  23.     /**
  24.      * 构造方法
  25.      */
  26.     public WifiAdmin(Context context) {
  27.         mWifiManager = (WifiManager) context
  28.                 .getSystemService(Context.WIFI_SERVICE);
  29.         mWifiInfo = mWifiManager.getConnectionInfo();
  30.     }
  31.     /**
  32.      * 打开Wifi网卡
  33.      */
  34.     public void openNetCard() {
  35.         if (!mWifiManager.isWifiEnabled()) {
  36.             mWifiManager.setWifiEnabled(true);
  37.         }
  38.     }
  39.     /**
  40.      * 关闭Wifi网卡
  41.      */
  42.     public void closeNetCard() {
  43.         if (mWifiManager.isWifiEnabled()) {
  44.             mWifiManager.setWifiEnabled(false);
  45.         }
  46.     }
  47.     /**
  48.      * 检查当前Wifi网卡状态
  49.      */
  50.     public void checkNetCardState() {
  51.         if (mWifiManager.getWifiState() == 0) {
  52.             Log.i(TAG, “网卡正在关闭”);
  53.         } else if (mWifiManager.getWifiState() == 1) {
  54.             Log.i(TAG, “网卡已经关闭”);
  55.         } else if (mWifiManager.getWifiState() == 2) {
  56.             Log.i(TAG, “网卡正在打开”);
  57.         } else if (mWifiManager.getWifiState() == 3) {
  58.             Log.i(TAG, “网卡已经打开”);
  59.         } else {
  60.             Log.i(TAG, “—_—晕……没有获取到状态—_—“);
  61.         }
  62.     }
  63.     /**
  64.      * 扫描周边网络
  65.      */
  66.     public void scan() {
  67.         mWifiManager.startScan();
  68.         listResult = mWifiManager.getScanResults();
  69.         if (listResult != null) {
  70.             Log.i(TAG, “当前区域存在无线网络,请查看扫描结果”);
  71.         } else {
  72.             Log.i(TAG, “当前区域没有无线网络”);
  73.         }
  74.     }
  75.     /**
  76.      * 得到扫描结果
  77.      */
  78.     public String getScanResult() {
  79.         // 每次点击扫描之前清空上一次的扫描结果
  80.         if (mStringBuffer != null) {
  81.             mStringBuffer = new StringBuffer();
  82.         }
  83.         // 开始扫描网络
  84.         scan();
  85.         listResult = mWifiManager.getScanResults();
  86.         if (listResult != null) {
  87.             for (int i = 0; i < listResult.size(); i++) {
  88.                 mScanResult = listResult.get(i);
  89.                 mStringBuffer = mStringBuffer.append(“NO.”).append(i + 1)
  90.                         .append(” :”).append(mScanResult.SSID).append(“->”)
  91.                         .append(mScanResult.BSSID).append(“->”)
  92.                         .append(mScanResult.capabilities).append(“->”)
  93.                         .append(mScanResult.frequency).append(“->”)
  94.                         .append(mScanResult.level).append(“->”)
  95.                         .append(mScanResult.describeContents()).append(“nn”);
  96.             }
  97.         }
  98.         Log.i(TAG, mStringBuffer.toString());
  99.         return mStringBuffer.toString();
  100.     }
  101.     /**
  102.      * 连接指定网络
  103.      */
  104.     public void connect() {
  105.         mWifiInfo = mWifiManager.getConnectionInfo();
  106.     }
  107.     /**
  108.      * 断开当前连接的网络
  109.      */
  110.     public void disconnectWifi() {
  111.         int netId = getNetworkId();
  112.         mWifiManager.disableNetwork(netId);
  113.         mWifiManager.disconnect();
  114.         mWifiInfo = null;
  115.     }
  116.     /**
  117.      * 检查当前网络状态
  118.      * 
  119.      * @return String
  120.      */
  121.     public void checkNetWorkState() {
  122.         if (mWifiInfo != null) {
  123.             Log.i(TAG, “网络正常工作”);
  124.         } else {
  125.             Log.i(TAG, “网络已断开”);
  126.         }
  127.     }
  128.     /**
  129.      * 得到连接的ID
  130.      */
  131.     public int getNetworkId() {
  132.         return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
  133.     }
  134.     /**
  135.      * 得到IP地址
  136.      */
  137.     public int getIPAddress() {
  138.         return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
  139.     }
  140.     // 锁定WifiLock
  141.     public void acquireWifiLock() {
  142.         mWifiLock.acquire();
  143.     }
  144.     // 解锁WifiLock
  145.     public void releaseWifiLock() {
  146.         // 判断时候锁定
  147.         if (mWifiLock.isHeld()) {
  148.             mWifiLock.acquire();
  149.         }
  150.     }
  151.     // 创建一个WifiLock
  152.     public void creatWifiLock() {
  153.         mWifiLock = mWifiManager.createWifiLock(“Test”);
  154.     }
  155.     // 得到配置好的网络
  156.     public List<WifiConfiguration> getConfiguration() {
  157.         return mWifiConfiguration;
  158.     }
  159.     // 指定配置好的网络进行连接
  160.     public void connectConfiguration(int index) {
  161.         // 索引大于配置好的网络索引返回
  162.         if (index >= mWifiConfiguration.size()) {
  163.             return;
  164.         }
  165.         // 连接配置好的指定ID的网络
  166.         mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId,
  167.                 true);
  168.     }
  169.     // 得到MAC地址
  170.     public String getMacAddress() {
  171.         return (mWifiInfo == null) ? “NULL” : mWifiInfo.getMacAddress();
  172.     }
  173.     // 得到接入点的BSSID
  174.     public String getBSSID() {
  175.         return (mWifiInfo == null) ? “NULL” : mWifiInfo.getBSSID();
  176.     }
  177.     // 得到WifiInfo的所有信息包
  178.     public String getWifiInfo() {
  179.         return (mWifiInfo == null) ? “NULL” : mWifiInfo.toString();
  180.     }
  181.     // 添加一个网络并连接
  182.     public int addNetwork(WifiConfiguration wcg) {
  183.         int wcgID = mWifiManager.addNetwork(mWifiConfiguration.get(3));
  184.         mWifiManager.enableNetwork(wcgID, true);
  185.         return wcgID;
  186.     }
  187. }

AndroidManifest.xml:(注意需要添加的权限)

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”
  3.     package=“org.sunchao” android:versionCode=“1” android:versionName=“1.0”>
  4.     <uses-sdk android:minSdkVersion=“8” />
  5.     <application android:icon=“@drawable/icon” android:label=“@string/app_name”>
  6.         <activity android:name=“.Main” android:label=“@string/app_name”>
  7.             <intent-filter>
  8.                 <action android:name=“android.intent.action.MAIN” />
  9.                 <category android:name=“android.intent.category.LAUNCHER” />
  10.             </intent-filter>
  11.         </activity>
  12.     </application>
  13.     <!– 以下是使用wifi访问网络所需的权限 –>
  14.     <uses-permission android:name=“android.permission.CHANGE_NETWORK_STATE”></uses-permission>
  15.     <uses-permission android:name=“android.permission.CHANGE_WIFI_STATE”></uses-permission>
  16.     <uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE”></uses-permission>
  17.     <uses-permission android:name=“android.permission.ACCESS_WIFI_STATE”></uses-permission>
  18. </manifest>

布局文件就不贴了,我想看界面你都可以自己写出来,如果有需要,这里可以下载源代码:http://download.csdn.net/source/3449837