[转]Android 应用自动更新及6.0,7.0,8.0适配安装
原贴:https://www.jianshu.com/p/ea42040c7ace
原贴:https://www.jianshu.com/p/ea42040c7ace
原贴:https://www.jianshu.com/p/ea42040c7ace
在线更新分为以下几个步骤:
, 通过接口获取线上版本号,versionCode
, 比较线上的versionCode 和本地的versionCode,弹出更新窗口
, 下载APK文件(文件下载)
,安装APK
首先创建 UpdateDownloadListener 接口类对下载的不同状态回调:
/**
* @desciption: 下载不同状态接口回调
*/
public interface UpdateDownloadListener {
/**
* 下载请求开始回调
*/
void onStart(); /**
* 请求成功,下载前的准备回调
*
* @param contentLength 文件长度
* @param downloadUrl 下载地址
*/
void onPrepared(long contentLength, String downloadUrl); /**
* 进度更新回调
*
* @param progress 进度
* @param downloadUrl 下载地址
*/
void onProgressChanged(int progress, String downloadUrl); /**
* 下载暂停回调
*
* @param progress 进度
* @param completeSize 已下载的文件大小
* @param downloadUrl 下载地址
*/
void onPaused(int progress, int completeSize, String downloadUrl); /**
* 下载完成回调
*
* @param completeSize 已下载的文件大小
* @param downloadUrl 下载地址
*/
void onFinished(int completeSize, String downloadUrl); /**
* 下载失败回调
*/
void onFailure();
}
然后创建 UpdateDownloadRequest 类负责处理文件的下载和线程间的通信:
/**
* @desciption: 负责处理文件的下载和线程间的通信
*/
public class UpdateDownloadRequest implements Runnable {
/**
* 开始下载的位置
*/
private int startPos = 0;
/**
* 下载地址
*/
private String downloadUrl;
/**
* 文件保存路径
*/
private String localFilePath;
/**
* 事件回调
*/
private UpdateDownloadListener mDownloadListener;
private DownloadResponseHandler mDownloadHandler;
/**
* 下载标志位
*/
private boolean isDownloading = false;
/**
* 文件长度
*/
private long mContentLength; public UpdateDownloadRequest(String downloadUrl, String localFilePath, UpdateDownloadListener downloadListener) {
this.downloadUrl = downloadUrl;
this.localFilePath = localFilePath;
mDownloadListener = downloadListener;
mDownloadHandler = new DownloadResponseHandler();
isDownloading = true;
} @Override
public void run() {
try {
makeRequest();
} catch (IOException e) {
if (mDownloadHandler != null) {
mDownloadHandler.sendFailureMessage(FailureCode.IO);
}
} catch (InterruptedException e) {
if (mDownloadHandler != null) {
mDownloadHandler.sendFailureMessage(FailureCode.Interrupted);
}
}
} /**
* 建立连接
*/
private void makeRequest() throws IOException, InterruptedException {
if (!Thread.currentThread().isInterrupted()) {
try {
URL url = new URL(downloadUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Range", "bytes=" + startPos + "-");
connection.setRequestProperty("Connection", "Keep-Alive");
//阻塞当前线程
connection.connect();
mContentLength = connection.getContentLength();
if (!Thread.currentThread().isInterrupted()) {
if (mDownloadHandler != null) {
//完成文件下载,取得与远程文件的流
mDownloadHandler.sendResponseMessage(connection.getInputStream());
}
}
} catch (IOException e) {
if (!Thread.currentThread().isInterrupted()) {
throw e;
} }
}
} public boolean isDownloading() {
return isDownloading;
} public void stopDownloading() {
isDownloading = false;
} /**
* 下载过程中所有可能出现的异常情况
*/
public enum FailureCode {
//
UnknownHost,
Socket,
SocketTimeout,
ConnectTimeout,
IO,
HttpResponse,
JSON,
Interrupted
} /**
* 下载文件,并发送消息和回调接口
*/
public class DownloadResponseHandler { protected static final int SUCCESS_MESSAGE = 0;
protected static final int FAILURE_MESSAGE = 1;
protected static final int START_MESSAGE = 2;
protected static final int FINISH_MESSAGE = 3;
protected static final int NETWORK_OFF = 4;
protected static final int PROGRESS_CHANGED = 5;
protected static final int PAUSED_MESSAGE = 6; private Handler mHandler;
private int mCompleteSize = 0;
private int progress = 0; public DownloadResponseHandler() {
if (Looper.myLooper() != null) {
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handleSelfMessage(msg);
}
};
}
} /**
* 发送暂停消息
*/
private void sendPausedMessage() {
sendMessage(obtainMessage(PAUSED_MESSAGE, null));
} /**
* 发送失败消息
*/
protected void sendFailureMessage(FailureCode failureCode) {
sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{failureCode}));
} private void sendMessage(Message message) {
if (mHandler != null) {
mHandler.sendMessage(message);
} else {
handleSelfMessage(message);
}
} private Message obtainMessage(int responseMessage, Object response) {
Message msg;
if (mHandler != null) {
msg = mHandler.obtainMessage(responseMessage, response);
} else {
msg = Message.obtain();
msg.what = responseMessage;
msg.obj = response;
}
return msg;
} private void handleSelfMessage(Message message) {
Object[] response;
switch (message.what) {
default:
break;
case FAILURE_MESSAGE:
response = (Object[]) message.obj;
handleFailureMessage((FailureCode) response[0]);
break;
case PROGRESS_CHANGED:
response = (Object[]) message.obj;
handleProgressChangedMessage((Integer) response[0]);
break;
case PAUSED_MESSAGE:
handlePausedMessage();
break;
case FINISH_MESSAGE:
onFinish();
break;
}
} /**
* 失败消息的处理逻辑
*/
protected void handleFailureMessage(FailureCode failureCode) {
onFailure(failureCode);
} /**
* 进度改变消息的处理逻辑
*/
protected void handleProgressChangedMessage(int progress) {
mDownloadListener.onProgressChanged(progress, "");
} /**
* 暂停消息的处理逻辑
*/
protected void handlePausedMessage() {
mDownloadListener.onPaused(progress, mCompleteSize, "");
} /**
* 外部接口完成的回调
*/
public void onFinish() {
mDownloadListener.onFinished(mCompleteSize, "");
} /**
* 外部接口失败的回调
*/
public void onFailure(FailureCode failureCode) {
mDownloadListener.onFailure();
} /**
* 文件下载方法,发送各种类型的事件
*/
public void sendResponseMessage(InputStream inputStream) {
//文件读写流
RandomAccessFile randomAccessFile = null;
mCompleteSize = 0;
try {
byte[] buffer = new byte[1024];
int length = -1;
int limit = 0;
randomAccessFile = new RandomAccessFile(localFilePath, "rwd");
randomAccessFile.seek(startPos);
boolean isPaused = false;
while ((length = inputStream.read(buffer)) != -1) {
if (isDownloading) {
randomAccessFile.write(buffer, 0, length);
mCompleteSize += length;
if ((startPos + mCompleteSize) < (mContentLength + startPos)) {
progress = (int) (Float.parseFloat(getToPointFloatStr(
(float) (startPos + mCompleteSize) / (mContentLength + startPos))) * 100);
//限制notification更新频率
if (limit % 30 == 0 || progress == 100) {
//在子线程中读取流数据,后转发到主线程中去。
sendProgressChangedMessage(progress);
}
}
limit++;
} else {
isPaused = true;
sendPausedMessage();
break;
}
}
stopDownloading();
if (!isPaused) {
sendFinishMessage();
}
} catch (IOException e) {
sendPausedMessage();
stopDownloading();
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
stopDownloading();
e.printStackTrace();
} }
} /**
* 数字格式化
*/
private String getToPointFloatStr(float value) {
DecimalFormat format = new DecimalFormat("0.00");
return format.format(value);
} /**
* 发送进度改变消息
*/
private void sendProgressChangedMessage(int progress) {
sendMessage(obtainMessage(PROGRESS_CHANGED, new Object[]{progress}));
} /**
* 发送完成消息
*/
protected void sendFinishMessage() {
sendMessage(obtainMessage(FINISH_MESSAGE, null));
}
}
}
然后创建 UpdateManager 类下载调度管理器,调用UpdateDownloadRequest:
/**
* @desciption: 下载调度管理器,调用UpdateDownloadRequest
*/
public class UpdateManager {
/**
* 线程池
*/
private ExecutorService mExecutorService;
private UpdateDownloadRequest mDownloadRequest; private UpdateManager() {
//创建cache线程池
mExecutorService = Executors.newCachedThreadPool();
} public static UpdateManager getInstance() {
return Holder.INSTANCE;
} public void startDownload(String downloadUrl, String localFilePath, UpdateDownloadListener listener) {
if (mDownloadRequest != null && mDownloadRequest.isDownloading()) {
return;
}
checkLocalFilePath(localFilePath);
mDownloadRequest = new UpdateDownloadRequest(downloadUrl, localFilePath, listener);
Future<?> future = mExecutorService.submit(mDownloadRequest);
new WeakReference<Future<?>>(future);
} /**
* 检查文件路径
*/
private void checkLocalFilePath(String localFilePath) {
File path = new File(localFilePath.substring(0,
localFilePath.lastIndexOf("/") + 1));
File file = new File(localFilePath);
if (!path.exists()) {
path.mkdirs();
} if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
} public interface InstallPermissionListener {
/**
* 权限申请成功
*/
void permissionSuccess(); /**
* 权限申请失败
*/
void permissionFail();
} private static class Holder {
private static final UpdateManager INSTANCE = new UpdateManager();
}
}
最后通过 Service 启动下载,创建 UpdateService 类:
/**
* @desciption: 应用更新组件入口,用来启动下载器并更新Notification
*/
public class UpdateService extends Service { public static final String APK_URL="apk_url";
/**
* 文件存放路径
*/
private String filePath;
/**
* 文件下载地址
*/
private String apkUrl; private NotificationUtils mNotificationUtils; @Override
public void onCreate() {
super.onCreate();
filePath = Environment.getExternalStorageDirectory() + "/videobusiness/videobusiness.apk";
mNotificationUtils = new NotificationUtils(this);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
apkUrl = intent.getStringExtra(APK_URL);
notifyUser(getString(R.string.update_download_start), 0);
startDownload();
return super.onStartCommand(intent, flags, startId);
} @Override
public IBinder onBind(Intent intent) {
return null;
} private void notifyUser(String content, int progress) {
mNotificationUtils.sendNotificationProgress(getString(R.string.app_name), content, progress, progress >= 100 ? getContentIntent() : PendingIntent.getActivity(this, 0,
new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
} private void startDownload() {
UpdateManager.getInstance().startDownload(apkUrl, filePath, new UpdateDownloadListener() {
@Override
public void onStart() { } @Override
public void onPrepared(long contentLength, String downloadUrl) { } @Override
public void onProgressChanged(int progress, String downloadUrl) {
notifyUser(getString(R.string.update_download_processing), progress);
} @Override
public void onPaused(int progress, int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_failed), progress);
deleteApkFile();
//停止服务自身
stopSelf();
} @Override
public void onFinished(int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_finish), 100);
//停止服务自身
stopSelf();
startActivity(getInstallApkIntent());
} @Override
public void onFailure() {
notifyUser(getString(R.string.update_download_failed), 0);
deleteApkFile();
//停止服务自身
stopSelf();
}
});
} private PendingIntent getContentIntent() {
return PendingIntent.getActivity(this, 0,
getInstallApkIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
} private Intent getInstallApkIntent() {
File apkFile = new File(filePath);
// 通过Intent安装APK文件
final Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.addCategory(Intent.CATEGORY_DEFAULT);
//兼容7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
//兼容8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls();
if (!hasInstallPermission) {
InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() {
@Override
public void permissionSuccess() {
installApk();
} @Override
public void permissionFail() {
Toast.makeText(UpdateService.this, "授权失败,无法安装应用", Toast.LENGTH_LONG).show();
}
};
Intent intent = new Intent(this, InstallPermissionActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}
} else {
installIntent.setDataAndType(Uri.parse("file://" + apkFile.getPath()),
"application/vnd.android.package-archive");
}
return installIntent;
} /**
* 8.0 权限获取后的安装
*/
private void installApk() {
File apkFile = new File(filePath);
// 通过Intent安装APK文件
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.addCategory(Intent.CATEGORY_DEFAULT);
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(installIntent);
} /**
* 删除无用apk文件
*/
private void deleteApkFile() {
File apkFile = new File(filePath);
if (apkFile.exists() && apkFile.isFile()) {
apkFile.delete();
}
}
}
我们是以通知的形式更新下载进度条,下面是封装的 Notification 类:
/**
* @desciption: 通知管理
*/
public class NotificationUtils extends ContextWrapper { public static final String CHANNEL_ID = "default";
private static final String CHANNEL_NAME = "Default Channel";
private static final String CHANNEL_DESCRIPTION = "this is default channel!";
private NotificationManager mManager; public NotificationUtils(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
} @TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
//是否绕过请勿打扰模式
channel.canBypassDnd();
//闪光灯
channel.enableLights(true);
//锁屏显示通知
channel.setLockscreenVisibility(VISIBILITY_SECRET);
//闪关灯的灯光颜色
channel.setLightColor(Color.RED);
//桌面launcher的消息角标
channel.canShowBadge();
//是否允许震动
channel.enableVibration(true);
//获取系统通知响铃声音的配置
channel.getAudioAttributes();
//获取通知取到组
channel.getGroup();
//设置可绕过 请勿打扰模式
channel.setBypassDnd(true);
//设置震动模式
channel.setVibrationPattern(new long[]{100, 100, 200});
//是否会有灯光
channel.shouldShowLights();
getManager().createNotificationChannel(channel);
} private NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
} /**
* 发送通知
*/
public void sendNotification(String title, String content) {
NotificationCompat.Builder builder = getNotification(title, content);
getManager().notify(1, builder.build());
} private NotificationCompat.Builder getNotification(String title, String content) {
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
} else {
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setPriority(PRIORITY_DEFAULT);
}
//标题
builder.setContentTitle(title);
//文本内容
builder.setContentText(content);
//小图标
builder.setSmallIcon(R.mipmap.ic_launcher);
//设置点击信息后自动清除通知
builder.setAutoCancel(true);
return builder;
} /**
* 发送通知
*/
public void sendNotification(int notifyId, String title, String content) {
NotificationCompat.Builder builder = getNotification(title, content);
getManager().notify(notifyId, builder.build());
} /**
* 发送带有进度的通知
*/
public void sendNotificationProgress(String title, String content, int progress, PendingIntent intent) {
NotificationCompat.Builder builder = getNotificationProgress(title, content, progress, intent);
getManager().notify(0, builder.build());
} /**
* 获取带有进度的Notification
*/
private NotificationCompat.Builder getNotificationProgress(String title, String content,
int progress, PendingIntent intent) {
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
} else {
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setPriority(PRIORITY_DEFAULT);
}
//标题
builder.setContentTitle(title);
//文本内容
builder.setContentText(content);
//小图标
builder.setSmallIcon(R.mipmap.ic_launcher);
//设置大图标,未设置时使用小图标代替,拉下通知栏显示的那个图标
//设置大图片 BitmpFactory.decodeResource(Resource res,int id) 根据给定的资源Id解析成位图
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
if (progress > 0 && progress < 100) {
//一种是有进度刻度的(false),一种是循环流动的(true)
//设置为false,表示刻度,设置为true,表示流动
builder.setProgress(100, progress, false);
} else {
//0,0,false,可以将进度条隐藏
builder.setProgress(0, 0, false);
builder.setContentText("下载完成");
}
//设置点击信息后自动清除通知
builder.setAutoCancel(true);
//通知的时间
builder.setWhen(System.currentTimeMillis());
//设置点击信息后的跳转(意图)
builder.setContentIntent(intent);
return builder;
}
}
使用Service 在manifest中注册
<!--服务-->
<service android:name=".service.update.UpdateService"/>
使用方式
Intent intent = new Intent(mContext, UpdateService.class);
//传递apk下载地址
intent.putExtra(UpdateService.APK_URL, apkurl);
mContext.startService(intent);
7.0 适配
在Android 7.0上,对文件的访问权限作出了修改,不能在使用file://格式的Uri 访问文件 ,Android 7.0提供 FileProvider,应该使用这个来获取apk地址,然后安装apk。如下进行简单的适配:
(1) 在res 目录下,新建一个xml 文件夹,在xml 下面创建一个文件provider_paths文件:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="" />
<external-files-path
name="Download"
path="" />
</paths>
(2) 在AndroidManifest.xml清单文件中申明Provider:
<!-- Android 7.0 照片、APK下载保存路径-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="packgeName.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
(3) Android 7.0上的文件地址获取:
Uri uri = FileProvider.getUriForFile(context,
"packageName.fileProvider",
new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), apkFile));
注意:把上面的 packageName 换成你自己的包名,把 apkFile 换成你自己的 apk 所在的文件。 File apkFile = new File(filePath); filePath 是apk存放路径。
适配Android 8.0:未知来源的应用权限
Android8.0以上,未知来源的应用是不可以通过代码来执行安装的(在sd卡中找找到apk,手动安装是可以的),未知应用安装权限的开关被除掉,取而代之的是未知来源应用的管理列表,需要列表里面开启你的应用的未知来源的安装权限。Google这么做是为了防止一开始正经的应用后来开始通过升级来做一些不合法的事情,侵犯用户权益。
1) 在清单文件中申明权限:REQUEST_INSTALL_PACKAGES
<!--8.0请求安装包权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
(2) 在代码中判断用户是否已经受过权限了,如果已经授权,可以直接安装,如果没有授权,则跳转到授权列表,让用户开启未知来源应用安装权限,开启后,再安装应用。
//兼容8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls();
if (!hasInstallPermission) {
InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() {
@Override
public void permissionSuccess() {
installApk();
} @Override
public void permissionFail() {
Toast.makeText(UpdateService.this, "授权失败,无法安装应用", Toast.LENGTH_LONG).show();
}
};
Intent intent = new Intent(this, InstallPermissionActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}
InstallPermissionActivity 代码如下:
/**
* @desciption: 兼容Android 8。0 APP 在线更新,权限申请界面
*/
public class InstallPermissionActivity extends BaseActivity { public static final int INSTALL_PACKAGES_REQUEST_CODE = 1;
public static UpdateManager.InstallPermissionListener sListener;
private AlertDialog mAlertDialog; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 弹窗
if (Build.VERSION.SDK_INT >= 26) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUEST_CODE);
} else {
finish();
}
} @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
default:
break;
case INSTALL_PACKAGES_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (sListener != null) {
sListener.permissionSuccess();
finish();
}
} else {
//startInstallPermissionSettingActivity();
showDialog();
}
break;
}
} private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_name);
builder.setMessage("为了正常升级 xxx APP,请点击设置按钮,允许安装未知来源应用,本功能只限用于 xxx APP版本升级");
builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onClick(DialogInterface dialogInterface, int i) {
startInstallPermissionSettingActivity();
mAlertDialog.dismiss();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (sListener != null) {
sListener.permissionFail();
}
mAlertDialog.dismiss();
finish();
}
});
mAlertDialog = builder.create();
mAlertDialog.setCancelable(false);
mAlertDialog.show();
} @RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity() {
//注意这个是8.0新API
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == INSTALL_PACKAGES_REQUEST_CODE && resultCode == RESULT_OK) {
// 授权成功
if (sListener != null) {
sListener.permissionSuccess();
}
} else {
// 授权失败
if (sListener != null) {
sListener.permissionFail();
}
}
finish();
} @Override
protected void onDestroy() {
super.onDestroy();
sListener = null;
}
}
注意:当通过Intent 跳转到未知应用授权列表的时候,一定要加上包名,这样就能直接跳转到你的app下,不然只能跳转到列表。
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity() {
//注意这个是8.0新API
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
这里把 InstallPermissionActivity 设置为透明:
<activity android:name=".activity.InstallPermissionActivity"
android:theme="@style/activity_translucent"/> <style name="activity_translucent" parent="AppTheme">
<item name="android:windowBackground">@color/translucent_background</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style> <color name="translucent_background">#00000000</color>
作者:wuchao226
链接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
作者:wuchao226
链接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
作者:wuchao226
链接:https://www.jianshu.com/p/ea42040c7ace
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
/**
* @desciption: 应用更新组件入口,用来启动下载器并更新Notification
*/
public class UpdateService extends Service {
public static final String APK_URL="apk_url";
/**
* 文件存放路径
*/
private String filePath;
/**
* 文件下载地址
*/
private String apkUrl;
private NotificationUtils mNotificationUtils;
@Override
public void onCreate() {
super.onCreate();
filePath = Environment.getExternalStorageDirectory() + "/videobusiness/videobusiness.apk";
mNotificationUtils = new NotificationUtils(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
apkUrl = intent.getStringExtra(APK_URL);
notifyUser(getString(R.string.update_download_start), 0);
startDownload();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void notifyUser(String content, int progress) {
mNotificationUtils.sendNotificationProgress(getString(R.string.app_name), content, progress, progress >= 100 ? getContentIntent() : PendingIntent.getActivity(this, 0,
new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
}
private void startDownload() {
UpdateManager.getInstance().startDownload(apkUrl, filePath, new UpdateDownloadListener() {
@Override
public void onStart() {
}
@Override
public void onPrepared(long contentLength, String downloadUrl) {
}
@Override
public void onProgressChanged(int progress, String downloadUrl) {
notifyUser(getString(R.string.update_download_processing), progress);
}
@Override
public void onPaused(int progress, int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_failed), progress);
deleteApkFile();
//停止服务自身
stopSelf();
}
@Override
public void onFinished(int completeSize, String downloadUrl) {
notifyUser(getString(R.string.update_download_finish), 100);
//停止服务自身
stopSelf();
startActivity(getInstallApkIntent());
}
@Override
public void onFailure() {
notifyUser(getString(R.string.update_download_failed), 0);
deleteApkFile();
//停止服务自身
stopSelf();
}
});
}
private PendingIntent getContentIntent() {
return PendingIntent.getActivity(this, 0,
getInstallApkIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
}
private Intent getInstallApkIntent() {
File apkFile = new File(filePath);
// 通过Intent安装APK文件
final Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.addCategory(Intent.CATEGORY_DEFAULT);
//兼容7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
//兼容8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasInstallPermission = this.getPackageManager().canRequestPackageInstalls();
if (!hasInstallPermission) {
InstallPermissionActivity.sListener = new UpdateManager.InstallPermissionListener() {
@Override
public void permissionSuccess() {
installApk();
}
@Override
public void permissionFail() {
Toast.makeText(UpdateService.this, "授权失败,无法安装应用", Toast.LENGTH_LONG).show();
}
};
Intent intent = new Intent(this, InstallPermissionActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}
} else {
installIntent.setDataAndType(Uri.parse("file://" + apkFile.getPath()),
"application/vnd.android.package-archive");
}
return installIntent;
}
/**
* 8.0 权限获取后的安装
*/
private void installApk() {
File apkFile = new File(filePath);
// 通过Intent安装APK文件
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.addCategory(Intent.CATEGORY_DEFAULT);
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(installIntent);
}
/**
* 删除无用apk文件
*/
private void deleteApkFile() {
File apkFile = new File(filePath);
if (apkFile.exists() && apkFile.isFile()) {
apkFile.delete();
}
}
}
[转]Android 应用自动更新及6.0,7.0,8.0适配安装的更多相关文章
- Android应用自动更新功能的实现!
Android应用自动更新功能的实现!http://blog.csdn.net/android_tutor/article/details/7015986 private static final i ...
- Android实现自动更新功能
Android实现自动更新功能 Android自动更新的功能可以使用第三方的SDK来实现,但是类似友盟,就不支持x86手机的自动更新,科大讯飞,弹窗是全局的,小米手机就会默认把弹窗权限关掉不允许弹出提 ...
- Android应用自动更新功能的代码实现
由于Android项目开源所致,市面上出现了N多安卓软件市场.为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增加了工作量.因此我们有必 ...
- 使用七牛云存储实现Android的自动更新
为了修复Bug或更新软件,我们通常需要实现自动更新,没有哪一个牛逼的人能够搞到每一个用户的机子去帮他们更新. 1.自动更新的流程 我们将了解一下自动更新的思路.既然软件要自动更新,那么它必须知道自己是 ...
- Android - 软件自动更新的实现
转自:http://blog.csdn.net/wwj_748/article/details/8195565 接触到一个很实用的技术,那就是软件自动更新.一般开发者是通过自行在应用平台添加更新版本的 ...
- Android 应用自动更新功能的代码
由于Android项目开源所致,市面上出现了N多安卓软件市场.为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增加了工作量.因此我们有必 ...
- Android应用自动更新功能的实现!!!
自动更新功能的实现原理,就是我们事先和后台协商好一个接口,我们在应用的主Activity里,去访问这个接口,如果需要更新,后台会返回一些数据(比如,提示语:最新版本的url等).然后我们给出提示框,用 ...
- Android应用自动更新功能的代码实现(转)
由于Android项目开源所致,市面上出现了N多安卓软件市场.为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增加了工作量.因此我们有必 ...
- [转]Android应用自动更新功能的代码实现
本文转自:http://www.cnblogs.com/coolszy/archive/2012/04/27/2474279.html 由于Android项目开源所致,市面上出现了N多安卓软件市场.为 ...
随机推荐
- Linux下文档与目录结构
目录分类 Linux目录结构的组织形式和Windows有很大的不同.首先Linux没有“盘(C盘.D盘.E盘)”的概念.已经建立文件系统的硬盘分区被挂载到某一个目录下,用户通过操作目录来实现磁盘读写. ...
- sqlserver 拼接字符串
SELECT CAST(USER_ID AS VARCHAR) + ',' FROM dbo.AUTH_USER FOR XML PATH('');
- 通过字节码分析Java异常处理机制
在上一次[https://www.cnblogs.com/webor2006/p/9691523.html]初步对异常表相关的概念进行了了解,先来回顾一下: 其源代码也贴一下: 下面来看一下jclas ...
- java- 泛型类到底是类还是接口,<T extends Comparable>的写法中为什么没有用implements
java-core P533 public static <T extands Comparable> T min(T[] a ) 这里产生了一个疑问,就是 泛型类的英文是generic ...
- C# GridView 的使用
1.GridView无代码分页排序: 1.AllowSorting设为True,aspx代码中是AllowSorting="True":2.默认1页10条,如果要修改每页条数,修改 ...
- koa常用api文档整理
koa 支持的配置 1.应用配置是 app 实例属性,目前支持的配置项如下 配置项名称 简介 app.name 应用名称(可选项) app.env 默认为 NODE_ENV 或者 developmen ...
- django 内置标签
1.autoescape 自动转义开关 官网:https://docs.djangoproject.com/en/2.2/ref/templates/builtins/ 作用:将 html 内容解析成 ...
- box-orient
box-orient 语法: box-orient:horizontal | vertical | inline-axis | block-axis 默认值:horizontal 适用于:伸缩盒容器大 ...
- 使用sysbench对MySQL进行压力测试
1.背景 出自percona公司,是一款多线程系统压测工具,可以根据影响数据库服务器性能的各种因素来评估系统的性能.例如,可以用来测试文件IO,操作系统调度器,内存分配和传输速度,POSIX线程以及 ...
- nc命令用法举
什么是nc nc是netcat的简写,有着网络界的瑞士军刀美誉.因为它短小精悍.功能实用,被设计为一个简单.可靠的网络工具 nc的作用 (1)实现任意TCP/UDP端口的侦听,nc可以作为server ...