前言
公司最近上马了Android 9和10的平台,我们也得哼哧哼哧的进行相关的开发。我只能说谷歌的工程师为了KPI考核对Android修改的老开心了,可苦了我们啊。这不今天在进行Android的静默安装的API封装,尼玛原来的相关接口都没有了。那么今天要说的就是在Android P上面怎么实施静默安装/卸载接口的封装。
一.开干
静默安装时比较高的权限,一般应用是不能的,所以必须具备system权限,这个是前提。好了不多说啥了,直接上代码。
1.1 实例代码
package com.pax.android9_api;
// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageInstaller;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class InstallApkSessionApi extends Activity {
private static final String PACKAGE_INSTALLED_ACTION =
"com.pax.install";
private static final String PACKAGE_UNINSTALLED_ACTION =
"com.pax.uninstall";
private static final String TAG = "install";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_apk_session_api);
// Watch for button clicks.
Button button = (Button) findViewById(R.id.install);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
PackageInstaller.Session session = null;
try {
//获取PackageInstaller对象
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
//创建一个Session
int sessionId = packageInstaller.createSession(params);
//建立和PackageManager的socket通道,Android中的通信不仅仅有Binder还有很多其它的
session = packageInstaller.openSession(sessionId);
//将App的内容通过session传输
addApkToInstallSession("HelloActivity.apk", session);
// Create an install status receiver.
Context context = InstallApkSessionApi.this;
Intent intent = new Intent(context, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
//开启安装
session.commit(statusReceiver);
} catch (IOException e) {
throw new RuntimeException("Couldn't install package", e);
} catch (RuntimeException e) {
if (session != null) {
session.abandon();
}
throw e;
}
}
});
Button uninstall = (Button)findViewById(R.id.uninstall);
uninstall.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
uninstall("com.example.android.helloactivity");
}
});
}
private void addApkToInstallSession(String assetName, PackageInstaller.Session session)
throws IOException {
// It's recommended to pass the file size to openWrite(). Otherwise installation may fail
// if the disk is almost full.
try (OutputStream packageInSession = session.openWrite("package", 0, -1);
InputStream is = getAssets().open(assetName)) {
byte[] buffer = new byte[16384];
int n;
while ((n = is.read(buffer)) >= 0) {
packageInSession.write(buffer, 0, n);
}
}
}
// Note: this Activity must run in singleTop launchMode for it to be able to receive the intent
// in onNewIntent().
//此处一定要运行单例模式或者singleTop模式,否则会一直创建该Activity
@Override
protected void onNewIntent(Intent intent) {
Bundle extras = intent.getExtras();
Log.e(TAG, intent.toString());
if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
Log.e(TAG, intent.getAction());
int status = extras.getInt(PackageInstaller.EXTRA_STATUS);
String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
switch (status) {
case PackageInstaller.STATUS_PENDING_USER_ACTION:
// This test app isn't privileged, so the user has to confirm the install.
Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
startActivity(confirmIntent);
break;
case PackageInstaller.STATUS_SUCCESS:
Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
Log.e(TAG,"Install succeeded!");
break;
case PackageInstaller.STATUS_FAILURE:
case PackageInstaller.STATUS_FAILURE_ABORTED:
case PackageInstaller.STATUS_FAILURE_BLOCKED:
case PackageInstaller.STATUS_FAILURE_CONFLICT:
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
case PackageInstaller.STATUS_FAILURE_INVALID:
case PackageInstaller.STATUS_FAILURE_STORAGE:
Toast.makeText(this, "Install failed! " + status + ", " + message,
Toast.LENGTH_SHORT).show();
Log.e(TAG,"Install failed! " + status + ", " + message);
break;
default:
Toast.makeText(this, "Unrecognized status received from installer: " + status,
Toast.LENGTH_SHORT).show();
Log.e(TAG,"Unrecognized status received from installer: " + status);
}
}
else if(PACKAGE_UNINSTALLED_ACTION.equals(intent.getAction())){
Log.e(TAG, intent.getAction());
int status = extras.getInt(PackageInstaller.EXTRA_STATUS);
String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
switch (status) {
case PackageInstaller.STATUS_PENDING_USER_ACTION:
// This test app isn't privileged, so the user has to confirm the install.
Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
startActivity(confirmIntent);
break;
case PackageInstaller.STATUS_SUCCESS:
Toast.makeText(this, "Uninstall succeeded!", Toast.LENGTH_SHORT).show();
Log.e(TAG,"Uninstall succeeded!");
break;
case PackageInstaller.STATUS_FAILURE:
case PackageInstaller.STATUS_FAILURE_ABORTED:
case PackageInstaller.STATUS_FAILURE_BLOCKED:
case PackageInstaller.STATUS_FAILURE_CONFLICT:
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
case PackageInstaller.STATUS_FAILURE_INVALID:
case PackageInstaller.STATUS_FAILURE_STORAGE:
Toast.makeText(this, "Install failed! " + status + ", " + message,
Toast.LENGTH_SHORT).show();
Log.e(TAG,"Uninstall failed! " + status + ", " + message);
break;
default:
Toast.makeText(this, "Unrecognized status received from installer: " + status,
Toast.LENGTH_SHORT).show();
Log.e(TAG,"Unrecognized status received from installer: " + status);
}
}
}
public void uninstall(String packageName) {
Intent broadcastIntent = new Intent(this, InstallApkSessionApi.class);
broadcastIntent.setAction(PACKAGE_UNINSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
}
}
1.2 代码分析
好了实例代码,已经编写OK,让我们简单分析一下,及其步骤:
安装流程
卸载流程:比较简单就不细述了。
这里需要注意的是这个测试Activiyt的Mode必须是此处一定要运行单例模式或者singleTop模式,否则会一直创建该Activity。
1.3 运行实例演示
运行实例,静默安装和卸载成功。
msm8953_64:/ # logcat -s install
--------- beginning of main
--------- beginning of system
01-15 02:10:45.258 7087 7087 E install : Intent { act=com.pax.install flg=0x10000000 cmp=com.pax.android9_api/.InstallApkSessionApi (has extras) }
01-15 02:10:45.259 7087 7087 E install : com.pax.install
01-15 02:10:45.304 7087 7087 E install : Install succeeded!
01-15 02:10:48.340 7087 7087 E install : Intent { act=com.pax.uninstall flg=0x10000000 cmp=com.pax.android9_api/.InstallApkSessionApi (has extras) }
01-15 02:10:48.340 7087 7087 E install : com.pax.uninstall
01-15 02:10:48.372 7087 7087 E install : Uninstall succeeded!
总结
文章至此,关于Android P静默安装和卸载的封装就结束了,如果想更详细的了解那么就只能跟读Android FrameWork的源码了。我也在跟进中,各位如果有兴趣也可以一起聊聊。
作者:进阶的凯子