Git仓库地址

配套四维导图地址

AndroidAZ系列有以下目的:

  1. Android程序猿的面试(初级,中级,高级,资深),拿到满意的offer。
  2. Android程序猿学习进阶。

标记说明:因为笔者是列出所有的Android知识点,因此面试不需要看那么多内容,如果是面试的知识点。笔者会加上标记Face,而如果不是面试的知识点,笔者会加上No标记,它是要学的东西;然后笔者将Android面试者或者面试者分为4个等级,初级A1,中级A2,高级A3,资深A4,如果这个知识点是所有等级的范围,那么笔者将会以all标记上。因此进阶路线就是A1->A2->A3->A4。也是面试者挑选的复习范围,假如你是中级程序员,那么你面试要看的内容就是包含A2&Face的标记。

  • All : 所有的Android工程师都看。

  • A1: 初级Android工程师。

  • A2: 中级Android工程师。

  • A3: 高级Android工程师。

  • A4: 资深Android工程师。

  • Face: 是面试的知识点。

  • No: 面试基本遇不到。

  • AndroidAZ:四大组件之Activity(All,Face)
    • 1.Activity 是什么
    • 2.Activity的四种形态
      • Active
      • Paused
      • Stoped
      • Killed
    • 3.Activity的生命周期
      • 3.1 正常情况下的生命周期
        • onCreate()
        • onStart()
        • onResume()
        • onPause()
        • onStop()
        • onDestroy()
        • onRestart()
      • 3.2 异常情况下的生命周期
        • onSaveInstanceSate()
        • onRestoreInstanceState()
      • 3.3 特殊情形下的生命周期
    • 4.Activity的启动模式
      • standard
      • singleTop
      • singleTask
      • singleInstance
    • 5.Activity组件之间的通信
      • 5.1 Activity -> Activity
      • 5.2 Activity -> Service
      • 5.3 Activity -> Fragment
      • 5.4 应用间通信: ContentProvider的使用
    • 6.Scheme跳转协议
      • 什么是URL Scheme
      • Scheme协议在Android中使用场景
      • Scheme协议跳转实例
      • URI和URL
    • 7.源码解读 startActivity都干了什么
      • 流程总结:

1.Activity 是什么

活动时一种包含用户界面的组件,主要用于和用户进行交互.

2.Activity的四种形态

Active

活动状态

Paused

暂停状态

Stoped

停止状态

Killed

销毁状态

3.Activity的生命周期

3.1 正常情况下的生命周期

onCreate()

在活动第一次被创建的时候调用,这个方法用于初始化操作,比如加载布局,绑定事件等

onStart()

在活动由不可见变为可见的时候调用

onResume()

在活动准备好和用户进行交互的时候调用,此时活动一定位于返回栈的栈顶,并且处于运行状态

onPause()

在系统准备去启动或者恢复另一个活动的时候调用,这个方法通常用于释放消耗CPU的资源,保存关键数据,但是不能执行耗时操作,不然新活动的打开会有延迟

onStop()

这个方法在活动完全不可见的时候调用,它和onPause()方法的区别在于,如果新启动的活动是弹窗式或背景半透明的话,那么会执行onPause()不会执行onStop().

onDestroy()

在活动被销毁之前调用,之后活动进入Killed状态

onRestart()

在活动由onStop变为onStart()状态之前调用,也就是活动被重新启动了

3.2 异常情况下的生命周期

onSaveInstanceSate()

onSaveInstanceState方法会在什么时候被执行,有这么几种情况:

  1. 当用户按下HOME键时

  2. 长按HOME键,选择运行其他的程序时。

  3. 按下电源按键(关闭屏幕显示)时。

  4. 从activity A中启动一个新的activity时。

  5. 屏幕方向切换时,例如从竖屏切换到横屏时。

总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据

static final String STATE_SCORE = "playerScore";static final String STATE_LEVEL = "playerLevel";...@Overridepublic void onSaveInstanceState(Bundle savedInstanceState) {    // 保存用户自定义的状态    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);    // 调用父类交给系统处理,这样系统能保存视图层次结构状态    super.onSaveInstanceState(savedInstanceState);}

onRestoreInstanceState()

当您的Activity在之前被破坏后重新创建时,您可以从Bundle系统通过您的Activity中恢复您的保存状态。这两个方法onCreate()和onRestoreInstanceState()回调方法都会收到Bundle包含实例状态信息的相同方法。

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState); // 记得总是调用父类    // 检查是否正在重新创建一个以前销毁的实例    if (savedInstanceState != null) {        // 从已保存状态恢复成员的值        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);    } else {        // 可能初始化一个新实例的默认值的成员    }    ...}

也可以选择执行onRestoreInstanceState(),而不是在系统调用onStart()方法之后恢复状态。系统onRestoreInstanceState()只有在存在保存状态的情况下才会恢复,因此您不需要检查是否Bundle为空:

public void onRestoreInstanceState(Bundle savedInstanceState) {    // 总是调用超类,以便它可以恢复视图层次超级    super.onRestoreInstanceState(savedInstanceState);    // 从已保存的实例中恢复状态成员    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);}

3.3 特殊情形下的生命周期

  1. Activity的横竖屏切换
  • 一般情况下的生命周期

    onCreate()

    onStart()

    onResume()

    onPause()

    onSaveInstanceState()

    onStop()

    onDestroy()

    切换中

    onCreate()

    onStart()

    onRestoreInstanceSate()

    onResume()

  • 设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时只会执行

    onConfigurationChanged()

  1. 什么时候Activity单独走onPause()而不走onStop()

    当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stoped 状态。

    当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然可见,但它已经失去了焦点故不可与用户交互,所以只会执行onPause()而不走onStop()。

  2. 什么情况导致Activity的onDestroy()不执行

  • 强制回收

    外部强制关闭进程

  • 出现异常

    异常崩溃的时候

4.Activity的启动模式

standard

活动的默认启动模式,在这个模式下,每当启动一个新的活动,他就会在返回栈中入栈,并处于栈顶的位置.对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的示例

singleTop

启动活动时如果返回栈的栈顶已经是该活动,则默认使用栈顶活动不会新建,但是当这个活动没有在栈顶时,还是会创建新的实例

singleTask

活动启动时或检查返回栈中的所有实例,如果已经存在则直接使用该实例,并把这个活动之上的所有活动统统出栈,如果没有则新建一个活动实例

singleInstance

指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈);并且该返回栈中只放入这一个活动。

整个手机操作系统里面只有一个实例存在。不同的应用去打开这个activity 共享公用的同一个activity。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。应用场景:呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。或者你确定你需要使Activity只有一个实例。
可以得出以下结论:

  1. 以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例。

  2. 以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

  3. 以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中。

  4. 被singleInstance模式的Activity开启的其他activity,能够在新的任务中启动,但不一定开启新的任务,也可能在已有的一个任务中开启。

5.Activity组件之间的通信

5.1 Activity -> Activity

  • Intent/Bundle

  • SharePreference, 文件存储, 数据库

  • 类的静态变量

  • Application全局变量

  • startActivityForResult() + onActivityResult()

5.2 Activity -> Service

  • 通过bundle传递数据,startService(intent)和bindService()方法启动服务

    分别调用startService(intent)和stopService(intent)来启动和停止服务。在Service中,我们通过onStartCommand(finalIntent intent, int flags, intstartId)这个函数来接收从Activity传过来的值

  • 通过Binder(activity和service属于同一个application应用进程)

    bindService(intent, connection, Context.BIND_AUTO_CREATE);在connection中获取到Binder对象,通过Binder获取到Service即可调用service中对应的方法。为了确保应用的安全性,启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。

    public class LocalService extends Service {    // 实例化自定义的Binder类    private final IBinder mBinder = new LocalBinder();    // 随机数的生成器    private final Random mGenerator = new Random();    /**    * 自定义的Binder类,这个是一个内部类,所以可以知道其外围类的对象,通过这个类,让Activity知道其Service的对象    */    public class LocalBinder extends Binder {  //Binder 实现了IBinder接口        LocalService getService() {            // 返回Activity所关联的Service对象,这样在Activity里,就可调用Service里的一些公用方法和公用属性            return LocalService.this;        }    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    /** public方法,Activity可以进行调用 */    public int getRandomNumber() {    return mGenerator.nextInt(100);    }}

    在Activity中创建connection对象

    /** 定交ServiceConnection,用于绑定Service的*/    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,IBinder service) {            // 已经绑定了LocalService,强转IBinder对象,调用方法得到LocalService对象            LocalBinder binder = (LocalBinder) service;            mService = binder.getService();        }        @Override        public void onServiceDisconnected(ComponentName arg0) {        }    };}
  • CallBack + Handler, 监听Service进程的变化

  1. 单向通信(Activity--->Service)

    使用Messenger和Service通信的步骤:

    Service中的工作:

    在Service中,创建Handler对象用来处理消息,并用该Handler创建Messenger对象;
    在Service中,用创建好的Messenger对象获取一个IBinder对象,通过Service的onBind方法中返回给Activity。
    Activity中的工作:

    用在ServiceConnection中获取的IBinder对象创建一个Messenger对象,该Messenger对象持有Service中的Handler对象;

    用Message msg = Message.obtain()方法创建消息,message可以通过setData(Bundle data)携带数据;

    用Messenger.send(Message msg)发送消息给Service中的Handler。

    通过上述几个步骤,即可实现单向通信(Activity--->Service),如果要实现双线通信,很容易想到只要在Service中获取到包含Activity中Handler对象的Messenger发送消息即可。

  2. 双向通信

    现在要实现双向通信的关键步骤是得获取到Activity中的Messenger对象。如何获取?在Message中有个属性:

    public Messenger    replyTo  //用来对该消息的回复

    关键步骤在Activity通过Messenger.send(Message msg)给Service发送消息之前,指定Message的replyTo=clientMessaenger,然后Service通过message.replayTo获取到Activity的Messaenger,这样Service就可以向Activity发送消息了,实现双向通信。

5.3 Activity -> Fragment

  • 通过fragment.setArguments(bundle)方式传递数据,bundle传递数据

    MyFragment myFragment = new MyFragment();Bundle bundle = new Bundle();bundle.putString("DATA",values);//这里的values就是我们要传的值myFragment.setArguments(bundle);
  • 在Fragment和Actiivty关联后,调用Activity中的public修饰的方法

    Fragment中的onAttach方法,表明Fragment和Activity进行了关联,用该种方式容易出现Fragment getActivity为null的情况,得注意Fragment生命周期,避免出现crash。

5.4 应用间通信: ContentProvider的使用

ContentProvider它主要的作用是:实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限。所以组件之间也可以通过ContentProvider来进行数据通信,本质是通过数据库文件实现通信。

ContentProvider提供数据供访问,需要使用ContentResolver来访问数据。最主要的工作就是操作数据库。两大步:

自定义ContentProvider,主要对数据库进行存取等操作。

获取ContentReolver对象,解析对应数据库的URI来访问和读取数据库等操作。如下查询操作(注意权限):

ContentResolver contentResolver = getContentResolver();Uri uri = Uri.parse("content://com.example.MyApplication.StudentsProvider/students");Cursor cursor = contentResolver.query(uri, null, null, null, null);

6.Scheme跳转协议

6.1 什么是URL Scheme

android中的scheme是一种页面内跳转协议。

通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;

通过scheme协议,服务器可以定制化告诉App跳转到APP内部页面。

之前项目都是我们客户端和服务器端用自定义json串的形式来告诉客户端如何跳转,这种方式要手动解析字段,有点麻烦。然而scheme协议自带字段解析,非常之方便,后面我们就放弃了json解析的方式。

6.2 Scheme协议在Android中使用场景

  • H5跳转到native页面

  • 客户端获取push消息中后,点击消息跳转到APP内部页面

  • APP根据URL跳转到另外一个APP指定页面

6.3 Scheme协议跳转实例

  1. 在Mainefest配置文件中配置需要用scheme协议跳转的Activity
                                                                                                                                                    
  1. 模拟从网络中获取scheme协议的url
public class MainActivity extends AppCompatActivity {    private TextView btnSchemeTv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btnSchemeTv = (TextView) findViewById(R.id.btn_scheme_tv);        btnSchemeTv.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                /**                 * (1)在manifest配置文件中配置了scheme参数                 * (2)网络端获取url                 * (3)跳转                 */                String url = "scheme://mtime/goodsDetail?goodsId=10011002";                Intent intent = new Intent(Intent.ACTION_VIEW,                        Uri.parse(url));                startActivity(intent);            }        });    }}
  1. 获取scheme协议参数
public class SchemeActivity extends Activity {    private static final String TAG = "SchemeActivity";    private TextView schemeTv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_scheme);        schemeTv = (TextView) findViewById(R.id.scheme_tv);        Uri data = getIntent().getData();        Log.i(TAG, "host = " + data.getHost() + " path = " + data.getPath() + " query = " + data.getQuery());        String param = data.getQueryParameter("goodsId");        schemeTv.setText("获取的参数为:" + param);    }}

6.4 URI和URL

  • URL :统一资源定位符

  • URI: 统一资源标识符

    统一资源标志符URI就是在某一规则下能把一个资源独一无二地标识出来。
    拿人做例子,假设这个世界上所有人的名字都不能重复,那么名字就是URI的一个实例,通过名字这个字符串就可以标识出唯一的一个人。
    现实当中名字当然是会重复的,所以身份证号才是URI,通过身份证号能让我们能且仅能确定一个人。
    那统一资源定位符URL是什么呢。也拿人做例子然后跟HTTP的URL做类比,就可以有:

    动物住址协议://地球/中国/浙江省/杭州市/西湖区/某大学/14号宿舍楼/525号寝/张三.人

    可以看到,这个字符串同样标识出了唯一的一个人,起到了URI的作用,所以URL是URI的子集。URL是以描述人的位置来唯一确定一个人的。
    在上文我们用身份证号也可以唯一确定一个人。对于这个在杭州的张三,我们也可以用:身份证号:123456789来标识他。
    所以不论是用定位的方式还是用编号的方式,我们都可以唯一确定一个人,都是URl的一种实现,而URL就是用定位的方式实现的URI。

    Web上,假设所有的Html文档都有唯一的编号,记作html:xxxxx,xxxxx是一串数字,即Html文档的身份证号码,这个能唯一标识一个Html文档,那么这个号码就是一个URI。
    而URL则通过描述是哪个主机上哪个路径上的文件来唯一确定一个资源,也就是定位的方式来实现的URI。
    对于现在网址我更倾向于叫它URL,毕竟它提供了资源的位置信息,如果有一天网址通过号码来标识变成了741236985.html,那感觉叫成URI更为合适.

7.源码解读 startActivity都干了什么

我们常用的startActivity方法

@Overridepublic void startActivity(Intent intent) {    this.startActivity(intent, null);}

调用了带Bundle参数的重载方法

@Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {    if (options != null) {        startActivityForResult(intent, -1, options);    } else {        // Note we want to go through this call for compatibility with        // applications that may have overridden the method.        startActivityForResult(intent, -1);    }}

最终会调用了startActivityForResult方法,也就是说我们平常调用的所有重载startAcitvity其实就是调用startAcitivityForResult,只不过requesetCode参数为-1
startAcitivityForResult里面调用了Instrumentation类的execStartActivity方法,Instrumentation这个类很重要,我们和四大组件打交道方法基本都是调用的这个类的方法,那这个对象什么时候给赋值的呢?

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {    if (mParent == null) {        options = transferSpringboardActivityOptions(options);        Instrumentation.ActivityResult ar =            mInstrumentation.execStartActivity(                this, mMainThread.getApplicationThread(), mToken, this,                intent, requestCode, options);        ...省略部分代码    }}

attach方法里面我们看到是系统创建好直接传递进来的,也就是说这个对象是外面创建好的,至于哪里创建好的,那要涉及到app的启动流程了,后面再讲。这里我们先知道,一个app的这个类只有一个实例对象。
可以看到attach方法里面传递了很多已经创建好的重要的对象,比如我们的application对象

final void attach(Context context, ActivityThread aThread,        Instrumentation instr, IBinder token, int ident,        Application application, Intent intent, ActivityInfo info,        CharSequence title, Activity parent, String id,        NonConfigurationInstances lastNonConfigurationInstances,        Configuration config, String referrer, IVoiceInteractor voiceInteractor,        Window window) {            attachBaseContext(context);            mFragments.attachHost(null /*parent*/);            mWindow = new PhoneWindow(this, window);            mUiThread = Thread.currentThread();            mInstrumentation = instr;            mToken = token;        ....    }

继续追踪Instrumentation里面的execStartActivity方法
可以看到里面调用了ActivityManagerNative.getDefault().startActivity方法。通过这个调用,我们大概可以猜到这个调用是获取单例,继续去看ActivityManagerNative

public ActivityResult execStartActivity(        Context who, IBinder contextThread, IBinder token, Activity target,        Intent intent, int requestCode, Bundle options) {        try {        intent.migrateExtraStreamToClipData();        intent.prepareToLeaveProcess(who);        int result = ActivityManagerNative.getDefault()            .startActivity(whoThread, who.getBasePackageName(), intent,                    intent.resolveTypeIfNeeded(who.getContentResolver()),                    token, target != null ? target.mEmbeddedID : null,                    requestCode, 0, null, options);        checkStartActivityResult(result, intent);    } catch (RemoteException e) {        throw new RuntimeException("Failure from system", e);    }    return null;}

发现这个ActivityManagerNative继承了Binder,而Binder是干嘛用的,Binder实现了IBinder接口,IBinder我们应该不陌生,在AIDL,绑定服务的时候,返回给调用的就是一个IBinder实现对象。这里其实就是返回了系统的ActivityManagerService的一个Binder代理对象,

public abstract class ActivityManagerNative extends Binder implements IActivityManager    static public IActivityManager getDefault() {        return gDefault.get();    }    //gDefult对象的创建,就是一个单例,    private static final Singleton gDefault = new Singleton() {        protected IActivityManager create() {            IBinder b = ServiceManager.getService("activity");            if (false) {                Log.v("ActivityManager", "default service binder = " + b);            }            IActivityManager am = asInterface(b);            if (false) {                Log.v("ActivityManager", "default service = " + am);            }            return am;        }};

查看SingleTon 就知道get()方法返回的就是create()方法创建的对象

public abstract class Singleton {    private T mInstance;    protected abstract T create();    public final T get() {        synchronized (this) {            if (mInstance == null) {                mInstance = create();            }            return mInstance;        }    }}

这个时候当前调用线程挂起,代码就跑到到系统进程来了,查看ActivityManagerServicestartActivity方法

public final class ActivityManagerService extends ActivityManagerNative        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback    @Override    public final int startActivity(IApplicationThread caller, String callingPackage,            Intent intent, ...省略参数) {        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,                resultWho, requestCode, startFlags, profilerInfo, bOptions,                UserHandle.getCallingUserId());    }}

内部调用了startAcitivityAsUser方法

@Overridepublic final int startActivityAsUser(IApplicationThread caller, String callingPackage,        Intent intent, ...省略参数) {    enforceNotIsolatedCaller("startActivity");    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),            userId, false, ALLOW_FULL_ONLY, "startActivity", null);    //调用ActivityStarter的startAcitivityMayWait方法    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,            profilerInfo, null, null, bOptions, false, userId, null, null);}

这里调用了ActivityStarterstartAcitivityMayWait方法,这个方法比较长,下面是分析一些关键的代码,可以看到ResolverInfo对象和AcitivityInfo对象这个时候开始创建了。这个ActivityInfo就是对应着调用者APP里面的Activity,同样,四大组件在系统服务这边都有着这样的对应关系。

final int startActivityMayWait(省略很长很长的参数) {        //生成ResolverInfo对象        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);         // 生成AcitivityInfo对象        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);        final ActivityRecord[] outRecord = new ActivityRecord[1];        //调用startAcitivityLocked方法        int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,                aInfo, rInfo, voiceSession, voiceInteractor,                resultTo, resultWho, requestCode, callingPid,                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,                options, ignoreTargetSecurity, componentSpecified, outRecord, container,                inTask);                return res;    }}

调用startAcitivityLocked方法,这个方法依然很长啊,前面一大段代码是检查工作,继续挑重点的看,调用了startAcitivityUnchecked

final int startActivityLocked() {    int err = ActivityManager.START_SUCCESS;    try {        mService.mWindowManager.deferSurfaceLayout();        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,                true, options, inTask);    } finally {        mService.mWindowManager.continueSurfaceLayout();    }    postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);    return err;}

startAcitivityUnchecked方法,依然看重点,这个调用了ActivityStackstartAcitivityLocked方法

private int startActivityUnchecked(省略参数) {       //省略前面代码    mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);    return START_SUCCESS;}

通过这个ActivityStack类名我们大概可以猜测这个类应该是操作acitivity栈的,因为我们都知道每个app都有一个或者多个acitivity栈,开启一个acitivity都需要入栈退出的acitivity都需要出栈,这些都是系统内部处理的。

进入AcitivityStack类的startAcitityLocked方法,可以看到startActivityLocked方法里面把当前activity放到了栈顶,并且当前activity所处的栈设置为前台栈

final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,        ActivityOptions options) {        TaskRecord task = null;//把当前activity设置到栈顶    task.addActivityToTop(r);  //把栈设置前台栈  task.setFrontOfTask();        if (VALIDATE_TOKENS) {        validateAppTokensLocked();    }}

调用完成后再回到AcitivityStarterstartAcitivityUnchecked

private int startActivityUnchecked(省略参数) {    mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);    mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,                    mOptions);        }   }

前面的startActivityLocked调用完成后,再调用AcitivityStackSupervisorresumeFocusedStackTopActivityLocked方法

boolean resumeFocusedStackTopActivityLocked(        ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {    if (targetStack != null && isFocusedStack(targetStack)) {        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);    }    final ActivityRecord r = mFocusedStack.topRunningActivityLocked();    if (r == null || r.state != RESUMED) {        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);    }    return false;}

又调用AcitivityStarterresumeTopActivityUncheckedLocked方法

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {    boolean result = false;    try {        // Protect against recursion.        mStackSupervisor.inResumeTopActivity = true;        if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {            mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;            mService.updateSleepIfNeededLocked();        }        result = resumeTopActivityInnerLocked(prev, options);    } finally {        mStackSupervisor.inResumeTopActivity = false;    }    return result;}

又调用了resumeTopActivityInnerLocked

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {    // 获取当前正在栈顶的Activity信息    final ActivityRecord next = topRunningActivityLocked();    final TaskRecord prevTask = prev != null ? prev.task : null;    next.delayedResume = false;        if (mResumedActivity != null) {        pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);          return true;}

继续调用了startPausingLocked,传入的这个next参数就是当前栈顶的activity 信息,看方法名称我们大概知道应该会走acitivity onPause方法回调了。
继续看startPausingLocked方法

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,        ActivityRecord resuming, boolean dontWait) {       ActivityRecord prev = mResumedActivity;        try {           //thread 即IApplicationThread            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,                    userLeaving, prev.configChangeFlags, dontWait);        } catch (Exception e) {            // Ignore exception, if process died other code will cleanup.            Slog.w(TAG, "Exception thrown during pause", e);            mPausingActivity = null;            mLastPausedActivity = null;            mLastNoHistoryActivity = null;        }    }        if (dontWait) {            // If the caller said they don't want to wait for the pause, then complete            // the pause now.            completePauseLocked(false, resuming);            return false;        } else {            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);            msg.obj = prev;            prev.pauseTime = SystemClock.uptimeMillis();            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");            return true;        }    } else {        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");        if (resuming == null) {            mStackSupervisor.resumeFocusedStackTopActivityLocked();        }        return false;    }}

这个IApplicationThread的实现就是ActivityThread 内部类ApplicationThread,这个类继承了ApplicationThreadNative,ApplicationThreadNative又继承了Binder,因此系统可以和我们的APP通讯。
上面是在系统进程里面执行的,但最终还是要回到我们自己的APP进程啊,这个时候系统持有我们APP 的一个Binder,和我们和系统进程通讯是一样的

public abstract class ApplicationThreadNative extends Binder        implements IApplicationThread//这个类是ActivityThread的内部类//这个时候回到我们自己的APP进程了。private class ApplicationThread extends ApplicationThreadNative {    private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";    private int mLastProcessState = -1;    public final void schedulePauseActivity(IBinder token, boolean finished,            boolean userLeaving, int configChanges, boolean dontReport) {        int seq = getLifecycleSeq();        if (DEBUG_ORDER) Slog.d(TAG, "pauseActivity " + ActivityThread.this                + " operation received seq: " + seq);        sendMessage(                finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,                token,                (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),                configChanges,                seq);    }private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {     Message msg = Message.obtain();    msg.what = what;    SomeArgs args = SomeArgs.obtain();    args.arg1 = obj;    args.argi1 = arg1;    args.argi2 = arg2;    args.argi3 = seq;    msg.obj = args;    mH.sendMessage(msg);}final H mH = new H();

内部有个Handler。通过这个Handler发送了一个PAUSE_ACTIVITY消息

private class H extends Handler//找到处理PAUSE_ACTIVITY消息的地方case PAUSE_ACTIVITY: {    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");    SomeArgs args = (SomeArgs) msg.obj;    handlePauseActivity((IBinder) args.arg1, false,            (args.argi1 & USER_LEAVING) != 0, args.argi2,            (args.argi1 & DONT_REPORT) != 0, args.argi3);    maybeSnapshot();    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;

继续查看handlePauseActivity方法,

private void handlePauseActivity(IBinder token, boolean finished,        boolean userLeaving, int configChanges, boolean dontReport, int seq) {            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");        // Tell the activity manager we have paused.        if (!dontReport) {            try {          //回调给系统ActivityManagerService                ActivityManagerNative.getDefault().activityPaused(token);            } catch (RemoteException ex) {                throw ex.rethrowFromSystemServer();            }        }        mSomeActivitiesChanged = true;    }}

调用了performPauseActivity

final Bundle performPauseActivity(IBinder token, boolean finished,        boolean saveState, String reason) {    ActivityClientRecord r = mActivities.get(token);    return r != null ? performPauseActivity(r, finished, saveState, reason) : null;}

performPauseActivity调用重载方法

final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,        boolean saveState, String reason) {    performPauseActivityIfNeeded(r, reason);    return !r.activity.mFinished && saveState ? r.state : null;}

调用了performPauseActivityIfNeeded方法

private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {    try {        r.activity.mCalled = false;        mInstrumentation.callActivityOnPause(r.activity);        EventLog.writeEvent(LOG_AM_ON_PAUSE_CALLED, UserHandle.myUserId(),                r.activity.getComponentName().getClassName(), reason);        if (!r.activity.mCalled) {            throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)                    + " did not call through to super.onPause()");        }    } catch (SuperNotCalledException e) {        throw e;    } catch (Exception e) {        if (!mInstrumentation.onException(r.activity, e)) {            throw new RuntimeException("Unable to pause activity "                    + safeToComponentShortString(r.intent) + ": " + e.toString(), e);        }    }    r.paused = true;}

调用了Instrumentation内的callAcitivityOnPause方法。终于调用到了Acitivity的方法。

public void callActivityOnPause(Activity activity) {    activity.performPause();}

ActivityperformPause方法。看到了熟悉的onPause生命周期方法。看到没有,如果我们没有调用super.onPause()方法,则会报异常。

final void performPause() {    mDoReportFullyDrawn = false;    mFragments.dispatchPause();    mCalled = false;    onPause();    mResumed = false;    if (!mCalled && getApplicationInfo().targetSdkVersion            >= android.os.Build.VERSION_CODES.GINGERBREAD) {        throw new SuperNotCalledException(                "Activity " + mComponent.toShortString() +                " did not call through to super.onPause()");    }    mResumed = false;}

分析到这终于看到调用我们自己的代码了。
真是漫漫长路。喝杯水先.........继续往下分析。
前面才是当前Acitivity调用了onPause()方法,要开启的acitivity的生命周期在哪里被调用的呢?
继续回到handlePauseAcitivity方法,这个时候又通过Binder调用ActivityManagerSer的方法

private void handlePauseActivity(IBinder token, boolean finished,        boolean userLeaving, int configChanges, boolean dontReport, int seq) {         // Tell the activity manager we have paused.        if (!dontReport) {            try {          //回调给系统ActivityManagerService                ActivityManagerNative.getDefault().activityPaused(token);            } catch (RemoteException ex) {                throw ex.rethrowFromSystemServer();            }        }        mSomeActivitiesChanged = true;    }}

继续到系统进程ActivityManagerService里面的acitivityPaused方法

@Overridepublic final void activityPaused(IBinder token) {    final long origId = Binder.clearCallingIdentity();    synchronized(this) {        ActivityStack stack = ActivityRecord.getStackLocked(token);        if (stack != null) {            stack.activityPausedLocked(token, false);        }    }    Binder.restoreCallingIdentity(origId);}

调用ActivityStackactivityPausedLocked方法

final void activityPausedLocked(IBinder token, boolean timeout) {    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,        "Activity paused: token=" + token + ", timeout=" + timeout);    final ActivityRecord r = isInStackLocked(token);    if (r != null) {        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);        if (mPausingActivity == r) {            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r                    + (timeout ? " (due to timeout)" : " (pause complete)"));            completePauseLocked(true, null);            return;        } else {                   }    }    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);}

调用completePauseLocked方法

private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {    ActivityRecord prev = mPausingActivity;    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);    if (resumeNext) {        final ActivityStack topStack = mStackSupervisor.getFocusedStack();        if (!mService.isSleepingOrShuttingDownLocked()) {            mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);        } else {            mStackSupervisor.checkReadyForSleepLocked();            ActivityRecord top = topStack.topRunningActivityLocked();            if (top == null || (prev != null && top != prev)) {                mStackSupervisor.resumeFocusedStackTopActivityLocked();            }        }    }    if (prev != null) {        prev.resumeKeyDispatchingLocked();        if (prev.app != null && prev.cpuTimeAtResume > 0                && mService.mBatteryStatsService.isOnBattery()) {            long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)                    - prev.cpuTimeAtResume;            if (diff > 0) {                BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();                synchronized (bsi) {                    BatteryStatsImpl.Uid.Proc ps =                            bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,                                    prev.info.packageName);                    if (ps != null) {                        ps.addForegroundTimeLocked(diff);                    }                }            }        }        prev.cpuTimeAtResume = 0; // reset it    }    if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause            || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {        mService.notifyTaskStackChangedLocked();        mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;    }    mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);}

调用了ActivityStackSupervisorresumeFocusedStackTopActivityLocked方法

boolean resumeFocusedStackTopActivityLocked(        ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {    if (targetStack != null && isFocusedStack(targetStack)) {        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);    }    final ActivityRecord r = mFocusedStack.topRunningActivityLocked();    if (r == null || r.state != RESUMED) {        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);    }    return false;}

回调了 ActivityStackresumeTopActivityUncheckedLocked方法

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {    boolean result = false;    try {        // Protect against recursion.        mStackSupervisor.inResumeTopActivity = true;        if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {            mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;            mService.updateSleepIfNeededLocked();        }        result = resumeTopActivityInnerLocked(prev, options);    } finally {        mStackSupervisor.inResumeTopActivity = false;    }    return result;}

又调用了resumeTopActivityInnerLocked方法

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {    mStackSupervisor.startSpecificActivityLocked(next, true, false);    return true;}

调用了ActivityStackSupervisorstartSpecificActivityLocked方法

void startSpecificActivityLocked(ActivityRecord r,        boolean andResume, boolean checkConfig) {    // Is this activity's application already running?    ProcessRecord app = mService.getProcessRecordLocked(r.processName,            r.info.applicationInfo.uid, true);    r.task.stack.setLaunchTime(r);    if (app != null && app.thread != null) {        try {            if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0                    || !"android".equals(r.info.packageName)) {                              app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,                        mService.mProcessStats);            }            realStartActivityLocked(r, app, andResume, checkConfig);            return;        } catch (RemoteException e) {            Slog.w(TAG, "Exception when starting activity "                    + r.intent.getComponent().flattenToShortString(), e);        }        // If a dead object exception was thrown -- fall through to        // restart the application.    }    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,            "activity", r.intent.getComponent(), false, false, true);}

调用了realStartActivityLocked方法

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,        boolean andResume, boolean checkConfig) throws RemoteException {    final ActivityStack stack = task.stack;    try {         List results = null;        List newIntents = null;        if (andResume) {            results = r.results;            newIntents = r.newIntents;        }               r.sleeping = false;        r.forceNewConfig = false;        mService.showUnsupportedZoomDialogIfNeededLocked(r);        mService.showAskCompatModeDialogLocked(r);        r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);        ProfilerInfo profilerInfo = null;       if (andResume) {            app.hasShownUi = true;            app.pendingUiClean = true;        }        app.forceProcessStateUpTo(mService.mTopProcessState);        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,                System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),                new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,                task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,                newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);    return true;}

又是回调了AcitivityThread的scheduleLaunchActivity方法。按照前面分析的套路,这里应该又是利用在Handler发送消息

@Overridepublic final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,        int procState, Bundle state, PersistableBundle persistentState,        List pendingResults, List pendingNewIntents,        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {    sendMessage(H.LAUNCH_ACTIVITY, r);}

发送了一个LAUNCH_ACTIVITY消息
找到处理LAUNCH_ACTIVITY消息的地方

case LAUNCH_ACTIVITY: {        handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {    // If we are getting ready to gc after going to the background, well    // we are back active so skip it.    unscheduleGcIdler();    mSomeActivitiesChanged = true;    if (r.profilerInfo != null) {        mProfiler.setProfiler(r.profilerInfo);        mProfiler.startProfiling();    }    if (localLOGV) Slog.v(        TAG, "Handling launch of " + r);    Activity a = performLaunchActivity(r, customIntent);    if (a != null) {        r.createdConfig = new Configuration(mConfiguration);        reportSizeConfigurations(r);        Bundle oldState = r.state;        handleResumeActivity(r.token, false, r.isForward,                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);        if (!r.activity.mFinished && r.startsNotResumed) {                 performPauseActivityIfNeeded(r, reason);            if (r.isPreHoneycomb()) {                r.state = oldState;            }        }    } else {        // If there was an error, for any reason, tell the activity manager to stop us.        try {            ActivityManagerNative.getDefault()                .finishActivity(r.token, Activity.RESULT_CANCELED, null,                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);        } catch (RemoteException ex) {            throw ex.rethrowFromSystemServer();        }    }}

调用了performLaunchActivity方法,这个里面的代码就很明了了。获取Intent里面的activity信息,创建一个Activitity实例出来,如果Application没有,则创建一个Application出来。先执行activityattach方法,在设置主题,然后回调onCreate方法,后又回调onStart方法。至此我们的activity就被创建出来了。至于后面的onResume 等等生命周期方法调用方式和这差不多。有兴趣自行分析。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");    ActivityInfo aInfo = r.activityInfo;    if (r.packageInfo == null) {        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,                Context.CONTEXT_INCLUDE_CODE);    }    ComponentName component = r.intent.getComponent();    if (component == null) {        component = r.intent.resolveActivity(            mInitialApplication.getPackageManager());        r.intent.setComponent(component);    }    if (r.activityInfo.targetActivity != null) {        component = new ComponentName(r.activityInfo.packageName,                r.activityInfo.targetActivity);    }    Activity activity = null;    //通过反射实例Acitivity    try {        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();        activity = mInstrumentation.newActivity(                cl, component.getClassName(), r.intent);        StrictMode.incrementExpectedActivityCount(activity.getClass());        r.intent.setExtrasClassLoader(cl);        r.intent.prepareToEnterProcess();        if (r.state != null) {            r.state.setClassLoader(cl);        }    } catch (Exception e) {        if (!mInstrumentation.onException(activity, e)) {            throw new RuntimeException(                "Unable to instantiate activity " + component                + ": " + e.toString(), e);        }    }    try {        //创建Application        Application app = r.packageInfo.makeApplication(false, mInstrumentation);        if (activity != null) {            Context appContext = createBaseContextForActivity(r, activity);            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());            Configuration config = new Configuration(mCompatConfiguration);            if (r.overrideConfig != null) {                config.updateFrom(r.overrideConfig);            }            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "                    + r.activityInfo.name + " with config " + config);            Window window = null;            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {                window = r.mPendingRemoveWindow;                r.mPendingRemoveWindow = null;                r.mPendingRemoveWindowManager = null;            }             //执行attach方法            activity.attach(appContext, this, getInstrumentation(), r.token,                    r.ident, app, r.intent, r.activityInfo, title, r.parent,                    r.embeddedID, r.lastNonConfigurationInstances, config,                    r.referrer, r.voiceInteractor, window);            if (customIntent != null) {                activity.mIntent = customIntent;            }            r.lastNonConfigurationInstances = null;            activity.mStartedActivity = false;            int theme = r.activityInfo.getThemeResource();            if (theme != 0) {                activity.setTheme(theme);            }            activity.mCalled = false;            if (r.isPersistable()) {                //回调OnCreate方法                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);            } else {                mInstrumentation.callActivityOnCreate(activity, r.state);            }            if (!activity.mCalled) {                throw new SuperNotCalledException(                    "Activity " + r.intent.getComponent().toShortString() +                    " did not call through to super.onCreate()");            }            r.activity = activity;            r.stopped = true;            if (!r.activity.mFinished) {                //回调onStart()方法                activity.performStart();                r.stopped = false;            }            if (!r.activity.mFinished) {                if (r.isPersistable()) {                    if (r.state != null || r.persistentState != null) {                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,                                r.persistentState);                    }                } else if (r.state != null) {                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);                }            }            if (!r.activity.mFinished) {                activity.mCalled = false;                if (r.isPersistable()) {                    mInstrumentation.callActivityOnPostCreate(activity, r.state,                            r.persistentState);                } else {                    mInstrumentation.callActivityOnPostCreate(activity, r.state);                }              }        }        r.paused = true;        mActivities.put(r.token, r);    }    return activity;}

Instrumentation的newActivity方法

public Activity newActivity(ClassLoader cl, String className,        Intent intent)        throws InstantiationException, IllegalAccessException,        ClassNotFoundException {    return (Activity)cl.loadClass(className).newInstance();}

流程总结:

简单说下调用流程,
Activity.startActivity()->

Instrumentation.execStartActivity()->

ActivityManagerService.startActivity调用到系统进程

然后还要回调到app: app.thread.scheduleXXX->ApplicationThread.scheduleXXX->H,利用Handler切换到当前线程

虽然我们只是调用了一句startActivity方法,但是里面的调用过程去很复杂,涉及到和系统进程通讯。

因为activity是四大组件中最复杂的一个,因此如果弄清了activity的调用过程,后面看其他三个组件的调用过程也就不那么费力。

APP和系统双向通讯都是利用Binder,我们的APP保存了系统服务 的Binder引用,引用类型是ActivityManagerServer。

但是Binder是单向传输的,所以系统也保留了我们的Binder引用,应用类型为ActivityThread.ApplicationtThread.

AcitivityThread.ApplicationtThread接收到系统的消息利用内部一个H类型变量mH把消息发送给主线程处理

更多相关文章

  1. Nginx系列教程(六)| 手把手教你搭建 LNMP 架构并部署天空网络电影
  2. Android(安卓)RecyclerView adapter notify*** 的几个方法分析
  3. android intent以及Dart&Henson
  4. 【腾讯Bugly干货分享】Android(安卓)进程保活招式大全
  5. Android开发指南-用户界面-通用布局对象
  6. Android实现Parcelable对象序列化的实例
  7. 线程执行android的looper,handler消息小结
  8. Qt 5.2正式版发布 全面支持移动平台
  9. Android(安卓)Zxing 扫描条码实现竖屏模式 Camera摄像头 旋转90

随机推荐

  1. 开发SpringMVC+MyBatis项目001
  2. 用 Eclipse + GDB调试Android中C/C++程序
  3. Android系统文件夹结构的说明
  4. android switch(String)错误:Cannot switch
  5. Android(安卓)UI设计——画廊Gallery与提
  6. Android(安卓)应用性能优化
  7. Compile a native C Android(安卓)applic
  8. Cannot run program "svn" (in directory
  9. Android(安卓)监听主进程被杀
  10. Android(安卓)Studio3.0 apk安装时提示受