IntentService+Notifcation實現應用app後臺下載完成後並安裝(已適配8.0)
阿新 • • 發佈:2019-02-02
更新———————————-
現在已相容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檔案,如下:
<?xml version="1.0" encoding="utf-8"?>
<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);
}
佈局也貼一下,根據自己的需求
<?xml version="1.0" encoding="utf-8"?>
<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");
}
}
}
}
最後不要忘記在清單檔案中註冊一下,