更新———————————-
现在已兼容8.0的通知栏显示,确保你的targetSdkVersion 是26或以上
以下为8.0的显示图片:
app的更新模块放在后台服务可以大大提高app的体验,采用IntentService这种google为我们封装好的用于执行服务中有网络操作的类并搭配Notification来实现一下(下载工具用的是自带的URLConnection,因为Retrofit+rxjava并没有提供进度的回调,网上也有很多改进的方案,可以自定义让retrofit+rxjava实现进度的回调,在此,没有采用)
效果图如下:(看了一下淘宝的更新,所以就成这样的界面了,)
代码:自定义的IntentService类
/**
* 更新包下载安装服务
*/
public class UpdateService extends IntentService {
private static final int NOTIFY_DOWNLOAD= 0;
private static final int NOTIFY_FINISH = 1;
private static final String PENDING_INSTALL_ACTION = "gaoxin.com.inforindustry.click.toinstall";
private Context mContext;
private String apkUrl;
public UpdateService() {
super("UpdateService");
}
private NotificationUtils notificationUtils;
private File downapkfile;
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case 0:
//正在下载中
RemoteViews contentView = notificationUtils.getNotification().contentView;
contentView.setTextViewText(R.id.notify_tv, "更新包下载中...");
contentView.setProgressBar(R.id.notify_progress_pb, 100, msg.arg1, false);
contentView.setTextViewText(R.id.notify_progress_tv,msg.arg1+"%");
// 更新UI
notificationUtils.getManager().notify(NOTIFY_DOWNLOAD,notificationUtils.getNotification());
break;
case 1:
notificationUtils.cancelNotification(NOTIFY_DOWNLOAD);
createNotification(NOTIFY_FINISH);
break;
}
return true;
}
});
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
apkUrl = intent.getStringExtra("apkurl");
handleActionFoo(apkUrl);
}
}
private void handleActionFoo(String param1) {
if(NetworkUtils.isConnected()){
createNotification(NOTIFY_DOWNLOAD);
try {
DownApk(param1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//发送消息进行更新进度条
public void sendMessage(int what,int mprogress) {
Message msg0 = mHandler.obtainMessage();
msg0.what = what;
msg0.arg1 = mprogress;
mHandler.sendMessage(msg0);
}
private void DownApk(String param1) {
int oldProcess = 0;
if (SDCardUtils.isSDCardEnable()){
downapkfile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/industry.apk");
try {
URL url = new URL(param1.trim());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection.getResponseCode() == 200){
InputStream inputStream = connection.getInputStream();
FileOutputStream fos = new FileOutputStream(downapkfile);
//总长度
int totalLength = connection.getContentLength();
//已下载的长度
int currentLength = 0;
byte[] bytes = new byte[512];
connection.connect();
int flag = 0;
while (flag < 100){
if (inputStream != null){
int read = inputStream.read(bytes);
if (read <= 0){
sendMessage(1,0);
break;
}else {
fos.write(bytes,0,read);
currentLength += read;
int mprogress = (int) ((currentLength*100)/totalLength);
if(oldProcess <= mprogress-5 ){
// 避免notifymanager ANR,每下载百分之5才进行通知一次
oldProcess =mprogress;
sendMessage(0, mprogress);
}
}
}
}
fos.close();
inputStream.close();
}
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void createNotification(int notifyId) {
switch (notifyId){
case NOTIFY_DOWNLOAD:
RemoteViews remoteViews = new RemoteViews(AppUtils.getAppPackageName(),R.layout.notify_custom_view_layout);
remoteViews.setTextViewText(R.id.notify_tv,"正在下载...");
remoteViews.setProgressBar(R.id.notify_progress_pb,100,0,false);
remoteViews.setTextViewText(R.id.notify_progress_tv,"0%");
notificationUtils.sendNotification(notifyId,"","",remoteViews,null);
break;
case NOTIFY_FINISH:
Intent intent = new Intent(getApplicationContext(),NotificationBroadCast.class);
intent.setAction(PENDING_INSTALL_ACTION);
intent.putExtra("notifyId",NOTIFY_FINISH);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,0,intent,PendingIntent.FLAG_ONE_SHOT);
notificationUtils.sendNotification(notifyId,"点击安装","更新包已下载完成",null,pendingIntent);
break;
default:
break;
}
}
@Override
public void onCreate() {
super.onCreate();
mContext = this;
//初始化通知窗口管理
notificationUtils = new NotificationUtils(getApplicationContext());
}
}
NotificationUtils,用来进行适配8.0系统
public class NotificationUtils extends ContextWrapper {
private NotificationManager mManager;
public static final String ANDROID_CHANNEL_ID = "com.gaoxin.industry.ANDROID";
public static final String ANDROID_CHANNEL_NAME = "ANDROID CHANNEL";
private Notification notification;
public NotificationUtils(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= 26){
createChannels();
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void createChannels() {
// create android channel
NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID,
ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
// Sets whether notifications posted to this channel should display notification lights
androidChannel.enableLights(true);
// Sets whether notification posted to this channel should vibrate.
androidChannel.enableVibration(true);
// Sets the notification light color for notifications posted to this channel
androidChannel.setLightColor(Color.GREEN);
// Sets whether notifications posted to this channel appear on the lockscreen or not
androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
getManager().createNotificationChannel(androidChannel);
}
public NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
@RequiresApi(api = Build.VERSION_CODES.O)
public Notification.Builder getAndroidChannelNotification(String title,String content) {
return new Notification.Builder(getApplicationContext(), ANDROID_CHANNEL_ID)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(android.R.drawable.stat_notify_more)
;
}
public NotificationCompat.Builder getNotification_25(String title, String content){
return new NotificationCompat.Builder(getApplicationContext())
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(android.R.drawable.stat_notify_more)
;
}
public void sendNotification(int id,String title, String content, RemoteViews remoteViews, PendingIntent intent){
if (Build.VERSION.SDK_INT>=26){
notification = getAndroidChannelNotification(title, content).setCustomContentView(remoteViews).setContentIntent(intent)
.build();
getManager().notify(id,notification);
}else{
notification = getNotification_25(title, content).setCustomContentView(remoteViews).setContentIntent(intent).build();
getManager().notify(id,notification);
}
}
public void cancelNotification(int id){
getManager().cancel(id);
}
public Notification getNotification(){
if (notification != null){
return notification;
}
return null;
}
}
需要注意的点: Notification必须设置smallIcon这个属性,否则会报错,
如果是下载完成后不希望自动安装,而是点击后进行安装,可以使用PendingIntent这个类来进行触发,否则点击后是没有什么效果的,必须传递意图Intent
Android 8.0在安装应用的时候需要权限
8.0系统设置中不再提供是否允许安装未知来源的应用这个选项,所以在进行安装的时候,一定注意这个权限,否则会报错
其他注意事项:清单文件中必须包含provider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
provider>
res文件夹下建立xml文件夹
在xml中新建file_paths.xml文件,如下:
<paths>
<external-path
name="external_storage_root"
path="." />
paths>
安装
public static void installApp(final File file, final String authority) {
if (!isFileExists(file)) return;
Utils.getApp().startActivity(IntentUtils.getInstallAppIntent(file, authority, true));
}
public static Intent getInstallAppIntent(final File file,
final String authority,
final boolean isNewTask) {
if (file == null) return null;
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri data;
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
data = Uri.fromFile(file);
} else {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
data = FileProvider.getUriForFile(Utils.getApp(), authority, file);
}
intent.setDataAndType(data, type);
return getIntent(intent, isNewTask);
}
布局也贴一下,根据自己的需求
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white_alpha_6">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/notify_download_iv"
android:src="@mipmap/ic_launcher_round"
android:layout_marginTop="12dp"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/notify_tv"
android:textColor="@color/black"
android:layout_marginTop="8dp"
android:layout_toRightOf="@+id/notify_download_iv"
android:text="正在下载..."/>
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/notify_progress_pb"
style="?android:attr/progressBarStyleHorizontal"
android:layout_below="@+id/notify_tv"
android:layout_toRightOf="@+id/notify_download_iv"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@color/black"
android:id="@+id/notify_progress_instruction_tv"
android:layout_below="@+id/notify_progress_pb"
android:text="客户端已经下载了"
android:layout_toRightOf="@+id/notify_download_iv"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:id="@+id/notify_progress_tv"
android:textColor="@color/black"
android:layout_toRightOf="@+id/notify_progress_instruction_tv"
android:layout_below="@+id/notify_progress_pb"
android:text="50%"
/>
RelativeLayout>
启动服务:
/**
* 传入url
*/
Intent intent = new Intent(MainActivity.this,UpdateService.class);
intent.putExtra("apkurl", updataVersionUrl);
startService(intent);
下载完成后,点击事件的处理,采用了BroadCastReceiver方式
public class NotificationBroadCast extends BroadcastReceiver {
private File downFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/industry.apk");
private static final String INSTALL_ACTION = "包名.click.toinstall";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(INSTALL_ACTION)){
int notifyId = intent.getIntExtra("notifyId", 0);
NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
manager.cancel(notifyId);
if (FileUtils.isFileExists(downFile) && downFile.length() > 0) {
AppUtils.installApp(downFile, "gaoxin.com.inforindustry.fileprovider");
}
}
}
}
最后不要忘记在清单文件中注册一下,