基于Android 13:包管理机制详解

outside_default.png

/   今日科技快讯   /

当地时间2月22日,谷歌宣布在量子计算机的纠错方面取得了突破,这一研究结果已发表在英国《自然》杂志上。在多数情况下,量子计算机很容易犯错。这是因为量子比特(量子位)依赖的量子态只能维持不到一秒钟。这意味着,计算机还没来得及完成计算,量子系统中编码的信息就很可能已经丢失了。

/   作者简介   /

明天周六,照常休息,祝大家周末愉快!

本篇文章转自小余的自习室的博客,文章主要通过源码分析了第三方应用的安装过程,相信会对大家有所帮助!

原文地址:

https://juejin.cn/post/7180373976089100344

/   前言   /

之前文章我们讲解了PKMS的启动过程。PKMS启动过程中主要做了以下事情:

1. 会对某些配置文件进行解析扫描,放到PKMS对象内存中

2. 会对系统中的应用包括:overlay,system,vendor,app等路径下的应用进行扫描,如果发现有版本更新,则进行应用更新操作。

3. 初始化包管理过程中需要使用到一些环境对象等。

接下面我们再来讲解下第三方应用的安装过程

/   应用安装过程   /

应用安装的方式有如下几种:

普通安装方式

在7.0之后,为了进一步提升文件读写的安全性,Android框架执行的StrictMode API政策禁止在您的应用外部公开file://URI。如果一项包含文件URI的intent离开您的应用,则应用出现故障,并出现FileUriExposedException异常。

这个时候需要使用FileProvider来授权外部文件读写权限

FileProvider

具体使用方式如下:

1. 在AndroidManifest文件中定义:

<provider
    android:authorities="${applicationId}.fileprovider"
    android:name="androidx.core.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true"
    >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/update_files"
        />
</provider>

2.在xml中定义文件update_files.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="external_storage_install"
        path="yuhb/install"/>
</paths>

3.在代码中调用

/**
普通应用安装方式
7.0以后需要使用FileProvider进行申请
@param apkFile
@param context
*/
public static void generateInstall(File apkFile, Context context){
    if(!apkFile.exists()){
        return;
    }
    Intent intent = getInstallIntent(apkFile, context);
    context.startActivity(intent);
}
//获取安装应用的Intent
private static Intent getInstallIntent(File apkFile, Context context) {
    Uri data;
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    data = getInstallUri(context,apkFile);
    //7.0以后使用FileProvider处理
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//授权其他应用的读权限
        intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);//防止app加固下出现授权失败情况
    //            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);//授权其他应用写权限
    }
    intent.setDataAndType(data,"application/vnd.android.package-archive");
    return intent;
}
//获取安装文件的uri
private static Uri getInstallUri(Context context,File apkFile) {
    Uri data;
    //7.0以后使用FileProvider处理
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
        data = FileProvider.getUriForFile(context,context.getPackageName()+".fileprovider",apkFile);
    }else {
        data = Uri.fromFile(apkFile);
    }
    return data;
}

静默安装方式(需要有root权限)

你是不是尝试了N种方法,打了N个debug,然后得到的却是各种各样的安装失败 ~ 首先类似静默功能一般是被系统所禁止的,只有厂商在自已平台才会开发权限(好比小米的系统应用,默认实现了静默功能,但是如果小米应用移植到vivo就无效了)。

具体使用方式如下:

/**静默安装方式,一般需要root权限或者是厂商自己的系统应用。
@param context
@param apkFilePath
*/
public static void silenceInstallApk(Context context,String apkFilePath) {
    /*apkFilePath:这里我们首先传入的是安装包的路径   installObserver:自定义安装的回调,不需要可以删了*/
    File apkFile = new File(apkFilePath);
    //判断路径下的文件是否存在
    if (!apkFile.exists()) {
        Log.e(TAG, "apkFile is null...");
        return;
    }
    String packageName = "";
    //获取安装包的信息
    PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(apkFilePath,
            PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
    if (packageInfo != null) {
        packageName = packageInfo.packageName;
        String versionName = packageInfo.versionName;
    }
    //获取packageInstaller,后面用来创建PackageInstaller.Session
    PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
    //获取创建PackageInstaller.Session的参数
    PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
    /*指示将在此会话中交付的所有APK的总大小(以字节为单位),系统可以使用它来确保在继续之前存在足够的磁盘空间,
    或者估计安装在外部存储上的容器大小*/
    sessionParams.setSize(apkFile.length());
    PackageInstaller.Session session = null;
    try {
        //代表一个session的唯一ID,这里我是在全局变量中声明,因为后面的另外一个方法用到了这个sessionId
        int mSessionId = packageInstaller.createSession(sessionParams);
        if (mSessionId != -1) {
            //也就是在这个外部的onTransfesApkFile()方法中,将会用到sessionId
            boolean copySuccess = onTransfesApkFile(mSessionId,context,apkFilePath, packageName);
            if (copySuccess) {
                session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
                //设置安装完成后需要发送的一个自定义安装结果广播,这里我设置了App的NAME,VERSION,PACKAGE
                Intent intent = new Intent(context,
                        InstallResultReceiver.class);
                intent.setAction(PackageInstaller.EXTRA_STATUS);
                intent.putExtra("APP_VERSION", "1.0");
                intent.putExtra("APP_PACKAGE", "com.allinpay.manager");
                //执行结束后,发送intent
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context,1,
                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
                //这里最终进行session的提交
                session.commit(pendingIntent.getIntentSender());
            } else {
                //此处是安装失败的回调,不需要可以删除
    //                    if (installObserver != null) {
    //                        installObserver.observer(false, apkFilePath, packageName);
    //                    }
            }
        }
    } catch (Exception exception) {
        Log.e(TAG, "installApk exception = " + exception.getLocalizedMessage());
    } finally {
        if (null != session) {
            session.close();
        }
        //安装完成需要删除文件
        if (apkFile != null && apkFile.exists()) {
    //                apkFile.delete();
        }
    }
}

private static boolean onTransfesApkFile(int mSessionId,Context context,String apkFilePath, String packageName) {
    InputStream in = null;
    OutputStream out = null;
    PackageInstaller.Session session = null;
    boolean success = false;
    try {
        File apkFile = new File(apkFilePath);
        //根据sessionId来获取其代表的session
        session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
        //向session中写入文件数据
        out = session.openWrite(packageName + "_base.apk", 0, apkFile.length());
        in = new FileInputStream(apkFile);
        int total = 0;
        int len;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            total += len;
            out.write(buffer, 0, len);
        }
        session.fsync(out);
        success = true;
    } catch (IOException exception) {
        exception.printStackTrace();
    } finally {
        if (null != session) {
            session.close();
        }
        try {
            if (null != out) {
                out.close();
            }
            if (null != in) {
                in.close();
            }
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
    return success;
}

这里我们来分析下方式一(普通应用更新方式

1. 提取关键代码:调用的startActivity中的Intent属性

Action:Intent.ACTION_VIEW

Flag:Intent.FLAG_ACTIVITY_NEW_TASK

Uri:content格式

Type:application/vnd.android.package-archive

2. 根据以上Intent,调用了startActivity,然后通过PKMS找到对应的Activity。最终定位到:InstallStart类,这个类就是启动安装时打开的第一个Activity

/**
Select which activity is the first visible activity of the installation and forward the intent to it.
*/
public class InstallStart extends Activity {
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        Uri packageUri = intent.getData();
        //如果Scheme是Content格式
        if (packageUri != null && packageUri.getScheme().equals(
                ContentResolver.SCHEME_CONTENT)) {
            // [IMPORTANT] This path is deprecated, but should still work. Only necessary
            // features should be added.
// Copy file to prevent it from being changed underneath this process
        nextActivity.setClass(this, InstallStaging.class);
    //如果Scheme是package格式
    } else if (packageUri != null && packageUri.getScheme().equals(
            PackageInstallerActivity.SCHEME_PACKAGE)) {
        nextActivity.setClass(this, PackageInstallerActivity.class);
    //如果Scheme是其他格式
    } else {
        Intent result = new Intent();
        result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                PackageManager.INSTALL_FAILED_INVALID_URI);
        setResult(RESULT_FIRST_USER, result);

        nextActivity = null;
    }

    if (nextActivity != null) {
        startActivity(nextActivity);
    }
    finish();
    ```
}

InstallStart的onCreate方法会对传入的Scheme格式进行判断,然后启动另外一个Activity,并结束自己。我们重点来看Content格式的Activity。最终启动的是InstallStaging.class。看父类名字应该是一个选择框类型的Activity。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
public class InstallStaging extends AlertActivity {
    protected void onResume() {
        ...
        mStagingTask = new StagingAsyncTask();
        //执行StagingAsyncTask
        mStagingTask.execute(getIntent().getData());
    }


    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
                    return false;
                }

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException | IllegalStateException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                // Now start the installation again from a file
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
                installIntent.setData(Uri.fromFile(mStagedFile));

                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                }

                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(installIntent);

                InstallStaging.this.finish();
            } else {
                showError();
            }
        }
    }

}

1. InstallStaging的onResume时会启用了一个ASyncTask,在后台读取apk文件,并写入到mStagedFile文件中。mStagedFile文件的作用是临时文件,防止在安装过程中对原文件变更。

2. 在文件读取完成后,调用AsyncTask的onPostExecute方法,这个方法中会再次启动一个DeleteStagedFileOnResult类Activity。

继续进入DeleteStagedFileOnResult

/**
 * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
   */
   public class DeleteStagedFileOnResult extends Activity {
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       if (savedInstanceState == null) {
           Intent installIntent = new Intent(getIntent());
           installIntent.setClass(this, PackageInstallerActivity.class);

           installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
           startActivityForResult(installIntent, 0);
       }

}

看谷歌给我们的注解:这个类是一个过渡Activity:最终是用来启动PackageInstallerActivity并删除mStagedFile临时文件,这在onCreate方法中也可以看出。

那就转到PackageInstallerActivity,在PackageInstallerActivity中会让引导用户点击安装按钮,点击之后会调用startInstall方法进行安装操作。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void startInstall() {
    // Start subactivity to actually install the application
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

startInstall重新启动了一个“InstallInstalling”去安装,并将启动应用需要的参数信息放到Intent中。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

public class InstallInstalling extends AlertActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        ...
        //注册一个安装结果监听器launchFinishBasedOnResult
        mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
        ...
        //创建一个createSession
        mSessionId = getPackageManager().getPackageInstaller().createSession(params);
    }


    @Override
    protected void onResume() {
        //启动一个InstallingAsyncTask
        mInstallingTask = new InstallingAsyncTask();
        mInstallingTask.execute();
    }

    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
        PackageInstaller.Session> {

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                //打开Session
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {

            }
            //设置session安装进度
            session.setStagingProgress(0);

            try {
                File file = new File(mPackageURI.getPath());

                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        byte[] buffer = new byte[1024 * 1024];
                        while (true) {
                            int numRead = in.read(buffer);

                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }

                            if (isCancelled()) {
                                session.close();
                                break;
                            }

                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }

                return session;
            } catch (IOException | SecurityException e) {

            }
        }

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                //注册一个broadcastIntent监听安装结果:BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                broadcastIntent.setPackage(getPackageName());
                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallInstalling.this,
                        mInstallId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
                //调用commit进行安装
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } 
        }
    }

}
}

InstallInstalling可以总结为下面几个步骤:

1. 创建session

2. 打开session

3. copy apk文件到Session中

4. 调用commit进行安装。

仔细观察你会发现:这里步骤和我们前面分析的静默安装方式步骤其实是一样的。而我们的InstallInstalling是运行在系统进程中,所以拥有静默安装权限, 而第三方应用是没有这个权限的

下面我们深入PackageInstaller看看其是如何实现安装过程的?

首先来看context.getPackageManager().getPackageInstaller()获取到的是哪个类?

如果你对Activity熟悉的话,应该知道context的实现类是ContextImpl类。

定位到它的getPackageManager。

//frameworks/base/core/java/android/app/ContextImpl.java
public PackageManager getPackageManager() {
    if (mPackageManager != null) {
        return mPackageManager;
    }

    final IPackageManager pm = ActivityThread.getPackageManager();
    if (pm != null) {
        // Doesn't matter if we make more than one instance.
        return (mPackageManager = new ApplicationPackageManager(this, pm));
    }

    return null;

}

由此可知getPackageManager返回的是一个ApplicationPackageManager,而这里有个关键参数pm,后期操作实际都是通过pm进行的。pm是通过ActivityThread.getPackageManager()获取。

//frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }
    final IBinder b = ServiceManager.getService("package");
    sPackageManager = IPackageManager.Stub.asInterface(b);
    return sPackageManager;
}

哦?原来就是获取ServiceManager中的package服务。如果你还有印象,前面我们分析过在PKMS的main方法中有下面这段代码。

//构造IPackageManagerImpl对象并将其add到ServiceManager中:name为package
IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService("package", iPackageManager);

所以这里返回的是一个IPackageManagerImpl对象。

好了,回到前面context.getPackageManager().getPackageInstaller()

context.getPackageManager():对应ApplicationPackageManager(context,IPackageManagerImpl)

进入ApplicationPackageManager的getPackageInstaller:

//frameworks/base/core/java/android/app/ApplicationPackageManager.java
public PackageInstaller getPackageInstaller() {
    if (mInstaller == null) {
        try {
            mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
                    mContext.getPackageName(), mContext.getAttributionTag(), getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    return mInstaller;
}

由此可知getPackageInstaller返回的是一个PackageInstaller对象,而关键看第一个参数mPM.getPackageInstaller(),这个参数也是实际进行安装的类。

前面分析过mPM是IPackageManagerImpl对象,进入IPackageManagerImpl.getPackageInstaller()

在IPackageManagerImpl父类IPackageManagerBase实现了getPackageInstaller

//frameworks/base/services/core/java/com/android/server/pm/IPackageManagerBase.java
public final IPackageInstaller getPackageInstaller() {
    ...
    return mInstallerService;
}

返回的是一个mInstallerService,这个mInstallerService是在哪里赋值的呢?经过几轮跳转,定位到。

mInstallerService是在PKMS的构造方法中赋值的:mInstallerService = mInjector.getPackageInstallerService();

mInjector是PackageManagerServiceInjector类(PKMS的运行时环境类)。

最终获取的mInstallerService是在PKMS构造过程中也就是系统开机时初始化的PackageInstallerService对象。

好了这里画了一张图来表示他们之间的关系:

outside_default.png

由以上分析可知:context.getPackageManager().getPackageInstaller()获取的是PackageInstaller,而实际安装操作是PackageInstaller的内部PackageInstallerService对象

下面我们根据前面分析出的安装步骤进行具体分析 1.创建session 2.打开session 3.copy apk文件到Session中 4.调用commit进行安装。

创建session

//frameworks/base/core/java/android/content/pm/PackageInstaller.java
public int createSession(@NonNull SessionParams params) throws IOException {
    //mInstaller为PackageInstallerService
    return mInstaller.createSession(params, mInstallerPackageName, mAttributionTag,
            mUserId);
}
PackageInstallerService的createSession方法会调用内置的createSessionInternal方法
private int createSessionInternal(SessionParams params, String installerPackageName...){
    //前面一大堆对Session创建的条件进行判断,不满足创建则抛出异常
    //创建随机数的sessionId
    sessionId = allocateSessionIdLocked();
    //创建SessionDir
    stageDir = buildSessionDir(sessionId, params);
    //InstallSource持有应用安装的apk源文件信息
    InstallSource installSource = InstallSource.create(installerPackageName,
                originatingPackageName, requestedInstallerPackageName,
                installerAttributionTag, params.packageSource);
    //创建session :PackageInstallerSession
    session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
            mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
            userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
            null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
            false, false, false, PackageManager.INSTALL_UNKNOWN, "");
    //将session放入到mSessions键值对中,key为sessionId
    synchronized (mSessions) {
        mSessions.put(sessionId, session);
    }
    //将InstallSource放入到PKMS的Setting集合中
    mPm.addInstallerPackageName(session.getInstallSource());
}

createSessionInternal过程主要就是创建了PackageInstallerSession对象,并将对象放入到mSessions集合中。

打开session

打开Session也是调用PackageInstallerService的createSession方法,内部调用openSessionInternal进行打开。

private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
    synchronized (mSessions) {
        final PackageInstallerSession session = mSessions.get(sessionId);
        if (!checkOpenSessionAccess(session)) {
            throw new SecurityException("Caller has no access to session " + sessionId);
        }
        session.open();
        return session;
    }
}

内容很简单,通过sessionId去mSessions获取Session对象,然后调用session.open()打开。

//frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
    ...
    boolean wasPrepared;
    synchronized (mLock) {
        wasPrepared = mPrepared;
        if (!mPrepared) {
            if (stageDir != null) {
                prepareStageDir(stageDir);
            }
            mPrepared = true;
        }
    }
}
static void prepareStageDir(File stageDir) throws IOException {


    try {
        Os.mkdir(stageDir.getAbsolutePath(), 0775);
        Os.chmod(stageDir.getAbsolutePath(), 0775);
    } catch (ErrnoException e) {

    }

}

open方法也只是调用Os的mkdir进行stageDir文件夹创建,并且给stageDir文件夹设置了对应的权限、 stageDir临时文件夹路径:new File("data/app", "vmdl" + sessionId + ".tmp");

copy apk文件到Session中

Session文件的写操作openWrite最终调用的是PackageInstallerSession的doWriteInternal,写文件就不介绍了,这个大家都非常清楚了。只要知道文件是写入到的是stageDir临时文件夹("data/app/vmdl{$sessionId}.tmp")下面

调用commit进行安装

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    dispatchSessionSealed();
}
private void dispatchSessionSealed() {
    mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {


            case MSG_ON_SESSION_SEALED:
                //内部发射一个MSG_STREAM_VALIDATE_AND_COMMIT msg
                handleSessionSealed();
                break;
            case MSG_STREAM_VALIDATE_AND_COMMIT:
                //内部发射一个MSG_INSTALL msg
                handleStreamValidateAndCommit();
                break;
            case MSG_INSTALL:
                //处理应用安装过程
                handleInstall();
                break;
            case MSG_ON_PACKAGE_INSTALLED:
                final SomeArgs args = (SomeArgs) msg.obj;
                final String packageName = (String) args.arg1;
                final String message = (String) args.arg2;
                final Bundle extras = (Bundle) args.arg3;
                final IntentSender statusReceiver = (IntentSender) args.arg4;
                final int returnCode = args.argi1;
                args.recycle();

                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                        isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                        packageName, returnCode, message, extras);

                break;
            case MSG_SESSION_VALIDATION_FAILURE:
                final int error = msg.arg1;
                final String detailMessage = (String) msg.obj;
                onSessionValidationFailure(error, detailMessage);
                break;
        }

        return true;
    }

};

发送MSG_ON_SESSION_SEALED的msg调用handleSessionSealed方法。handleSessionSealed方法内部又发送了MSG_STREAM_VALIDATE_AND_COMMIT的msg。然后在handleStreamValidateAndCommit又发送了MSG_INSTALL。所以最终调用了handleInstall方法进行安装。

handleInstall方法可以大致分为:

1. apk文件的校验

2. apk文件的安装

@WorkerThread
private void handleInstall() {
    ...
    if (params.isStaged) {
        mStagedSession.verifySession();
    } else {
        verify();
    }
}

mStagedSession.verifySession最终也会走到verify,可以直接看verify方法

private void verify() {
    try {
        //1.创建安装apk需要的文件夹:
        prepareInheritedFiles();
        //2.解析APK文件并提取so库文件。其实就是解析AndroidManfest中的四大组件信息
        parseApkAndExtractNativeLibraries();
        //3.检验apk的过程
        verifyNonStaged();
    } catch (PackageManagerException e) {
        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
        final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
        setSessionFailed(e.error, errorMsg);
        onSessionVerificationFailure(e.error, errorMsg);
    }
}

重点来看3.检验apk的过程,verifyNonStaged在经过一系列session检查之后,最终会调用到PackageSessionVerifier的verifyAPK方法, verifyAPK内部设置了安装结果监听IPackageInstallObserver2:

//frameworks/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
private void verifyAPK(PackageInstallerSession session, Callback callback)
            throws PackageManagerException {
    final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            throw new IllegalStateException();
        }
        @Override
        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                Bundle extras) {
            if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // Continue verification for staged sessions
                verifyStaged(session.mStagedSession, callback);
                return;
            }
            if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
                String errorMessage = PackageManager.installStatusToString(returnCode, msg);
                session.setSessionFailed(returnCode, errorMessage);
                callback.onResult(returnCode, msg);
            } else {
                session.setSessionReady();
                callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
            }
        }
    };
    final VerificationParams verifyingSession = makeVerificationParams(session, observer);
    ...
    verifyingSession.verifyStage();

}
frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
public void verifyStage() {
    mPm.mHandler.post(this::startCopy);
}

可以看到verifyStage最终调用了mPm.mHandler post了一个startCopy的任务。

final void startCopy() {
    handleStartCopy();
    handleReturnCode();
}

handleStartCopy和handleReturnCode是两个抽象方法:具体实现是在VerificationParams类中

public void handleStartCopy() {
    //获取需要安装的apk包信息
    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
            mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
    //校验需要更新的app的VersionCode,这里面会对VersionCode版本和原始版本进行校验。
    Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
            pkgLite, mRequiredInstalledVersionCode, mInstallFlags);


    if (!mOriginInfo.mExisting) {
        //如果是PackageManager.INSTALL_APEX不是APEX包,也就是apk包,则调用sendApkVerificationRequest对APK包进行更新
        if ((mInstallFlags & PackageManager.INSTALL_APEX) == 0) {   
            sendApkVerificationRequest(pkgLite);//关注点1
        }
        //回溯版本走的
        if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
            sendEnableRollbackRequest();
        }
    }

}
//关注点1
private void sendApkVerificationRequest(PackageInfoLite pkgLite) {
    ...
    //发送完整的校验请求
    sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
    //发送package安装包校验
    sendPackageVerificationRequest(
            verificationId, pkgLite, verificationState);
    ...
}

sendPackageVerificationRequest主要校验下面几个:

1. 四大组件包信息校验

2. apk打包公钥校验

3. 校验打包的sdk版本信息,通过添加广播的方式进行。

好了,继续回到startCopy的handleReturnCode方法

//frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
void handleReturnCode() {
    sendVerificationCompleteNotification();
}
private void sendVerificationCompleteNotification() {
    ...
    try {
        mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                new Bundle());
    } catch (RemoteException e) {
        Slog.i(TAG, "Observer no longer exists.");
    }
    ...
}

在校验完毕成功以后,回调mObserver的onPackageInstalled方法。而mObserver之前说过是在verifyAPK方法时传入的。

final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {


    @Override
    public void onPackageInstalled(String basePackageName, int returnCode, String msg,
            Bundle extras) {
        ...
        if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
            String errorMessage = PackageManager.installStatusToString(returnCode, msg);
            session.setSessionFailed(returnCode, errorMessage);
            callback.onResult(returnCode, msg);
        } else {
            session.setSessionReady();
            callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
        }
    }

};

onPackageInstalled回调成功后会再次调用callback的onResult方法

callBack是在前面分析的PackageInstallerSession的verifyNonStaged方法中传入的,一层一层向外回调。

private void verifyNonStaged()
        throws PackageManagerException {


    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });

}

最终回调到onVerificationComplete方法,可以看到前面很大一部分是在对应用进行校验的部分。

下面分析的才是具体安装的过程:

@WorkerThread
private void onVerificationComplete() {
    ...
    install();
}
private CompletableFuture<Void> install() {
    List<CompletableFuture<InstallResult>> futures = installNonStaged();
    ...
}

private List<CompletableFuture<InstallResult>> installNonStaged() {
    ... 
    final InstallParams installingSession = makeInstallParams(future);  
    installingSession.installStage();
    ...
}

frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void installStage() {
    final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);


    msg.obj = this;
    mPm.mHandler.sendMessage(msg);

}

installStage发送了一个INIT_COPY的msg,定位到mPm = PackageManagerImpl.java mPm.mHandler = PackageHandler

final class PackageHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        try {
            doHandleMessage(msg);
        } finally {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        }
    }

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    ...
                    params.startCopy();                
                }
                break;
            }
        }
    }

}

INIT_COPY的msg调用了HandlerParams的startCopy方法处理,而这个时候的HandlerParams的实现类是InstallParams.java,前面校验过程中的实现类是VerificationParams

final void startCopy() {
    handleStartCopy();//关注点1
    handleReturnCode();//关注点2
}

先看关注点1

//frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void handleStartCopy() {


    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
            mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);

    if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
            == InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
        //先释放一部分不需要的缓存。
        pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
                pkgLite.recommendedInstallLocation, mPackageLite,
                mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
    }
    //根据默认的规则重写安装路径,主要是区分使用外置sdcard路径还是内置路径
    mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
            pkgLite.installLocation);

}

再看关注点2:
void handleReturnCode() {
    processPendingInstall();
}
private void processPendingInstall() {
    //创建安装的参数信息
    InstallArgs args = createInstallArgs(this);
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        //关注点3 拷贝apk
        mRet = args.copyApk();
    }
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
        F2fsUtils.releaseCompressedBlocks(
                mPm.mContext.getContentResolver(), new File(args.getCodePath()));
    }
    if (mParentInstallParams != null) {
        mParentInstallParams.tryProcessInstallRequest(args, mRet);
    } else {


        PackageInstalledInfo res = new PackageInstalledInfo(mRet);
        //关注点4
        processInstallRequestsAsync(
                res.mReturnCode == PackageManager.INSTALL_SUCCEEDED,
                Collections.singletonList(new InstallRequest(args, res)));
    }

}

processPendingInstall关注两个部分:

1.拷贝apk

2.安装apk

1. 拷贝apk:mRet = args.copyApk();

而args 是FileInstallArgs类对象

//frameworks/base/services/core/java/com/android/server/pm/FileInstallArgs.java
int copyApk() {
    return doCopyApk();
}
private int doCopyApk() {
    //1.给StageDir分配对应的临时文件夹以及权限
    final File tempDir = mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral);    
    mCodeFile = tempDir;
    //2.拷贝Package,这里面主要是四大组件信息的拷贝
    int ret = PackageManagerServiceUtils.copyPackage(
            mOriginInfo.mFile.getAbsolutePath(), mCodeFile);


    //3.根据abifilter 拷贝NativeLibrary ,so库到对应的lib目录下
    handle = NativeLibraryHelper.Handle.create(mCodeFile);
    ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
            mAbiOverride, isIncremental);

    return ret;

}

先来分析1处allocateStageDirLegacy

public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
    synchronized (mSessions) {
        try {
            final int sessionId = allocateSessionIdLocked();
            mLegacySessions.put(sessionId, true);
            final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
            prepareStageDir(sessionStageDir);
            return sessionStageDir;
        } catch (IllegalStateException e) {
            throw new IOException(e);
        }
    }
}

看buildTmpSessionDir,这个前面也分析过,最后返回的File路径为:data/app/vmdl{$sessionId}.tmp

再来分析2处PackageManagerServiceUtils.copyPackage

public static int copyPackage(String packagePath, File targetDir) {
    try {
        final File packageFile = new File(packagePath);
        //解析APK文件到ParseResult中
        final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                input.reset(), packageFile, /* flags */ 0);
        //获取apk的文件PackageLite信息
        final PackageLite pkg = result.getResult();
        //拷贝file,核心方法
        copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");


        return PackageManager.INSTALL_SUCCEEDED;
    } catch (IOException | ErrnoException e) {
    }

}
private static void copyFile(String sourcePath, File targetDir, String targetName)
        throws ErrnoException, IOException {


    final File targetFile = new File(targetDir, targetName);
    final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
            O_RDWR | O_CREAT, 0644);
    Os.chmod(targetFile.getAbsolutePath(), 0644);
    FileInputStream source = null;
    try {
        source = new FileInputStream(sourcePath);
        FileUtils.copy(source.getFD(), targetFd);
    } finally {
        IoUtils.closeQuietly(source);
    }

}

copyPackage会先去解析apk文件,然后调用copyFile方法,copyFile中调用Os.open去打开targetFile目标文件, 调用FileUtils.copy方法将原文件拷贝到目标文件中

2.安装apk:processInstallRequestsAsync

// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
        List<InstallRequest> installRequests) {
    mPm.mHandler.post(() -> {
        mInstallPackageHelper.processInstallRequests(success, installRequests);
    });
}
frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
public void processInstallRequests(boolean success, List<InstallRequest> installRequests) {


    List<InstallRequest> apkInstallRequests = new ArrayList<>();
    for (InstallRequest request : installRequests) {
        ...
        apkInstallRequests.add(request);

    }

    if (success) {
        for (InstallRequest request : apkInstallRequests) {
            //预安装,内部主要是做clean操作
            request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
        }
        synchronized (mPm.mInstallLock) {
            //实际安装apk过程
            installPackagesTracedLI(apkInstallRequests);
        }
    }
    ...

}
private void installPackagesTracedLI(List<InstallRequest> requests) {
    ...
    installPackagesLI(requests);
    ...
}

下面我们重点来分析下installPackagesLI

private void installPackagesLI(List<InstallRequest> requests) {


    for (InstallRequest request : requests) {       
        //阶段1:prepare阶段
        repareResult = preparePackageLI(request.mArgs, request.mInstallResult);         
        //阶段2:scan阶段
        final ScanResult result = scanPackageTracedLI(
                prepareResult.mPackageToScan, prepareResult.mParseFlags,
                prepareResult.mScanFlags, System.currentTimeMillis(),
                request.mArgs.mUser, request.mArgs.mAbiOverride);       
    }   
    //阶段3:Reconcile阶段
    reconciledPackages = ReconcilePackageUtils.reconcilePackages(
            reconcileRequest, mSharedLibraries,
            mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
    }

    commitRequest = new CommitRequest(reconciledPackages,
                            mPm.mUserManager.getUserIds());
    //阶段4:Commit阶段
    commitPackagesLocked(commitRequest);
    //阶段5:完成apk安装
    executePostCommitSteps(commitRequest);

}

installPackagesLI是最终安装应用的方法:主要分为4个阶段

阶段1:prepare阶段:分析当前安装包的状态,解析安装包并对其做初始化验证

阶段2:scan阶段:根据prepare阶段中收集的安装包状态信息去扫描解析出来的包

阶段3:Reconcile阶段:验证scan阶段扫描到的Package信息以及当前系统状态,确保apk的正确安装。

阶段4:Commit阶段:提交所有扫描的包并更新系统状态。这是唯一可以在安装流程和所有可预测错误中修改系统状态的地方.

在 preparePackageLI() 内使用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息;使用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包最终路径 等。

完成了解析和校验准备工作后,最后一步就是对apk的安装了。这里调用了executePostCommitSteps准备app数据,并执行dex优化

最后通过executePostCommitSteps完成apk的安装,执行dex优化等操作

阶段5:完成apk安装

private void executePostCommitSteps(CommitRequest commitRequest) {


    for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {

        // prepareAppDataPostCommitLIF经过一系列调用会走到Installer的createAppData方法。
        mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);

        /*
        检测是否需要进行dex优化:同时满足下面三种情况就需要
        1.不是一个即时应用app或者如果是的话通过gservices进行dex优化操作
        2.debuggable为false
        3.不在增量文件系统上。
        */
        final boolean performDexopt =
                (!instantApp || android.provider.Settings.Global.getInt(
                        mContext.getContentResolver(),
                        android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                        && !pkg.isDebuggable()
                        && (!onIncremental)
                        && dexoptOptions.isCompilationEnabled();

        if (performDexopt) {

            //获取so库所在的目录
            PackageSetting realPkgSetting = result.mExistingSettingCopied
                    ? result.mRequest.mPkgSetting : result.mPkgSetting;

            boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState()
                    .isUpdatedSystemApp();
            //更新系统app信息。
            realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
            //进行dex优化
            mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                    null /* instructionSets */,
                    mPm.getOrCreateCompilerPackageStats(pkg),
                    mDexManager.getPackageUseInfoOrDefault(packageName),
                    dexoptOptions);

        }

        //通知BackgroundDexOptService服务当前packageName的应用进行了更新。
        BackgroundDexOptService.getService().notifyPackageChanged(packageName);

        notifyPackageChangeObserversOnUpdate(reconciledPkg);
    }

}

阶段5:主要做了下面两件事

任务1:调用prepareAppDataPostCommitLIF方法,最终执行到createAppData方法进行app的安装.

public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
    throws InstallerException {
...
try {
    return mInstalld.createAppData(args);
} catch (Exception e) {
    throw InstallerException.from(e);
}
}

mInstalld在前面分析过了,installd进程 的执行权限为 root,所有实际的应用安装,卸载等操作都是通过这个服务进行的。PKMS只是java层的封装。mInstalld进程和PKMS是通过binder进行通讯的。

任务2.调用performDexOpt进行dex优化 同时满足下面三种情况就需要

1.不是一个即时应用app或者如果是的话通过gservices进行dex优化操作

2.debuggable为false

3.不在增量文件系统上。

然后关于dex优化部分,后面会单独出一篇文章来讲解。

关于应用安装部分就讲到这里了。

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Compose跨平台又来了,这次能开发iOS了

2022年终总结,我的10年Android之旅

欢迎关注我的公众号

学习技术或投稿

outside_default.png

outside_default.png

长按上图,识别图中二维码即可关注

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录回到顶部↑ 第一篇 基础篇 第1章 android概述 2 1.1 android的前世今生 3 1.1.1 android的产生 3 1.1.2 android的发展 3 1.2 android的平台架构及特性 4 1.2.1 android平台特性 5 1.2.2 android平台架构 5 1.3 android market 7 1.4 android应用程序组件 8 1.4.1 activity 9 1.4.2 service 9 1.4.3 broadcast receiver 9 1.4.4 contentprovider 9 1.4.5 view 10 1.4.6 intent 10 1.5 android与java me的区别与联系 10 1.5.1 二者的区别 10 1.5.2 二者的联系 10 1.5.3 各自的优势 11 ↓展开全部内容 前言回到顶部↑   移动互联网时代 来临,一个崭新的时代开始了。所谓移动互联网就是将移动通信和互联网整合在一起,使移动设备(括手机和上网本)可以随时随地地访问互联网资源和应用。互联网时代创造了一个经济神话,也造就了很多时代英雄,他们一个个令人仰慕。试想为数亿的手机用户和数亿的网民建立一个共同的平台,使其应用到企业、商业和和农村之间,又会是怎样一个惊天动地的伟业呢?新时代开始了,你愿意输在起跑线上吗?   智能手机 是移动互联网时代一个标志性的客户端工具,它和传统的手机是有区别的,它就像一台“小电脑”,具有独立的操作系统,可以自由安装、卸载软件,具有强大的计算和存储能力,可以通过移动通信网络来实现无线网络接入。智能手机一般具备如下特点:高速度处理芯片、大存储芯片和存储扩展能力、面积大、标准化、可触摸的显示屏、摄像头至少300万像素、支持播放式的手机电视、必须支持GPS导航、操作系统必须支持新应用的安装等。   互联网的竞争格局基本定型,那么移动互联网时代竞争的焦点在智能手机终端上,软件部分括智能手机操作系统和应用软件。   目前智能手机操作系统有:诺基亚的Symbian、微软的Windows Mobile和Google的Android等。   Google 于1998年9月7日创立,经过十几年在搜索引擎方面的精耕细作,成为全球互联网巨头,尤其在地图搜索的应用更是引人注目。Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统,名称为Android,中文译为“机器人”。这意味着Google在移动互联网时代开始抢跑并领跑。   Android 是一个真正意义上的开源智能手机操作系统,该系统由底层的Linux、中间层的软件和上层的Java应用程序组合而成。该系统一经推出立即受到了全球移动设备厂商和开发者的热捧。   2008年9月22日,美国运营商T-Mobile USA在纽约正式发布第一款Google手机——T-Mobile G1。该款手机为中国台湾宏达电代工制造,是世界上第一部使用Android操作系统的手机,它支持WCDMA/HSPA网络,理论下载速率为7.2Mbps,并支持Wi-Fi。到了2010年1月,Google开始发布自家品牌手机Nexus One。该款手机使用的操作系统是Android 2.1。如下左图为G1,右图为Nexus One。   图1 G1和Nexus One   内容简介   本书分为三个部分,括基础篇、技术篇和应用篇。由浅入深地讲述了Android应用开发的方方面面。   篇 名章 名内容简介   第一篇 基础篇第1章 Android概述Android概述,讲述了Android的前生后世、架构和特点、Android Market、应用程序组件和Android与Java ME的区别及联系   第2章 Android开发基础Android开发基础,讲述了Android开发环境的搭建、Android常用工具的使用和第一个Android应用程序的开发   第二篇 技术篇第3章 Android中的资源访问Android 中的资源访问,讲述了如何定义和访问Android中的外部资源。   第4章 Android用户界面Android 用户界面,讲述了Android中的事件处理机制、布局管理和常用组件的使用。   第5章 Android基本程序单元ActivityAndroid 基本程序单元Activity,讲述了Android中重要组件Activity的创建、启动和生命周期等内容   续表   篇 名章 名内容简介   第二篇 技术篇第6章 Android组件之间的信使IntentAndroid 组件之间的信使Intent,讲述了Intent对象及其属性、Intent的实现策略和Intent的常见应用   第7章 Android Service组件Android Service组件,讲述了Android中的后台服务Service的概念、创建和使用,并详细讲解了远程服务的调用
目录结构: 第2章、Android应用程序界面设计,即View 2.2、布局管理(Layout):LinearLayout、TableLayout、FrameLayout、RelativeLayout; 2.3、基本界面组件:TextView、EditText; Button、ImageButton; 9Patch; RadioButton、CheckBox; ToggleButton;AnalogClock、DigitalClock; ImageView; 2.4、高级界面组件:AutoCompleteTextView; Spinner; DatePicker、TimePicker; ProgressBar; SeekBar; RatingBar; TabHost; ScrollView; ListView、ListActivity; ExpandableListView; GridView、ImageSwitcher; Gallery; 2.5、对话框:AlertDialog; PopupWindow; DatePickerDialog、TimePickerDialog; ProgressDialog; 2.6、消息提示:Toast; Notification; 2.7、菜单:OptionMenu、SubMenu; ContextMenu; 第3章、Android事件处理,括按键响应机制和消息传递机制 3.2、基于监听器的事件处理: 3.3、基于回调的事件的处理: 3.4、响应系统设置的事件: 3.5、Handler消息传递机制: 第4章、深入理解Activity 4.1、建立、配置和使用Activity: 4.2、Activity的回调机制: 4.3、Activity的生命周期: 第5章、使用Intent和IntentFilter进行通信 5.1、Intent对象详解: 5.2、Intent的属性及intent-filter配置:Component属性; Action、Category属性与intent-filter配置; Data、Type属性与intent-filter配置; Extra属性; 5.3、使用Intent创建Tab页面: 第6章、Android应用的资源 6.1、资源的类型及存储方式: 6.2、使用字符串、颜色、尺寸资源: 6.3、数组资源: 6.4、使用Drawable资源:图片资源; StateListDrawable资源; LayerDrawable资源; ShapeDrawable资源; ClipDrawable资源; AnimationDrawable资源; 6.5、使用原始XML资源: 6.6、使用Layout资源: 6.7、使用菜单(Menu)资源: 6.8、样式(Style)和主题(Theme)资源: 6.9、属性(Attribute)资源: 6.10、使用原始资源: 6.11、国际化和资源自适应: 第7章、图形与图像处理 7.1、使用简单图片:Drawable; Bitmap、BitmapFactory; 7.2、绘图:Canvas; Paint; Path; 7.3、图形特效处理:使用Matrix控制变换; 使用drawBitmapMesh扭曲图像; 使用Shader填充图形; 7.4、逐帧(Frame)动画:AnimationDrawable; 7.5、补间(Tween)动画:Interpolator; 位置、大小、旋转度、透明度; 7.6、使用SurfaceView实现动画: 第8章、Android的数据存储和IO 8.1、使用SharedPreferences:SharedPreferences; Editor; 8.2、File存储:openFileOutput和openFileInput; 读写SD卡文件; 8.3、SQLite数据库:SQL语句; SQLiteDatabase; SQLiteOpenHelper; sqlite3 tools; 8.4、手势(Gesture): 8.5、自动朗读(TTS): 8.6、网络存储: 第9章、使用ContentProvider实现数据共享 9.1、数据共享标准:ContentProvider; Uri; ContentResolver; 9.2、操作系统的ContentProvider:使用ContentProvider管理联系人和多媒体; 9.3、实现ContentProvider:创建ContentProvider的步骤; 9.4、监听ContentProvider的数据:ContentObserver; 第10章、Service与BroadcastReceiver 10.1、Service:Service的创建、配置、启动、停止、绑定和通信; Service的生命周期; 10.2、跨进程调用Service(AIDL服务):创建AIDL文件; 将接口暴露给客户端; 客户端访问AIDLService; 10.3、电话管理器:TelephoneManager; 10.4、短信管理器:SmsManager; 10.5、音频管理器:AudioManager; 10.6、振动器:Vibrator; 10.7、手机闹钟服务:AlarmManager; 10.8、接受广播信息:BroadcastReceiver; 10.9、接受系统广播消息: 第11章、多媒体应用开发 11.1、音频和视频的播放:MediaPlayer; SoundPool; VideoView; 11.2、使用MediaRecorder录制音频: 11.3、控制摄像头拍照:Camera; 第12章、OpenGL与3D应用开发 12.2、OpenGL ES基础: 12.3、绘制2D图形: 12.4、绘制3D图形: 第13章、Android的网络应用 13.1、基于TCP协议的网络通信(套接字Socket):Socket; ServerSocket; 13.2、使用URL访问网络资源:URL; URLConnection; 13.3、使用HTTP访问网络:HttpURLConnection; HttpClient; 13.4、使用WebView视图显示网页: 13.5、使用WebService进行网络编程: 第14章、管理Android手机桌面 14.1、管理手机桌面: 14.2、改变手机壁纸: 14.3、桌面快捷方式: 14.4、管理桌面小控件: 14.5、实时文件夹(LiveFolder): 第15章、传感器应用开发 15.2、Android的常用传感器:方向传感器Orientation; 磁场传感器Magnetic Field; 温度传感器Temperature; 光传感器Light; 压力传感器Pressure; 第16章、GPS应用开发 16.1、支持GPS的核心API: 16.2、获取LocationProvider: 16.3、获取定位信息: 16.4、临近警告: 第17章、使用Google Map服务 17.1、调用Google Map的准备: 17.2、根据GPS信息在地图上定位: 17.3、GPS导航: 17.4、根据地址定位: 第18章、疯狂连连看 第19章、电子拍卖系统
目录 第一篇 Android开发初步 第1章 Android初识 1.1 Android简介 1.1.1 认识Android 1.1.2 Android系统框架 1.1.3 应用程序框架 1.2 Eclipse开发环境 1.2.1 安装ADT插件 1.2.2 安装SDK 1.2.3 配置源代码 1.2.4 创建AVD 1.3 Android模拟器 1.3.1 ADB工具 1.3.2 其它常用工具 1.4 本章小结 第2章 应用程序构建 2.1 创建Android工程 2.2 HelloAndroid详解 2.2.1 程序结构 2.2.2 代码分析 2.3 权限permission 2.4 LogCat日志调试 2.5 示例练习 2.5.1 登录界面 2.5.2 事件处理 2.6 本章小结 第二篇 Android开发关键组件 第3章 Activity(活动) 3.1 什么是任务 3.2 Activity的生命周期 3.3 基本用法 3.3.1 创建Activity 3.3.2 启动Activity 3.3.3 窗口Activity 3.3.4 Activity生命周期验证 3.4 Activity之间通信 3.4.1 Activity传递一般类型 3.4.2 Activity传递对象类型 3.4.2 Activity回传数据 3.5 Activity加载模式 3.6 本章小结 第4章 Intent (意图) 4.1 显式Intent 4.2 隐式Intent 4.2.1 IntentFilter 4.2.2 调用系统组件 4.3 本章小结 第5章 BroadcastReceiver (广播) 5.1 生命周期 5.2 广播类型 5.2.1 普通广播 5.2.2 有序广播 5.2.3 异步广播 5.3 系统广播应用 5.3.1 系统广播 5.3.2 开机启动程序 5.3.3 电量监测 5.4 本章小结 第6章 Service(服务) 6.1 生命周期 6.2 Binder机制 6.3 AIDL基础 6.3.1 定义AIDL接口 6.3.2 使用AIDL开发程序的一般步骤 6.3.3 实现远程控制计数器示例 6.4 AIDL深入练习 6.4.1 服务端实现 6.4.2 客户端实现 6.5 系统服务 6.5.1 获得系统服务 6.5.2 获取屏幕分辨率 6.5.3 剪贴板服务 6.5.4 电话服务 6.5.5 定时提醒服务 6.5.6 音频服务 6.5.7 传感器服务 6.5.8 位置服务 6.6 本章小结 第三篇 Android开发基础详解 第7章 常用控件 7.1 Button(按钮) 7.2 ImageButton(图片按钮) 7.3 ToggleButton(开关按钮) 7.4 TextView(文本视图) 7.5 ImageView(图片视图) 7.6 EditText(编辑框) 7.7 RadioButton(单选按钮) 7.8 CheckBox(多选框) 7.9 Spinner(下拉列表) 7.10 AutoCompleteTextview(自动完成) 7.11 DataPicker&TimePicker;(日期&时间) 7.12 ProgressBar (进度条) 7.12.1 横向进度条 7.12.2 旋转型进度条 7.13 SeekBar (拖动条) 7.14 RatingBar(评分条) 7.15 本章小结 第8章 界面布局 8.1 基本布局 8.1.1 线性布局 8.1.2 相对布局 8.1.3 绝对布局 8.1.4 Frame布局 8.1.5 表格布局 8.2 Tab布局 8.3 ScrollView 8.4 ListView(列表) 8.4.1 继承自ListActivity 8.4.2 ListView灵活运用 8.5 GirdView(网格) 8.6 WebView(网页) 8.6.1 WebView加载web页面 8.6.2 WebView中对JavaScript的支持 8.7 Gallery (画廊) 8.8 SlidingDrawer(滑动抽屉) 8.9 Dialog(对话框) 8.9.1 AlertDialog警告框 8.9.2 ProgressDialog进度框 8.9.3 DatePickerDialog & TimePickerDialog(日期时间选择框) 8.10 Menu(菜单) 8.10.1 options menu 8.10.2 contex
Android Binder机制是Android系统中一种进程间通信(IPC)的机制,用于在不同进程之间进行数据交换和通信。通过Binder机制,Android应用程序可以实现进程间的数据共享和相互调用。 Binder机制基于C/S架构,主要由服务端、客户端和Binder驱动组成。服务端提供一个或多个服务,将其注册到Binder驱动中,并通过Binder对象发送和接收数据;客户端通过获取服务端的Binder对象,与其进行通信和交互;而Binder驱动负责管理Binder对象的创建、销毁和通信。 在Binder机制中,Binder对象充当了交互的桥梁。每个Binder对象都有一个唯一的标识符(具体是一个32位的整数),用于识别和查找对应的服务端。通过Binder对象,客户端和服务端可以进行方法调用、数据传输等操作。服务端通过Binder对象将数据发送给客户端,客户端通过Binder对象将数据传递给服务端。 Binder机制设计了多种数据结构来实现进程间通信,如BpBinder、BpRefBase、Parcel等。BpBinder负责处理进程间的通信,并通过Binder Proxy将方法调用转发给服务端;BpRefBase用于引用计数,确保对象在不再使用时能够正确释放;Parcel用于在进程间传递数据,可以实现序列化和反序列化。 总结来说,Android Binder机制是Android系统中一种进程间通信的机制,通过Binder对象实现不同进程之间的数据交换和通信。通过服务端、客户端和Binder驱动的协作,应用程序可以实现进程间的数据共享和相互调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值